200 std::tuple<TStressPiola&...> piolaStress,
201 std::tuple<TStressCauchy&...> cauchyStress,
202 const bool& projectForces=
false)
205 int numberOfPiolaStresses=
sizeof...(TStressPiola);
206 int numberOfCauchyStresses=
sizeof...(TStressCauchy);
209 if (numberOfPiolaStresses == 0 && numberOfCauchyStresses == 0)
211 MY_WARNING(
"No stress calculation requested. Returning to caller.");
217 auto start = std::chrono::system_clock::now();
218 std::time_t startTime = std::chrono::system_clock::to_time_t(start);
219 std::cout <<
"Time stamp: " << std::ctime(&startTime) << std::endl;
222 recursiveNullifyStress(piolaStress);
223 recursiveNullifyStress(cauchyStress);
225 if (numberOfPiolaStresses > 0)
227 std::cout <<
"Number of Piola stresses requested : " << numberOfPiolaStresses << std::endl;
228 std::cout << std::endl;
229 std::cout << std::setw(25) <<
"Grid" << std::setw(25) <<
"Averaging domain size" << std::endl;
230 auto referenceGridDomainSizePairs= getTGridDomainSizePairs(std::move(piolaStress));
231 for (
const auto& pair : referenceGridDomainSizePairs)
232 std::cout << std::setw(25) << (
GridBase*) pair.first << std::setw(25) << pair.second << std::endl;
236 MY_WARNING(
"No reference coordinates detected to compute Piola stress.");
237 if (numberOfCauchyStresses>0)
239 MY_WARNING(
"Restarting stress calculation with only Cauchy stress.");
244 MY_ERROR(
"Error in stress computation.");
254 if (numberOfCauchyStresses>0)
256 std::cout <<
"Number of Cauchy stresses requested: " << numberOfCauchyStresses << std::endl;
257 std::cout << std::endl;
258 std::cout << std::setw(25) <<
"Grid" << std::setw(25) <<
"Averaging domain size" << std::endl;
259 auto currentGridDomainSizePairs= getTGridDomainSizePairs(cauchyStress);
260 for (
const auto& pair : currentGridDomainSizePairs)
261 std::cout << std::setw(25) << (
GridBase*) pair.first << std::setw(25) << pair.second << std::endl;
265 std::cout << std::endl;
268 double maxAveragingDomainSize= std::max(averagingDomainSize_max(piolaStress),averagingDomainSize_max(cauchyStress));
269 std::cout <<
"Maximum averaging domain size across all stresses = " << maxAveragingDomainSize << std::endl;
271 if (body.
pbc.any() == 1)
273 std::unique_ptr<const Configuration> pconfig;
274 MY_HEADING(
"Generating padding atoms for periodic boundary conditions");
277 pconfig.reset(body.
getConfiguration(2*influenceDistance+maxAveragingDomainSize));
278 std::cout <<
"Thickness of padding = 2 influence distance + maximum averaging domain size = "
279 << 2*influenceDistance+maxAveragingDomainSize << std::endl;
280 std::cout <<
"Total number of atoms including padding atoms = " << pconfig->numberOfParticles << std::endl;
281 std::cout << std::endl;
284 status=
calculateStress(pconfig.get(),kim,piolaStress,cauchyStress,projectForces);
288 status=
calculateStress(&body,kim,piolaStress,cauchyStress,projectForces);
296 std::cout << std::endl;
297 std::cout << std::endl;
298 std::cout << std::endl;
299 std::cout << std::endl;
300 auto end= std::chrono::system_clock::now();
301 std::time_t endTime= std::chrono::system_clock::to_time_t(end);
302 std::chrono::duration<double> elapsedSeconds(end-start);
303 std::cout <<
"Elapsed time: " << elapsedSeconds.count() <<
" seconds" << std::endl;
304 std::cout <<
"End of simulation" << std::endl;
309 MY_ERROR(
"Error in stress computation.");
316 std::tuple<TStressPiola&...> piolaStress,
317 std::tuple<TStressCauchy&...> cauchyStress,
318 const bool& projectForces=
false)
320 assert(!(kim.
kim_ptr==
nullptr) &&
"Model not initialized");
322 int numberOfPiolaStresses=
sizeof...(TStressPiola);
323 int numberOfCauchyStresses=
sizeof...(TStressCauchy);
331 const auto referenceGridAveragingDomainSizeMap=
332 recursiveGridMaxAveragingDomainSizeMap(piolaStress);
333 const auto currentGridAveragingDomainSizeMap=
334 recursiveGridMaxAveragingDomainSizeMap(cauchyStress);
338 if (numberOfPiolaStresses>0)
340 std::cout <<
"Piola Stress" << std::endl;
341 std::cout << std::setw(25) <<
"Unique grid" << std::setw(30) <<
"Max. Averaging domain size" << std::endl;
342 std::cout << std::setw(25) <<
"-----------" << std::setw(30) <<
"--------------------------" << std::endl;
343 for(
const auto& [pgrid,domainSize] : referenceGridAveragingDomainSizeMap)
345 std::cout << std::setw(25) << (
GridBase*)pgrid << std::setw(25) << domainSize << std::endl;
346 stencil.
expandStencil(pgrid,domainSize+influenceDistance,influenceDistance);
350 if (numberOfCauchyStresses>0)
352 std::cout <<
"Cauchy Stress" << std::endl;
353 std::cout << std::setw(25) <<
"Unique grid" << std::setw(30) <<
"Max. Averaging domain size" << std::endl;
354 std::cout << std::setw(25) <<
"-----------" << std::setw(30) <<
"--------------------------" << std::endl;
355 for(
const auto& [pgrid,domainSize] : currentGridAveragingDomainSizeMap)
357 std::cout << std::setw(25) << (
GridBase*)pgrid << std::setw(25) << domainSize << std::endl;
358 stencil.
expandStencil(pgrid,domainSize+influenceDistance,influenceDistance);
362 std::cout << std::endl;
363 std::cout <<
"Creating a local configuration using the above grids." << std::endl;
365 int numberOfParticles= subconfig.numberOfParticles;
366 if (numberOfParticles == 0)
368 std::string message=
"All grids away from the current material. Stresses are identically zero. Returning to the caller.";
370 throw(std::runtime_error(message));
372 std::cout <<
"Number of particle in the local configuration = " << numberOfParticles << std::endl;
373 std::cout <<
"Number of contributing particles = " << subconfig.particleContributing.sum() << std::endl;
379 std::vector<GridSubConfiguration<Reference>> neighborListsOfReferenceGridsOne;
380 std::vector<GridSubConfiguration<Reference>> neighborListsOfReferenceGridsTwo;
381 std::vector<GridSubConfiguration<Current>> neighborListsOfCurrentGridsOne;
382 std::vector<GridSubConfiguration<Current>> neighborListsOfCurrentGridsTwo;
384 auto referenceGridDomainSizePairs= getTGridDomainSizePairs(std::move(piolaStress));
385 assert(numberOfPiolaStresses==referenceGridDomainSizePairs.size());
387 auto currentGridDomainSizePairs= getTGridDomainSizePairs(cauchyStress);
388 assert(numberOfCauchyStresses == currentGridDomainSizePairs.size());
390 for(
const auto& gridDomainSizePair : referenceGridDomainSizePairs)
392 const auto& pgrid= gridDomainSizePair.first;
393 const auto& domainSize= gridDomainSizePair.second;
394 neighborListsOfReferenceGridsOne.emplace_back( *pgrid,subconfig,domainSize+influenceDistance);
395 neighborListsOfReferenceGridsTwo.emplace_back( *pgrid,subconfig,domainSize+2*influenceDistance);
397 for(
const auto& gridDomainSizePair : currentGridDomainSizePairs)
399 const auto& pgrid= gridDomainSizePair.first;
400 const auto& domainSize= gridDomainSizePair.second;
401 neighborListsOfCurrentGridsOne.emplace_back(*pgrid,subconfig,domainSize+influenceDistance);
402 neighborListsOfCurrentGridsTwo.emplace_back(*pgrid,subconfig,domainSize+2*influenceDistance);
404 assert(neighborListsOfCurrentGridsOne.size() == numberOfCauchyStresses &&
405 neighborListsOfCurrentGridsTwo.size() == numberOfCauchyStresses &&
406 neighborListsOfReferenceGridsOne.size() == numberOfPiolaStresses &&
407 neighborListsOfReferenceGridsTwo.size() == numberOfPiolaStresses);
409 std::vector<GridBase*> pgridListPiola= getBaseGridList(piolaStress);
410 std::vector<GridBase*> pgridListCauchy= getBaseGridList(cauchyStress);
411 std::vector<GridBase*> pgridList;
412 pgridList.reserve( pgridListPiola.size() + pgridListCauchy.size() );
413 pgridList.insert( pgridList.end(), pgridListPiola.begin(), pgridListPiola.end() );
414 pgridList.insert( pgridList.end(), pgridListCauchy.begin(), pgridListCauchy.end() );
415 assert(pgridList.size() == numberOfPiolaStresses + numberOfCauchyStresses);
423 bondCutoff= 2.0*influenceDistance;
424 std::cout <<
"bond cutoff = " << bondCutoff << std::endl;
428 if (
nbl_build(nlForBonds,numberOfParticles,
429 subconfig.coordinates.at(
Current).data(),
433 subconfig.particleContributing.data()))
435 MY_ERROR(
"Failed to build bond neighbor list.");
450 subconfig.coordinates.at(
Current).data(),
452 numberOfNeighborLists,
454 subconfig.particleContributing.data()))
456 MY_ERROR(
"Failed to build particle neighbor list.");
459 int neighborListSize= 0;
460 for (
int i_particle=0; i_particle<numberOfParticles; i_particle++)
462 std::cout <<
"Size of neighbor list = " <<neighborListSize << std::endl;
468 if (!projectForces) {
474 subconfig.particleContributing,
480 std::cout <<
"Done" << std::endl;
485 auto start = std::chrono::system_clock::now();
486 std::time_t startTime = std::chrono::system_clock::to_time_t(start);
488 std::cout <<
"Time stamp at the beginning of process de-dr interatomic force calculation: " << std::ctime(&startTime) << std::endl;
490 auto end= std::chrono::system_clock::now();
491 std::time_t endTime= std::chrono::system_clock::to_time_t(end);
492 std::chrono::duration<double> elapsedSeconds(end-start);
493 std::cout <<
"Elapsed time for calculating process-dedr interatomic forces: " << elapsedSeconds.count() <<
" seconds" << std::endl;
494 std::cout <<
"Done" << std::endl;
500 subconfig.particleContributing,
512 auto start = std::chrono::system_clock::now();
513 std::time_t startTime = std::chrono::system_clock::to_time_t(start);
514 std::cout <<
"Time stamp at the beginning of interatomic force projection: " << std::ctime(&startTime) << std::endl;
515 std::ofstream null_stream(
"/dev/null");
516 std::streambuf* cout_buf = std::cout.rdbuf();
517 std::cout.rdbuf(null_stream.rdbuf());
524 std::vector<double> fijCopy(bonds.
fij.size(),0.0);
526 for (
int i_particlei = 0; i_particlei < subconfig.numberOfParticles; ++i_particlei) {
528 if (subconfig.particleContributing[i_particlei] == 0)
continue;
530 std::cout << i_particlei << std::endl;
532 Stencil singleParticleStencil(subconfig);
533 std::vector<Vector3d> centerParticleCoordinates;
534 centerParticleCoordinates.push_back(subconfig.coordinates.at(
Current).row(i_particlei));
535 singleParticleStencil.
expandStencil(centerParticleCoordinates, subconfig.coordinates.at(
Current), 0.0,
540 const double *cutoffs = p_kimLocal->
getCutoffs();
546 if (
nbl_build(nlOfParticle, subconfigOfParticle.numberOfParticles,
547 subconfigOfParticle.coordinates.at(
Current).data(),
549 numberOfNeighborLists,
551 subconfigOfParticle.particleContributing.data()))
553 MY_ERROR(
"Failed to build projected-force neighbor list.");
556 MatrixXd localForces(subconfigOfParticle.numberOfParticles,
DIM);
557 localForces.setZero();
561 subconfigOfParticle.particleContributing,
588 double forceMax= localForces.cwiseAbs().maxCoeff();
590 std::vector<double> fij = rigidity.
project(localForces.reshaped<Eigen::RowMajor>()/forceMax);
594 for(
int kLocal= 0; kLocal<subconfigOfParticle.numberOfParticles; ++kLocal) {
595 int i_particlek = subconfigOfParticle.localGlobalMap.at(kLocal);
596 for (
int jLocal= kLocal+1; jLocal < subconfigOfParticle.numberOfParticles; ++jLocal) {
598 int i_particlej = subconfigOfParticle.localGlobalMap.at(jLocal);
600 for (
int i_neighborOfk = 0; i_neighborOfk < bonds.
nlOne_ptr->
Nneighbors[i_particlek]; ++i_neighborOfk) {
603 fijCopy[index] -= fij[indexLocal]*forceMax;
609 for (
int i_neighborOfj = 0;
613 fijCopy[index] -= fij[indexLocal]*forceMax;
627 for(
const auto& elem : fijCopy)
629 bonds.
fij[i_fijCopy]+= elem;
635 std::cout.rdbuf(cout_buf);
636 auto end= std::chrono::system_clock::now();
637 std::time_t endTime= std::chrono::system_clock::to_time_t(end);
638 std::chrono::duration<double> elapsedSeconds(end-start);
639 std::cout <<
"Elapsed time for calculating projected interatomic forces: " << elapsedSeconds.count() <<
" seconds" << std::endl;
640 std::cout <<
"Done with local force calculations" << std::endl;
646 MY_HEADING(
"Checking error in interatomic forces");
650 for(
int i_particlei=0; i_particlei<subconfig.numberOfParticles; ++i_particlei) {
651 if (subconfig.particleContributing[i_particlei] == 0)
continue;
652 Vector3d particlei = subconfig.coordinates.at(
Current).row(i_particlei);
653 for (
int i_neighborOfi = 0; i_neighborOfi < bonds.
nlOne_ptr->
Nneighbors[i_particlei]; ++i_neighborOfi) {
656 Vector3d particlej = subconfig.coordinates.at(
Current).row(i_particlej);
657 Vector3d eij = (particlei - particlej).normalized();
658 fi.row(i_particlei) -= bonds.
fij[index] * eij;
664 for(
int i_particlei=0; i_particlei<subconfig.numberOfParticles; ++i_particlei) {
665 if (subconfig.particleContributing[i_particlei] == 0)
continue;
666 maxError= std::max(maxError,(forces.row(i_particlei)-fi.row(i_particlei)).norm());
668 std::cout <<
"Maximum error in f_i - sum_j f_ij: " << maxError << std::endl;
669 std::cout <<
"Done" << std::endl;
677 for(
const auto& pgrid : pgridList)
681 int numberOfGridPoints= pgrid->coordinates.size();
682 std::cout << i_grid+1 <<
". Number of grid points: " << numberOfGridPoints << std::endl;
684 #pragma omp parallel for
686 for(
int i_gridPoint=0; i_gridPoint<numberOfGridPoints; i_gridPoint++)
688 const auto& gridPoint= pgrid->coordinates[i_gridPoint];
689 if ( numberOfGridPoints<10 || (i_gridPoint+1)%(numberOfGridPoints/10) == 0)
691 progress= (double)(i_gridPoint+1)/numberOfGridPoints;
694 std::set<int> neighborListOne, neighborListTwo;
695 if (i_grid<numberOfPiolaStresses)
697 neighborListOne= neighborListsOfReferenceGridsOne[i_grid].getGridPointNeighbors(i_gridPoint);
698 neighborListTwo= neighborListsOfReferenceGridsTwo[i_grid].getGridPointNeighbors(i_gridPoint);
702 neighborListOne= neighborListsOfCurrentGridsOne[i_grid-numberOfPiolaStresses].getGridPointNeighbors(i_gridPoint);
703 neighborListTwo= neighborListsOfCurrentGridsTwo[i_grid-numberOfPiolaStresses].getGridPointNeighbors(i_gridPoint);
706 if (i_grid>=numberOfPiolaStresses)
708 for (
const auto& particle : neighborListOne)
710 Vector3d ra= subconfig.coordinates.at(
Current).row(particle) - gridPoint;
711 Vector3d velocity= subconfig.velocities.row(particle);
712 recursiveBuildContinuumFields(subconfig.masses(particle),
716 i_grid-numberOfPiolaStresses,
719 recursiveFinalizeContinuumVelocity(i_gridPoint,i_grid-numberOfPiolaStresses,cauchyStress);
721 recursiveGetContinuumVelocity(i_gridPoint,i_grid-numberOfPiolaStresses,cauchyStress);
723 for (
const auto& particle : neighborListOne)
725 Vector3d ra= subconfig.coordinates.at(
Current).row(particle) - gridPoint;
726 Vector3d velocity= subconfig.velocities.row(particle);
727 Vector3d relativeVelocity= velocity - continuumVelocity;
728 recursiveBuildKineticStress(subconfig.masses(particle),
732 i_grid-numberOfPiolaStresses,
737 for (
const auto& particle1 : neighborListOne)
740 ra= rA= rb= rB= rab= rAB= Vector3d::Zero();
742 if(numberOfPiolaStresses>0) rA= subconfig.coordinates.at(
Reference).row(particle1) - gridPoint;
743 ra= subconfig.coordinates.at(
Current).row(particle1) - gridPoint;
748 for(
int i_neighborOf1= 0; i_neighborOf1<numberOfNeighborsOf1; i_neighborOf1++)
751 double fij= bonds.
fij[index];
752 if (fij == 0)
continue;
756 if(numberOfPiolaStresses>0) rB= subconfig.coordinates.at(
Reference).row(particle2) - gridPoint;
757 rb= subconfig.coordinates.at(
Current).row(particle2) - gridPoint;
760 if ( (neighborListOne.find(particle2) != neighborListOne.end() && particle1 < particle2))
767 if ( neighborListOne.find(particle2) != neighborListOne.end() ||
768 neighborListTwo.find(particle2) != neighborListTwo.end())
770 if(numberOfPiolaStresses>0) rAB= subconfig.coordinates.at(
Reference).row(particle1)-subconfig.coordinates.at(
Reference).row(particle2);
771 rab= subconfig.coordinates.at(
Current).row(particle1)-subconfig.coordinates.at(
Current).row(particle2);
772 if (i_grid<numberOfPiolaStresses)
775 recursiveBuildStress(fij,ra,rA,rb,rB,rab,rAB,i_gridPoint,i_grid-numberOfPiolaStresses,cauchyStress);
781 std::cout <<
"Done with grid " << pgrid << std::endl;
782 std::cout << std::endl;