forked from lijiext/lammps
486 lines
18 KiB
C++
486 lines
18 KiB
C++
// ATC headers
|
|
#include "ATC_TransferKernel.h"
|
|
#include "ATC_Error.h"
|
|
#include "FE_Engine.h"
|
|
#include "KernelFunction.h"
|
|
#include "LammpsInterface.h"
|
|
#include "Quadrature.h"
|
|
#include "PerPairQuantity.h"
|
|
#include "Stress.h"
|
|
#ifdef HAS_DXA
|
|
#include "DislocationExtractor.h"
|
|
#endif
|
|
|
|
// Other Headers
|
|
#include <vector>
|
|
#include <map>
|
|
#include <set>
|
|
#include <utility>
|
|
#include <fstream>
|
|
#include <exception>
|
|
|
|
using namespace std;
|
|
|
|
namespace ATC {
|
|
|
|
using ATC_Utility::to_string;
|
|
|
|
ATC_TransferKernel::ATC_TransferKernel(string groupName,
|
|
double **& perAtomArray,
|
|
LAMMPS_NS::Fix * thisFix,
|
|
string matParamFile)
|
|
: ATC_Transfer(groupName,perAtomArray,thisFix,matParamFile)
|
|
{
|
|
kernelBased_ = true;
|
|
}
|
|
|
|
//-------------------------------------------------------------------
|
|
ATC_TransferKernel::~ATC_TransferKernel()
|
|
{
|
|
}
|
|
|
|
//-------------------------------------------------------------------
|
|
bool ATC_TransferKernel::modify(int narg, char **arg)
|
|
{
|
|
bool match = false;
|
|
|
|
/*! \page man_hardy_kernel fix_modify AtC kernel
|
|
\section syntax
|
|
fix_modify AtC kernel <type> <parameters>
|
|
- type (keyword) = step, cell, cubic_bar, cubic_cylinder, cubic_sphere,
|
|
quartic_bar, quartic_cylinder, quartic_sphere \n
|
|
- parameters :\n
|
|
step = radius (double) \n
|
|
cell = hx, hy, hz (double) or h (double) \n
|
|
cubic_bar = half-width (double) \n
|
|
cubic_cylinder = radius (double) \n
|
|
cubic_sphere = radius (double) \n
|
|
quartic_bar = half-width (double) \n
|
|
quartic_cylinder = radius (double) \n
|
|
quartic_sphere = radius (double) \n
|
|
\section examples
|
|
<TT> fix_modify AtC kernel cell 1.0 1.0 1.0 </TT> \n
|
|
<TT> fix_modify AtC kernel quartic_sphere 10.0 </TT>
|
|
\section description
|
|
|
|
\section restrictions
|
|
Must be used with the hardy AtC fix \n
|
|
For bar kernel types, half-width oriented along x-direction \n
|
|
For cylinder kernel types, cylindrical axis is assumed to be in z-direction \n
|
|
( see \ref man_fix_atc )
|
|
\section related
|
|
\section default
|
|
No default
|
|
*/
|
|
|
|
// no match, call base class parser
|
|
if (!match) {
|
|
match = ATC_Transfer::modify(narg, arg);
|
|
}
|
|
|
|
return match;
|
|
|
|
}
|
|
|
|
//-------------------------------------------------------------------
|
|
void ATC_TransferKernel::compute_kernel_matrix_molecule(void) // KKM add
|
|
{
|
|
int nLocalMol = smallMoleculeSet_->local_molecule_count();
|
|
if (nLocal_>0) {
|
|
SPAR_MAT & N(kernelAccumulantMol_.set_quantity());
|
|
N.reset(nLocalMol,nNodes_);
|
|
SPAR_MAT & dN(kernelAccumulantMolGrad_.set_quantity());
|
|
dN.reset(nLocalMol,nNodes_);
|
|
DENS_VEC derivKer(nsd_);
|
|
DENS_VEC xI(nsd_),xm(nsd_),xmI(nsd_);
|
|
const DENS_MAT & centroidMolMatrix(moleculeCentroid_->quantity());
|
|
ATC::LammpsInterface::instance()->stream_msg_once("computing kernel matrix molecule ",true,false);
|
|
int heartbeatFreq = (nNodes_ <= 10 ? 1 : (int) nNodes_ / 10);
|
|
for (int i = 0; i < nNodes_; i++) {
|
|
if (i % heartbeatFreq == 0 ) {
|
|
ATC::LammpsInterface::instance()->stream_msg_once(".",false,false);
|
|
}
|
|
xI = (feEngine_->fe_mesh())->nodal_coordinates(i);
|
|
for (int j = 0; j < nLocalMol; j++) {
|
|
for (int k = 0; k < nsd_; k++) {
|
|
xm(k) = centroidMolMatrix(j,k);
|
|
}
|
|
xmI = xm - xI;
|
|
lammpsInterface_->periodicity_correction(xmI.ptr());
|
|
double val = kernelFunction_->value(xmI);
|
|
if (val > 0) N.add(j,i,val);
|
|
kernelFunction_->derivative(xmI,derivKer);
|
|
double val_grad = derivKer(2);
|
|
if (val_grad!= 0) dN.add(j,i,val_grad);
|
|
}
|
|
}
|
|
// reset kernelShpFunctions with the weights of molecules on processors
|
|
DENS_VEC fractions(N.nRows());
|
|
DENS_VEC fractions_deriv(dN.nRows());
|
|
for (int i = 0; i < nLocalMol; i++) {
|
|
fractions(i) = smallMoleculeSet_->local_fraction(i);
|
|
}
|
|
N.row_scale(fractions);
|
|
N.compress();
|
|
dN.row_scale(fractions);
|
|
dN.compress();
|
|
if (lammpsInterface_->rank_zero()) {
|
|
ATC::LammpsInterface::instance()->stream_msg_once("done",false,true);
|
|
}
|
|
}
|
|
}
|
|
|
|
//-------------------------------------------------------------------
|
|
void ATC_TransferKernel::compute_projection(const DENS_MAT & atomData,
|
|
DENS_MAT & nodeData)
|
|
{
|
|
DENS_MAT workNodeArray(nNodes_, atomData.nCols());
|
|
workNodeArray.zero();
|
|
nodeData.reset(workNodeArray.nRows(),workNodeArray.nCols());
|
|
nodeData.zero();
|
|
|
|
if (nLocal_>0) {
|
|
set_xPointer();
|
|
DENS_VEC xI(nsd_),xa(nsd_),xaI(nsd_);
|
|
double val;
|
|
for (int i = 0; i < nNodes_; i++) {
|
|
xI = (feEngine_->fe_mesh())->nodal_coordinates(i);
|
|
for (int j = 0; j < nLocal_; j++) {
|
|
int lammps_j = internalToAtom_(j);
|
|
xa.copy(xPointer_[lammps_j],3);
|
|
xaI = xa - xI;
|
|
lammpsInterface_->periodicity_correction(xaI.ptr());
|
|
val = kernelFunction_->value(xaI);
|
|
if (val > 0) {
|
|
for (int k=0; k < atomData.nCols(); k++) {
|
|
workNodeArray(i,k) += val*atomData(j,k);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// accumulate across processors
|
|
int count = workNodeArray.nRows()*workNodeArray.nCols();
|
|
lammpsInterface_->allsum(workNodeArray.ptr(),nodeData.ptr(),count);
|
|
}
|
|
|
|
|
|
//-------------------------------------------------------------------
|
|
void ATC_TransferKernel::compute_bond_matrix()
|
|
{
|
|
atomicBondMatrix_=bondMatrix_->quantity();
|
|
}
|
|
|
|
//-------------------------------------------------------------------
|
|
// on-the-fly calculation of stress
|
|
void ATC_TransferKernel::compute_potential_stress(DENS_MAT& stress)
|
|
{
|
|
set_xPointer();
|
|
stress.zero();
|
|
// neighbor lists
|
|
int *numneigh = lammpsInterface_->neighbor_list_numneigh();
|
|
int **firstneigh = lammpsInterface_->neighbor_list_firstneigh();
|
|
double ** xatom = lammpsInterface_->xatom();
|
|
double lam1,lam2;
|
|
double bond_value;
|
|
// process differently for mesh vs translation-invariant kernels
|
|
ATC::LammpsInterface::instance()->stream_msg_once("computing potential stress: ",true,false);
|
|
int heartbeatFreq = (nNodes_ <= 10 ? 1 : (int) nNodes_ / 10);
|
|
// "normal" kernel functions
|
|
DENS_VEC xa(nsd_),xI(nsd_),xaI(nsd_),xb(nsd_),xbI(nsd_),xba(nsd_);
|
|
double kernel_inv_vol = kernelFunction_->inv_vol();
|
|
for (int i = 0; i < nNodes_; i++) {
|
|
if (i % heartbeatFreq == 0 ) {
|
|
ATC::LammpsInterface::instance()->stream_msg_once(".",false,false);
|
|
}
|
|
// point
|
|
xI = (feEngine_->fe_mesh())->nodal_coordinates(i);
|
|
if (!kernelFunction_->node_contributes(xI)) {
|
|
continue;
|
|
}
|
|
int inode = i;
|
|
for (int j = 0; j < nLocal_; j++) {
|
|
// second (neighbor) atom location
|
|
int lammps_j = internalToAtom_(j);
|
|
xa.copy(xPointer_[lammps_j],3);
|
|
// difference vector
|
|
xaI = xa - xI;
|
|
lammpsInterface_->periodicity_correction(xaI.ptr());
|
|
for (int k = 0; k < numneigh[lammps_j]; ++k) {
|
|
int lammps_k = firstneigh[lammps_j][k];
|
|
// first atom location
|
|
xb.copy(xPointer_[lammps_k],3);
|
|
// difference vector
|
|
xba = xb - xa;
|
|
xbI = xba + xaI;
|
|
kernelFunction_->bond_intercepts(xaI,xbI,lam1,lam2);
|
|
// compute virial
|
|
if (lam1 < lam2) {
|
|
bond_value
|
|
= kernel_inv_vol*(kernelFunction_->bond(xaI,xbI,lam1,lam2));
|
|
double delx = xatom[lammps_j][0] - xatom[lammps_k][0];
|
|
double dely = xatom[lammps_j][1] - xatom[lammps_k][1];
|
|
double delz = xatom[lammps_j][2] - xatom[lammps_k][2];
|
|
double rsq = delx*delx + dely*dely + delz*delz;
|
|
double fforce = 0;
|
|
lammpsInterface_->pair_force(lammps_j,lammps_k,rsq,fforce);
|
|
fforce *= 0.5; // dbl count
|
|
if (atomToElementMapType_ == LAGRANGIAN) {
|
|
double delX = xref_[lammps_j][0] - xref_[lammps_k][0];
|
|
double delY = xref_[lammps_j][1] - xref_[lammps_k][1];
|
|
double delZ = xref_[lammps_j][2] - xref_[lammps_k][2];
|
|
stress(inode,0) +=-delx*fforce*delX*bond_value;
|
|
stress(inode,1) +=-delx*fforce*delY*bond_value;
|
|
stress(inode,2) +=-delx*fforce*delZ*bond_value;
|
|
stress(inode,3) +=-dely*fforce*delX*bond_value;
|
|
stress(inode,4) +=-dely*fforce*delY*bond_value;
|
|
stress(inode,5) +=-dely*fforce*delZ*bond_value;
|
|
stress(inode,6) +=-delz*fforce*delX*bond_value;
|
|
stress(inode,7) +=-delz*fforce*delY*bond_value;
|
|
stress(inode,8) +=-delz*fforce*delZ*bond_value;
|
|
}
|
|
else { //EULERIAN
|
|
stress(inode,0) +=-delx*delx*fforce*bond_value;
|
|
stress(inode,1) +=-dely*dely*fforce*bond_value;
|
|
stress(inode,2) +=-delz*delz*fforce*bond_value;
|
|
stress(inode,3) +=-delx*dely*fforce*bond_value;
|
|
stress(inode,4) +=-delx*delz*fforce*bond_value;
|
|
stress(inode,5) +=-dely*delz*fforce*bond_value;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
ATC::LammpsInterface::instance()->stream_msg_once("done",false,true);
|
|
}
|
|
|
|
//-------------------------------------------------------------------
|
|
// on-the-fly calculation of the heat flux
|
|
void ATC_TransferKernel::compute_potential_heatflux(DENS_MAT& flux)
|
|
{
|
|
set_xPointer();
|
|
flux.zero();
|
|
// neighbor lists
|
|
int *numneigh = lammpsInterface_->neighbor_list_numneigh();
|
|
int **firstneigh = lammpsInterface_->neighbor_list_firstneigh();
|
|
double ** xatom = lammpsInterface_->xatom();
|
|
double ** vatom = lammpsInterface_->vatom();
|
|
|
|
double lam1,lam2;
|
|
double bond_value;
|
|
// process differently for mesh vs translation-invariant kernels
|
|
// "normal" kernel functions
|
|
DENS_VEC xa(nsd_),xI(nsd_),xaI(nsd_),xb(nsd_),xbI(nsd_),xba(nsd_);
|
|
double kernel_inv_vol = kernelFunction_->inv_vol();
|
|
for (int i = 0; i < nNodes_; i++) {
|
|
int inode = i;
|
|
// point
|
|
xI = (feEngine_->fe_mesh())->nodal_coordinates(i);
|
|
if (!kernelFunction_->node_contributes(xI)) {
|
|
continue;
|
|
}
|
|
for (int j = 0; j < nLocal_; j++) {
|
|
int lammps_j = internalToAtom_(j);
|
|
xa.copy(xPointer_[lammps_j],3);
|
|
// difference vector
|
|
xaI = xa - xI;
|
|
lammpsInterface_->periodicity_correction(xaI.ptr());
|
|
for (int k = 0; k < numneigh[lammps_j]; ++k) {
|
|
int lammps_k = firstneigh[lammps_j][k];
|
|
// first atom location
|
|
xb.copy(xPointer_[lammps_k],3);
|
|
// difference vector
|
|
xba = xb - xa;
|
|
xbI = xba + xaI;
|
|
kernelFunction_->bond_intercepts(xaI,xbI,lam1,lam2);
|
|
// compute virial
|
|
if (lam1 < lam2) {
|
|
bond_value
|
|
= kernel_inv_vol*(kernelFunction_->bond(xaI,xbI,lam1,lam2));
|
|
double delx = xatom[lammps_j][0] - xatom[lammps_k][0];
|
|
double dely = xatom[lammps_j][1] - xatom[lammps_k][1];
|
|
double delz = xatom[lammps_j][2] - xatom[lammps_k][2];
|
|
double rsq = delx*delx + dely*dely + delz*delz;
|
|
double fforce = 0;
|
|
lammpsInterface_->pair_force(lammps_j,lammps_k,rsq,fforce);
|
|
fforce *= 0.5; // dbl count
|
|
double * v = vatom[lammps_j];
|
|
fforce *= (delx*v[0] + dely*v[1] + delz*v[2]);
|
|
if (atomToElementMapType_ == LAGRANGIAN) {
|
|
double delX = xref_[lammps_j][0] - xref_[lammps_k][0];
|
|
double delY = xref_[lammps_j][1] - xref_[lammps_k][1];
|
|
double delZ = xref_[lammps_j][2] - xref_[lammps_k][2];
|
|
flux(inode,0) +=fforce*delX*bond_value;
|
|
flux(inode,1) +=fforce*delY*bond_value;
|
|
flux(inode,2) +=fforce*delZ*bond_value;
|
|
}
|
|
else { // EULERIAN
|
|
flux(inode,0) +=fforce*delx*bond_value;
|
|
flux(inode,1) +=fforce*dely*bond_value;
|
|
flux(inode,2) +=fforce*delz*bond_value;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//-------------------------------------------------------------------
|
|
// calculation of the dislocation density tensor
|
|
void ATC_TransferKernel::compute_dislocation_density(DENS_MAT & A)
|
|
{
|
|
A.reset(nNodes_,9);
|
|
#ifdef HAS_DXA
|
|
double cnaCutoff = lammpsInterface_->near_neighbor_cutoff();
|
|
// Extract dislocation lines within the processor's domain.
|
|
DXADislocationExtractor extractor(lammpsInterface_->lammps_pointer(),dxaExactMode_);
|
|
extractor.extractDislocations(lammpsInterface_->neighbor_list(), cnaCutoff);
|
|
|
|
// Calculate scalar dislocation density and density tensor.
|
|
double dislocationDensity = 0.0;
|
|
double dislocationDensityTensor[9] = {0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0};
|
|
|
|
|
|
const std::vector<DislocationSegment*>& segments = extractor.getSegments();
|
|
int localNumberLines = (int) segments.size();
|
|
int totalNumberLines;
|
|
lammpsInterface_->int_allsum(&localNumberLines,&totalNumberLines,1);
|
|
if (totalNumberLines == 0) {
|
|
ATC::LammpsInterface::instance()->print_msg_once("no dislocation lines found");
|
|
return;
|
|
}
|
|
|
|
// for output
|
|
int nPt = 0, nSeg = 0;
|
|
for(unsigned segmentIndex = 0; segmentIndex < segments.size(); segmentIndex++) {
|
|
DislocationSegment* segment = segments[segmentIndex];
|
|
const std::deque<Point3>& line = segment->line;
|
|
nPt += line.size();
|
|
nSeg += line.size()-1;
|
|
}
|
|
|
|
DENS_MAT segCoor(3,nPt);
|
|
Array2D<int> segConn(2,nSeg);
|
|
DENS_MAT segBurg(nPt,3);
|
|
|
|
|
|
DENS_MAT local_A(nNodes_,9);
|
|
local_A.zero();
|
|
DENS_VEC xa(nsd_),xI(nsd_),xaI(nsd_),xb(nsd_),xbI(nsd_),xba(nsd_);
|
|
double kernel_inv_vol = kernelFunction_->inv_vol();
|
|
int iPt = 0, iSeg= 0;
|
|
for(unsigned segmentIndex = 0; segmentIndex < segments.size(); segmentIndex++) {
|
|
|
|
DislocationSegment* segment = segments[segmentIndex];
|
|
const std::deque<Point3>& line = segment->line;
|
|
|
|
Vector3 burgers = segment->burgersVectorWorld;
|
|
Point3 x1, x2;
|
|
for(std::deque<Point3>::const_iterator p1 = line.begin(), p2 = line.begin() + 1; p2 < line.end(); ++p1, ++p2) {
|
|
x1 = (*p1);
|
|
x2 = (*p2);
|
|
Vector3 delta = x2 - x1;
|
|
// totals
|
|
dislocationDensity += Length(delta);
|
|
for(int i = 0; i < 3; i++) {
|
|
for(int j = 0; j < 3; j++) {
|
|
dislocationDensityTensor[3*j+i] += delta[i] * burgers[j];
|
|
}
|
|
}
|
|
// nodal partition
|
|
for(int k = 0; k < 3; k++) {
|
|
xa(k) = x1[k];
|
|
xb(k) = x2[k];
|
|
xba(k) = delta[k];
|
|
}
|
|
for (int I = 0; I < nNodes_; I++) {
|
|
xI = (feEngine_->fe_mesh())->nodal_coordinates(I);
|
|
if (!kernelFunction_->node_contributes(xI)) {
|
|
continue;
|
|
}
|
|
xaI = xa - xI;
|
|
lammpsInterface_->periodicity_correction(xaI.ptr());
|
|
xbI = xba + xaI;
|
|
double lam1=0,lam2=0;
|
|
kernelFunction_->bond_intercepts(xaI,xbI,lam1,lam2);
|
|
if (lam1 < lam2) {
|
|
double bond_value
|
|
= kernel_inv_vol*(kernelFunction_->bond(xaI,xbI,lam1,lam2));
|
|
local_A(I,0) += xba(0)*burgers[0]*bond_value;
|
|
local_A(I,1) += xba(0)*burgers[1]*bond_value;
|
|
local_A(I,2) += xba(0)*burgers[2]*bond_value;
|
|
local_A(I,3) += xba(1)*burgers[0]*bond_value;
|
|
local_A(I,4) += xba(1)*burgers[1]*bond_value;
|
|
local_A(I,5) += xba(1)*burgers[2]*bond_value;
|
|
local_A(I,6) += xba(2)*burgers[0]*bond_value;
|
|
local_A(I,7) += xba(2)*burgers[1]*bond_value;
|
|
local_A(I,8) += xba(2)*burgers[2]*bond_value;
|
|
}
|
|
}
|
|
segCoor(0,iPt) = x1[0];
|
|
segCoor(1,iPt) = x1[1];
|
|
segCoor(2,iPt) = x1[2];
|
|
segBurg(iPt,0) = burgers[0];
|
|
segBurg(iPt,1) = burgers[1];
|
|
segBurg(iPt,2) = burgers[2];
|
|
segConn(0,iSeg) = iPt;
|
|
segConn(1,iSeg) = iPt+1;
|
|
iPt++;
|
|
iSeg++;
|
|
}
|
|
segCoor(0,iPt) = x2[0];
|
|
segCoor(1,iPt) = x2[1];
|
|
segCoor(2,iPt) = x2[2];
|
|
segBurg(iPt,0) = burgers[0];
|
|
segBurg(iPt,1) = burgers[1];
|
|
segBurg(iPt,2) = burgers[2];
|
|
iPt++;
|
|
}
|
|
|
|
int count = nNodes_*9;
|
|
lammpsInterface_->allsum(local_A.ptr(),A.ptr(),count);
|
|
|
|
double totalDislocationDensity;
|
|
lammpsInterface_->allsum(&dislocationDensity,&totalDislocationDensity,1);
|
|
double totalDislocationDensityTensor[9];
|
|
lammpsInterface_->allsum(dislocationDensityTensor,totalDislocationDensityTensor,9);
|
|
int totalNumberSegments;
|
|
lammpsInterface_->int_allsum(&nSeg,&totalNumberSegments,1);
|
|
|
|
// output
|
|
double volume = lammpsInterface_->domain_volume();
|
|
stringstream ss;
|
|
ss << "total dislocation line length = " << totalDislocationDensity;
|
|
ss << " lines = " << totalNumberLines << " segments = " << totalNumberSegments;
|
|
ss << "\n ";
|
|
ss << "total dislocation density tensor = \n";
|
|
for(int i = 0; i < 3; i++) {
|
|
ss << " ";
|
|
for(int j = 0; j < 3; j++) {
|
|
totalDislocationDensityTensor[3*j+i] /= volume;
|
|
ss << totalDislocationDensityTensor[3*j+i] << " ";
|
|
}
|
|
ss << "\n";
|
|
}
|
|
ATC::LammpsInterface::instance()->print_msg_once(ss.str());
|
|
if (nSeg > 0) {
|
|
set<int> otypes;
|
|
otypes.insert(VTK);
|
|
otypes.insert(FULL_GNUPLOT);
|
|
string name = "dislocation_segments_step=" ;
|
|
name += to_string(output_index());
|
|
OutputManager segOutput(name,otypes);
|
|
segOutput.write_geometry(&segCoor,&segConn);
|
|
OUTPUT_LIST segOut;
|
|
segOut["burgers_vector"] = &segBurg;
|
|
segOutput.write_data(0,&segOut);
|
|
}
|
|
#else
|
|
throw ATC_Error("unimplemented function compute_dislocation_density (DXA support not included");
|
|
#endif
|
|
}
|
|
|
|
} // end namespace ATC
|
|
|