SolidProblem.h
Go to the documentation of this file.
1 // This file is part of the MercuryDPM project (https://www.mercurydpm.org).
2 // Copyright (c), The MercuryDPM Developers Team. All rights reserved.
3 // License: BSD 3-Clause License; see the LICENSE file in the root directory.
4 
10 #ifndef SOLID_PROBLEM_H
11 #define SOLID_PROBLEM_H
12 
13 // Generic oomph-lib headers
14 #include "generic.h"
15 #include "solid.h"
16 #include "constitutive.h"
17 #include "Math/Vector.h"
18 #include <array>
19 #include <cstdio>
20 #include "OomphHelpers.h"
21 #include "AnisotropicHookean.h"
22 #include "RefineableQDPVDElement.h"
23 
24 //The mesh
25 #include "solid_cubic_mesh.h"
26 #include "meshes/tetgen_mesh.h"
27 
29 
30 using namespace oomph;
31 
32 #ifdef OOMPH_HAS_MPI
33  #define OOMPH_MPI_PROCESSOR_NUM communicator_pt()->nproc()
34 #else
35  #define OOMPH_MPI_PROCESSOR_NUM 1
36 #endif
37 
38 #ifdef OOMPH_HAS_MPI
39  #define OOMPH_MPI_PROCESSOR_ID communicator_pt()->my_rank()
40 #else
41  #define OOMPH_MPI_PROCESSOR_ID 0
42 #endif
43 
44 
58 template<class ELEMENT_TYPE>
59 class SolidProblem : public Problem
60 {
61 protected:
62  //define element type (should be done by template)
63  typedef ELEMENT_TYPE ELEMENT;
64 
65  // name of output files (user-defined)
67 
69  double elasticModulus_ = 0;
70 
72  double poissonRatio_ = 0;
73 
75  double density_ = 0;
76 
78  double gravity_ = 0;
79 
81  void (* body_force_fct)(const double& time, const Vector<double>& xi, Vector<double>& b) = nullptr;
82 
84  ConstitutiveLaw* constitutive_law_pt = nullptr;
85 
87  SolidMesh* Solid_mesh_pt = nullptr;
88 
90  SolidMesh* Traction_mesh_pt = nullptr;
91 
93  std::function<bool(SolidNode*, unsigned)> isPinned_;
94 
95 public:
96 
99  {
100  logger(INFO, "Set default constitutive law (AnisotropicHookean) and time stepper (Newmark<2>)");
101 
102  // Set Newton solver tolerance and maximum number of Newton iterations
104  Newton_solver_tolerance = 1e-10;
105  Max_residuals = constants::inf;
106 
107  // Set constitutive law
108  constitutive_law_pt = new AnisotropicHookean(&poissonRatio_, &elasticModulus_);
109 
110  // Allocate the timestepper
111  add_time_stepper_pt(new Newmark<2>);
112 
113  //build_mesh();
114 
115  // setup boundary conditions
116  //pin_and_assign_eqn_numbers();
117  };
118 
120  void setName(const std::string& name)
121  {
122  name_ = name;
123  logger(INFO, "Name: %", name_);
124  }
125 
128  {
129  return name_;
130  }
131 
133  void setElasticModulus(double elasticModulus)
134  {
135  elasticModulus_ = elasticModulus;
136  logger(INFO, "Elastic Modulus: %", elasticModulus_);
137  }
138 
140  double getElasticModulus() const
141  {
142  return elasticModulus_;
143  }
144 
145  // add dissipation
146  void addDissipation(double dissipation)
147  {
148  logger(INFO,"Adding dissipation %",dissipation);
149  for (int i = 0; i < solid_mesh_pt()->nelement(); ++i)
150  {
151  dynamic_cast<ELEMENT*>(solid_mesh_pt()->element_pt(i))->dissipation_ = dissipation;
152  }
153  }
154 
155  void setupRefinementParameters(const double& min_error_target, const double& max_error_target, Z2ErrorEstimator*& error_estimator_pt)
156  {
157  RefineableSolidCubicMesh<ELEMENT_TYPE>* refineable_mesh_pt = dynamic_cast<RefineableSolidCubicMesh<ELEMENT_TYPE>*>(solid_mesh_pt());
158  // Set error estimator
159  refineable_mesh_pt->spatial_error_estimator_pt()=error_estimator_pt;
160  // Error targets for adaptive refinement
161  refineable_mesh_pt->max_permitted_error()=max_error_target;
162  refineable_mesh_pt->min_permitted_error()=min_error_target;
163  }
164 
165  void addAnisotropy(double Ex, double Ey, double Ez)
166  {
167  logger(INFO,"Adding anisotropy E = [% % %]",Ex, Ey, Ez);
168  // make constitutive law anisotropic in x-direction
169  elasticModulus_ = Ex;
170  auto hooke_law_pt = dynamic_cast<AnisotropicHookean*>(constitutive_law_pt);
171  hooke_law_pt->setAnisotropy({1.0, Ey/Ex, Ez/Ex});
172  }
173 
176  {
177  gravity_ = gravity;
178  logger(INFO, "Elastic Modulus: %", gravity_);
179  }
180 
182  double getOomphGravity() const
183  {
184  return gravity_;
185  }
186 
188  void setPoissonRatio(double poissonRatio)
189  {
190  poissonRatio_ = poissonRatio;
191  logger(INFO, "Poisson Ratio: %", poissonRatio_);
192  }
193 
195  double getPoissonRatio() const
196  {
197  return poissonRatio_;
198  }
199 
201  void setDensity(double density)
202  {
203  density_ = density;
204  logger(INFO, "Density: %", density_);
205  }
206 
208  double getDensity() const
209  {
210  return density_;
211  }
212 
214  void setBodyForceAsGravity(double gravity = 9.8)
215  {
216  logger(INFO, "Setting oomph-gravity in z-direction");
217  gravity_ = gravity;
218  // define a static body force
219  static double& Density = density_;
220  static double& Gravity = gravity_;
221  body_force_fct = [](const double& time, const Vector<double>& xi, Vector<double>& b) {
222  b[0] = 0.0;
223  b[1] = 0.0;
224  b[2] = -Gravity * Density;
225  };
226  }
227 
229  void setIsPinned(std::function<bool(SolidNode*, unsigned)> isPinned)
230  {
231  isPinned_ = std::move(isPinned);
232  logger(INFO, "Setting which positions on which nodes are pinned");
233  }
234 
235  // set is_pinned such that a certain boundary is pinned
236  void pinBoundary(unsigned b) {
237  logger(INFO, "Pinning nodes on boundary %", b);
238  isPinned_ = [b](SolidNode *n, unsigned d) {
239  return n->is_on_boundary(b);
240  };
241  }
242 
243  // set is_pinned such that a certain boundary is pinned
244  void pinBoundaries(std::vector<unsigned> b) {
245  for (const auto a: b) {
246  logger(INFO, "Pinning nodes on boundary %", a);
247  }
248  isPinned_ = [b](SolidNode *n, unsigned d) {
249  for (const auto a : b) {
250  if (n->is_on_boundary(a)) return true;
251  }
252  return false;
253  };
254  }
255 
257  std::enable_if<std::is_base_of<RefineableQDPVDElement<3,2>, ELEMENT>::value, void> setDissipation(double dissipation)
258  {
259  for (int i = 0; i < solid_mesh_pt()->nelement(); ++i)
260  {
261  dynamic_cast<RefineableQDPVDElement<3,2>*>(solid_mesh_pt()->element_pt(i))->setDissipation(dissipation);
262  }
263  }
264 
266  void setNewtonSolverTolerance(double Newton_solver_tolerance)
267  {
268  this->Newton_solver_tolerance = Newton_solver_tolerance;
269  }
270 
273  {
274  this->Max_newton_iterations = Max_newton_iterations;
275  }
276 
278  void setOomphTimeStep(double dt) {
279  time_pt()->initialise_dt(dt);
280  }
281 
283  double getOomphTimeStep () const {
284  return time_pt()->dt();
285  }
286 
288  double getOomphTime () const {
289  return time_pt()->time();
290  }
291 
293  SolidMesh*& solid_mesh_pt() { return Solid_mesh_pt; }
294 
296  SolidMesh*& traction_mesh_pt() { return Traction_mesh_pt; }
297 
299  SolidMesh* const & solid_mesh_pt() const { return Solid_mesh_pt; }
300 
302  SolidMesh* const & traction_mesh_pt() const { return Traction_mesh_pt; }
303 
305  void getDomainSize(std::array<double, 3>& min, std::array<double, 3>& max) const
306  {
307  min[0] = min[1] = min[2] = constants::inf;
308  max[0] = max[1] = max[2] = -constants::inf;
309  logger.assert_always(solid_mesh_pt(),"mesh not found");
310  for (unsigned i = 0; i < solid_mesh_pt()->nnode(); i++)
311  {
312  const auto n = solid_mesh_pt()->node_pt(i);
313  for (int j = 0; j < 3; ++j)
314  {
315  min[j] = std::min(min[j], n->xi(j));
316  max[j] = std::max(max[j], n->xi(j));
317  }
318  }
319  }
320 
332  {
333  // check certain values are set
334  logger.assert_always(!name_.empty(), "Set name via setName(..)");
335  logger.assert_always(elasticModulus_>0, "Set elastic modulus via setElasticModulus(..)");
336  logger.assert_always(density_>0, "Set density via setDensity(..)");
337  logger.assert_always(solid_mesh_pt(), "Set solid mesh via e.g. setSolidCubicMesh(..)");
338 
339  // Assign constitutive_law_pt and body_force_fct_pt of each element
340  logger(INFO, "Assign constitutive_law, body_force, density to all elements");
341  for (unsigned i = 0; i < solid_mesh_pt()->nelement(); i++)
342  {
343  //Cast to a solid element
344  ELEMENT* el_pt = dynamic_cast<ELEMENT*>(solid_mesh_pt()->element_pt(i));
346  // Set the constitutive law
347  el_pt->constitutive_law_pt() = constitutive_law_pt;
348  // Set body force
349  el_pt->body_force_fct_pt() = body_force_fct;
350  // Set density
351  el_pt->lambda_sq_pt() = &density_;
352  }
353 
354  // Construct the traction element mesh
355  // Traction_mesh_pt = new SolidMesh;
356  // create_traction_elements();
357 
358  //Build combined "global" mesh
359  add_sub_mesh(solid_mesh_pt());
360  if (traction_mesh_pt()) {
361  add_sub_mesh(traction_mesh_pt());
362  logger(INFO, "Built global mesh from solid mesh and traction mesh");
363  } else {
364  logger(INFO, "Built global mesh from solid mesh");
365  }
366  build_global_mesh();
367 
368  //Pin nodes
369  if (isPinned_)
370  {
371  for ( unsigned n = 0; n < solid_mesh_pt()->nnode(); n++ )
372  {
373  SolidNode* n_pt = solid_mesh_pt()->node_pt( n );
374  //Pin all nodes
375  for ( unsigned i = 0; i < 3; i++ )
376  {
377  if ( isPinned_( n_pt, i ) )
378  {
379  n_pt->pin_position( i );
380  }
381  else
382  {
383  n_pt->unpin_position( i );
384  }
385  }
386  }
387  }
388  countPinned();
389 
390  // Pin the redundant solid pressures (if any)
392  solid_mesh_pt()->element_pt());
393  logger(INFO, "Pinned redundant nodal solid pressures");
394 
395  // Attach the boundary conditions to the mesh
396  unsigned n_eq = assign_eqn_numbers();
397  logger(INFO, "Assigned % equation numbers", n_eq);
398  }
399 
401  void countPinned()
402  {
403  // count pinned
404  std::array<unsigned,3> countPinned {0,0,0};
405  unsigned countAll = 0;
406  for (unsigned n = 0; n < solid_mesh_pt()->nnode(); n++)
407  {
408  for (unsigned i = 0; i < 3; i++)
409  {
410  countPinned[i] += solid_mesh_pt()->node_pt(n)->position_is_pinned(i);
411  countAll++;
412  }
413  }
414  unsigned countPinnedAll = countPinned[0] + countPinned[1] +countPinned[2];
415  logger(INFO, "Pinned % of % positions (% free): % in x, % in y, % in z", countPinnedAll, countAll, countAll - countPinnedAll, countPinned[0], countPinned[1], countPinned[2]);
416  }
417 
421  void solveSteady(const unsigned& max_adapt=0)
422  {
423  logger.assert_always(mesh_pt(), "Mesh pointer not set; did you call prepareForSolve?");
424  logger(INFO, "Solve steady-state problem");
425  actionsBeforeSolve();
426  if(max_adapt==0) newton_solve();
427  else newton_solve(max_adapt);
428  actionsAfterSolve();
429  writeToVTK();
430  saveSolidMesh();
431  }
432 
433  virtual void actionsBeforeSolve() {}
434 
435  virtual void actionsAfterSolve() {}
436 
437  virtual void actionsBeforeOomphTimeStep() {}
438 
442  void solveUnsteady(double timeMax, double dt, unsigned saveCount = 10, const unsigned& max_adapt = 0)
443  {
444  logger.assert_always(mesh_pt(), "Mesh pointer not set; did you call prepareForSolve?");
445 
446  std::cout << "Solving oomph with dt=" << dt << " until timeMax=" << timeMax << std::endl;
447 
448  // Setup initial conditions. Default is a non-impulsive start
449  this->time_pt()->initialise_dt(dt);
450  assign_initial_values_impulsive(dt);
451  actionsBeforeSolve();
452 
453  // This is the main loop over advancing time
454  double& time = time_stepper_pt()->time();
455  unsigned count = 0;
456  const unsigned countMax = std::ceil(timeMax/dt);
457  while (time < timeMax)
458  {
459  logger(INFO, "Time %s of %s (% of %)", time, timeMax, count, countMax);
460  actionsBeforeOomphTimeStep();
461  // solve the oomphProb for one time step (this also increments time)
463  //adaptive_unsteady_newton_solve(dt,2e-7);
464  if(max_adapt == 0)
465  {
466  unsteady_newton_solve(dt, true);
467  }
468  else
469  {
470  unsteady_newton_solve(dt, max_adapt, false, true);
471  }
472  // increase count
474  //count++;
475  // write outputs of the oomphProb
476  if (count++ % saveCount == 0 or time + dt > timeMax) {
477  writeToVTK();
478  // save in case code breaks
479  // saveSolidMesh();
480  }
481  // abort if problem
482  if (Max_res.back()==0) {
483  saveSolidMesh();
484  logger(ERROR, "Maximum residual is 0; exiting the code");
485  }
486  }
487  saveSolidMesh();
488  actionsAfterSolve();
489  }
490 
494  void solveUnsteadyForgiving(double timeMax, double timeMaxMin, double dt, unsigned saveCount = 10) {
495  // solve
496  try {
497  solveUnsteady(timeMax, dt, saveCount);
498  } catch(OomphLibError& error) {
499  //Store output if newton solver fails
500  saveSolidMesh();
501  if (time_stepper_pt()->time()-dt >= timeMaxMin) {
502  // take it as successful if a fraction of the time evolution has finished
503  logger(INFO,"Newton solver failed at t=% (tMax=%), but code will continue.",
504  time_stepper_pt()->time()-dt, timeMax);
505  exit(0);
506  } else {
507  logger(ERROR,"Newton solver failed at t=% (tMax=%).",
508  time_stepper_pt()->time()-dt, timeMax);
509  }
510  }
511  }
512 
516  void removeOldFiles() const
517  {
518  for (int i = 0; true; ++i)
519  {
520  std::string fileName = name_ + "FEM_" + std::to_string(i) + ".vtu";
521  if (remove(fileName.c_str())) break;
522  std::cout << "Deleted " << fileName << '\n';
523  }
524  }
525 
529  void get_x(const Vector<double>& xi, Vector<double>& x) const
530  {
531  if (OOMPH_MPI_PROCESSOR_NUM>1) {
532  logger(INFO, "get_x does not work with MPI");
533  for (int i = 0; i < 3; ++i) {
534  x[i] = xi[i];
535  }
536  } else {
537  Vector<double> s(3);
538  GeomObject *geom_obj_pt = nullptr;
539  const unsigned long nelement = solid_mesh_pt()->nelement();
540  for (unsigned long i = 0; i < nelement; i++) {
541  auto el_pt = dynamic_cast<ELEMENT *>(solid_mesh_pt()->element_pt(i));
542  el_pt->locate_zeta(xi, geom_obj_pt, s);
543  if (geom_obj_pt) {
544  //logger(INFO,"Point % % % is in element % at % % %",
545  // xi[0],xi[1],xi[2],i,s[0], s[1], s[2]);
546  el_pt->interpolated_x(s, x); //deformed coordinate
547  return;
548  }
549  }
550  logger(ERROR, "x(xi) could not be found");
551  }
552  }
553 
557  double getDeflection(Vector<double> xi, unsigned d) const
558  {
559  Vector<double> x(3);
560  get_x(xi, x);
561  return x[d] - xi[d];
562  }
563 
564  // Boundary types (from cubic mesh)
565  enum Boundary : unsigned
566  {
567  Z_MIN = 0, Y_MIN = 1, X_MAX = 2, Y_MAX = 3, X_MIN = 4, Z_MAX = 5
568  };
569 
570  // Create a solid cubic mesh
571  void setSolidCubicMesh(const unsigned& nx, const unsigned& ny, const unsigned& nz,
572  const double& xMin, const double& xMax, const double& yMin,
573  const double& yMax, const double& zMin, const double& zMax)
574  {
575  // Create mesh
576  solid_mesh_pt() = new RefineableSolidCubicMesh<ELEMENT_TYPE>(nx, ny, nz, xMin, xMax, yMin, yMax, zMin, zMax, time_stepper_pt());
577 
578  logger(INFO, "Created %x%x% cubic mesh on domain [%,%]x[%,%]x[%,%]",
579  nx, ny, nz, xMin, xMax, yMin, yMax, zMin, zMax);
580  }
581 
583  {
584  std::string filename = name_;
585 #ifdef OOMPH_HAS_MPI
586  // When the problem hasn't been distributed, the whole mesh is stored on every processor.
587  // Prevent writing the same file multiple times, so only write for the first processor.
588  if (!Problem_has_been_distributed && OOMPH_MPI_PROCESSOR_ID != 0)
589  return;
590 
591  // When the problem has been distributed, add the processor id to the filename.
592  if (Problem_has_been_distributed)
594 #endif
595  filename += ".mesh";
596 
597  //if (useTetgen()) logger(ERROR, "Not implemented for Tetgen meshes");
598 
599  std::ofstream mesh(filename);
600  auto solid_cubic_mesh_pt = dynamic_cast<RefineableSolidCubicMesh<ELEMENT_TYPE>*>(solid_mesh_pt());
601  if (solid_cubic_mesh_pt) {
602  //logger.assert_always(solid_cubic_mesh_pt, "Mesh is not cubic");
603  mesh << solid_cubic_mesh_pt->nx() << ' ';
604  mesh << solid_cubic_mesh_pt->ny() << ' ';
605  mesh << solid_cubic_mesh_pt->nz() << '\n';
606  }
607  for (int i = 0; i < solid_mesh_pt()->nnode(); ++i)
608  {
609  SolidNode* n = solid_mesh_pt()->node_pt(i);
610  for (int j = 0; j < 3; ++j)
611  {
612  mesh << n->xi(j) << ' ' << n->x(j) << ' ' << n->position_is_pinned(j) << ' ';
613  }
614  mesh << '\n';
615  }
616  if (solid_cubic_mesh_pt) {
617  logger(INFO, "Saved %x%x% mesh to %",
618  solid_cubic_mesh_pt->nx(), solid_cubic_mesh_pt->ny(), solid_cubic_mesh_pt->nz(), filename);
619  } else {
620  logger(INFO, "Saved mesh to % (% nodes)", filename, solid_mesh_pt()->nnode());
621  }
622  }
623 
624  void loadSolidMesh(std::string infileName, bool cubic=true)
625  {
626  logger(INFO, "Loading % (cubic=%)",infileName, cubic);
627  //if (useTetgen()) logger(ERROR, "Not implemented for Tetgen meshes");
628 
629  std::ifstream mesh(infileName);
630  logger.assert_always(mesh.good(),"Mesh file % could not be opened",infileName);
631 
632  if (cubic) {
633  unsigned nx, ny, nz;
634  mesh >> nx >> ny >> nz;
635  logger(INFO, "Loaded %x%x% cubic mesh from %", nx, ny, nz, infileName);
636  logger.assert_debug(nx > 1 and ny > 1 and nz > 1, "Mesh size invalid");
637  Solid_mesh_pt = new RefineableSolidCubicMesh<ELEMENT_TYPE>(nx, ny, nz, 0, 1, 0, 1, 0, 1, time_stepper_pt());
638  // Assign physical properties to the elements before any refinement
639  for (unsigned i = 0; i < solid_mesh_pt()->nelement(); i++)
640  {
641  //Cast to a solid element
642  ELEMENT* el_pt = dynamic_cast<ELEMENT*>(solid_mesh_pt()->element_pt(i));
643  // Set the constitutive law
644  el_pt->constitutive_law_pt() = constitutive_law_pt;
645  }
646  }
647 
648  double xi, x;
649  bool pin;
650  logger(INFO, "Loading % nodes", solid_mesh_pt()->nnode());
651  for (int i = 0; i < solid_mesh_pt()->nnode(); ++i)
652  {
653  SolidNode* n = solid_mesh_pt()->node_pt(i);
654  for (int j = 0; j < 3; ++j)
655  {
656  mesh >> xi >> x >> pin;
657  n->xi(j) = xi;
658  n->x(j) = x;
659  if (pin) n->pin_position(j);
660  }
661  }
662  }
663 
664  // stores results in vtk file
665  void writeToVTK()
666  {
667 #ifdef OOMPH_HAS_MPI
668  // When the problem hasn't been distributed, the whole mesh is stored on every processor.
669  // Prevent writing the same file multiple times, so only write for the first processor.
670  if (!Problem_has_been_distributed && OOMPH_MPI_PROCESSOR_ID != 0)
671  return;
672 #endif
673 
674  //set local coordinates list
675  std::vector<std::vector<double>> sList0;
676  // order of nodes
677  std::vector<unsigned> nList;
678  // vtkFormat for given shape
679  // https://kitware.github.io/vtk-examples/site/VTKFileFormats/
680  unsigned vtkFormat;
681 
682  // define differently for tetrahedral and hexahedral (quad) elements
683  if (std::is_base_of<SolidTElement<3, 2>, ELEMENT>::value)
684  {
685  vtkFormat = 10; // TETRA
686 
687  sList0 = {
688  {1, 0, 0},
689  {0, 1, 0},
690  {0, 0, 1},
691  {0, 0, 0}
692  };
693 
694  nList = {0, 1, 2, 3};
695  }
696  else if (std::is_base_of<SolidQElement<3, 2>, ELEMENT>::value)
697  {
698  vtkFormat = 12; // HEXAHEDRON
699 
700  sList0 = {
701  {-1, -1, -1},
702  {-1, -1, +1},
703  {-1, +1, +1},
704  {-1, +1, -1},
705  {+1, -1, -1},
706  {+1, -1, +1},
707  {+1, +1, +1},
708  {+1, +1, -1}
709  };
710  // order of nodes
711  nList = {0, 4, 6, 2, 1, 5, 7, 3};
712  } else {
713  logger(ERROR,"Element type unknown");
714  }
715 
716  // convert to Vector
717  std::vector<Vector<double>> sList;
718  for (auto s0 : sList0)
719  {
720  Vector<double> s(3);
721  s[0] = s0[0];
722  s[1] = s0[1];
723  s[2] = s0[2];
724  sList.push_back(s);
725  }
726 
727  // number of cells
728  unsigned nel = solid_mesh_pt()->nelement();
729 
730  // set up vtu Points
731  struct Point
732  {
733  Vector<double> coordinate;
734  struct Data
735  {
737  std::vector<double> value;
738  };
739  std::vector<Data> data;
740  };
741  std::vector<Point> points;
742  points.reserve(nel * sList.size());
743 
744  // set up vtu Cells
745  struct Cell
746  {
747  std::vector<unsigned long> connectivity;
748  unsigned long offset;
749  unsigned type;
750  };
751  std::vector<Cell> cells;
752  points.reserve(nel);
753 
754  //for all elements
755  for (unsigned e = 0; e < nel; e++)
756  {
757  // Get pointer to element
758  auto el_pt = dynamic_cast<ELEMENT*>(
759  solid_mesh_pt()->element_pt(e));
760 
761  std::vector<unsigned long> connectivity;
762  unsigned ix = 0; // node index
763  for (const auto& s : sList)
764  {
765  // pointer to node info
766  auto n = nList[ix];
767  auto n_pt = dynamic_cast<SolidNode*>(el_pt->node_pt(n));
768  // Get Eulerian coordinates
769  Vector<double> x(3);
770  el_pt->interpolated_x(s, x);
771  // get velocity
772  Vector<double> dxdt(3);
773  el_pt->interpolated_dxdt(s, 1, dxdt);
774  // get displacement
775  Vector<double> xi(3);
776  el_pt->interpolated_xi(s, xi);
777  // getBodyForce
779  auto bodyForceFct = el_pt->body_force_fct_pt();
780  if (bodyForceFct) bodyForceFct(time(), xi, body_force);
781  // get coupling residual (fails)
782  //std::vector<double> couplingResidual
783  // {el_pt->get_nodal_coupling_residual(n, 0),
784  // el_pt->get_nodal_coupling_residual(n, 1),
785  // el_pt->get_nodal_coupling_residual(n, 2)};
786  // get stress/pressure
788  el_pt->get_stress(s, sigma);
789  // first invariant
790  double pressure = (sigma(0,0)+sigma(1,1)+sigma(2,2))/3;
791  //https://en.wikipedia.org/wiki/Von_Mises_yield_criterion
792  double J2 = 0;
793  for (int i = 0; i < 3; ++i) {
794  for (int j = 0; j < 3; ++j) {
795  J2 += 0.5*sigma(i,j)*sigma(i,j);
796  }
797  }
798  //std::cout << " " << J2;
799  // get strain
800  DenseMatrix<double> strain(3,3);
801  el_pt->get_strain(s, strain);
802  // get velocity (fails)
803  std::vector<double> dudt
804  {el_pt->dnodal_position_dt(n, 0),
805  el_pt->dnodal_position_dt(n, 1),
806  el_pt->dnodal_position_dt(n, 2)};
807  // get boundary (works)
808  std::set<unsigned>* boundaries_pt;
809  n_pt->get_boundaries_pt(boundaries_pt);
810  double b = boundaries_pt ? *boundaries_pt->begin()+1 : 0;
811  std::vector<double> pin {(double) n_pt->position_is_pinned(0),
812  (double) n_pt->position_is_pinned(1),
813  (double) n_pt->position_is_pinned(2)};
814  points.push_back(
815  {x, {
816  {"Velocity", {dxdt[0], dxdt[1], dxdt[2]}},
817  {"Displacement", {x[0] - xi[0], x[1] - xi[1], x[2] - xi[2]}},
818  {"BodyForce", {body_force[0], body_force[1], body_force[2]}},
819  {"Pin", pin},
820  //{"CouplingResidual", couplingResidual},
821  {"Velocity2", dudt},
822  {"Pressure", {pressure}},
823  {"J2", {J2}},
824  {"Stress", {sigma(0,0), sigma(0,1), sigma(0,2),
825  sigma(1,0), sigma(1,1), sigma(1,2),
826  sigma(2,0), sigma(2,1), sigma(2,2)}},
827 // {"Strain", {strain(0,0), strain(0,1), strain(0,2),
828 // strain(1,0), strain(1,1), strain(1,2),
829 // strain(2,0), strain(2,1), strain(2,2)}},
830  //\todo undo this comment, but it causes a problem in the output
831  {"Boundary", {b}}
832  }});
833 
834  // add to connectivity
835  connectivity.push_back(points.size() - 1);
836  ix++;
837  }
838  cells.push_back({connectivity, points.size(), vtkFormat});
839  }
840 
841  //open vtk file
842  static unsigned count = 0;
843 #ifdef OOMPH_HAS_MPI
844  std::string vtkFileName = name_ + "FEM_" + std::to_string(OOMPH_MPI_PROCESSOR_ID) + "_" + std::to_string(count++) + ".vtu";
845 #else
846  std::string vtkFileName = name_ + "FEM_" + std::to_string(count++) + ".vtu";
847 #endif
848 
849  std::ofstream vtk(vtkFileName);
850 
851  //write vtk file
852  vtk << "<?xml version=\"1.0\"?>\n"
853  "<!-- time 10.548-->\n"
854  "<VTKFile type=\"UnstructuredGrid\" version=\"0.1\" byte_order=\"LittleEndian\">\n"
855  "<UnstructuredGrid>\n"
856  "<Piece NumberOfPoints=\""
857  << points.size()
858  << "\" NumberOfCells=\""
859  << cells.size()
860  << "\">\n"
861  "\n"
862  "<Points>\n"
863  "<DataArray type=\"Float32\" Name=\"Position\" NumberOfComponents=\"3\" format=\"ascii\">\n";
864  for (auto point : points)
865  {
866  vtk << point.coordinate[0] << " " << point.coordinate[1] << " " << point.coordinate[2] << "\n";
867  }
868  vtk << "</DataArray>\n"
869  "</Points>\n\n";
870  if (not points.empty())
871  {
872  vtk << "<PointData Vectors=\"vector\">\n";
873  for (int i = 0; i < points.front().data.size(); ++i)
874  {
875  auto data = points.front().data[i];
876  vtk << R"(<DataArray type="Float32" Name=")"
877  << points.front().data[i].name
878  << R"(" NumberOfComponents=")"
879  << points.front().data[i].value.size()
880  << R"(" format="ascii">)" << "\n";
881  for (const Point& point : points)
882  {
883  for (const auto& value : point.data[i].value)
884  {
885  //ensure values are not too small (VTK issue)
886  vtk << ((!isfinite(value) or fabs(value)<1e-33 or fabs(value)>1e33)?0.0:value) << " ";
887  }
888  }
889  vtk << "\n"
890  "</DataArray>\n";
891  }
892  vtk << "</PointData>\n"
893  "\n";
894  }
895  vtk << "<Cells>\n"
896  "<DataArray type=\"Int32\" Name=\"connectivity\" format=\"ascii\">\n";
897  for (const Cell& cell : cells)
898  {
899  for (auto point : cell.connectivity)
900  {
901  vtk << point << " ";
902  }
903  }
904  vtk << "</DataArray>\n"
905  "<DataArray type=\"Int32\" Name=\"offsets\" format=\"ascii\">\n";
906  for (const Cell& cell : cells)
907  {
908  vtk << cell.offset << " ";
909  }
910  vtk << "</DataArray>\n"
911  "<DataArray type=\"UInt8\" Name=\"types\" format=\"ascii\">\n";
912  for (const Cell& cell : cells)
913  {
914  vtk << cell.type << " ";
915  }
916  vtk << "\n"
917  "</DataArray>\n"
918  "</Cells>\n"
919  "\n"
920  "</Piece>\n"
921  "</UnstructuredGrid>\n"
922  "</VTKFile>\n";
923  vtk.close();
924  logger(INFO, "Written %", vtkFileName);
925  }
926 
928  void getMassMomentumEnergy(double& mass, Vector<double>& com, Vector<double>& linearMomentum, Vector<double>& angularMomentum, double& elasticEnergy,
929  double& kineticEnergy) const {
930  // Initialise mass
931  mass = 0;
932  // Initialise center of mass
933  com.initialise(0);
934  // Initialise momentum
935  linearMomentum.initialise(0);
936  angularMomentum.initialise(0);
937  // Initialise energy
938  elasticEnergy = 0;
939  kineticEnergy = 0;
940 
941  // For each element
942  const unsigned long nelement = this->solid_mesh_pt()->nelement();
943  for (unsigned long e = 0; e < nelement; e++) {
944 
945  // Get the pointer to the element
946  ELEMENT* e_pt = dynamic_cast<ELEMENT*>(Solid_mesh_pt->element_pt(e));
947 
948  const unsigned DIM = e_pt->dim();
949 
950  //Find out how many integration points there are
951  unsigned n_intpt = e_pt->integral_pt()->nweight();
952 
953  //Set the Vector to hold local coordinates
955 
956  //Find out how many nodes there are
957  const unsigned n_node = e_pt->nnode();
958 
959  //Find out how many positional dofs there are
960  const unsigned n_position_type = e_pt->nnodal_position_type();
961 
962  //Set up memory for the shape functions
963  Shape psi(n_node, n_position_type);
964  DShape dpsidxi(n_node, n_position_type, DIM);
965 
966  // Timescale ratio (non-dim density)
967  double lambda_sq = e_pt->lambda_sq();
968 
969  //Loop over the integration points
970  for (unsigned ipt = 0; ipt < n_intpt; ipt++) {
971  //Assign local coordinate s
972  for (unsigned i = 0; i < DIM; i++) { s[i] = e_pt->integral_pt()->knot(ipt, i); }
973 
974  //Get the integral weight
975  double w = e_pt->integral_pt()->weight(ipt);
976 
977  //Evaluate the shape function and its derivatives, and get Jacobian
978  double J = e_pt->dshape_lagrangian_at_knot(ipt, psi, dpsidxi);
979 
980  //Get mass and the coupling weight at the integration point
981  double coupling_w = 0;
982  //for (unsigned l = 0; l < n_node; l++)
983  //{
984  // double nodal_coupling_w = dynamic_cast<SolidNode*>(e_pt->node_pt(l))->get_coupling_weight();
985  // double psi_ = psi(l);
986  // coupling_w += psi_ * nodal_coupling_w;
987  //}
988 
989  //Get the coordinates of the integration point
990  Vector<double> x(DIM, 0.0);
991  e_pt->interpolated_x(s,x);
992 
993  //Storage for Lagrangian coordinates and velocity (initialised to zero)
994  Vector<double> interpolated_xi(DIM, 0.0);
995  Vector<double> veloc(DIM, 0.0);
996 
997  //Calculate lagrangian coordinates
998  for (unsigned l = 0; l < n_node; l++) {
999  //Loop over positional dofs
1000  for (unsigned k = 0; k < n_position_type; k++) {
1001  //Loop over displacement components (deformed position)
1002  for (unsigned i = 0; i < DIM; i++) {
1003  //Calculate the Lagrangian coordinates
1004  interpolated_xi[i] += e_pt->lagrangian_position_gen(l, k, i) * psi(l, k);
1005 
1006  //Calculate the velocity components (if unsteady solve)
1007  if (e_pt->is_inertia_enabled()) {
1008  veloc[i] += e_pt->dnodal_position_gen_dt(l, k, i) * psi(l, k);
1009  }
1010  }
1011  }
1012  }
1013 
1014  //Get isotropic growth factor
1015  double gamma = 1.0;
1016  e_pt->get_isotropic_growth(ipt, s, interpolated_xi, gamma);
1017 
1018  //Premultiply the undeformed volume ratio (from the isotropic
1019  // growth), the integral weights, the coupling weights, and the Jacobian
1020  double W = gamma * w * (1.0 - coupling_w) * J;
1021 
1023  DenseMatrix<double> strain(DIM, DIM);
1024 
1025  //Now calculate the stress tensor from the constitutive law
1026  e_pt->get_stress(s, sigma);
1027 
1028  // Add pre-stress
1029  for (unsigned i = 0; i < DIM; i++) {
1030  for (unsigned j = 0; j < DIM; j++) {
1031  sigma(i, j) += e_pt->prestress(i, j, interpolated_xi);
1032  }
1033  }
1034 
1035  //get the strain
1036  e_pt->get_strain(s, strain);
1037 
1038  // Initialise
1039  double localElasticEnergy = 0;
1040  double velocitySquared = 0;
1041 
1042  // Compute integrals
1043  for (unsigned i = 0; i < DIM; i++) {
1044  for (unsigned j = 0; j < DIM; j++) {
1045  localElasticEnergy += sigma(i, j) * strain(i, j);
1046  }
1047  velocitySquared += veloc[i] * veloc[i];
1048  }
1049 
1050  // Mass
1051  mass += lambda_sq * W;
1052  // Linear momentum and angular momentum
1053  Vector<double> cross_product(DIM, 0);
1054  VectorHelpers::cross(interpolated_xi, veloc, cross_product);
1055  for (unsigned i = 0; i < DIM; i++) {
1056  com[i] += lambda_sq * W * x[i];
1057  linearMomentum[i] += lambda_sq * veloc[i] * W;
1058  angularMomentum[i] += lambda_sq * cross_product[i] * W;
1059  }
1060  // Potential energy
1061  elasticEnergy += 0.5 * localElasticEnergy * W;
1062  // Kinetic energy
1063  kineticEnergy += lambda_sq * 0.5 * velocitySquared * W;
1064  }
1065  }
1066  for (unsigned i = 0; i < com.size(); i++) {
1067  com[i] /= mass;
1068  }
1069  }
1070 
1071 };
1072 #endif //SOLID_PROBLEM_H
int i
Definition: BiCGSTAB_step_by_step.cpp:9
const unsigned n
Definition: CG3DPackingUnitTest.cpp:11
Array< double, 1, 3 > e(1./3., 0.5, 2.)
JacobiRotation< float > J
Definition: Jacobi_makeJacobi.cpp:3
Logger< MERCURYDPM_LOGLEVEL > logger("MercuryKernel")
Definition of different loggers with certain modules. A user can define its own custom logger here.
LL< Log::ERROR > ERROR
Error log level.
Definition: Logger.cc:32
int data[]
Definition: Map_placement_new.cpp:1
RowVector3d w
Definition: Matrix_resize_int.cpp:3
#define OOMPH_MPI_PROCESSOR_ID
Definition: SolidProblem.h:41
#define OOMPH_MPI_PROCESSOR_NUM
Definition: SolidProblem.h:35
Scalar * b
Definition: benchVecAdd.cpp:17
Definition: SolidProblem.h:60
SolidMesh *& traction_mesh_pt()
Get function for the traction mesh pointer.
Definition: SolidProblem.h:296
virtual void actionsBeforeOomphTimeStep()
Definition: SolidProblem.h:437
void solveSteady(const unsigned &max_adapt=0)
Definition: SolidProblem.h:421
void addAnisotropy(double Ex, double Ey, double Ez)
Definition: SolidProblem.h:165
Boundary
Definition: SolidProblem.h:566
void setElasticModulus(double elasticModulus)
set function for elasticModulus_
Definition: SolidProblem.h:133
void setName(const std::string &name)
set function for name_
Definition: SolidProblem.h:120
void solveUnsteadyForgiving(double timeMax, double timeMaxMin, double dt, unsigned saveCount=10)
Definition: SolidProblem.h:494
void prepareForSolve()
Definition: SolidProblem.h:331
void saveSolidMesh()
Definition: SolidProblem.h:582
void removeOldFiles() const
Definition: SolidProblem.h:516
double getOomphTime() const
get function for current time
Definition: SolidProblem.h:288
void setSolidCubicMesh(const unsigned &nx, const unsigned &ny, const unsigned &nz, const double &xMin, const double &xMax, const double &yMin, const double &yMax, const double &zMin, const double &zMax)
Definition: SolidProblem.h:571
void pinBoundary(unsigned b)
Definition: SolidProblem.h:236
virtual void actionsBeforeSolve()
Definition: SolidProblem.h:433
void writeToVTK()
Definition: SolidProblem.h:665
std::enable_if< std::is_base_of< RefineableQDPVDElement< 3, 2 >, ELEMENT >::value, void > setDissipation(double dissipation)
Sets the dissipation coefficient for all elements.
Definition: SolidProblem.h:257
void solveUnsteady(double timeMax, double dt, unsigned saveCount=10, const unsigned &max_adapt=0)
Definition: SolidProblem.h:442
void setOomphTimeStep(double dt)
set function for time step
Definition: SolidProblem.h:278
void loadSolidMesh(std::string infileName, bool cubic=true)
Definition: SolidProblem.h:624
ELEMENT_TYPE ELEMENT
Definition: SolidProblem.h:63
std::string name_
Definition: SolidProblem.h:66
void getMassMomentumEnergy(double &mass, Vector< double > &com, Vector< double > &linearMomentum, Vector< double > &angularMomentum, double &elasticEnergy, double &kineticEnergy) const
See PVDEquationsBase<DIM>::get_energy.
Definition: SolidProblem.h:928
void setOomphGravity(double gravity)
set function for elasticModulus_
Definition: SolidProblem.h:175
double getDeflection(Vector< double > xi, unsigned d) const
Definition: SolidProblem.h:557
void setPoissonRatio(double poissonRatio)
set function for poissonRatio_
Definition: SolidProblem.h:188
void getDomainSize(std::array< double, 3 > &min, std::array< double, 3 > &max) const
Computes the domain size (min/max of the nodal positions in x/y/z)
Definition: SolidProblem.h:305
double getPoissonRatio() const
get function for poissonRatio_
Definition: SolidProblem.h:195
SolidProblem()
Constructor: set default constitutive law and time stepper.
Definition: SolidProblem.h:98
void get_x(const Vector< double > &xi, Vector< double > &x) const
Definition: SolidProblem.h:529
SolidMesh *const & traction_mesh_pt() const
Get function for the traction mesh pointer.
Definition: SolidProblem.h:302
std::function< bool(SolidNode *, unsigned)> isPinned_
Function to determine which nodal positions are pinned.
Definition: SolidProblem.h:93
void setDensity(double density)
set function for density_
Definition: SolidProblem.h:201
double getOomphGravity() const
get function for gravity_
Definition: SolidProblem.h:182
void setIsPinned(std::function< bool(SolidNode *, unsigned)> isPinned)
set function for isPinned_
Definition: SolidProblem.h:229
virtual void actionsAfterSolve()
Definition: SolidProblem.h:435
void setBodyForceAsGravity(double gravity=9.8)
set function for body_force_pt
Definition: SolidProblem.h:214
void addDissipation(double dissipation)
Definition: SolidProblem.h:146
void setNewtonSolverTolerance(double Newton_solver_tolerance)
set function for Newton_solver_tolerance
Definition: SolidProblem.h:266
double getOomphTimeStep() const
get function for time step
Definition: SolidProblem.h:283
double getDensity() const
get function for density_
Definition: SolidProblem.h:208
void setMaxNewtonIterations(unsigned Max_newton_iterations)
set function for Max_newton_iterations
Definition: SolidProblem.h:272
SolidMesh *& solid_mesh_pt()
Get function for the solid mesh pointer.
Definition: SolidProblem.h:293
void setupRefinementParameters(const double &min_error_target, const double &max_error_target, Z2ErrorEstimator *&error_estimator_pt)
Definition: SolidProblem.h:155
void countPinned()
returns statistics about pinned nodes to the console
Definition: SolidProblem.h:401
SolidMesh *const & solid_mesh_pt() const
Get function for the solid mesh pointer.
Definition: SolidProblem.h:299
double getElasticModulus() const
get function for elasticModulus_
Definition: SolidProblem.h:140
std::string getName() const
get function for name_
Definition: SolidProblem.h:127
void pinBoundaries(std::vector< unsigned > b)
Definition: SolidProblem.h:244
Definition: AnisotropicHookean.h:16
void setAnisotropy(std::array< double, 3 > anisotropy)
Definition: AnisotropicHookean.h:21
Definition: constitutive_laws.h:471
Definition: shape.h:278
Definition: nodes.h:86
Definition: geom_objects.h:101
virtual void locate_zeta(const Vector< double > &zeta, GeomObject *&sub_geom_object_pt, Vector< double > &s, const bool &use_coordinate_as_initial_guess=false)
Definition: geom_objects.h:381
GeneralisedElement *& element_pt(const unsigned long &e)
Return pointer to element e.
Definition: mesh.h:448
Definition: oomph_definitions.h:222
static void pin_redundant_nodal_solid_pressures(const Vector< GeneralisedElement * > &element_pt)
Definition: solid_elements.h:208
Definition: problem.h:151
double & min_permitted_error()
Definition: refineable_mesh.h:156
double & max_permitted_error()
Definition: refineable_mesh.h:163
ErrorEstimator *& spatial_error_estimator_pt()
Access to spatial error estimator.
Definition: refineable_mesh.h:143
Definition: RefineableQDPVDElement.h:23
Definition: solid_cubic_mesh.h:17
Definition: shape.h:76
const unsigned & nx() const
Access function for number of elements in x directions.
Definition: simple_cubic_mesh.template.h:108
Definition: mesh.h:2562
Definition: nodes.h:1686
void pin_position(const unsigned &i)
Pin the nodal position.
Definition: nodes.h:1816
void unpin_position(const unsigned &i)
Unpin the nodal position.
Definition: nodes.h:1829
Definition: Qelements.h:1742
Definition: Telements.h:3728
void initialise(const _Tp &__value)
Iterate over all values and set to the desired value.
Definition: oomph-lib/src/generic/Vector.h:167
Definition: error_estimator.h:266
#define min(a, b)
Definition: datatypes.h:22
#define max(a, b)
Definition: datatypes.h:23
RealScalar s
Definition: level1_cplx_impl.h:130
const Scalar * a
Definition: level2_cplx_impl.h:32
char char char int int * k
Definition: level2_impl.h:374
#define DIM
Definition: linearised_navier_stokes_elements.h:44
#define isfinite(X)
Definition: main.h:111
#define INFO(i)
Definition: mumps_solver.h:54
EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC bfloat16 ceil(const bfloat16 &a)
Definition: BFloat16.h:644
squared absolute value
Definition: GlobalFunctions.h:87
void body_force(const double &time, const Vector< double > &x, Vector< double > &result)
Definition: axisym_linear_elasticity/cylinder/cylinder.cc:96
Definition: ConstraintElementsUnitTest.cpp:19
void gravity(const double &t, const Vector< double > &xi, Vector< double > &b)
Definition: ConstraintElementsUnitTest.cpp:20
string filename
Definition: MergeRestartFiles.py:39
Z2ErrorEstimator * error_estimator_pt
Definition: MortaringCantileverCompareToNonMortaring.cpp:190
const unsigned nz
Definition: ConstraintElementsUnitTest.cpp:32
const unsigned nx
Definition: ConstraintElementsUnitTest.cpp:30
const unsigned ny
Definition: ConstraintElementsUnitTest.cpp:31
density
Definition: UniformPSDSelfTest.py:19
string fileName
Definition: UniformPSDSelfTest.py:10
Real fabs(const Real &a)
Definition: boostmultiprec.cpp:117
int error
Definition: calibrate.py:297
int sigma
Definition: calibrate.py:179
type
Definition: compute_granudrum_aor.py:141
const Mdouble inf
Definition: GeneralDefine.h:23
Definition: MortaringCantileverCompareToNonMortaring.cpp:176
Mdouble gamma(Mdouble gamma_in)
This is the gamma function returns the true value for the half integer value.
Definition: ExtendedMath.cc:116
T cubic(const T val)
calculates the cube of a number
Definition: ExtendedMath.h:95
std::string string(const unsigned &i)
Definition: oomph_definitions.cc:286
unsigned Max_newton_iterations
Maximum number of newton iterations.
Definition: elements.cc:1654
@ W
Definition: quadtree.h:63
std::string to_string(T object, unsigned float_precision=8)
Definition: oomph_utilities.h:189
void cross(const Vector< double > &A, const Vector< double > &B, Vector< double > &C)
Definition: oomph-lib/src/generic/Vector.h:319
DRAIG: Change all instances of (SPATIAL_DIM) to (DIM-1).
Definition: AnisotropicHookean.h:10
list x
Definition: plotDoE.py:28
string name
Definition: plotDoE.py:33
std::ptrdiff_t j
Definition: tut_arithmetic_redux_minmax.cpp:2