// ATC headers #include "ATC_TransferPartitionOfUnity.h" #include "ATC_Error.h" #include "FE_Engine.h" #include "LammpsInterface.h" #include "Quadrature.h" #include "PerPairQuantity.h" #ifdef HAS_DXA #include "DislocationExtractor.h" #endif // Other Headers #include #include #include #include #include #include using namespace std; static const int line_ngauss = 10; static double line_xg[line_ngauss], line_wg[line_ngauss]; namespace ATC { ATC_TransferPartitionOfUnity::ATC_TransferPartitionOfUnity( string groupName, double ** & perAtomArray, LAMMPS_NS::Fix * thisFix, string matParamFile) : ATC_Transfer(groupName,perAtomArray,thisFix,matParamFile) { ATC::Quadrature::instance()->set_line_quadrature(line_ngauss,line_xg,line_wg); // transform gauss points from [-1,1] to [0,1] double lam1 = 0.0, lam2 = 1.0; double del_lambda = 0.5*(lam2 - lam1); double avg_lambda = 0.5*(lam2 + lam1); for (int i = 0; i < line_ngauss; i++) { double lambda = del_lambda*line_xg[i] +avg_lambda; line_xg[i] = lambda; line_wg[i] *= 0.5; } } //------------------------------------------------------------------- ATC_TransferPartitionOfUnity::~ATC_TransferPartitionOfUnity() { // clear out all managed memory to avoid conflicts with dependencies on class member data interscaleManager_.clear(); } //------------------------------------------------------------------- void ATC_TransferPartitionOfUnity::compute_projection( const DENS_MAT & atomData, DENS_MAT & nodeData) { throw ATC_Error("unimplemented function"); } //------------------------------------------------------------------- void ATC_TransferPartitionOfUnity::compute_bond_matrix() { atomicBondMatrix_ = bondMatrix_->quantity(); } //------------------------------------------------------------------- // kinetic energy portion of stress /** * @class KineticTensor * @brief Class for computing the quantity - m v' (x) v' */ void ATC_TransferPartitionOfUnity::compute_kinetic_stress( DENS_MAT& stress) { compute_variation_velocity(); int * type = lammpsInterface_->atom_type(); double * mass = lammpsInterface_->atom_mass(); double * rmass = lammpsInterface_->atom_rmass(); double mvv2e = lammpsInterface_->mvv2e(); // [MV^2]-->[Energy] DENS_MAT & v = variationVelocity_; atomicTensor_.reset(nLocal_,6); for (int i = 0; i < nLocal_; i++) { int atomIdx = internalToAtom_(i); double ma = mass ? mass[type[atomIdx]]: rmass[atomIdx]; ma *= mvv2e; // convert mass to appropriate units atomicTensor_(i,0) -= ma*v(i,0)*v(i,0); atomicTensor_(i,1) -= ma*v(i,1)*v(i,1); atomicTensor_(i,2) -= ma*v(i,2)*v(i,2); atomicTensor_(i,3) -= ma*v(i,0)*v(i,1); atomicTensor_(i,4) -= ma*v(i,0)*v(i,2); atomicTensor_(i,5) -= ma*v(i,1)*v(i,2); } project_volume_normalized(atomicTensor_, stress); } //------------------------------------------------------------------- // on-the-fly calculation of stress void ATC_TransferPartitionOfUnity::compute_potential_stress(DENS_MAT& stress) { int nCols; if (atomToElementMapType_ == LAGRANGIAN) nCols = 9; else // EULERIAN nCols = 6; stress.reset(nNodes_,nCols); // neighbor lists int *numneigh = lammpsInterface_->neighbor_list_numneigh(); int **firstneigh = lammpsInterface_->neighbor_list_firstneigh(); double ** xatom = lammpsInterface_->xatom(); Array latticePeriodicity(3); latticePeriodicity(0) = (bool) periodicity[0]; latticePeriodicity(1) = (bool) periodicity[1]; latticePeriodicity(2) = (bool) periodicity[2]; // process differently for mesh vs translation-invariant kernels ATC::LammpsInterface::instance()->stream_msg_once("computing potential stress: ",true,false); int heartbeatFreq = (nLocal_ <= 10 ? 1 : (int) nLocal_ / 10); // mesh-based kernel functions int nodesPerElement = feEngine_->fe_mesh()->num_nodes_per_element(); Array node_list(nodesPerElement); DENS_VEC shp(nodesPerElement); DENS_VEC xa(nsd_),xb(nsd_),xab(nsd_),xlambda(nsd_); DENS_VEC virial(nCols); for (int j = 0; j < nLocal_; j++) { if (j % heartbeatFreq == 0 ) { ATC::LammpsInterface::instance()->stream_msg_once(".",false,false); } // first atom location int lammps_j = internalToAtom_(j); xa.copy(xPointer_[lammps_j],3); for (int k = 0; k < numneigh[lammps_j]; ++k) { int lammps_k = firstneigh[lammps_j][k]; //if (lammps_k < lammps_j) continue; // full neighbor list // second (neighbor) atom location xb.copy(xPointer_[lammps_k],3); 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; // 1/2 sum_ab = sum_(ab) 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]; virial[0] =-delx*fforce*delX; virial[1] =-delx*fforce*delY; virial[2] =-delx*fforce*delZ; virial[3] =-dely*fforce*delX; virial[4] =-dely*fforce*delY; virial[5] =-dely*fforce*delZ; virial[6] =-delz*fforce*delX; virial[7] =-delz*fforce*delY; virial[8] =-delz*fforce*delZ; } else {// EULERIAN virial[0] =-delx*delx*fforce; virial[1] =-dely*dely*fforce; virial[2] =-delz*delz*fforce; virial[3] =-delx*dely*fforce; virial[4] =-delx*delz*fforce; virial[5] =-dely*delz*fforce; } xab = xa - xb; for (int i = 0; i < line_ngauss; i++) { double lambda = line_xg[i]; xlambda = lambda*xab + xb; lammpsInterface_->periodicity_correction(xlambda.ptr()); feEngine_->shape_functions(xlambda,shp,node_list); // accumulate to nodes whose support overlaps the integration point for (int I = 0; I < nodesPerElement; I++) { int inode = node_list(I); double inv_vol = (accumulantInverseVolumes_->quantity())(inode,inode); double bond_value = inv_vol*shp(I)*line_wg[i]; for (int j = 0; j < nCols; j++) stress(inode,j) += virial[j]*bond_value; } } } } if (lammpsInterface_->comm_rank() == 0) { ATC::LammpsInterface::instance()->stream_msg_once("done",false,true); } } //------------------------------------------------------------------- // compute kinetic part of heat flux void ATC_TransferPartitionOfUnity::compute_kinetic_heatflux( DENS_MAT& flux) { compute_variation_velocity(); int * type = lammpsInterface_->atom_type(); double * mass = lammpsInterface_->atom_mass(); double * rmass = lammpsInterface_->atom_rmass(); double mvv2e = lammpsInterface_->mvv2e(); double * atomPE = lammpsInterface_->compute_pe_peratom(); double atomKE, atomEnergy; atomicVector_.reset(nLocal_,3); for (int i = 0; i < nLocal_; i++) { int atomIdx = internalToAtom_(i); double ma = mass ? mass[type[atomIdx]]: rmass[atomIdx]; ma *= mvv2e; // convert mass to appropriate units atomKE = 0.0; for (int j = 0; j < nsd_; j++) { atomKE += 0.5*ma*(variationVelocity_(i,j)*variationVelocity_(i,j)); } atomEnergy = atomKE + atomPE[atomIdx]; for (int j = 0; j < nsd_; j++) { atomicVector_(i,j) += atomEnergy*variationVelocity_(i,j); } } project_volume_normalized(atomicVector_,flux); } //------------------------------------------------------------------- // on-the-fly calculation of the heat flux void ATC_TransferPartitionOfUnity::compute_potential_heatflux(DENS_MAT& flux) { compute_variation_velocity(); flux.zero(); // neighbor lists int *numneigh = lammpsInterface_->neighbor_list_numneigh(); int **firstneigh = lammpsInterface_->neighbor_list_firstneigh(); double ** xatom = lammpsInterface_->xatom(); Array latticePeriodicity(3); latticePeriodicity(0) = (bool) periodicity[0]; latticePeriodicity(1) = (bool) periodicity[1]; latticePeriodicity(2) = (bool) periodicity[2]; // process differently for mesh vs translation-invariant kernels // mesh-based kernel functions int nodesPerElement = feEngine_->fe_mesh()->num_nodes_per_element(); Array node_list(nodesPerElement); DENS_VEC shp(nodesPerElement); DENS_VEC xa(nsd_),xb(nsd_),xab(nsd_),xlambda(nsd_); for (int j = 0; j < nLocal_; j++) { // first atom location int lammps_j = internalToAtom_(j); xa.copy(xPointer_[lammps_j],3); for (int k = 0; k < numneigh[lammps_j]; ++k) { int lammps_k = firstneigh[lammps_j][k]; // second (neighbor) atom location xb.copy(xPointer_[lammps_k],3); 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; // 1/2 sum_ab = sum_(ab) fforce *= (delx*variationVelocity_(j,0) + dely*variationVelocity_(j,1) + delz*variationVelocity_(j,2)); double flux_vec[3]; 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_vec[0] =fforce*delX; flux_vec[1] =fforce*delY; flux_vec[2] =fforce*delZ; } else {// EULERIAN flux_vec[0] =fforce*delx; flux_vec[1] =fforce*dely; flux_vec[2] =fforce*delz; } xab = xa - xb; for (int i = 0; i < line_ngauss; i++) { double lambda = line_xg[i]; xlambda = lambda*xab + xb; lammpsInterface_->periodicity_correction(xlambda.ptr()); feEngine_->shape_functions(xlambda,shp,node_list); // accumulate to nodes whose support overlaps the integration point for (int I = 0; I < nodesPerElement; I++) { int inode = node_list(I); double inv_vol = (accumulantInverseVolumes_->quantity())(inode,inode); double bond_value = inv_vol*shp(I)*line_wg[i]; flux(inode,0) += flux_vec[0]*bond_value; flux(inode,1) += flux_vec[1]*bond_value; flux(inode,2) += flux_vec[2]*bond_value; } } } } } //------------------------------------------------------------------- void ATC_TransferPartitionOfUnity::compute_variation_velocity() { // now compute v'_a = v_a - N_Ia v_I variationVelocity_.reset(nLocal_,nsd_); if (nLocal_>0) { // interpolate nodal velocities to the atoms vbar_.reset(nLocal_,nsd_); double ** v = lammpsInterface_->vatom(); PerAtomQuantity * vbar = interscaleManager_.per_atom_quantity(field_to_prolongation_name(VELOCITY)); if (!vbar) { DENS_MAN * nodeVelocity = interscaleManager_.dense_matrix(field_to_string(VELOCITY)); if (this->kernel_on_the_fly()) { vbar = new OnTheFlyShapeFunctionProlongation(this, nodeVelocity,this->atom_coarsegraining_positions()); } else { vbar = new FtaShapeFunctionProlongation(this, nodeVelocity,this->interpolant()); } interscaleManager_.add_per_atom_quantity(vbar, field_to_prolongation_name(VELOCITY)); } // use of prolong assumes atom system contained within mesh vbar_ = vbar->quantity(); // compute and store variation velocities of atoms for (int i = 0; i < nLocal_; i++) { int atomIdx = internalToAtom_(i); for (int j = 0; j < nsd_; j++) { variationVelocity_(i,j) = v[atomIdx][j] - vbar_(i,j); } } } } //------------------------------------------------------------------- // calculation of the dislocation density tensor void ATC_TransferPartitionOfUnity::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& 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& line = segment->line; nPt += line.size(); nSeg += line.size()-1; } DENS_MAT segCoor(3,nPt); Array2D segConn(2,nSeg); DENS_MAT segBurg(nPt,3); DENS_MAT local_A(nNodes_,9); local_A.zero(); Array latticePeriodicity(3); latticePeriodicity(0) = (bool) periodicity[0]; latticePeriodicity(1) = (bool) periodicity[1]; latticePeriodicity(2) = (bool) periodicity[2]; // mesh-based kernel functions int nodesPerElement = feEngine_->fe_mesh()->num_nodes_per_element(); Array node_list(nodesPerElement); DENS_VEC shp(nodesPerElement); DENS_VEC xa(nsd_),xb(nsd_),xba(nsd_),xlambda(nsd_); int iPt = 0, iSeg= 0; for(unsigned segmentIndex = 0; segmentIndex < segments.size(); segmentIndex++) { DislocationSegment* segment = segments[segmentIndex]; const std::deque& line = segment->line; Vector3 burgers = segment->burgersVectorWorld; Point3 x1, x2; for(std::deque::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 < line_ngauss; i++) { double lambda = line_xg[i]; xlambda = lambda*xba + xa; lammpsInterface_->periodicity_correction(xlambda.ptr()); feEngine_->shape_functions(xlambda,shp,node_list); // accumulate to nodes whose support overlaps the integration point for (int I = 0; I < nodesPerElement; I++) { int inode = node_list(I); double inv_vol = (accumulantInverseVolumes_->quantity())(inode,inode); double bond_value = inv_vol*shp(I)*line_wg[i]; local_A(inode,0) += xba(0)*burgers[0]*bond_value; local_A(inode,1) += xba(0)*burgers[1]*bond_value; local_A(inode,2) += xba(0)*burgers[2]*bond_value; local_A(inode,3) += xba(1)*burgers[0]*bond_value; local_A(inode,4) += xba(1)*burgers[1]*bond_value; local_A(inode,5) += xba(1)*burgers[2]*bond_value; local_A(inode,6) += xba(2)*burgers[0]*bond_value; local_A(inode,7) += xba(2)*burgers[1]*bond_value; local_A(inode,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()); ss.str(""); DENS_VEC A_avg(9); for (int i = 0; i < nNodes_; i++) { for (int j = 0; j < 9; j++) { A_avg(j) += A(i,j); } } A_avg /= nNodes_; ss << "average nodal dislocation density tensor = \n"; ss << A_avg(0) << " " << A_avg(1) << " " << A_avg(2) << "\n"; ss << A_avg(3) << " " << A_avg(4) << " " << A_avg(5) << "\n"; ss << A_avg(6) << " " << A_avg(7) << " " << A_avg(8) << "\n"; ATC::LammpsInterface::instance()->print_msg_once(ss.str()); if (nSeg > 0) { set 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("TransferParititionOfUnity::compute_dislocaton_density - unimplemented function"); #endif } } // end namespace ATC