OOFEM  2.4
OOFEM.org - Object Oriented Finite Element Solver
prescribedgradientbcperiodic.C
Go to the documentation of this file.
1 /*
2  *
3  * ##### ##### ###### ###### ### ###
4  * ## ## ## ## ## ## ## ### ##
5  * ## ## ## ## #### #### ## # ##
6  * ## ## ## ## ## ## ## ##
7  * ## ## ## ## ## ## ## ##
8  * ##### ##### ## ###### ## ##
9  *
10  *
11  * OOFEM : Object Oriented Finite Element Code
12  *
13  * Copyright (C) 1993 - 2013 Borek Patzak
14  *
15  *
16  *
17  * Czech Technical University, Faculty of Civil Engineering,
18  * Department of Structural Mechanics, 166 29 Prague, Czech Republic
19  *
20  * This library is free software; you can redistribute it and/or
21  * modify it under the terms of the GNU Lesser General Public
22  * License as published by the Free Software Foundation; either
23  * version 2.1 of the License, or (at your option) any later version.
24  *
25  * This program is distributed in the hope that it will be useful,
26  * but WITHOUT ANY WARRANTY; without even the implied warranty of
27  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
28  * Lesser General Public License for more details.
29  *
30  * You should have received a copy of the GNU Lesser General Public
31  * License along with this library; if not, write to the Free Software
32  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
33  */
34 
36 #include "dofiditem.h"
37 #include "node.h"
38 #include "dofmanager.h"
39 #include "dof.h"
40 #include "valuemodetype.h"
41 #include "floatarray.h"
42 #include "floatmatrix.h"
43 #include "engngm.h"
44 #include "node.h"
45 #include "activedof.h"
46 #include "masterdof.h"
47 #include "classfactory.h"
48 #include "sparsemtrxtype.h"
49 #include "sparsemtrx.h"
50 #include "sparselinsystemnm.h"
51 #include "dynamicinputrecord.h"
52 #include "domain.h"
53 #include "spatiallocalizer.h"
54 #include "feinterpol.h"
55 #include "unknownnumberingscheme.h"
56 #include "function.h"
57 #include "timestep.h"
58 #include "mathfem.h"
59 
60 namespace oofem {
61 REGISTER_BoundaryCondition(PrescribedGradientBCPeriodic);
62 
64  strain( new Node(1, d) )
65 {
66  // The unknown volumetric strain
67  int nsd = d->giveNumberOfSpatialDimensions();
68  int components = nsd * nsd;
69  // The prescribed strains.
70  for ( int i = 0; i < components; i++ ) {
71  int dofid = d->giveNextFreeDofID();
72  strain_id.followedBy(dofid);
73  // Just putting in X_i id-items since they don't matter.
74  // These don't actually need to be active, they are masterdofs with prescribed values, its
75  // easier to just have them here rather than trying to make another Dirichlet boundary condition.
76  //strain->appendDof( new ActiveDof( strain.get(), (DofIDItem)dofid, this->giveNumber() ) );
77  strain->appendDof( new MasterDof(strain.get(), this->giveNumber(), 0, (DofIDItem)dofid ) );
78  }
79 }
80 
81 
83 {
84 }
85 
86 
88 {
89  return 1;
90 }
91 
92 
94 {
95  return this->strain.get();
96 }
97 
98 
100 {
101  FloatArray coord;
103  //Set *masterSet = this->domain->giveSet(2);
104  const IntArray &nodes = this->domain->giveSet(this->set)->giveNodeList(); // Split into slave set and master set?
105  int nsd = jump.giveSize();
106  std :: vector< FloatArray > jumps;
107  // Construct all the possible jumps;
108  jumps.reserve((2 << (nsd-1)) - 1);
109  if ( nsd != 3 ) {
110  OOFEM_ERROR("Only 3d implemented yet!");
111  }
112  jumps.emplace_back(jump);
113  jumps.emplace_back(FloatArray{jump.at(1), jump.at(2), 0.});
114  jumps.emplace_back(FloatArray{jump.at(1), 0., jump.at(3)});
115  jumps.emplace_back(FloatArray{0., jump.at(2), jump.at(3)});
116  jumps.emplace_back(FloatArray{jump.at(1), 0., 0.});
117  jumps.emplace_back(FloatArray{0., jump.at(2), 0.});
118  jumps.emplace_back(FloatArray{0., 0., jump.at(3)});
119 
120  this->slavemap.clear();
121  for ( int inode : nodes ) {
122  Node *masterNode = NULL;
123  Node *node = this->domain->giveNode(inode);
124  const FloatArray &masterCoord = *node->giveCoordinates();
125  //printf("node %d\n", node->giveLabel()); masterCoord.printYourself();
126  // The difficult part, what offset to subtract to find the master side;
127  for ( FloatArray &testJump : jumps ) {
128  coord.beDifferenceOf(masterCoord, testJump);
129  masterNode = sl->giveNodeClosestToPoint(coord, fabs(jump.at(1))*1e-5);
130  if ( masterNode != NULL ) {
131  //printf("Found master (%d) to node %d (distance = %e)\n", masterNode->giveNumber(), node->giveNumber(),
132  // masterNode->giveCoordinates()->distance(coord));
133  break;
134  }
135  }
136  if ( masterNode != NULL ) {
137  this->slavemap.insert({node->giveNumber(), masterNode->giveNumber()});
138  } else {
139  OOFEM_ERROR("Couldn't find master node!");
140  }
141  }
142 }
143 
144 
146 {
147  if ( this->isStrainDof(dof) ) {
148  return 1;
149  }
150  return this->giveDomain()->giveNumberOfSpatialDimensions() + 1;
151 }
152 
153 
155 {
156  if ( this->isStrainDof(dof) ) {
157  return NULL;
158  }
159  if ( mdof == 1 ) {
160  int node = this->slavemap[dof->giveDofManager()->giveNumber()];
161  //printf("dofid = %d, slave node = %d, master node = %d\n", dof->giveDofID(),dof->giveDofManager()->giveNumber(), node );
162  //this->domain->giveDofManager(node)->printYourself();
163  return this->domain->giveDofManager(node)->giveDofWithID(dof->giveDofID());
164  } else {
165  DofIDItem dofid = dof->giveDofID();
166  FloatArray *coords = dof->giveDofManager()->giveCoordinates();
167  int nsd = coords->giveSize();
168  if ( dofid == D_u || dofid == V_u ) {
169  return this->strain->giveDofWithID(strain_id[nsd*(mdof-2)]);
170  } else if ( dofid == D_v || dofid == V_v ) {
171  return this->strain->giveDofWithID(strain_id[nsd*(mdof-2)+1]);
172  } else /* if ( dofid == D_u || dofid == V_u ) */ {
173  return this->strain->giveDofWithID(strain_id[nsd*(mdof-2)+2]);
174  }
175  }
176 }
177 
178 
180 {
181  DofIDEquationNumbering pnum(true, strain_id);
182  EngngModel *emodel = this->giveDomain()->giveEngngModel();
183  FloatArray tmp, sig_tmp;
184  int npeq = strain_id.giveSize();
185  // sigma = residual (since we use the slave dofs) = f_ext - f_int
186  sig_tmp.resize(npeq);
187  sig_tmp.zero();
188  emodel->assembleVector(sig_tmp, tStep, InternalForceAssembler(), VM_Total, pnum, this->domain);
189  tmp.resize(npeq);
190  tmp.zero();
191  emodel->assembleVector(tmp, tStep, ExternalForceAssembler(), VM_Total, pnum, this->domain);
192  sig_tmp.subtract(tmp);
193  // Divide by the RVE-volume
194  sig_tmp.times(1.0 / ( this->domainSize(this->giveDomain(), this->set) + this->domainSize(this->giveDomain(), this->masterSet) ));
195 
196  sigma.resize(sig_tmp.giveSize());
197  if ( sig_tmp.giveSize() == 9 ) {
198  sigma.assemble(sig_tmp, {1, 9, 8, 6, 2, 7, 5, 4, 3});
199  } else if ( sig_tmp.giveSize() == 4 ) {
200  sigma.assemble(sig_tmp, {1, 4, 3, 2});
201  } else {
202  sigma = sig_tmp;
203  }
204 }
205 
206 
208 {
210  DofIDEquationNumbering pnum(true, strain_id);
211  EngngModel *rve = this->giveDomain()->giveEngngModel();
213  std :: unique_ptr< SparseLinearSystemNM > solver( classFactory.createSparseLinSolver( ST_Petsc, this->domain, this->domain->giveEngngModel() ) ); // = rve->giveLinearSolver();
214  SparseMtrxType stype = solver->giveRecommendedMatrix(true);
215  std :: unique_ptr< SparseMtrx > Kff( classFactory.createSparseMtrx( stype ) );
216  std :: unique_ptr< SparseMtrx > Kfp( classFactory.createSparseMtrx( stype ) );
217  std :: unique_ptr< SparseMtrx > Kpp( classFactory.createSparseMtrx( stype ) );
218 
219  Kff->buildInternalStructure(rve, this->domain->giveNumber(), fnum);
220  Kfp->buildInternalStructure(rve, this->domain->giveNumber(), fnum, pnum);
221  Kpp->buildInternalStructure(rve, this->domain->giveNumber(), pnum);
222  rve->assemble(*Kff, tStep, TangentAssembler(TangentStiffness), fnum, this->domain);
223  rve->assemble(*Kfp, tStep, TangentAssembler(TangentStiffness), fnum, pnum, this->domain);
224  rve->assemble(*Kpp, tStep, TangentAssembler(TangentStiffness), pnum, this->domain);
225 
226  int neq = Kfp->giveNumberOfRows();
227  int nsd = this->domain->giveNumberOfSpatialDimensions();
228  int ncomp = nsd * nsd;
229 
230  FloatMatrix grad_pert(ncomp, ncomp), rhs, sol(neq, ncomp);
231  grad_pert.resize(ncomp, ncomp); // In fact, npeq should most likely equal ndev
232  grad_pert.beUnitMatrix();
233 
234  // Compute the solution to each of the pertubation of eps
235  Kfp->times(grad_pert, rhs);
236  solver->solve(*Kff, rhs, sol);
237 
238  // Compute the solution to each of the pertubation of eps
239  FloatMatrix E_tmp;
240  Kfp->timesT(sol, E_tmp); // Assuming symmetry of stiffness matrix
241  // This is probably always zero, but for generality
242  FloatMatrix tmpMat;
243  Kpp->times(grad_pert, tmpMat);
244  E_tmp.subtract(tmpMat);
245  E_tmp.times( - 1.0 / ( this->domainSize(this->giveDomain(), this->set) + this->domainSize(this->giveDomain(), this->masterSet) ));
246 
247  E.resize(E_tmp.giveNumberOfRows(), E_tmp.giveNumberOfColumns());
248  if ( nsd == 3 ) {
249  if ( E_tmp.giveNumberOfRows() == 6 ) {
250  E.assemble(E_tmp, {1, 6, 5, 6, 2, 4, 5, 4, 3});
251  } else {
252  E.assemble(E_tmp, {1, 9, 8, 6, 2, 7, 5, 4, 3});
253  }
254  } else if ( nsd == 2 ) {
255  E.assemble(E_tmp, {1, 4, 3, 2});
256  } else {
257  E = E_tmp;
258  }
259 }
260 
261 
263 {
264  DofManager *master = this->domain->giveDofManager(this->slavemap[dof->giveDofManager()->giveNumber()]);
265  FloatArray *coords = dof->giveDofManager()->giveCoordinates();
266  FloatArray *masterCoords = master->giveCoordinates();
267 
268  FloatArray dx;
269  dx.beDifferenceOf(* coords, * masterCoords );
270 
271  int nsd = dx.giveSize(); // Number of spatial dimensions
272 
273  masterContribs.resize(nsd + 1);
274 
275  masterContribs.at(1) = 1.; // Master dof is always weight 1.0
276  for ( int i = 1; i <= dx.giveSize(); ++i ) {
277  masterContribs.at(i+1) = dx.at(i);
278  }
279 }
280 
281 
283 {
284  DofManager *master = this->domain->giveDofManager(this->slavemap[dof->giveDofManager()->giveNumber()]);
285  DofIDItem id = dof->giveDofID();
286  FloatArray *coords = dof->giveDofManager()->giveCoordinates();
287  FloatArray *masterCoords = master->giveCoordinates();
288  FloatArray dx, uM;
289  dx.beDifferenceOf(* coords, * masterCoords );
290 
291  int ind;
292  if ( id == D_u || id == V_u || id == P_f || id == T_f ) {
293  ind = 1;
294  } else if ( id == D_v || id == V_v ) {
295  ind = 2;
296  } else { /*if ( id == D_w || id == V_w )*/ // 3D only:
297  ind = 3;
298  }
299 
300  FloatMatrix grad(3, 3);
301  for ( int i = 0; i < this->strain_id.giveSize(); ++i ) {
302  Dof *dof = this->strain->giveDofWithID(strain_id[i]);
303  grad(i % 3, i / 3) = dof->giveUnknown(mode, tStep);
304  }
305  uM.beProductOf(grad, dx); // The "jump" part of the unknown ( u^+ = [[u^M]] + u^- )
306 
307  return val + uM.at(ind);
308 }
309 
310 
312 {
313  if ( this->isStrainDof(dof) ) {
314  int ind = strain_id.findFirstIndexOf(dof->giveDofID()) - 1;
315  return this->mGradient(ind % 3, ind / 3) * this->giveTimeFunction()->evaluateAtTime(tStep->giveTargetTime());
316  }
317 
318  DofManager *master = this->domain->giveDofManager(this->slavemap[dof->giveDofManager()->giveNumber()]);
319  double val = master->giveDofWithID(dof->giveDofID())->giveUnknown(field, mode, tStep);
320  return this->giveUnknown(val, mode, tStep, dof);
321 }
322 
323 
325 {
326  if ( this->isStrainDof(dof) ) {
327  int ind = strain_id.findFirstIndexOf(dof->giveDofID()) - 1;
328  return this->mGradient(ind % 3, ind / 3) * this->giveTimeFunction()->evaluateAtTime(tStep->giveTargetTime());
329  }
330 
331  DofManager *master = this->domain->giveDofManager(this->slavemap[dof->giveDofManager()->giveNumber()]);
332 
333  if ( mode == VM_Incremental ) {
334  double val = master->giveDofWithID(dof->giveDofID())->giveUnknown(mode, tStep);
335  return this->giveUnknown(val, mode, tStep, dof);
336  }
337  double val = master->giveDofWithID(dof->giveDofID())->giveUnknown(mode, tStep);
338  return this->giveUnknown(val, mode, tStep, dof);
339 }
340 
341 
343 {
344  return this->isStrainDof(dof);
345 }
346 
347 
349 {
350  if ( this->isStrainDof(dof) ) {
351  int index = strain_id.findFirstIndexOf(dof->giveDofID()) - 1;
352  return this->mGradient( index % 3, index / 3 ) * this->giveTimeFunction()->evaluateAtTime(tStep->giveTargetTime());;
353  }
354  OOFEM_ERROR("Has no prescribed value from bc.");
355  return 0.0;
356 }
357 
358 
360 {
361  return this->isStrainDof(dof);
362 }
363 
364 
366 {
367  return this->strain.get() == dof->giveDofManager();
368 }
369 
370 
372 {
373  IRResultType result;
374 
377 
380 }
381 
382 
384 {
389 }
390 
391 
393 {
394  this->findSlaveToMasterMap();
395 }
396 } // end namespace oofem
The base class for all spatial localizers.
void setField(int item, InputFieldType id)
int giveNumberOfColumns() const
Returns number of columns of receiver.
Definition: floatmatrix.h:158
The representation of EngngModel default unknown numbering.
void subtract(const FloatArray &src)
Subtracts array src to receiver.
Definition: floatarray.C:258
virtual void computeTangent(FloatMatrix &E, TimeStep *tStep)
Computes the macroscopic tangent for homogenization problems through sensitivity analysis.
REGISTER_BoundaryCondition(BoundaryCondition)
Class and object Domain.
Definition: domain.h:115
Implementation for assembling internal forces vectors in standard monolithic, nonlinear FE-problems...
virtual void giveInputRecord(DynamicInputRecord &input)
Domain * domain
Link to domain object, useful for communicating with other FEM components.
Definition: femcmpnn.h:82
IRResultType initializeFrom(InputRecord *ir)
Initializes receiver according to object description stored in input record.
Definition: activebc.h:75
SparseMtrx * createSparseMtrx(SparseMtrxType type)
Creates new instance of sparse matrix corresponding to given keyword.
Definition: classfactory.C:105
Class for homogenization of applied gradients.
Abstract class representing field of primary variables (those, which are unknown and are typically as...
Definition: primaryfield.h:104
Specialized numbering scheme for assembling only specified DofIDs.
virtual Dof * giveMasterDof(ActiveDof *dof, int mdof)
Give the pointer to master dof belonging to active DOF.
double & at(int i)
Coefficient access function.
Definition: floatarray.h:131
ValueModeType
Type representing the mode of UnknownType or CharType, or similar types.
Definition: valuemodetype.h:78
virtual int giveNumberOfMasterDofs(ActiveDof *dof)
Allows for active boundary conditions to handle their own special DOF.
virtual FloatArray * giveCoordinates()
Definition: dofmanager.h:382
virtual double giveUnknown(ValueModeType mode, TimeStep *tStep)=0
The key method of class Dof.
EngngModel * giveEngngModel()
Returns engineering model to which receiver is associated.
Definition: domain.C:433
Implementation for assembling tangent matrices in standard monolithic FE-problems.
double giveTargetTime()
Returns target time.
Definition: timestep.h:146
virtual IRResultType initializeFrom(InputRecord *ir)
Initializes receiver according to object description stored in input record.
Base class for dof managers.
Definition: dofmanager.h:113
int giveNumber()
Returns domain number.
Definition: domain.h:266
int giveNumberOfSpatialDimensions()
Returns number of spatial dimensions.
Definition: domain.C:1067
Class implementing an array of integers.
Definition: intarray.h:61
virtual void computeField(FloatArray &sigma, TimeStep *tStep)
Computes the homogenized, macroscopic, field (stress).
void beDifferenceOf(const FloatArray &a, const FloatArray &b)
Sets receiver to be a - b.
Definition: floatarray.C:341
virtual double giveBcValue(Dof *dof, ValueModeType mode, TimeStep *tStep)
Returns the prescribed value of a dof (if any).
virtual DofManager * giveInternalDofManager(int i)
Gives an internal dof manager from receiver.
virtual void assemble(SparseMtrx &answer, TimeStep *tStep, const MatrixAssembler &ma, const UnknownNumberingScheme &s, Domain *domain)
Assembles characteristic matrix of required type into given sparse matrix.
Definition: engngm.C:803
Class representing "master" degree of freedom.
Definition: masterdof.h:92
virtual bool isPrimaryDof(ActiveDof *dof)
Checks to see if the dof is a primary DOF.
#define _IFT_PrescribedGradientBCPeriodic_masterSet
#define _IFT_PrescribedGradientBCPeriodic_jump
#define E(p)
Definition: mdm.C:368
#define OOFEM_ERROR(...)
Definition: error.h:61
DofIDItem
Type representing particular dof type.
Definition: dofiditem.h:86
SparseMtrxType
Enumerative type used to identify the sparse matrix type.
virtual Node * giveNodeClosestToPoint(const FloatArray &coords, double maxDist)=0
Returns the node closest to the given coordinate.
Implementation for assembling external forces vectors in standard monolithic FE-problems.
void times(double f)
Multiplies receiver by factor f.
Definition: floatmatrix.C:1594
SpatialLocalizer * giveSpatialLocalizer()
Returns receiver&#39;s associated spatial localizer.
Definition: domain.C:1184
const IntArray & giveNodeList()
Returns list of all nodes within set.
Definition: set.C:146
virtual void postInitialize()
Performs post initialization steps.
DofIDItem giveDofID() const
Returns DofID value of receiver, which determines type of of unknown connected to receiver (e...
Definition: dof.h:276
Set * giveSet(int n)
Service for accessing particular domain set.
Definition: domain.C:363
virtual bool hasBc(Dof *dof, TimeStep *tStep)
Returns the prescribed value of a dof (if any).
void beProductOf(const FloatMatrix &aMatrix, const FloatArray &anArray)
Receiver becomes the result of the product of aMatrix and anArray.
Definition: floatarray.C:676
Abstract base class for all active boundary conditions.
Definition: activebc.h:63
virtual IRResultType initializeFrom(InputRecord *ir)
Initializes receiver according to object description stored in input record.
Class representing "slave" degree of freedom with an active boundary condition.
Definition: activedof.h:48
void subtract(const FloatMatrix &a)
Subtracts matrix from the receiver.
Definition: floatmatrix.C:1084
int giveNextFreeDofID(int increment=1)
Gives the next free dof ID.
Definition: domain.C:1411
Class representing vector of real numbers.
Definition: floatarray.h:82
SparseLinearSystemNM * createSparseLinSolver(LinSystSolverType st, Domain *d, EngngModel *m)
Creates new instance of SparseLinearSystemNM corresponding to given type.
Definition: classfactory.C:120
void findSlaveToMasterMap()
This is the central support function, which finds the corresponding slave nodes for each master node...
Implementation of matrix containing floating point numbers.
Definition: floatmatrix.h:94
IRResultType
Type defining the return values of InputRecord reading operations.
Definition: irresulttype.h:47
void assemble(const FloatArray &fe, const IntArray &loc)
Assembles the array fe (typically, the load vector of a finite element) into the receiver, using loc as location array.
Definition: floatarray.C:551
void resize(int rows, int cols)
Checks size of receiver towards requested bounds.
Definition: floatmatrix.C:1358
virtual void giveInputRecord(DynamicInputRecord &input)
Class representing the general Input Record.
Definition: inputrecord.h:101
Dof * giveDofWithID(int dofID) const
Returns DOF with given dofID; issues error if not present.
Definition: dofmanager.C:119
void followedBy(const IntArray &b, int allocChunk=0)
Appends array b at the end of receiver.
Definition: intarray.C:145
void zero()
Zeroes all coefficients of receiver.
Definition: floatarray.C:658
Class representing the a dynamic Input Record.
void times(double s)
Multiplies receiver with scalar.
Definition: floatarray.C:818
ClassFactory & classFactory
Definition: classfactory.C:59
#define new
virtual FloatArray * giveCoordinates()
Definition: node.h:114
Domain * giveDomain() const
Definition: femcmpnn.h:100
virtual void giveInputRecord(DynamicInputRecord &input)
Setups the input record string of receiver.
Abstract base class representing the "problem" under consideration.
Definition: engngm.h:181
DofManager * giveDofManager() const
Definition: dof.h:123
int giveSize() const
Definition: intarray.h:203
int giveSize() const
Returns the size of receiver.
Definition: floatarray.h:218
Node * giveNode(int n)
Service for accessing particular domain node.
Definition: domain.h:371
the oofem namespace is to define a context or scope in which all oofem names are defined.
void assemble(const FloatMatrix &src, const IntArray &loc)
Assembles the contribution using localization array into receiver.
Definition: floatmatrix.C:251
Class implementing node in finite element mesh.
Definition: node.h:87
#define IR_GIVE_FIELD(__ir, __value, __id)
Macro facilitating the use of input record reading methods.
Definition: inputrecord.h:69
Abstract class Dof represents Degree Of Freedom in finite element mesh.
Definition: dof.h:93
int giveNumber() const
Definition: femcmpnn.h:107
DofManager * giveDofManager(int n)
Service for accessing particular domain dof manager.
Definition: domain.C:314
int giveNumberOfRows() const
Returns number of rows of receiver.
Definition: floatmatrix.h:156
virtual void computeDofTransformation(ActiveDof *dof, FloatArray &masterContribs)
virtual double evaluateAtTime(double t)
Returns the value of the function at given time.
Definition: function.C:76
virtual int giveNumberOfInternalDofManagers()
Gives the number of internal dof managers.
void assembleVector(FloatArray &answer, TimeStep *tStep, const VectorAssembler &va, ValueModeType mode, const UnknownNumberingScheme &s, Domain *domain, FloatArray *eNorms=NULL)
Assembles characteristic vector of required type from dofManagers, element, and active boundary condi...
Definition: engngm.C:986
Class representing solution step.
Definition: timestep.h:80
int findFirstIndexOf(int value) const
Finds index of first occurrence of given value in array.
Definition: intarray.C:331
double giveUnknown(double val, ValueModeType mode, TimeStep *tStep, ActiveDof *dof)
void resize(int s)
Resizes receiver towards requested size.
Definition: floatarray.C:631

This page is part of the OOFEM documentation. Copyright (c) 2011 Borek Patzak
Project e-mail: info@oofem.org
Generated at Tue Jan 2 2018 20:07:30 for OOFEM by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2011