MDStressLab++
Loading...
Searching...
No Matches
calculateStress.cpp
Go to the documentation of this file.
1/*
2 * calculateStress.cpp
3 *
4 * Created on: Nov 7, 2019
5 * Author: Nikhil
6 */
7
8#include <string>
9#include <iostream>
10#include <vector>
11#include "neighbor_list.h"
12#include "InteratomicForces.h"
13#include "kim.h"
14#include "BoxConfiguration.h"
15#include "Configuration.h"
16#include "SubConfiguration.h"
17#include "Stress.h"
18#include "typedef.h"
19#include "StressTuple.h"
20#include "helper.hpp"
21#include "Rigidity.h"
22#include <tuple>
23#include <chrono>
24#include <omp.h>
25
27 Kim& kim,
28 std::tuple<> stress,
29 const bool& projectForces=false)
30{
31
32 MY_WARNING("No stress calculation requested. Returning to caller.");
33 return 1;
34
35}
36
37template<typename ...BF>
39 Kim& kim,
40 std::tuple<Stress<BF,Cauchy>&...> stress,
41 const bool& projectForces=false)
42{
43 std::tuple<> emptyTuple;
44 return calculateStress(body,
45 kim,
46 emptyTuple,
47 stress,
48 projectForces);
49 /*
50 if (stressType == Piola)
51 return calculateStress(body,
52 kim,
53 stress,
54 emptyTuple);
55 else if (stressType == Cauchy)
56 return calculateStress(body,
57 kim,
58 emptyTuple,
59 stress);
60 else
61 MY_ERROR("Unrecognized stress type " + std::to_string(stressType));
62 */
63}
64template<typename ...BF>
66 Kim& kim,
67 std::tuple<Stress<BF,Piola>&...> stress,
68 const bool& projectForces=false)
69{
70 std::tuple<> emptyTuple;
71 return calculateStress(body,
72 kim,
73 stress,
74 emptyTuple,
75 projectForces);
76}
77
79 std::tuple<> cauchyStress)
80{
81 MY_WARNING("No Cauchy stress calculation requested. Returning to caller.");
82 return 1;
83}
84
95template<typename ...BF>
97 std::tuple<Stress<BF,Cauchy>&...> cauchyStress)
98{
99 int numberOfCauchyStresses= sizeof...(BF);
100 if (numberOfCauchyStresses == 0)
101 {
102 MY_WARNING("No Cauchy stress calculation requested. Returning to caller.");
103 return 1;
104 }
105
106 recursiveNullifyStress(cauchyStress);
107
108 const auto currentGridAveragingDomainSizeMap=
109 recursiveGridMaxAveragingDomainSizeMap(cauchyStress);
110
111 Stencil stencil(*pconfig);
112 for(const auto& [pgrid,domainSize] : currentGridAveragingDomainSizeMap)
113 stencil.expandStencil(pgrid,domainSize,0.0);
114
115 SubConfiguration subconfig{stencil};
116 if (subconfig.numberOfParticles == 0)
117 {
118 MY_WARNING("All grids away from the current material. Kinetic stresses are identically zero.");
119 return 1;
120 }
121
122 auto currentGridDomainSizePairs= getTGridDomainSizePairs(cauchyStress);
123 assert(numberOfCauchyStresses == currentGridDomainSizePairs.size());
124
125 std::vector<GridSubConfiguration<Current>> neighborListsOfCurrentGrids;
126 for(const auto& gridDomainSizePair : currentGridDomainSizePairs)
127 neighborListsOfCurrentGrids.emplace_back(*gridDomainSizePair.first,subconfig,gridDomainSizePair.second);
128
129 int i_grid= 0;
130 for(const auto& gridDomainSizePair : currentGridDomainSizePairs)
131 {
132 const auto& pgrid= gridDomainSizePair.first;
133 int numberOfGridPoints= pgrid->coordinates.size();
134
135 #pragma omp parallel for
136 for(int i_gridPoint=0; i_gridPoint<numberOfGridPoints; i_gridPoint++)
137 {
138 const auto& gridPoint= pgrid->coordinates[i_gridPoint];
139 std::set<int> neighborList=
140 neighborListsOfCurrentGrids[i_grid].getGridPointNeighbors(i_gridPoint);
141
142 for (const auto& particle : neighborList)
143 {
144 Vector3d ra= subconfig.coordinates.at(Current).row(particle) - gridPoint;
145 Vector3d velocity= subconfig.velocities.row(particle);
146 recursiveBuildContinuumFields(subconfig.masses(particle),
147 velocity,
148 ra,
149 i_gridPoint,
150 i_grid,
151 cauchyStress);
152 }
153 recursiveFinalizeContinuumVelocity(i_gridPoint,i_grid,cauchyStress);
154 Vector3d continuumVelocity= recursiveGetContinuumVelocity(i_gridPoint,i_grid,cauchyStress);
155
156 for (const auto& particle : neighborList)
157 {
158 Vector3d ra= subconfig.coordinates.at(Current).row(particle) - gridPoint;
159 Vector3d velocity= subconfig.velocities.row(particle);
160 Vector3d relativeVelocity= velocity - continuumVelocity;
161 recursiveBuildKineticStress(subconfig.masses(particle),
162 relativeVelocity,
163 ra,
164 i_gridPoint,
165 i_grid,
166 cauchyStress);
167 }
168 }
169 i_grid++;
170 }
171
172 return 1;
173}
174
182template<typename ...BF>
184 std::tuple<Stress<BF,Cauchy>&...> cauchyStress)
185{
186 double maxAveragingDomainSize= averagingDomainSize_max(cauchyStress);
187 if (body.pbc.any() == 1)
188 {
189 std::unique_ptr<const Configuration> pconfig;
190 pconfig.reset(body.getConfiguration(maxAveragingDomainSize));
191 return calculateKineticStress(pconfig.get(),cauchyStress);
192 }
193 return calculateKineticStress(&body,cauchyStress);
194}
195
196// This is the main driver of stress calculation
197template<typename ...TStressPiola, typename ...TStressCauchy>
199 Kim& kim,
200 std::tuple<TStressPiola&...> piolaStress,
201 std::tuple<TStressCauchy&...> cauchyStress,
202 const bool& projectForces=false)
203{
204 int status= 0;
205 int numberOfPiolaStresses= sizeof...(TStressPiola);
206 int numberOfCauchyStresses= sizeof...(TStressCauchy);
207
208
209 if (numberOfPiolaStresses == 0 && numberOfCauchyStresses == 0)
210 {
211 MY_WARNING("No stress calculation requested. Returning to caller.");
212 return 1;
213 }
214
215 // At least one stress is being requested
216 MY_BANNER("Begin stress calculation");
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;
220
221 // nullify the stress fields before starting
222 recursiveNullifyStress(piolaStress);
223 recursiveNullifyStress(cauchyStress);
224
225 if (numberOfPiolaStresses > 0)
226 {
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;
233
234 if (body.coordinates.at(Reference).rows() == 0)
235 {
236 MY_WARNING("No reference coordinates detected to compute Piola stress.");
237 if (numberOfCauchyStresses>0)
238 {
239 MY_WARNING("Restarting stress calculation with only Cauchy stress.");
240 status= calculateStress(body,kim,cauchyStress,projectForces);
241 if (status==1)
242 return status;
243 else
244 MY_ERROR("Error in stress computation.");
245 }
246 else
247 {
248 MY_BANNER("End of stress calculation");
249 return 1;
250 }
251 }
252 }
253
254 if (numberOfCauchyStresses>0)
255 {
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;
262
263
264 }
265 std::cout << std::endl;
266
267
268 double maxAveragingDomainSize= std::max(averagingDomainSize_max(piolaStress),averagingDomainSize_max(cauchyStress));
269 std::cout << "Maximum averaging domain size across all stresses = " << maxAveragingDomainSize << std::endl;
270
271 if (body.pbc.any() == 1)
272 {
273 std::unique_ptr<const Configuration> pconfig;
274 MY_HEADING("Generating padding atoms for periodic boundary conditions");
275
276 double influenceDistance= kim.influenceDistance;
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;
282
283
284 status= calculateStress(pconfig.get(),kim,piolaStress,cauchyStress,projectForces);
285 }
286 else
287 {
288 status= calculateStress(&body,kim,piolaStress,cauchyStress,projectForces);
289 }
290
291// recursiveWriteStressAndGrid(piolaStress);
292// recursiveWriteStressAndGrid(cauchyStress);
293
294 if (status==1)
295 {
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;
305 MY_BANNER("End of stress calculation");
306 return status;
307 }
308 else
309 MY_ERROR("Error in stress computation.");
310
311}
312
313template<typename ...TStressPiola,typename ...TStressCauchy>
315 Kim& kim,
316 std::tuple<TStressPiola&...> piolaStress,
317 std::tuple<TStressCauchy&...> cauchyStress,
318 const bool& projectForces=false)
319{
320 assert(!(kim.kim_ptr==nullptr) && "Model not initialized");
321 double influenceDistance= kim.influenceDistance;
322 int numberOfPiolaStresses= sizeof...(TStressPiola);
323 int numberOfCauchyStresses= sizeof...(TStressCauchy);
324
325// ------------------------------------------------------------------
326// Generate a local configuration
327// ------------------------------------------------------------------
328 MY_HEADING("Building a local configuration");
329
330// Mappings from set of unique grids to the set of maximum averaging domain sizes
331 const auto referenceGridAveragingDomainSizeMap=
332 recursiveGridMaxAveragingDomainSizeMap(piolaStress);
333 const auto currentGridAveragingDomainSizeMap=
334 recursiveGridMaxAveragingDomainSizeMap(cauchyStress);
335
336 Stencil stencil(*pconfig);
337
338 if (numberOfPiolaStresses>0)
339 {
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)
344 {
345 std::cout << std::setw(25) << (GridBase*)pgrid << std::setw(25) << domainSize << std::endl;
346 stencil.expandStencil(pgrid,domainSize+influenceDistance,influenceDistance);
347 }
348 }
349
350 if (numberOfCauchyStresses>0)
351 {
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)
356 {
357 std::cout << std::setw(25) << (GridBase*)pgrid << std::setw(25) << domainSize << std::endl;
358 stencil.expandStencil(pgrid,domainSize+influenceDistance,influenceDistance);
359 }
360 }
361
362 std::cout << std::endl;
363 std::cout << "Creating a local configuration using the above grids." << std::endl;
364 SubConfiguration subconfig{stencil};
365 int numberOfParticles= subconfig.numberOfParticles;
366 if (numberOfParticles == 0)
367 {
368 std::string message= "All grids away from the current material. Stresses are identically zero. Returning to the caller.";
369 //MY_ERROR(message);
370 throw(std::runtime_error(message));
371 }
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;
374
375
376// ------------------------------------------------------------------
377// Generate neighbor lists for all the grids
378// ------------------------------------------------------------------
379 std::vector<GridSubConfiguration<Reference>> neighborListsOfReferenceGridsOne;
380 std::vector<GridSubConfiguration<Reference>> neighborListsOfReferenceGridsTwo;
381 std::vector<GridSubConfiguration<Current>> neighborListsOfCurrentGridsOne;
382 std::vector<GridSubConfiguration<Current>> neighborListsOfCurrentGridsTwo;
383
384 auto referenceGridDomainSizePairs= getTGridDomainSizePairs(std::move(piolaStress));
385 assert(numberOfPiolaStresses==referenceGridDomainSizePairs.size());
386
387 auto currentGridDomainSizePairs= getTGridDomainSizePairs(cauchyStress);
388 assert(numberOfCauchyStresses == currentGridDomainSizePairs.size());
389
390 for(const auto& gridDomainSizePair : referenceGridDomainSizePairs)
391 {
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);
396 }
397 for(const auto& gridDomainSizePair : currentGridDomainSizePairs)
398 {
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);
403 }
404 assert(neighborListsOfCurrentGridsOne.size() == numberOfCauchyStresses &&
405 neighborListsOfCurrentGridsTwo.size() == numberOfCauchyStresses &&
406 neighborListsOfReferenceGridsOne.size() == numberOfPiolaStresses &&
407 neighborListsOfReferenceGridsTwo.size() == numberOfPiolaStresses);
408
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);
416
417
418
419// ------------------------------------------------------------------
420// Building neighbor list for bonds
421// ------------------------------------------------------------------
422 double bondCutoff;
423 bondCutoff= 2.0*influenceDistance;
424 std::cout << "bond cutoff = " << bondCutoff << std::endl;
425
426 NeighList* nlForBonds;
427 nbl_initialize(&nlForBonds);
428 if (nbl_build(nlForBonds,numberOfParticles,
429 subconfig.coordinates.at(Current).data(),
430 bondCutoff,
431 1,
432 &bondCutoff,
433 subconfig.particleContributing.data()))
434 {
435 MY_ERROR("Failed to build bond neighbor list.");
436 }
437 InteratomicForces bonds(nlForBonds);
438
439 // ------------------------------------------------------------------
440 // Build neighbor list of particles
441 // ------------------------------------------------------------------
442 MY_HEADING("Building neighbor list");
443 const double* cutoffs= kim.getCutoffs();
444 int numberOfNeighborLists= kim.getNumberOfNeighborLists();
445
446 // TODO: assert whenever the subconfiguration is empty
447 NeighList* nl;
448 nbl_initialize(&nl);
449 if (nbl_build(nl,numberOfParticles,
450 subconfig.coordinates.at(Current).data(),
451 influenceDistance,
452 numberOfNeighborLists,
453 cutoffs,
454 subconfig.particleContributing.data()))
455 {
456 MY_ERROR("Failed to build particle neighbor list.");
457 }
458
459 int neighborListSize= 0;
460 for (int i_particle=0; i_particle<numberOfParticles; i_particle++)
461 neighborListSize+= nl->lists->Nneighbors[i_particle];
462 std::cout << "Size of neighbor list = " <<neighborListSize << std::endl;
463
464
465 MatrixXd forces(numberOfParticles,DIM);
466 forces.setZero();
467
468 if (!projectForces) {
469 // ------------------------------------------------------------------
470 // Broadcast to model
471 // ------------------------------------------------------------------
472 MY_HEADING("Broadcasting to model");
473 kim.broadcastToModel(&subconfig,
474 subconfig.particleContributing,
475 &forces,
476 nl,
477 (KIM::Function *) &nbl_get_neigh,
478 &bonds,
479 (KIM::Function *) &process_DEDr);
480 std::cout << "Done" << std::endl;
481
482 // ------------------------------------------------------------------
483 // Compute forces
484 // ------------------------------------------------------------------
485 auto start = std::chrono::system_clock::now();
486 std::time_t startTime = std::chrono::system_clock::to_time_t(start);
487 MY_HEADING("Computing forces");
488 std::cout << "Time stamp at the beginning of process de-dr interatomic force calculation: " << std::ctime(&startTime) << std::endl;
489 kim.compute();
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;
495 //nbl_clean(&nl);
496 }
497 else
498 {
499 kim.broadcastToModel(&subconfig,
500 subconfig.particleContributing,
501 &forces,
502 nl,
503 (KIM::Function *) &nbl_get_neigh,
504 nullptr,
505 nullptr);
506 kim.compute();
507
508 // ------------------------------------------------------------------
509 // Beginning force projection
510 // ------------------------------------------------------------------
511 MY_HEADING("Beginning force projection")
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"); // For Unix/Linux/macOS
516 std::streambuf* cout_buf = std::cout.rdbuf(); // Save original buffer
517 std::cout.rdbuf(null_stream.rdbuf()); // Redirect std::cout to null
518 #pragma omp parallel
519 {
520 Kim* p_kimLocal;
521 #pragma omp critical
522 p_kimLocal= new Kim(kim.modelname);
523 //Kim kimLocal(kim.modelname);
524 std::vector<double> fijCopy(bonds.fij.size(),0.0);
525 #pragma omp for
526 for (int i_particlei = 0; i_particlei < subconfig.numberOfParticles; ++i_particlei) {
527 // consider only contributing particles
528 if (subconfig.particleContributing[i_particlei] == 0) continue;
529
530 std::cout << i_particlei << std::endl;
531 // stencil out particle and its neighborhood
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,
536 influenceDistance);
537 SubConfiguration subconfigOfParticle{singleParticleStencil};
538
539 // form neighborlist of the particle
540 const double *cutoffs = p_kimLocal->getCutoffs();
541 int numberOfNeighborLists = p_kimLocal->getNumberOfNeighborLists();
542
543 // TODO: assert whenever the subconfiguration is empty
544 NeighList *nlOfParticle;
545 nbl_initialize(&nlOfParticle);
546 if (nbl_build(nlOfParticle, subconfigOfParticle.numberOfParticles,
547 subconfigOfParticle.coordinates.at(Current).data(),
548 influenceDistance,
549 numberOfNeighborLists,
550 cutoffs,
551 subconfigOfParticle.particleContributing.data()))
552 {
553 MY_ERROR("Failed to build projected-force neighbor list.");
554 }
555
556 MatrixXd localForces(subconfigOfParticle.numberOfParticles, DIM);
557 localForces.setZero();
558
559 // broadcast to model
560 p_kimLocal->broadcastToModel(&subconfigOfParticle,
561 subconfigOfParticle.particleContributing,
562 &localForces,
563 nlOfParticle,
564 (KIM::Function *) &nbl_get_neigh,
565 nullptr,
566 nullptr);
567 // compute partial forces
568 p_kimLocal->compute();
569
570 /*
571 // check moment
572 Vector3d moment, totalForce;
573 moment.setZero(); totalForce.setZero();
574 for(int i_row=0; i_row<forces.rows(); ++i_row)
575 {
576 Vector3d pos= subconfigOfParticle.coordinates.at(Current).row(i_row);
577 Vector3d f= forces.row(i_row);
578 moment= moment+pos.cross(f);
579 totalForce= totalForce + f;
580 }
581 std::cout << "moment = " << std::endl;
582 std::cout << moment << std::endl;
583 std::cout << "total force = " << std::endl;
584 std::cout << totalForce << std::endl;
585 */
586
587 Rigidity rigidity(subconfigOfParticle.coordinates.at(Current));
588 double forceMax= localForces.cwiseAbs().maxCoeff();
589
590 std::vector<double> fij = rigidity.project(localForces.reshaped<Eigen::RowMajor>()/forceMax);
591
592 // loop over the local bonds and collect all interatomic forces
593 int indexLocal= -1;
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) {
597 indexLocal++;
598 int i_particlej = subconfigOfParticle.localGlobalMap.at(jLocal);
599 // look for particlej in the neighborhood of particlek in bonds
600 for (int i_neighborOfk = 0; i_neighborOfk < bonds.nlOne_ptr->Nneighbors[i_particlek]; ++i_neighborOfk) {
601 int index = bonds.nlOne_ptr->beginIndex[i_particlek] + i_neighborOfk;
602 if (i_particlej == bonds.nlOne_ptr->neighborList[index]) {
603 fijCopy[index] -= fij[indexLocal]*forceMax;
604 break;
605 }
606 }
607
608 // look for particlek in the neighborhood of particlej
609 for (int i_neighborOfj = 0;
610 i_neighborOfj < bonds.nlOne_ptr->Nneighbors[i_particlej]; ++i_neighborOfj) {
611 int index = bonds.nlOne_ptr->beginIndex[i_particlej] + i_neighborOfj;
612 if (i_particlek == bonds.nlOne_ptr->neighborList[index]) {
613 fijCopy[index] -= fij[indexLocal]*forceMax;
614 break;
615 }
616 }
617 }
618 }
619 nbl_clean(&nlOfParticle);
620 }
621
622 delete p_kimLocal;
623 p_kimLocal= nullptr;
624 #pragma omp critical
625 {
626 int i_fijCopy= 0;
627 for(const auto& elem : fijCopy)
628 {
629 bonds.fij[i_fijCopy]+= elem;
630 i_fijCopy++;
631 }
632 }
633 }
634
635 std::cout.rdbuf(cout_buf); // Restore the original stream buffer
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;
641 }
642
643 // ------------------------------------------------------------------
644 // Checking error in interatomic forces
645 // ------------------------------------------------------------------
646 MY_HEADING("Checking error in interatomic forces");
647 // fi: total force from the interatomic force projection
648 MatrixXd fi(numberOfParticles,DIM);
649 fi.setZero();
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) {
654 int index = bonds.nlOne_ptr->beginIndex[i_particlei] + i_neighborOfi;
655 int i_particlej = bonds.nlOne_ptr->neighborList[index];
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;
659 }
660 }
661
662 // check if gi~fi
663 double maxError=0;
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());
667 }
668 std::cout << "Maximum error in f_i - sum_j f_ij: " << maxError << std::endl;
669 std::cout << "Done" << std::endl;
670 nbl_clean(&nl);
671// ------------------------------------------------------------------
672// Loop over local grid points and accumulate stress
673// ------------------------------------------------------------------
674 MY_HEADING("Looping over grids");
675
676 int i_grid= 0;
677 for(const auto& pgrid : pgridList)
678 {
679 //int i_gridPoint= 0;
680 double progress= 0;
681 int numberOfGridPoints= pgrid->coordinates.size();
682 std::cout << i_grid+1 << ". Number of grid points: " << numberOfGridPoints << std::endl;
683
684 #pragma omp parallel for
685 //for (const auto& gridPoint : pgrid->coordinates)
686 for(int i_gridPoint=0; i_gridPoint<numberOfGridPoints; i_gridPoint++)
687 {
688 const auto& gridPoint= pgrid->coordinates[i_gridPoint];
689 if ( numberOfGridPoints<10 || (i_gridPoint+1)%(numberOfGridPoints/10) == 0)
690 {
691 progress= (double)(i_gridPoint+1)/numberOfGridPoints;
692 progressBar(progress);
693 }
694 std::set<int> neighborListOne, neighborListTwo;
695 if (i_grid<numberOfPiolaStresses)
696 {
697 neighborListOne= neighborListsOfReferenceGridsOne[i_grid].getGridPointNeighbors(i_gridPoint);
698 neighborListTwo= neighborListsOfReferenceGridsTwo[i_grid].getGridPointNeighbors(i_gridPoint);
699 }
700 else
701 {
702 neighborListOne= neighborListsOfCurrentGridsOne[i_grid-numberOfPiolaStresses].getGridPointNeighbors(i_gridPoint);
703 neighborListTwo= neighborListsOfCurrentGridsTwo[i_grid-numberOfPiolaStresses].getGridPointNeighbors(i_gridPoint);
704 }
705
706 if (i_grid>=numberOfPiolaStresses)
707 {
708 for (const auto& particle : neighborListOne)
709 {
710 Vector3d ra= subconfig.coordinates.at(Current).row(particle) - gridPoint;
711 Vector3d velocity= subconfig.velocities.row(particle);
712 recursiveBuildContinuumFields(subconfig.masses(particle),
713 velocity,
714 ra,
715 i_gridPoint,
716 i_grid-numberOfPiolaStresses,
717 cauchyStress);
718 }
719 recursiveFinalizeContinuumVelocity(i_gridPoint,i_grid-numberOfPiolaStresses,cauchyStress);
720 Vector3d continuumVelocity=
721 recursiveGetContinuumVelocity(i_gridPoint,i_grid-numberOfPiolaStresses,cauchyStress);
722
723 for (const auto& particle : neighborListOne)
724 {
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),
729 relativeVelocity,
730 ra,
731 i_gridPoint,
732 i_grid-numberOfPiolaStresses,
733 cauchyStress);
734 }
735 }
736
737 for (const auto& particle1 : neighborListOne)
738 {
739 Vector3d ra,rA,rb,rB,rab,rAB;
740 ra= rA= rb= rB= rab= rAB= Vector3d::Zero();
741
742 if(numberOfPiolaStresses>0) rA= subconfig.coordinates.at(Reference).row(particle1) - gridPoint;
743 ra= subconfig.coordinates.at(Current).row(particle1) - gridPoint;
744 int index;
745 int numberOfNeighborsOf1= bonds.nlOne_ptr->Nneighbors[particle1];
746
747// Loop through the bond neighbors of particle1
748 for(int i_neighborOf1= 0; i_neighborOf1<numberOfNeighborsOf1; i_neighborOf1++)
749 {
750 index= bonds.nlOne_ptr->beginIndex[particle1]+i_neighborOf1;
751 double fij= bonds.fij[index];
752 if (fij == 0) continue;
753
754// At this point, the force in the bond connecting particles 1 and 2 is nonzero
755 int particle2= bonds.nlOne_ptr->neighborList[index];
756 if(numberOfPiolaStresses>0) rB= subconfig.coordinates.at(Reference).row(particle2) - gridPoint;
757 rb= subconfig.coordinates.at(Current).row(particle2) - gridPoint;
758// Ignore if (particle2 is in neighborListOne and particle 1 > particle 2) as this pair
759// is encountered twice
760 if ( (neighborListOne.find(particle2) != neighborListOne.end() && particle1 < particle2))
761 continue;
762
763// Since neighborListOne \subset of neighborListTwo, the following condition if(A||B)
764// is equivalent if(B). Nevertheless, we have if(A||B) since B is expensive, and it is never
765// evaluated if A is true.
766
767 if ( neighborListOne.find(particle2) != neighborListOne.end() ||
768 neighborListTwo.find(particle2) != neighborListTwo.end())
769 {
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)
773 recursiveBuildStress(fij,ra,rA,rb,rB,rab,rAB,i_gridPoint,i_grid,piolaStress);
774 else
775 recursiveBuildStress(fij,ra,rA,rb,rB,rab,rAB,i_gridPoint,i_grid-numberOfPiolaStresses,cauchyStress);
776 }
777 }
778 }
779 //i_gridpoint++;
780 }
781 std::cout << "Done with grid " << pgrid << std::endl;
782 std::cout << std::endl;
783
784 i_grid++;
785 }
786
787 // Collect all the stress fields in processor 0
788
789 nbl_clean(&nlForBonds);
790 return 1;
791}
792
793
794int process_DEDr(const void* dataObject, const double de, const double r, const double* const dx, const int i, const int j)
795 {
796 InteratomicForces* bonds_ptr= (InteratomicForces*) dataObject;
797 int index;
798 int numberOfNeighborsOfi = bonds_ptr->nlOne_ptr->Nneighbors[i];
799 int numberOfNeighborsOfj = bonds_ptr->nlOne_ptr->Nneighbors[j];
800 bool iFound= false;
801 bool jFound= false;
802
803 // Look for j in the neighbor list of i
804 for(int i_neighborOfi= 0; i_neighborOfi<numberOfNeighborsOfi; i_neighborOfi++)
805 {
806 index= bonds_ptr->nlOne_ptr->beginIndex[i]+i_neighborOfi;
807 if(bonds_ptr->nlOne_ptr->neighborList[index] == j)
808 {
809 bonds_ptr->fij[index]+= de;
810 jFound= true;
811 break;
812 }
813 }
814 // Look for i in the neighbor list of j
815 for(int i_neighborOfj= 0; i_neighborOfj<numberOfNeighborsOfj; i_neighborOfj++)
816 {
817 index= bonds_ptr->nlOne_ptr->beginIndex[j]+i_neighborOfj;
818 if(bonds_ptr->nlOne_ptr->neighborList[index] == i)
819 {
820 bonds_ptr->fij[index]+= de;
821 iFound= true;
822 break;
823 }
824
825 }
826 return 0;
827 }
std::enable_if< I==sizeof...(TStress), void >::type recursiveBuildStress(const double &fij, const Vector3d &ra, const Vector3d &rA, const Vector3d &rb, const Vector3d &rB, const Vector3d &rab, const Vector3d &rAB, const int &i_gridPoint, const int &i_stress, std::tuple< TStress &... > t)
Definition StressTuple.h:14
int calculateKineticStress(const BoxConfiguration &body, std::tuple<> cauchyStress)
int calculateStress(const BoxConfiguration &body, Kim &kim, std::tuple<> stress, const bool &projectForces=false)
int process_DEDr(const void *dataObject, const double de, const double r, const double *const dx, const int i, const int j)
Represents a particle configuration including simulation box information.
Vector3i pbc
Periodic boundary conditions. pbc=(1,0,1) implies periodicity along the and -directions.
Configuration * getConfiguration(double padding) const
This function returns a padded configuration by adding padding atoms originating due to pbcs.
Represents atomic configuration data including coordinates, velocities, species, and masses.
std::map< ConfigType, MatrixXd > coordinates
Map from configuration type (Reference or Current) to coordinate matrices.
const NeighListOne * nlOne_ptr
std::vector< double > fij
Definition kim.h:21
const double * getCutoffs() const
Definition kim.cpp:48
double influenceDistance
Definition kim.h:25
void compute()
Definition kim.cpp:303
void broadcastToModel(const Configuration *config_ptr, const VectorXi &particleContributing, const MatrixXd *forces_ptr, NeighList *nl_ptr, KIM::Function *get_neigh_ptr, InteratomicForces *bonds, KIM::Function *processDEDr_ptr)
Definition kim.cpp:254
std::string modelname
Definition kim.h:24
int getNumberOfNeighborLists() const
Definition kim.cpp:59
KIM::Model * kim_ptr
Definition kim.h:26
std::vector< double > project(const Eigen::VectorXd &gi) const
Definition Rigidity.cpp:40
void expandStencil(const std::vector< Vector3d > &gridCoordinates, const MatrixXd &coordinates, const double &contributingNeighborhoodSize, const double &noncontributingNeighborhoodSize)
Definition Stencil.h:30
Three-dimensional stress field on a grid.
Definition Stress.h:42
void progressBar(const double &progress)
Definition helper.hpp:116
void nbl_initialize(NeighList **const nl)
int nbl_build(NeighList *const nl, int const numberOfParticles, double const *coordinates, double const influenceDistance, int const numberOfCutoffs, double const *cutoffs, int const *needNeighbors)
void nbl_clean(NeighList **const nl)
#define DIM
int nbl_get_neigh(void const *const dataObject, int const numberOfCutoffs, double const *const cutoffs, int const neighborListIndex, int const particleNumber, int *const numberOfNeighbors, int const **const neighborsOfParticle)
NeighListOne * lists
#define MY_ERROR(message)
Definition typedef.h:17
Eigen::Matrix< double, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor > MatrixXd
Definition typedef.h:54
Eigen::Matrix< double, 1, DIM, Eigen::RowMajor > Vector3d
Definition typedef.h:60
@ Current
Definition typedef.h:70
@ Reference
Definition typedef.h:69
#define MY_HEADING(heading)
Definition typedef.h:36
#define MY_WARNING(message)
Definition typedef.h:24
#define MY_BANNER(announcement)
Definition typedef.h:30