KratosMultiphysics
KRATOS Multiphysics (Kratos) is a framework for building parallel, multi-disciplinary simulation software, aiming at modularity, extensibility, and high performance. Kratos is written in C++, and counts with an extensive Python interface.
custom_functions.h
Go to the documentation of this file.
1 //
2 // Project Name: Kratos
3 // Last Modified by: $Author: G.Casas (gcasas@cimmne.upc.edu)$
4 // Date: $Date: 2011-6-13 08:56:42 $
5 // Revision: $Revision: 1.5 $
6 //
7 //
8 //README::::look to the key word "VERSION" if you want to find all the points where you have to change something so that you can pass from a kdtree to a bin data search structure;
9 
10 #if !defined(KRATOS_CUSTOM_FUNCTIONS)
11 #define KRATOS_CUSTOM_FUNCTIONS
12 
13 // /* External includes */
14 #ifdef _OPENMP
15 #include <omp.h>
16 #endif
17 
18 // System includes
19 #include <vector>
20 
21 // Project includes
22 #include "includes/model_part.h"
23 #include "utilities/timer.h"
24 #include "utilities/openmp_utils.h"
26 
27 //Database includes
29 #include "includes/define.h"
32 #include "custom_utilities/AuxiliaryFunctions.h"
35 #include "utilities/geometry_utilities.h"
36 
37 namespace Kratos
38 {
39 template <std::size_t TDim>
41 {
42 public:
43 
44 typedef ModelPart::ElementsContainerType::iterator ElementIterator;
45 typedef ModelPart::NodesContainerType::iterator NodeIterator;
47 
49 
50 CustomFunctionsCalculator(): mPressuresFilled(false), mFirstGradientRecovery(true), mFirstLaplacianRecovery(true), mSomeCloudsDontWork(false), mCalculatingTheGradient(false), mCalculatingTheLaplacian(false), mFirstTimeAppending(true){}
52 
54 
56 
57 //**************************************************************************************************************************************************
58 //**************************************************************************************************************************************************
59 
61 {
62  for (NodeIterator inode = r_model_part.NodesBegin(); inode != r_model_part.NodesEnd(); ++inode){
63  noalias(inode->FastGetSolutionStepValue(PRESSURE_GRADIENT)) = ZeroVector(3);
64  }
65 
66  array_1d <double, 3> grad = ZeroVector(3); // its dimension is always 3
67  array_1d <double, TDim + 1 > elemental_pressures;
68  array_1d <double, TDim + 1 > N; // shape functions vector
70 
71  for (ModelPart::ElementIterator ielem = r_model_part.ElementsBegin(); ielem != r_model_part.ElementsEnd(); ++ielem){
72  // computing the shape function derivatives
73  Geometry<Node >& geom = ielem->GetGeometry();
74  double Volume;
75 
76  GeometryUtils::CalculateGeometryData(geom, DN_DX, N, Volume);
77 
78  // getting the pressure gradients;
79 
80  for (unsigned int i = 0; i < TDim + 1; ++i){
81  elemental_pressures[i] = geom[i].FastGetSolutionStepValue(PRESSURE);
82  }
83 
84 
85  array_1d <double, TDim> grad_aux = prod(trans(DN_DX), elemental_pressures); // its dimension may be 2
86 
87  for (unsigned int i = 0; i < TDim; ++i){
88  grad[i] = grad_aux[i];
89  }
90 
91  double nodal_area = Volume / static_cast<double>(TDim + 1);
92  grad *= nodal_area;
93 
94  for (unsigned int i = 0; i < TDim + 1; ++i){
95  geom[i].FastGetSolutionStepValue(PRESSURE_GRADIENT) += grad;
96  }
97  }
98 
99  for (NodeIterator inode = r_model_part.NodesBegin(); inode != r_model_part.NodesEnd(); ++inode){
100  inode->FastGetSolutionStepValue(PRESSURE_GRADIENT) /= inode->FastGetSolutionStepValue(NODAL_AREA);
101  }
102 }
103 
104 //**************************************************************************************************************************************************
105 //**************************************************************************************************************************************************
106 // This function assesses the stationarity based on the pressure field variation.
107 // Its tolerance applies to the non-dimensional pressure variation between consecutive
108 // measurements.
109 bool AssessStationarity(ModelPart& r_model_part, const double& tol)
110 {
111  if (!mPressuresFilled){
112  PerformFirstStepComputations(r_model_part);
113 
114  return(false);
115  }
116 
117  else {
118  double max_pressure_change_rate = 0.0; // measure of stationarity
119  double mean_celerity = 0.0; // used to adimensionalize the time step
120 
121  // filling up mPressures and calculating the mean velocities and the maximum nodal pressure change
122 
123  unsigned int i = 0;
124 
125  for (NodeIterator inode = r_model_part.NodesBegin(); inode != r_model_part.NodesEnd(); ++inode){
126  const array_1d<double, 3>& velocity = inode->FastGetSolutionStepValue(VELOCITY);
127  mean_celerity += SWIMMING_MODULUS_3(velocity);
128 
129  const double new_pressure = inode->FastGetSolutionStepValue(PRESSURE);
130  double& old_pressure = mPressures[i];
131  const double delta_p = std::abs(new_pressure - old_pressure);
132  max_pressure_change_rate = std::max(delta_p, max_pressure_change_rate);
133  old_pressure = new_pressure;
134 
135  ++i;
136  }
137 
138  mean_celerity /= i;
139  const double delta_t = r_model_part.GetProcessInfo()[TIME] - mLastMeasurementTime;
140 
141  if (delta_t > 0.0){
142  max_pressure_change_rate /= delta_t;
143 
144  // calculating coefficients for adimensionalization of the pressure change rate
145  const double characteristic_length = std::pow(mTotalDomainVolume, 1.0 / 3); // characteristic length of the model. Should be improved: a hydraulic radius or such
146  const double reciprocal_of_characteristic_time = mean_celerity / characteristic_length;
147  const double pressure_spatial_variation = GetRangeWithinVector(mPressures);
148  mLastPressureVariation = pressure_spatial_variation;
149  const double characteristic_pressure_variation = 0.5 * (pressure_spatial_variation + mLastPressureVariation);
150 
151  if (std::abs(characteristic_pressure_variation) == 0.0 || std::abs(reciprocal_of_characteristic_time) == 0.0){ // unlikely
152  std::cout << "Uniform problem: stationarity check being performed with dimensional values...! " << "\n";
153 
154  if (max_pressure_change_rate <= tol){ // go with the absolute value
155  return true;
156  }
157  }
158 
159  max_pressure_change_rate /= reciprocal_of_characteristic_time * characteristic_pressure_variation ;
160  }
161 
162  else {
163  KRATOS_ERROR << "Trying to calculate pressure variations between two coincident time steps! (null time variation since last recorded time)" << std::endl;
164  }
165 
166  std::cout << "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++" << "\n";
167  std::cout << "The stationarity condition tolerance is " << "\n";
168  KRATOS_INFO("SwimmingDEM") << tol << std::endl;
169  std::cout << "The stationarity residual is now " << "\n";
170  KRATOS_INFO("SwimmingDEM") << max_pressure_change_rate << std::endl;
171  std::cout << "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++" << "\n";
172 
173  return max_pressure_change_rate <= tol;
174  }
175 }
176 
177 //**************************************************************************************************************************************************
178 //**************************************************************************************************************************************************
179 double CalculateDomainVolume(ModelPart& r_fluid_model_part)
180 {
181  OpenMPUtils::CreatePartition(ParallelUtilities::GetNumThreads(), r_fluid_model_part.GetCommunicator().LocalMesh().Elements().size(), mElementsPartition);
182 
183  double added_volume = 0.0;
184 
185  #pragma omp parallel for reduction(+ : added_volume)
186  for (int k = 0; k < ParallelUtilities::GetNumThreads(); ++k){
187 
188  for (ElementIterator it = GetElementPartitionBegin(r_fluid_model_part, k); it != GetElementPartitionEnd(r_fluid_model_part, k); ++it){
189  added_volume += it->GetGeometry().DomainSize();
190  }
191  }
192 
193  return added_volume;
194 }
195 
196 //**************************************************************************************************************************************************
197 //**************************************************************************************************************************************************
198 // this function assumes linear elements are used
199 
201 {
202  OpenMPUtils::CreatePartition(ParallelUtilities::GetNumThreads(), r_dem_model_part.GetCommunicator().LocalMesh().Elements().size(), mElementsPartition);
203 
204  std::vector<array_1d <double, 3> > added_force_vect;
205  added_force_vect.resize(ParallelUtilities::GetNumThreads());
206 
207  for (unsigned int k = 0; k < added_force_vect.size(); ++k){
208  added_force_vect[k] = ZeroVector(3);
209  }
210 
211  #pragma omp parallel for
212  for (int k = 0; k < ParallelUtilities::GetNumThreads(); ++k){
213 
214  for (ElementIterator it = GetElementPartitionBegin(r_dem_model_part, k); it != GetElementPartitionEnd(r_dem_model_part, k); ++it){
215  Geometry< Node >& geom = it->GetGeometry();
216  array_1d <double, 3> element_force;
217 
218  if (geom[0].SolutionStepsDataHas(HYDRODYNAMIC_FORCE)){
219  element_force = geom[0].FastGetSolutionStepValue(HYDRODYNAMIC_FORCE);
220  }
221 
222  else {
223  element_force = ZeroVector(3);
224  }
225 
226  added_force_vect[k] += element_force;
227  }
228  }
229 
230  force = added_force_vect[0];
231 
232  for (unsigned int k = 1; k < added_force_vect.size(); ++k){
233  force += added_force_vect[k];
234  }
235 }
236 
237 //**************************************************************************************************************************************************
238 //**************************************************************************************************************************************************
239 // this function assumes linear elements are used
240 
241 void CalculateTotalHydrodynamicForceOnFluid(ModelPart& r_fluid_model_part, array_1d <double, 3>& instantaneous_force, array_1d <double, 3>& mean_force)
242 {
243  OpenMPUtils::CreatePartition(ParallelUtilities::GetNumThreads(), r_fluid_model_part.GetCommunicator().LocalMesh().Elements().size(), mElementsPartition);
244 
245  std::vector<array_1d <double, 3> > added_force_vect;
246  added_force_vect.resize(ParallelUtilities::GetNumThreads());
247  std::vector<array_1d <double, 3> > added_mean_force_vect;
248  added_mean_force_vect.resize(ParallelUtilities::GetNumThreads());
249 
250  for (unsigned int k = 0; k < added_force_vect.size(); ++k){
251  added_force_vect[k] = ZeroVector(3);
252  added_mean_force_vect[k] = ZeroVector(3);
253  }
254 
255  #pragma omp parallel for
256  for (int k = 0; k < ParallelUtilities::GetNumThreads(); ++k){
257 
258  for (ElementIterator it = GetElementPartitionBegin(r_fluid_model_part, k); it != GetElementPartitionEnd(r_fluid_model_part, k); ++it){
259  Geometry< Node >& geom = it->GetGeometry();
260  double element_volume;
261  array_1d <double, 3> element_force;
262  array_1d <double, 3> element_mean_force;
263 
264  if (geom[0].SolutionStepsDataHas(HYDRODYNAMIC_REACTION) && geom[0].SolutionStepsDataHas(FLUID_FRACTION)){
265  element_force = CalculateVectorIntegralOfLinearInterpolationPerUnitFluidMass(geom, HYDRODYNAMIC_REACTION, element_volume);
266  }
267 
268  else {
269  element_force = ZeroVector(3);
270  }
271 
272  if (geom[0].SolutionStepsDataHas(MEAN_HYDRODYNAMIC_REACTION) && geom[0].SolutionStepsDataHas(FLUID_FRACTION)){
273  element_mean_force = CalculateVectorIntegralOfLinearInterpolationPerUnitFluidMass(geom, MEAN_HYDRODYNAMIC_REACTION, element_volume);
274  }
275 
276  else {
277  element_mean_force = ZeroVector(3);
278  }
279 
280  added_force_vect[k] += element_force;
281  added_mean_force_vect[k] += element_mean_force;
282  }
283  }
284 
285  instantaneous_force = added_force_vect[0];
286  mean_force = added_force_vect[0];
287 
288  for (unsigned int k = 1; k < added_force_vect.size(); ++k){
289  instantaneous_force += added_force_vect[k];
290  mean_force += added_mean_force_vect[k];
291  }
292 }
293 
294 //**************************************************************************************************************************************************
295 //**************************************************************************************************************************************************
296 // this function assumes linear elements are used
297 
298 double CalculateGlobalFluidVolume(ModelPart& r_fluid_model_part)
299 {
300  OpenMPUtils::CreatePartition(ParallelUtilities::GetNumThreads(), r_fluid_model_part.GetCommunicator().LocalMesh().Elements().size(), mElementsPartition);
301 
302  double added_fluid_volume = 0.0;
303 
304  #pragma omp parallel for reduction(+ : added_fluid_volume)
305  for (int k = 0; k < ParallelUtilities::GetNumThreads(); ++k){
306 
307  for (ElementIterator it = GetElementPartitionBegin(r_fluid_model_part, k); it != GetElementPartitionEnd(r_fluid_model_part, k); ++it){
308  Geometry< Node >& geom = it->GetGeometry();
309  double element_volume;
310  double element_fluid_volume;
311 
312  if (geom[0].SolutionStepsDataHas(FLUID_FRACTION)){
313  element_fluid_volume = CalculateScalarIntegralOfLinearInterpolation(geom, FLUID_FRACTION, element_volume);
314  }
315 
316  else {
317  element_fluid_volume = geom.DomainSize();
318  }
319 
320  added_fluid_volume += element_fluid_volume;
321  }
322  }
323 
324  return added_fluid_volume;
325 }
326 
327 //**************************************************************************************************************************************************
328 //**************************************************************************************************************************************************
329 template<class matrix_T>
330 double determinant(boost::numeric::ublas::matrix_expression<matrix_T> const& mat_r)
331 {
332  double det = 1.0;
333 
334  matrix_T mLu(mat_r() );
335  boost::numeric::ublas::permutation_matrix<std::size_t> pivots(mat_r().size1() );
336 
337  int is_singular = lu_factorize(mLu, pivots);
338 
339  if (!is_singular)
340  {
341  for (std::size_t i=0; i < pivots.size(); ++i)
342  {
343  if (pivots(i) != i)
344  det *= -1.0;
345 
346  det *= mLu(i,i);
347  }
348  }
349  else
350  det = 0.0;
351 
352  return det;
353 }
354 //**************************************************************************************************************************************************
355 //**************************************************************************************************************************************************
357  const DenseMatrix<double>& m)
358 {
359  assert(m.size1() == m.size2() && "Can only calculate the inverse of square matrices");
360 
361  switch(m.size1())
362  {
363  case 1:
364  {
365  assert(m.size1() == 1 && m.size2() == 1 && "Only for 1x1 matrices");
366  const double determinant = CalcDeterminant(m);
367  assert(determinant != 0.0);
368  assert(m(0,0) != 0.0 && "Cannot take the inverse of matrix [0]");
369  DenseMatrix<double> n(1,1);
370  n(0,0) = 1.0 / determinant;
371  return n;
372  }
373  case 2:
374  {
375  assert(m.size1() == 2 && m.size2() == 2 && "Only for 2x2 matrices");
376  const double determinant = CalcDeterminant(m);
377  assert(determinant != 0.0);
378  const double a = m(0,0);
379  const double b = m(0,1);
380  const double c = m(1,0);
381  const double d = m(1,1);
382  DenseMatrix<double> n(2,2);
383  n(0,0) = d / determinant;
384  n(0,1) = -b / determinant;
385  n(1,0) = -c / determinant;
386  n(1,1) = a / determinant;
387  return n;
388  }
389  case 3:
390  {
391  assert(m.size1() == 3 && m.size2() == 3 && "Only for 3x3 matrices");
392  const double determinant = CalcDeterminant(m);
393  assert(determinant != 0.0);
394  const double a = m(0,0);
395  const double b = m(0,1);
396  const double c = m(0,2);
397  const double d = m(1,0);
398  const double e = m(1,1);
399  const double f = m(1,2);
400  const double g = m(2,0);
401  const double h = m(2,1);
402  const double k = m(2,2);
403  DenseMatrix<double> n(3,3);
404  const double new_a = ((e*k)-(f*h)) / determinant;
405  const double new_b = -((d*k)-(f*g)) / determinant;
406  const double new_c = ((d*h)-(e*g)) / determinant;
407  const double new_d = -((b*k)-(c*h)) / determinant;
408  const double new_e = ((a*k)-(c*g)) / determinant;
409  const double new_f = -((a*h)-(b*g)) / determinant;
410  const double new_g = ((b*f)-(c*e)) / determinant;
411  const double new_h = -((a*f)-(c*d)) / determinant;
412  const double new_k = ((a*e)-(b*d)) / determinant;
413  n(0,0) = new_a;
414  n(1,0) = new_b;
415  n(2,0) = new_c;
416  n(0,1) = new_d;
417  n(1,1) = new_e;
418  n(2,1) = new_f;
419  n(0,2) = new_g;
420  n(1,2) = new_h;
421  n(2,2) = new_k;
422  return n;
423  }
424  default:
425  {
426  //Use blockwise inversion
427  //Matrix::Chop returns a std::vector
428  //[ A at [0] B at [1] ]
429  //[ C at [2] D at [4] ]
430  const std::vector<DenseMatrix<double> > v = Chop(m);
431  const DenseMatrix<double>& a = v[0];
432  assert(a.size1() == a.size2());
433  const DenseMatrix<double> a_inv = Inverse(a);
434  const DenseMatrix<double>& b = v[1];
435  const DenseMatrix<double>& c = v[2];
436  const DenseMatrix<double>& d = v[3];
437  const DenseMatrix<double> term
438  = d
439  - prod(
440  DenseMatrix<double>(prod(c,a_inv)),
441  b
442  );
443  const DenseMatrix<double> term_inv = Inverse(term);
444  const DenseMatrix<double> new_a
445  = a_inv
450  a_inv,
451  b)),
452  term_inv)),
453  c)),
454  a_inv));
455 
456  const DenseMatrix<double> new_b
457  =
460  a_inv,
461  b)),
462  term_inv));
463 
464  const DenseMatrix<double> new_c
465  =
468  term_inv,
469  c)),
470  a_inv));
471 
472  const DenseMatrix<double> new_d = term_inv;
473  std::vector<DenseMatrix<double> > w;
474  w.push_back(new_a);
475  w.push_back(new_b);
476  w.push_back(new_c);
477  w.push_back(new_d);
478  const DenseMatrix<double> result = Unchop(w);
479  return result;
480  }
481  }
482 }
483 
484 //**************************************************************************************************************************************************
485 //**************************************************************************************************************************************************
486 void CopyValuesFromFirstToSecond(ModelPart& r_model_part, const Variable<double>& origin_variable, const Variable<double>& destination_variable)
487 {
488  #pragma omp parallel for
489  for (int i = 0; i < (int)r_model_part.Nodes().size(); ++i){
490  ModelPart::NodesContainerType::iterator i_particle = r_model_part.NodesBegin() + i;
491  Node::Pointer p_node = *(i_particle.base());
492  double& destination_value = p_node->FastGetSolutionStepValue(destination_variable);
493  const double& origin_value = p_node->FastGetSolutionStepValue(origin_variable);
494  destination_value = origin_value;
495  }
496 }
497 //**************************************************************************************************************************************************
498 //**************************************************************************************************************************************************
499 void CopyValuesFromFirstToSecond(ModelPart& r_model_part, const Variable<array_1d<double, 3>>& origin_variable, const Variable<array_1d<double, 3>>& destination_variable)
500 {
501  #pragma omp parallel for
502  for (int i = 0; i < (int)r_model_part.Nodes().size(); ++i){
503  ModelPart::NodesContainerType::iterator i_particle = r_model_part.NodesBegin() + i;
504  Node::Pointer p_node = *(i_particle.base());
505  array_1d<double, 3>& destination_value = p_node->FastGetSolutionStepValue(destination_variable);
506  const array_1d<double, 3>& origin_value = p_node->FastGetSolutionStepValue(origin_variable);
507  noalias(destination_value) = origin_value;
508  }
509 }
510 //**************************************************************************************************************************************************
511 //**************************************************************************************************************************************************
512 void SetValueOfAllNotes(ModelPart& r_model_part, const double& value, const Variable<double>& destination_variable)
513 {
514  #pragma omp parallel for
515  for (int i = 0; i < (int)r_model_part.Nodes().size(); ++i){
516  ModelPart::NodesContainerType::iterator i_particle = r_model_part.NodesBegin() + i;
517  Node::Pointer p_node = *(i_particle.base());
518  double& destination_value = p_node->FastGetSolutionStepValue(destination_variable);
519  destination_value = value;
520  }
521 }
522 //**************************************************************************************************************************************************
523 //**************************************************************************************************************************************************
524 void SetValueOfAllNotes(ModelPart& r_model_part, const array_1d<double, 3>& value, const Variable<array_1d<double, 3>>& destination_variable)
525 {
526  #pragma omp parallel for
527  for (int i = 0; i < (int)r_model_part.Nodes().size(); ++i){
528  ModelPart::NodesContainerType::iterator i_particle = r_model_part.NodesBegin() + i;
529  Node::Pointer p_node = *(i_particle.base());
530  array_1d<double, 3>& destination_value = p_node->FastGetSolutionStepValue(destination_variable);
531  noalias(destination_value) = value;
532  }
533 }
534 //**************************************************************************************************************************************************
535 //**************************************************************************************************************************************************
536 
537 private:
538 
539 bool mPressuresFilled;
540 bool mFirstGradientRecovery;
541 bool mFirstLaplacianRecovery;
542 bool mSomeCloudsDontWork;
543 bool mCalculatingTheGradient;
544 bool mCalculatingTheLaplacian;
545 bool mFirstTimeAppending;
546 double mLastMeasurementTime;
547 double mLastPressureVariation;
548 double mTotalDomainVolume;
549 std::vector<double> mPressures;
550 std::vector<DenseVector<double> > mFirstRowsOfB;
551 
552 //**************************************************************************************************************************************************
553 //**************************************************************************************************************************************************
554 
555 inline double CalculateArea(const double x0, const double y0,
556  const double x1, const double y1,
557  const double x2, const double y2)
558 {
559  const double x10 = x1 - x0;
560  const double y10 = y1 - y0;
561 
562  const double x20 = x2 - x0;
563  const double y20 = y2 - y0;
564 
565  const double area = 0.5 * std::abs(x10 * y20 - x20 * y10);
566 
567  return area;
568 }
569 //**************************************************************************************************************************************************
570 //**************************************************************************************************************************************************
571 
572 inline double CalculateVol(const double x0, const double y0, const double z0,
573  const double x1, const double y1, const double z1,
574  const double x2, const double y2, const double z2,
575  const double x3, const double y3, const double z3)
576 {
577  double x10 = x1 - x0;
578  double y10 = y1 - y0;
579  double z10 = z1 - z0;
580 
581  double x20 = x2 - x0;
582  double y20 = y2 - y0;
583  double z20 = z2 - z0;
584 
585  double x30 = x3 - x0;
586  double y30 = y3 - y0;
587  double z30 = z3 - z0;
588 
589  double detJ = x10 * y20 * z30 - x10 * y30 * z20 +
590  y10 * z20 * x30 - y10 * x20 * z30 +
591  z10 * x20 * y30 - z10 * y20 * x30;
592 
593  return detJ * 0.1666666666666666666666667;
594 }
595 
596 //***************************************************************************************************************
597 //***************************************************************************************************************
598 double CalculateElementalVolume(const Geometry<Node >& geom)
599 {
600  double vol;
601  double h;
602  if constexpr (TDim == 2){
603  double x0 = geom[0].X();
604  double y0 = geom[0].Y();
605  double x1 = geom[1].X();
606  double y1 = geom[1].Y();
607  double x2 = geom[2].X();
608  double y2 = geom[2].Y();
609 
610  vol = CalculateArea(x0, y0, x1, y1, x2, y2);
611  h = CalculateDiameter(x0, y0, x1, y1, x2, y2);
612  }
613 
614  else {
615  double x0 = geom[0].X();
616  double y0 = geom[0].Y();
617  double z0 = geom[0].Z();
618  double x1 = geom[1].X();
619  double y1 = geom[1].Y();
620  double z1 = geom[1].Z();
621  double x2 = geom[2].X();
622  double y2 = geom[2].Y();
623  double z2 = geom[2].Z();
624  double x3 = geom[3].X();
625  double y3 = geom[3].Y();
626  double z3 = geom[3].Z();
627 
628  vol = CalculateVol(x0, y0, z0, x1, y1, z1, x2, y2, z2, x3, y3, z3);
629  h = CalculateDiameter(x0, y0, z0, x1, y1, z1, x2, y2, z2, x3, y3, z3);
630  }
631  if (std::abs(vol)/std::pow(h, 3) < std::numeric_limits<double>::epsilon()){
632  KRATOS_ERROR << "Element with zero area found with the current geometry "<< geom << std::endl;
633  }
634 
635  return vol;
636 }
637 
638 //**************************************************************************************************************************************************
639 //**************************************************************************************************************************************************
640 
641 double CalculateDiameter(const double x0, const double y0,
642  const double x1, const double y1,
643  const double x2, const double y2)
644 {
645  double x10 = x1 - x0;
646  double y10 = y1 - y0;
647 
648  double x12 = x1 - x0;
649  double y12 = y1 - y0;
650 
651  double x20 = x2 - x0;
652  double y20 = y2 - y0;
653 
654  double dist_10 = std::sqrt(std::pow(x10,2) + std::pow(y10,2));
655  double dist_12 = std::sqrt(std::pow(x12,2) + std::pow(y12,2));
656  double dist_20 = std::sqrt(std::pow(x20,2) + std::pow(y20,2));
657 
658  double arr[] = {dist_10, dist_12, dist_20};
659 
660  double *max = std::max_element(std::begin(arr), std::end(arr));
661 
662  return *max;
663 }
664 
665 //**************************************************************************************************************************************************
666 //**************************************************************************************************************************************************
667 
668 double CalculateDiameter(const double x0, const double y0, const double z0,
669  const double x1, const double y1, const double z1,
670  const double x2, const double y2, const double z2,
671  const double x3, const double y3, const double z3)
672 {
673  double x10 = x1 - x0;
674  double y10 = y1 - y0;
675  double z10 = z1 - z0;
676 
677  double x12 = x1 - x0;
678  double y12 = y1 - y0;
679  double z12 = z1 - z0;
680 
681  double x13 = x1 - x3;
682  double y13 = y1 - y3;
683  double z13 = z1 - z3;
684 
685  double x20 = x2 - x0;
686  double y20 = y2 - y0;
687  double z20 = z2 - z0;
688 
689  double x23 = x2 - x3;
690  double y23 = y2 - y3;
691  double z23 = z2 - z3;
692 
693  double x30 = x3 - x0;
694  double y30 = y3 - y0;
695  double z30 = z3 - z0;
696 
697  double dist_10 = std::sqrt(std::pow(x10,2) + std::pow(y10,2) + std::pow(z10,2));
698  double dist_12 = std::sqrt(std::pow(x12,2) + std::pow(y12,2) + std::pow(z12,2));
699  double dist_13 = std::sqrt(std::pow(x13,2) + std::pow(y13,2) + std::pow(z13,2));
700  double dist_20 = std::sqrt(std::pow(x20,2) + std::pow(y20,2) + std::pow(z20,2));
701  double dist_23 = std::sqrt(std::pow(x23,2) + std::pow(y23,2) + std::pow(z23,2));
702  double dist_30 = std::sqrt(std::pow(x30,2) + std::pow(y30,2) + std::pow(z30,2));
703 
704  double arr[] = {dist_10, dist_12, dist_13, dist_20, dist_23, dist_30};
705 
706  double *max = std::max_element(std::begin(arr), std::end(arr));
707 
708  return *max;
709 }
710 
711 //**************************************************************************************************************************************************
712 //**************************************************************************************************************************************************
713 
714 double CalculateScalarIntegralOfLinearInterpolation(const Geometry<Node >& geom, const Variable<double>& r_var, double& vol)
715 {
716 
717  vol = geom.DomainSize();
718 
719  KRATOS_ERROR_IF(std::abs(vol) == 0.0) << "Element with zero area found. Its geometry is given by "<< geom << std::endl;
720 
721  const std::size_t n_nodes = geom.PointsNumber();
722  const auto& integration_points = geom.IntegrationPoints(GeometryData::IntegrationMethod::GI_GAUSS_2);
723  const IndexType number_of_gauss_points = integration_points.size();
724 
725  const Matrix& N_container = geom.ShapeFunctionsValues(GeometryData::IntegrationMethod::GI_GAUSS_2);
726  Vector N;
727 
728  for (IndexType g = 0; g < number_of_gauss_points; ++g) {
729  N = row(N_container, g);
730  }
731 
732  double value_at_gauss_point = 0.0;
733 
734  for (unsigned int i_node = 0; i_node < n_nodes; ++i_node){
735  value_at_gauss_point += N[i_node] * geom[i_node].FastGetSolutionStepValue(r_var, 0);
736  }
737 
738  return value_at_gauss_point;
739 }
740 
741 //**************************************************************************************************************************************************
742 //**************************************************************************************************************************************************
743 
744 array_1d <double, 3> CalculateVectorIntegralOfLinearInterpolation(const Geometry<Node >& geom, const Variable<array_1d <double, 3> >& r_var, double& vol)
745 {
746 
747  vol = geom.DomainSize();
748 
749  KRATOS_ERROR_IF(std::abs(vol) == 0.0) << "Element with zero area found. Its geometry is given by " << geom << std::endl;
750 
751  const std::size_t n_nodes = geom.PointsNumber();
752  const auto& integration_points = geom.IntegrationPoints(GeometryData::IntegrationMethod::GI_GAUSS_2);
753  const IndexType number_of_gauss_points = integration_points.size();
754 
755  const Matrix& N_container = geom.ShapeFunctionsValues(GeometryData::IntegrationMethod::GI_GAUSS_2);
756  Vector N;
757 
758  for (IndexType g = 0; g < number_of_gauss_points; ++g) {
759  N = row(N_container, g);
760  }
761 
762  array_1d <double, 3> value_at_gauss_point = N[0] * geom[0].FastGetSolutionStepValue(r_var);
763 
764  for (unsigned int i_node = 1; i_node < n_nodes; ++i_node){
765  value_at_gauss_point += N[i_node] * geom[i_node].FastGetSolutionStepValue(r_var);
766  }
767 
768  return value_at_gauss_point;
769 }
770 
771 //**************************************************************************************************************************************************
772 //**************************************************************************************************************************************************
773 
774 array_1d <double, 3> CalculateVectorIntegralOfLinearInterpolationPerUnitFluidMass(const Geometry<Node >& geom, const Variable<array_1d <double, 3> >& r_var, double& vol)
775 {
776 
777  vol = geom.DomainSize();
778 
779  KRATOS_ERROR_IF(std::abs(vol) == 0.0) << "Element with zero area found. Its geometry is given by " << geom << std::endl;
780 
781  const std::size_t n_nodes = geom.PointsNumber();
782  const auto& integration_points = geom.IntegrationPoints(GeometryData::IntegrationMethod::GI_GAUSS_2);
783  const IndexType number_of_gauss_points = integration_points.size();
784 
785  const Matrix& N_container = geom.ShapeFunctionsValues(GeometryData::IntegrationMethod::GI_GAUSS_2);
786 
787  Vector detJ_vector(number_of_gauss_points);
788  geom.DeterminantOfJacobian(detJ_vector, GeometryData::IntegrationMethod::GI_GAUSS_2);
789  Vector N;
790 
791  double integration_weight;
792 
793  array_1d <double, 3> value_at_gauss_point = {};
794 
795  for (IndexType g = 0; g < number_of_gauss_points; ++g) {
796  N = row(N_container, g);
797  integration_weight = integration_points[g].Weight() * detJ_vector[g];
798 
799 
800  for (unsigned int i_node = 0; i_node < n_nodes; ++i_node){
801  value_at_gauss_point += integration_weight * N[i_node] * geom[i_node].FastGetSolutionStepValue(r_var) * geom[i_node].FastGetSolutionStepValue(DENSITY) * geom[i_node].FastGetSolutionStepValue(FLUID_FRACTION);
802  }
803  }
804 
805  return value_at_gauss_point;
806 }
807 
808 //**************************************************************************************************************************************************
809 //**************************************************************************************************************************************************
810 
811 void PerformFirstStepComputations(ModelPart& r_model_part)
812 {
813  mTotalDomainVolume = CalculateDomainVolume(r_model_part);
814  mPressures.resize(r_model_part.Nodes().size());
815  mLastMeasurementTime = r_model_part.GetProcessInfo()[TIME];
816 
817  unsigned int i = 0;
818 
819  for (NodeIterator inode = r_model_part.NodesBegin(); inode != r_model_part.NodesEnd(); ++inode) {
820  mPressures[i] = inode->FastGetSolutionStepValue(PRESSURE);
821  ++i;
822  }
823 
824  mPressuresFilled = true;
825  mLastPressureVariation = GetRangeWithinVector(mPressures);
826 }
827 
828 //**************************************************************************************************************************************************
829 //**************************************************************************************************************************************************
830 
831 struct IsCloser{
832  bool operator()(std::pair<unsigned int, double> const& first_pair, std::pair<unsigned int, double> const& second_pair)
833  {
834  return(first_pair.second < second_pair.second || (first_pair.second == second_pair.second && first_pair.first < second_pair.first));
835  }
836 };
837 
838 //**************************************************************************************************************************************************
839 //**************************************************************************************************************************************************
840 
841 inline int Factorial(const unsigned int n){
842 
843  if (n == 0){
844  return 1;
845  }
846 
847  unsigned int k = n;
848 
849  for (unsigned int i = n - 1; i > 0; --i){
850  k *= i;
851  }
852 
853  return k;
854 }
855 
856 //**************************************************************************************************************************************************
857 //**************************************************************************************************************************************************
858 
859 double CalculateTheMaximumEdgeLength(ModelPart& r_model_part)
860 {
861  double max_distance_yet = 0.0;
862 
863  for (ModelPart::ElementIterator ielem = r_model_part.ElementsBegin(); ielem != r_model_part.ElementsEnd(); ++ielem){
864  Geometry<Node >& geom = ielem->GetGeometry();
865  unsigned int n_nodes = static_cast<unsigned int>(TDim + 1);
866 
867  for (unsigned int k = 1; k < n_nodes - 1; ++k){
868  for (unsigned int i = k; i < n_nodes; ++i){
869  array_1d <double, 3> delta_i = geom[k - 1] - geom[i];
870  double distance_2 = DEM_INNER_PRODUCT_3(delta_i, delta_i);
871  max_distance_yet = max_distance_yet > distance_2 ? max_distance_yet : distance_2;
872  }
873  }
874  }
875 
876  return(std::sqrt(max_distance_yet));
877 }
878 
879 //**************************************************************************************************************************************************
880 //**************************************************************************************************************************************************
881 
882 double CalculateTheMinumumEdgeLength(ModelPart& r_model_part)
883 {
884  double min_distance_yet = 0.0;
885 
886  bool first_node = true;
887 
888  for (ModelPart::ElementIterator ielem = r_model_part.ElementsBegin(); ielem != r_model_part.ElementsEnd(); ++ielem){
889  Geometry<Node >& geom = ielem->GetGeometry();
890 
891  if (first_node){ // assign the distance (squared) between any two nodes to min_distance_yet
892  array_1d <double, 3> delta = geom[0] - geom[1];
894  min_distance_yet = distance_2;
895  }
896 
897  unsigned int n_nodes = static_cast<unsigned int>(TDim + 1);
898 
899  for (unsigned int k = 1; k < n_nodes - 1; ++k){
900  for (unsigned int i = k; i < n_nodes; ++i){
901  array_1d <double, 3> delta_i = geom[k - 1] - geom[i];
902  double distance_2 = DEM_INNER_PRODUCT_3(delta_i, delta_i);
903 
904  min_distance_yet = min_distance_yet < distance_2 ? min_distance_yet : distance_2;
905  }
906  }
907  }
908 
909  return(std::sqrt(min_distance_yet));
910 }
911 
912 //**************************************************************************************************************************************************
913 //**************************************************************************************************************************************************
914 
915 // The following block of functions is used to calculate explicit matrix inverses and was taken from
916 // Richel BilderBeek's website (http://www.richelbilderbeek.nl/CppUblasMatrixExample6.htm), and it is
917 // transcribed here with a very minor modification
918 
919 double CalcDeterminant(const DenseMatrix<double>& m)
920 {
921  assert(m.size1() == m.size2() && "Can only calculate the determinant of square matrices");
922  switch(m.size1())
923  {
924  case 1:
925  {
926  return m(0,0);
927  }
928  case 2:
929  {
930  const double a = m(0,0);
931  const double b = m(0,1);
932  const double c = m(1,0);
933  const double d = m(1,1);
934  const double determinant = (a * d) - (b * c);
935  return determinant;
936  }
937  case 3:
938  {
939  assert(m.size1() == 3 && m.size2() == 3 && "Only for 3x3 matrices");
940  const double a = m(0,0);
941  const double b = m(0,1);
942  const double c = m(0,2);
943  const double d = m(1,0);
944  const double e = m(1,1);
945  const double f = m(1,2);
946  const double g = m(2,0);
947  const double h = m(2,1);
948  const double k = m(2,2);
949  const double determinant
950  = (a * ((e*k) - (f*h)))
951  - (b * ((k*d) - (f*g)))
952  + (c * ((d*h) - (e*g)));
953  return determinant;
954  }
955  default:
956  assert(!"Should not get here: unsupported matrix size");
957  throw std::runtime_error("Unsupported matrix size");
958  }
959 }
960 
962 //[ A at [0] B at [1] ]
963 //[ C at [2] D at [4] ]
964 const std::vector<DenseMatrix<double> > Chop(
965  const DenseMatrix<double>& m)
966 {
967  using boost::numeric::ublas::range;
968  using boost::numeric::ublas::matrix_range;
969  std::vector<matrix<double> > v;
970  v.reserve(4);
971  const int midy = m.size1() / 2;
972  const int midx = m.size2() / 2;
973  const matrix_range<const matrix<double> > top_left( m,range(0 ,midy ),range(0 ,midx ));
974  const matrix_range<const matrix<double> > bottom_left( m,range(midy,m.size1()),range(0 ,midx ));
975  const matrix_range<const matrix<double> > top_right( m,range(0 ,midy ),range(midx,m.size2()));
976  const matrix_range<const matrix<double> > bottom_right(m,range(midy,m.size1()),range(midx,m.size2()));
977  v.push_back(matrix<double>(top_left));
978  v.push_back(matrix<double>(top_right));
979  v.push_back(matrix<double>(bottom_left));
980  v.push_back(matrix<double>(bottom_right));
981  return v;
982 }
983 
985 const DenseMatrix<double> Unchop(
986  const std::vector<DenseMatrix<double> >& v)
987 {
988  //Chop returns a std::vector of sub-matrices
989  //[ A at [0] B at [1] ]
990  //[ C at [2] D at [4] ]
991  using boost::numeric::ublas::range;
992  using boost::numeric::ublas::matrix_range;
993  assert(v.size() == 4);
994  assert(v[0].size1() == v[1].size1());
995  assert(v[2].size1() == v[3].size1());
996  assert(v[0].size2() == v[2].size2());
997  assert(v[1].size2() == v[3].size2());
998  DenseMatrix<double> m(v[0].size1() + v[2].size1(),v[0].size2() + v[1].size2());
999  for (int quadrant=0; quadrant!=4; ++quadrant)
1000  {
1001  const DenseMatrix<double>& w = v[quadrant];
1002  const std::size_t n_rows = v[quadrant].size1();
1003  const std::size_t n_cols = v[quadrant].size2();
1004  const int offset_x = quadrant % 2 ? v[0].size2() : 0;
1005  const int offset_y = quadrant / 2 ? v[0].size1() : 0;
1006  for (std::size_t row=0; row!=n_rows; ++row)
1007  {
1008  for (std::size_t col=0; col!=n_cols; ++col)
1009  {
1010  m(offset_y + row, offset_x + col) = w(row,col);
1011  }
1012  }
1013  }
1014 
1015  assert(v[0].size1() + v[2].size1() == m.size1());
1016  assert(v[1].size1() + v[3].size1() == m.size1());
1017  assert(v[0].size2() + v[1].size2() == m.size2());
1018  assert(v[2].size2() + v[3].size2() == m.size2());
1019 
1020  return m;
1021 }
1022 
1023 //**************************************************************************************************************************************************
1024 //**************************************************************************************************************************************************
1025 
1029 DenseVector<unsigned int> mElementsPartition;
1030 
1034 
1035 double GetRangeWithinVector(const std::vector<double>& vector)
1036 {
1037  double min = vector[0];
1038  double max = vector[0];
1039 
1040  for (unsigned int i = 0; i != vector.size(); ++i){
1041  min = std::min(min, mPressures[i]);
1042  max = std::max(max, mPressures[i]);
1043  }
1044 
1045  return (max - min);
1046 }
1047 
1048 DenseVector<unsigned int>& GetElementPartition()
1049 {
1050  return mElementsPartition;
1051 }
1052 
1053 ElementIterator GetElementPartitionBegin(ModelPart& r_model_part, unsigned int k)
1054 {
1055  return r_model_part.GetCommunicator().LocalMesh().Elements().ptr_begin() + mElementsPartition[k];
1056 }
1057 
1058 ElementIterator GetElementPartitionEnd(ModelPart& r_model_part, unsigned int k)
1059 {
1060  return r_model_part.GetCommunicator().LocalMesh().Elements().ptr_begin() + mElementsPartition[k + 1];
1061 }
1062 
1063 //**************************************************************************************************************************************************
1064 //**************************************************************************************************************************************************
1065 
1066 }; // Class CustomFunctionsCalculator
1067 } // namespace Kratos.
1068 
1069 #endif // KRATOS_CREATE_AND_DESTROY defined
1070 
1071 
#define DEM_INNER_PRODUCT_3(a, b)
Definition: DEM_application_variables.h:31
MeshType & LocalMesh()
Returns the reference to the mesh storing all local entities.
Definition: communicator.cpp:245
Definition: custom_functions.h:41
void CalculateTotalHydrodynamicForceOnParticles(ModelPart &r_dem_model_part, array_1d< double, 3 > &force)
Definition: custom_functions.h:200
ModelPart::NodesContainerType NodesArrayType
Definition: custom_functions.h:46
const DenseMatrix< double > Inverse(const DenseMatrix< double > &m)
Definition: custom_functions.h:356
double determinant(boost::numeric::ublas::matrix_expression< matrix_T > const &mat_r)
Definition: custom_functions.h:330
void CopyValuesFromFirstToSecond(ModelPart &r_model_part, const Variable< array_1d< double, 3 >> &origin_variable, const Variable< array_1d< double, 3 >> &destination_variable)
Definition: custom_functions.h:499
double CalculateDomainVolume(ModelPart &r_fluid_model_part)
Definition: custom_functions.h:179
CustomFunctionsCalculator()
Definition: custom_functions.h:50
void CalculatePressureGradient(ModelPart &r_model_part)
Default calculator.
Definition: custom_functions.h:60
ModelPart::ElementsContainerType::iterator ElementIterator
Definition: custom_functions.h:44
void SetValueOfAllNotes(ModelPart &r_model_part, const array_1d< double, 3 > &value, const Variable< array_1d< double, 3 >> &destination_variable)
Definition: custom_functions.h:524
ModelPart::NodesContainerType::iterator NodeIterator
Definition: custom_functions.h:45
bool AssessStationarity(ModelPart &r_model_part, const double &tol)
Definition: custom_functions.h:109
KRATOS_CLASS_POINTER_DEFINITION(CustomFunctionsCalculator)
void SetValueOfAllNotes(ModelPart &r_model_part, const double &value, const Variable< double > &destination_variable)
Definition: custom_functions.h:512
void CopyValuesFromFirstToSecond(ModelPart &r_model_part, const Variable< double > &origin_variable, const Variable< double > &destination_variable)
Definition: custom_functions.h:486
virtual ~CustomFunctionsCalculator()
Calculator.
Definition: custom_functions.h:53
double CalculateGlobalFluidVolume(ModelPart &r_fluid_model_part)
Definition: custom_functions.h:298
void CalculateTotalHydrodynamicForceOnFluid(ModelPart &r_fluid_model_part, array_1d< double, 3 > &instantaneous_force, array_1d< double, 3 > &mean_force)
Definition: custom_functions.h:241
Geometry base class.
Definition: geometry.h:71
virtual double DomainSize() const
This method calculate and return length, area or volume of this geometry depending to it's dimension.
Definition: geometry.h:1371
static void CalculateGeometryData(const GeometryType &rGeometry, BoundedMatrix< double, 4, 3 > &rDN_DX, array_1d< double, 4 > &rN, double &rVolume)
This function is designed to compute the shape function derivatives, shape functions and volume in 3D...
Definition: geometry_utilities.h:176
Definition: amatrix_interface.h:41
ElementsContainerType & Elements()
Definition: mesh.h:568
This class aims to manage meshes for multi-physics simulations.
Definition: model_part.h:77
ElementIterator ElementsBegin(IndexType ThisIndex=0)
Definition: model_part.h:1169
Communicator & GetCommunicator()
Definition: model_part.h:1821
NodeIterator NodesBegin(IndexType ThisIndex=0)
Definition: model_part.h:487
ProcessInfo & GetProcessInfo()
Definition: model_part.h:1746
MeshType::NodesContainerType NodesContainerType
Nodes container. Which is a vector set of nodes with their Id's as key.
Definition: model_part.h:128
ElementIterator ElementsEnd(IndexType ThisIndex=0)
Definition: model_part.h:1179
MeshType::ElementIterator ElementIterator
Definition: model_part.h:174
NodeIterator NodesEnd(IndexType ThisIndex=0)
Definition: model_part.h:497
NodesContainerType & Nodes(IndexType ThisIndex=0)
Definition: model_part.h:507
static int GetNumThreads()
Returns the current number of threads.
Definition: parallel_utilities.cpp:34
size_type size() const
Returns the number of elements in the container.
Definition: pointer_vector_set.h:502
std::size_t IndexType
The definition of the index type.
Definition: key_hash.h:35
#define KRATOS_ERROR
Definition: exception.h:161
#define KRATOS_ERROR_IF(conditional)
Definition: exception.h:162
#define KRATOS_INFO(label)
Definition: logger.h:250
end
Definition: DEM_benchmarks.py:180
static double max(double a, double b)
Definition: GeometryFunctions.h:79
static double min(double a, double b)
Definition: GeometryFunctions.h:71
REF: G. R. Cowper, GAUSSIAN QUADRATURE FORMULAS FOR TRIANGLES.
Definition: mesh_condition.cpp:21
KratosZeroVector< double > ZeroVector
Definition: amatrix_interface.h:561
Internals::Matrix< double, AMatrix::dynamic, 1 > Vector
Definition: amatrix_interface.h:472
Internals::Matrix< double, AMatrix::dynamic, AMatrix::dynamic > Matrix
Definition: amatrix_interface.h:470
AMatrix::MatrixProductExpression< TExpression1Type, TExpression2Type > prod(AMatrix::MatrixExpression< TExpression1Type, TCategory1 > const &First, AMatrix::MatrixExpression< TExpression2Type, TCategory2 > const &Second)
Definition: amatrix_interface.h:568
T & noalias(T &TheMatrix)
Definition: amatrix_interface.h:484
AMatrix::MatrixRow< const TExpressionType > row(AMatrix::MatrixExpression< TExpressionType, TCategory > const &TheExpression, std::size_t RowIndex)
Definition: amatrix_interface.h:649
AMatrix::TransposeMatrix< const T > trans(const T &TheMatrix)
Definition: amatrix_interface.h:486
REACTION_CHECK_STIFFNESS_FACTOR int
Definition: contact_structural_mechanics_application_variables.h:75
float velocity
Definition: PecletTest.py:54
v
Definition: generate_convection_diffusion_explicit_element.py:114
w
Definition: generate_convection_diffusion_explicit_element.py:108
f
Definition: generate_convection_diffusion_explicit_element.py:112
h
Definition: generate_droplet_dynamics.py:91
x2
Definition: generate_frictional_mortar_condition.py:122
x1
Definition: generate_frictional_mortar_condition.py:121
a
Definition: generate_stokes_twofluid_element.py:77
b
Definition: generate_total_lagrangian_mixed_volumetric_strain_element.py:31
int n_nodes
Definition: generate_total_lagrangian_mixed_volumetric_strain_element.py:15
c
Definition: generate_weakly_compressible_navier_stokes_element.py:108
grad
Definition: hinsberg_optimization.py:148
int tol
Definition: hinsberg_optimization.py:138
characteristic_length
Definition: isotropic_damage_automatic_differentiation.py:135
int d
Definition: ode_solve.py:397
int n
manufactured solution and derivatives (u=0 at z=0 dudz=0 at z=domain_height)
Definition: ode_solve.py:402
int k
Definition: quadrature.py:595
float delta_t
Definition: rotatingcone_PureConvectionBenchmarking.py:129
int m
Definition: run_marine_rain_substepping.py:8
N
Definition: sensitivityMatrix.py:29
distance_2
Definition: sp_statistics.py:71
integer i
Definition: TensorModule.f:17
double precision, dimension(3, 3), public delta
Definition: TensorModule.f:16
e
Definition: run_cpp_mpi_tests.py:31
#define SWIMMING_MODULUS_3(a)
Definition: swimming_DEM_application.h:53