MDStressLab++
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 <tuple>
22 #include <chrono>
23 
25  Kim& kim,
26  std::tuple<> stress)
27 {
28 
29  MY_WARNING("No stress calculation requested. Returning to caller.");
30  return 1;
31 
32 }
33 template<typename ...BF, StressType stressType>
35  Kim& kim,
36  std::tuple<Stress<BF,stressType>&...> stress)
37 {
38  std::tuple<> emptyTuple;
39  if (stressType == Piola)
40  return calculateStress(body,
41  kim,
42  stress,
43  emptyTuple);
44  else if (stressType == Cauchy)
45  return calculateStress(body,
46  kim,
47  emptyTuple,
48  stress);
49  else
50  MY_ERROR("Unrecognized stress type " + std::to_string(stressType));
51 }
52 
53 // This is the main driver of stress calculation
54 template<typename ...TStressPiola, typename ...TStressCauchy>
56  Kim& kim,
57  std::tuple<TStressPiola&...> piolaStress,
58  std::tuple<TStressCauchy&...> cauchyStress)
59 {
60  int status= 0;
61  int numberOfPiolaStresses= sizeof...(TStressPiola);
62  int numberOfCauchyStresses= sizeof...(TStressCauchy);
63 
64 
65  if (numberOfPiolaStresses == 0 && numberOfCauchyStresses == 0)
66  {
67  MY_WARNING("No stress calculation requested. Returning to caller.");
68  return 1;
69  }
70 
71  // At least one stress is being requested
72  MY_BANNER("Begin stress calculation");
73  auto start = std::chrono::system_clock::now();
74  std::time_t startTime = std::chrono::system_clock::to_time_t(start);
75  std::cout << "Time stamp: " << std::ctime(&startTime) << std::endl;
76 
77  // nullify the stress fields before starting
78  recursiveNullifyStress(piolaStress);
79  recursiveNullifyStress(cauchyStress);
80 
81  if (numberOfPiolaStresses > 0)
82  {
83  std::cout << "Number of Piola stresses requested : " << numberOfPiolaStresses << std::endl;
84  std::cout << std::endl;
85  std::cout << std::setw(25) << "Grid" << std::setw(25) << "Averaging domain size" << std::endl;
86  auto referenceGridDomainSizePairs= getTGridDomainSizePairs(piolaStress);
87  for (const auto& pair : referenceGridDomainSizePairs)
88  std::cout << std::setw(25) << (GridBase*) pair.first << std::setw(25) << pair.second << std::endl;
89 
90  if (body.coordinates.at(Reference).rows() == 0)
91  {
92  MY_WARNING("No reference coordinates detected to compute Piola stress.");
93  if (numberOfCauchyStresses>0)
94  {
95  MY_WARNING("Restarting stress calculation with only Cauchy stress.");
96  status= calculateStress(body,kim,cauchyStress);
97  if (status==1)
98  return status;
99  else
100  MY_ERROR("Error in stress computation.");
101  }
102  else
103  {
104  MY_BANNER("End of stress calculation");
105  return 1;
106  }
107  }
108  }
109 
110  if (numberOfCauchyStresses>0)
111  {
112  std::cout << "Number of Cauchy stresses requested: " << numberOfCauchyStresses << std::endl;
113  std::cout << std::endl;
114  std::cout << std::setw(25) << "Grid" << std::setw(25) << "Averaging domain size" << std::endl;
115  auto currentGridDomainSizePairs= getTGridDomainSizePairs(cauchyStress);
116  for (const auto& pair : currentGridDomainSizePairs)
117  std::cout << std::setw(25) << (GridBase*) pair.first << std::setw(25) << pair.second << std::endl;
118 
119 
120  }
121  std::cout << std::endl;
122 
123 
124  double maxAveragingDomainSize= std::max(averagingDomainSize_max(piolaStress),averagingDomainSize_max(cauchyStress));
125  std::cout << "Maximum averaging domain size across all stresses = " << maxAveragingDomainSize << std::endl;
126 
127  if (body.pbc.any() == 1)
128  {
129  std::unique_ptr<const Configuration> pconfig;
130  MY_HEADING("Generating padding atoms for periodic boundary conditions");
131 
132  double influenceDistance= kim.influenceDistance;
133  pconfig.reset(body.getConfiguration(2*influenceDistance+maxAveragingDomainSize));
134  std::cout << "Thickness of padding = 2 influence distance + maximum averaging domain size = "
135  << 2*influenceDistance+maxAveragingDomainSize << std::endl;
136  std::cout << "Total number of atoms including padding atoms = " << pconfig->numberOfParticles << std::endl;
137  std::cout << std::endl;
138 
139 
140  // TODO Change step to accommodate non-orthogonal boundary conditions
141  Vector3d origin= Vector3d::Zero();
142  Vector3d step= body.box.diagonal();
143  recursiveFold(origin,step,body.pbc,piolaStress);
144  recursiveFold(origin,step,body.pbc,cauchyStress);
145 
146  status= calculateStress(pconfig.get(),kim,piolaStress,cauchyStress);
147  }
148  else
149  {
150  status= calculateStress(&body,kim,piolaStress,cauchyStress);
151  }
152 
153 // recursiveWriteStressAndGrid(piolaStress);
154 // recursiveWriteStressAndGrid(cauchyStress);
155 
156  if (status==1)
157  {
158  std::cout << std::endl;
159  std::cout << std::endl;
160  std::cout << std::endl;
161  std::cout << std::endl;
162  auto end= std::chrono::system_clock::now();
163  std::time_t endTime= std::chrono::system_clock::to_time_t(end);
164  std::chrono::duration<double> elapsedSeconds(end-start);
165  std::cout << "Elapsed time: " << elapsedSeconds.count() << " seconds" << std::endl;
166  std::cout << "End of simulation" << std::endl;
167  MY_BANNER("End of stress calculation");
168  return status;
169  }
170  else
171  MY_ERROR("Error in stress computation.");
172 
173 }
174 
175 template<typename ...TStressPiola,typename ...TStressCauchy>
176 int calculateStress(const Configuration* pconfig,
177  Kim& kim,
178  std::tuple<TStressPiola&...> piolaStress,
179  std::tuple<TStressCauchy&...> cauchyStress)
180 {
181  assert(!(kim.kim_ptr==nullptr) && "Model not initialized");
182  double influenceDistance= kim.influenceDistance;
183  int numberOfPiolaStresses= sizeof...(TStressPiola);
184  int numberOfCauchyStresses= sizeof...(TStressCauchy);
185 
186 // ------------------------------------------------------------------
187 // Generate a local configuration
188 // ------------------------------------------------------------------
189  MY_HEADING("Building a local configuration");
190 
191 // Mappings from set of unique grids to the set of maximum averaging domain sizes
192  const auto referenceGridAveragingDomainSizeMap=
193  recursiveGridMaxAveragingDomainSizeMap(piolaStress);
194  const auto currentGridAveragingDomainSizeMap=
195  recursiveGridMaxAveragingDomainSizeMap(cauchyStress);
196 
197  Stencil stencil(*pconfig);
198 
199  if (numberOfPiolaStresses>0)
200  {
201  std::cout << "Piola Stress" << std::endl;
202  std::cout << std::setw(25) << "Unique grid" << std::setw(30) << "Max. Averaging domain size" << std::endl;
203  std::cout << std::setw(25) << "-----------" << std::setw(30) << "--------------------------" << std::endl;
204  for(const auto& [pgrid,domainSize] : referenceGridAveragingDomainSizeMap)
205  {
206  std::cout << std::setw(25) << (GridBase*)pgrid << std::setw(25) << domainSize << std::endl;
207  stencil.expandStencil(pgrid,domainSize+influenceDistance,influenceDistance);
208  }
209  }
210 
211  if (numberOfCauchyStresses>0)
212  {
213  std::cout << "Cauchy Stress" << std::endl;
214  std::cout << std::setw(25) << "Unique grid" << std::setw(30) << "Max. Averaging domain size" << std::endl;
215  std::cout << std::setw(25) << "-----------" << std::setw(30) << "--------------------------" << std::endl;
216  for(const auto& [pgrid,domainSize] : currentGridAveragingDomainSizeMap)
217  {
218  std::cout << std::setw(25) << (GridBase*)pgrid << std::setw(25) << domainSize << std::endl;
219  stencil.expandStencil(pgrid,domainSize+influenceDistance,influenceDistance);
220  }
221  }
222 
223  std::cout << std::endl;
224  std::cout << "Creating a local configuration using the above grids." << std::endl;
225  SubConfiguration subconfig{stencil};
226  int numberOfParticles= subconfig.numberOfParticles;
227  if (numberOfParticles == 0)
228  {
229  MY_ERROR("All grids away from the current material. Stresses are identically zero. Returning to the caller.")
230  return 1;
231  }
232  std::cout << "Number of particle in the local configuration = " << numberOfParticles << std::endl;
233  std::cout << "Number of contributing particles = " << subconfig.particleContributing.sum() << std::endl;
234 
235 
236 // ------------------------------------------------------------------
237 // Generate neighbor lists for all the grids
238 // ------------------------------------------------------------------
239  std::vector<std::vector<std::set<int>>> neighborListsOfGridsOne,neighborListsOfGridsTwo;
240 
241  auto referenceGridDomainSizePairs= getTGridDomainSizePairs(piolaStress);
242  assert(numberOfPiolaStresses==referenceGridDomainSizePairs.size());
243 
244  auto currentGridDomainSizePairs= getTGridDomainSizePairs(cauchyStress);
245  assert(numberOfCauchyStresses == currentGridDomainSizePairs.size());
246 
247  for(const auto& gridDomainSizePair : referenceGridDomainSizePairs)
248  {
249  const auto& pgrid= gridDomainSizePair.first;
250  const auto& domainSize= gridDomainSizePair.second;
251  neighborListsOfGridsOne.push_back(pgrid->getGridNeighborLists(subconfig,domainSize+influenceDistance));
252  neighborListsOfGridsTwo.push_back(pgrid->getGridNeighborLists(subconfig,domainSize+2*influenceDistance));
253  }
254  for(const auto& gridDomainSizePair : currentGridDomainSizePairs)
255  {
256  const auto& pgrid= gridDomainSizePair.first;
257  const auto& domainSize= gridDomainSizePair.second;
258  neighborListsOfGridsOne.push_back(pgrid->getGridNeighborLists(subconfig,domainSize+influenceDistance));
259  neighborListsOfGridsTwo.push_back(pgrid->getGridNeighborLists(subconfig,domainSize+2*influenceDistance));
260  }
261  assert(neighborListsOfGridsOne.size() == numberOfPiolaStresses + numberOfCauchyStresses &&
262  neighborListsOfGridsTwo.size() == numberOfPiolaStresses + numberOfCauchyStresses);
263 
264  std::vector<GridBase*> pgridListPiola= getBaseGridList(piolaStress);
265  std::vector<GridBase*> pgridListCauchy= getBaseGridList(cauchyStress);
266  std::vector<GridBase*> pgridList;
267  pgridList.reserve( pgridListPiola.size() + pgridListCauchy.size() );
268  pgridList.insert( pgridList.end(), pgridListPiola.begin(), pgridListPiola.end() );
269  pgridList.insert( pgridList.end(), pgridListCauchy.begin(), pgridListCauchy.end() );
270  assert(pgridList.size() == numberOfPiolaStresses + numberOfCauchyStresses);
271 
272 
273 
274 // ------------------------------------------------------------------
275 // Build neighbor list of particles
276 // ------------------------------------------------------------------
277  MY_HEADING("Building neighbor list");
278  const double* cutoffs= kim.getCutoffs();
279  int numberOfNeighborLists= kim.getNumberOfNeighborLists();
280 
281  // TODO: assert whenever the subconfiguration is empty
282  NeighList* nl;
283  nbl_initialize(&nl);
284  nbl_build(nl,numberOfParticles,
285  subconfig.coordinates.at(Current).data(),
286  influenceDistance,
287  numberOfNeighborLists,
288  cutoffs,
289  subconfig.particleContributing.data());
290 
291  int neighborListSize= 0;
292  for (int i_particle=0; i_particle<numberOfParticles; i_particle++)
293  neighborListSize+= nl->lists->Nneighbors[i_particle];
294  std::cout << "Size of neighbor list = " <<neighborListSize << std::endl;
295 
296 
297 // ------------------------------------------------------------------
298 // Building neighbor list for bonds
299 // ------------------------------------------------------------------
300  double bondCutoff= 2.0*influenceDistance;
301  NeighList* nlForBonds;
302  nbl_initialize(&nlForBonds);
303  nbl_build(nlForBonds,numberOfParticles,
304  subconfig.coordinates.at(Current).data(),
305  bondCutoff,
306  1,
307  &bondCutoff,
308  subconfig.particleContributing.data());
309  InteratomicForces bonds(nlForBonds);
310 
311 // ------------------------------------------------------------------
312 // Broadcast to model
313 // ------------------------------------------------------------------
314  MY_HEADING("Broadcasting to model");
315  kim.broadcastToModel(&subconfig,
316  subconfig.particleContributing,
317  nl,
318  (KIM::Function*) &nbl_get_neigh,
319  &bonds,
320  (KIM::Function*) &process_DEDr);
321  std::cout << "Done" << std::endl;
322 
323 // ------------------------------------------------------------------
324 // Compute forces
325 // ------------------------------------------------------------------
326  MY_HEADING("Computing forces");
327  kim.compute();
328  std::cout << "Done" << std::endl;
329 
330 // ------------------------------------------------------------------
331 // Loop over local grid points and accumulate stress
332 // ------------------------------------------------------------------
333  MY_HEADING("Looping over grids");
334 
335  Vector3d ra,rA,rb,rB,rab,rAB;
336  ra= rA= rb= rB= rab= rAB= Vector3d::Zero();
337  int i_grid= 0;
338  for(const auto& pgrid : pgridList)
339  {
340  const std::vector<std::set<int>>& neighborListsOne= neighborListsOfGridsOne[i_grid];
341  const std::vector<std::set<int>>& neighborListsTwo= neighborListsOfGridsTwo[i_grid];
342 
343  int i_gridPoint= 0;
344  double progress= 0;
345  int numberOfGridPoints= pgrid->coordinates.size();
346  std::cout << i_grid+1 << ". Number of grid points: " << numberOfGridPoints << std::endl;
347  for (const auto& gridPoint : pgrid->coordinates)
348  {
349  if ( numberOfGridPoints<10 || (i_gridPoint+1)%(numberOfGridPoints/10) == 0)
350  {
351  progress= (double)(i_gridPoint+1)/numberOfGridPoints;
352  progressBar(progress);
353  }
354  const std::set<int>& neighborListOne= neighborListsOne[i_gridPoint];
355  const std::set<int>& neighborListTwo= neighborListsTwo[i_gridPoint];
356  for (const auto& particle1 : neighborListOne)
357  {
358  if(numberOfPiolaStresses>0) rA= subconfig.coordinates.at(Reference).row(particle1) - gridPoint;
359  ra= subconfig.coordinates.at(Current).row(particle1) - gridPoint;
360  int index;
361  int numberOfNeighborsOf1= bonds.nlOne_ptr->Nneighbors[particle1];
362 
363 // Loop through the bond neighbors of particle1
364  for(int i_neighborOf1= 0; i_neighborOf1<numberOfNeighborsOf1; i_neighborOf1++)
365  {
366  index= bonds.nlOne_ptr->beginIndex[particle1]+i_neighborOf1;
367  double fij= bonds.fij[index];
368  if (fij == 0) continue;
369 
370 // At this point, the force in the bond connecting particles 1 and 2 is nonzero
371  int particle2= bonds.nlOne_ptr->neighborList[index];
372  if(numberOfPiolaStresses>0) rB= subconfig.coordinates.at(Reference).row(particle2) - gridPoint;
373  rb= subconfig.coordinates.at(Current).row(particle2) - gridPoint;
374 // Ignore if (particle2 is in neighborListOne and particle 1 > particle 2) as this pair
375 // is encountered twice
376  if ( (neighborListOne.find(particle2) != neighborListOne.end() && particle1 < particle2))
377  continue;
378 
379 // Since neighborListOne \subset of neighborListTwo, the following condition if(A||B)
380 // is equivalent if(B). Nevertheless, we have if(A||B) since B is expensive, and it is never
381 // evaluated if A is true.
382 
383  if ( neighborListOne.find(particle2) != neighborListOne.end() ||
384  neighborListTwo.find(particle2) != neighborListTwo.end())
385  {
386  if(numberOfPiolaStresses>0) rAB= subconfig.coordinates.at(Reference).row(particle1)-subconfig.coordinates.at(Reference).row(particle2);
387  rab= subconfig.coordinates.at(Current).row(particle1)-subconfig.coordinates.at(Current).row(particle2);
388  if (i_grid<numberOfPiolaStresses)
389  recursiveBuildStress(fij,ra,rA,rb,rB,rab,rAB,i_gridPoint,i_grid,piolaStress);
390  else
391  recursiveBuildStress(fij,ra,rA,rb,rB,rab,rAB,i_gridPoint,i_grid-numberOfPiolaStresses,cauchyStress);
392  }
393  }
394  }
395  i_gridPoint++;
396  }
397  std::cout << "Done with grid " << pgrid << std::endl;
398  std::cout << std::endl;
399 
400  i_grid++;
401  }
402 
403  // Collect all the stress fields in processor 0
404 
405  nbl_clean(&nl);
406  nbl_clean(&nlForBonds);
407  return 1;
408 }
409 
410 
411 int process_DEDr(const void* dataObject, const double de, const double r, const double* const dx, const int i, const int j)
412  {
413  InteratomicForces* bonds_ptr= (InteratomicForces*) dataObject;
414  int index;
415  int numberOfNeighborsOfi = bonds_ptr->nlOne_ptr->Nneighbors[i];
416  int numberOfNeighborsOfj = bonds_ptr->nlOne_ptr->Nneighbors[j];
417  bool iFound= false;
418  bool jFound= false;
419 
420  // Look for j in the neighbor list of i
421  for(int i_neighborOfi= 0; i_neighborOfi<numberOfNeighborsOfi; i_neighborOfi++)
422  {
423  index= bonds_ptr->nlOne_ptr->beginIndex[i]+i_neighborOfi;
424  if(bonds_ptr->nlOne_ptr->neighborList[index] == j)
425  {
426  bonds_ptr->fij[index]+= de;
427  jFound= true;
428  break;
429  }
430  }
431  // Look for i in the neighbor list of j
432  for(int i_neighborOfj= 0; i_neighborOfj<numberOfNeighborsOfj; i_neighborOfj++)
433  {
434  index= bonds_ptr->nlOne_ptr->beginIndex[j]+i_neighborOfj;
435  if(bonds_ptr->nlOne_ptr->neighborList[index] == i)
436  {
437  bonds_ptr->fij[index]+= de;
438  iFound= true;
439  break;
440  }
441 
442  }
443  return 0;
444  }
#define MY_HEADING(heading)
Definition: typedef.h:36
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
std::map< ConfigType, MatrixXd > coordinates
Definition: Configuration.h:24
void nbl_initialize(NeighList **const nl)
void broadcastToModel(const Configuration *config_ptr, const VectorXi &particleContributing, NeighList *nl_ptr, KIM::Function *get_neigh_ptr, InteratomicForces *bonds, KIM::Function *processDEDr_ptr)
Definition: kim.cpp:252
#define MY_WARNING(message)
Definition: typedef.h:24
void progressBar(const double &progress)
Definition: helper.hpp:116
StressType
Definition: typedef.h:63
Configuration * getConfiguration(double) const
void nbl_clean(NeighList **const nl)
Definition: kim.h:17
#define MY_BANNER(announcement)
Definition: typedef.h:30
NeighListOne * lists
Definition: neighbor_list.h:27
Definition: Grid.h:15
double influenceDistance
Definition: kim.h:22
const double * getCutoffs() const
Definition: kim.cpp:46
void expandStencil(const Grid< configType > *pgrid, const double &contributingNeighborhoodSize, const double &noncontributingNeighborhoodSize)
Definition: Stencil.h:31
Definition: Stress.h:23
#define MY_ERROR(message)
Definition: typedef.h:17
std::vector< double > fij
int * beginIndex
Definition: neighbor_list.h:20
int calculateStress(const BoxConfiguration &body, Kim &kim, std::tuple<> stress)
Eigen::Matrix< double, 1, DIM, Eigen::RowMajor > Vector3d
Definition: typedef.h:60
KIM::Model * kim_ptr
Definition: kim.h:20
Definition: typedef.h:64
int * neighborList
Definition: neighbor_list.h:19
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)
void compute()
Definition: kim.cpp:290
const NeighListOne * nlOne_ptr
int getNumberOfNeighborLists() const
Definition: kim.cpp:57
int nbl_build(NeighList *const nl, int const numberOfParticles, double const *coordinates, double const influenceDistance, int const numberOfCutoffs, double const *cutoffs, int const *needNeighbors)
int * Nneighbors
Definition: neighbor_list.h:18
Definition: typedef.h:65
int process_DEDr(const void *dataObject, const double de, const double r, const double *const dx, const int i, const int j)