OOFEM 3.0
Loading...
Searching...
No Matches
xfemstructuremanager.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 - 2025 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 "classfactory.h"
37#include "dynamicinputrecord.h"
38#include "dynamicdatareader.h"
39#include "datareader.h"
40#include "mathfem.h"
41
44#include "geometry.h"
45
46#include "matforceevaluator.h"
47
48namespace oofem {
50
51XfemStructureManager :: XfemStructureManager(Domain *domain) :
52 XfemManager(domain),
53 mSplitCracks(false),
54 mNonstandardCz(false),
55 mMinCrackLength(0.0),
56 mCrackMergeTol(0.0),
57 mpMatForceEvaluator( new MaterialForceEvaluator() )
58{}
59
60XfemStructureManager :: ~XfemStructureManager()
61{}
62
63void XfemStructureManager :: initializeFrom(InputRecord &ir)
64{
65 XfemManager :: initializeFrom(ir);
66
67 int splitCracks = 0;
69 if ( splitCracks == 1 ) {
70 mSplitCracks = true;
71 }
72
73 int nonStdCz = 0;
75 if ( nonStdCz == 1 ) {
76 mNonstandardCz = true;
77 }
78
80
81#if 0
82 if ( mMinCrackLength < 1.0e-12 ) {
83 printf("mMinCrackLength: %e\n", mMinCrackLength);
84 }
85#endif
86
88
89 if ( mCrackMergeTol > 1.0e-12 ) {
90 printf("mCrackMergeTol: %e\n", mCrackMergeTol);
91 }
92}
93
94void XfemStructureManager :: postInitialize() {
95 XfemManager :: postInitialize();
96
97 if ( mSplitCracks ) {
99 }
102
103}
104
105void XfemStructureManager :: giveInputRecord(DynamicInputRecord &input)
106{
107 XfemManager :: giveInputRecord(input);
108
109 if ( mSplitCracks ) {
111 }
112
114
116
117 if ( mNonstandardCz ) {
119 }
120}
121
122int XfemStructureManager :: instanciateYourself(DataReader &dr)
123{
124 int result = XfemManager :: instanciateYourself(dr);
125
126
127 return result;
128}
129
130void XfemStructureManager :: propagateFronts(bool &oAnyFronHasPropagated)
131{
132 oAnyFronHasPropagated = false;
133
134 for ( auto &ei: enrichmentItemList ) {
135
136 bool eiHasPropagated = false;
137 ei->propagateFronts(eiHasPropagated);
138
139 if ( eiHasPropagated ) {
140 oAnyFronHasPropagated = true;
141 }
142 }
143
145}
146
147
148void XfemStructureManager :: updateYourself(TimeStep *tStep)
149{
151}
152
153void XfemStructureManager :: splitCracks()
154{
155 // Loop over cracks
156 for ( int i = 1; i <= giveNumberOfEnrichmentItems(); i++ ) {
157 Crack *crack_i = dynamic_cast< Crack * >( this->giveEnrichmentItem(i) );
158 if ( crack_i ) {
159 // Check if crack i intersects with any of the cracks [1,i-1]:
160 for ( int j = 1; j < i; j++ ) {
161 // TODO: To improve performance, we may wish to use
162 // a tree structure here.
163 bool splittedCrack = false;
164
165 Crack *crack_j = dynamic_cast< Crack * >( this->giveEnrichmentItem(j) );
166 if ( crack_j ) {
167 // If so, find the arc length positions of the intersections on crack i ...
168
169 std :: vector< FloatArray >intersectionPoints;
170 std :: vector< double >arcPositions_i, arcPositions_j;
171 crack_i->computeCrackIntersectionPoints(* crack_j, intersectionPoints, arcPositions_i);
172 crack_j->computeArcPoints(intersectionPoints, arcPositions_j);
173
174 const double arcLengthTol = 1.0e-6;
175
176 for ( int k = 0; k < int( arcPositions_i.size() ); k++ ) {
177 if ( arcPositions_i [ k ] < arcLengthTol || arcPositions_i [ k ] > ( 1.0 - arcLengthTol ) || arcPositions_j [ k ] < arcLengthTol || arcPositions_j [ k ] > ( 1.0 - arcLengthTol ) ) {
178 arcPositions_i.erase(arcPositions_i.begin() + k);
179 arcPositions_j.erase(arcPositions_j.begin() + k);
180 k--;
181 }
182 }
183
184 if ( arcPositions_i.size() > 0 ) {
185 arcPositions_i.insert(arcPositions_i.begin(), 0.0);
186 arcPositions_i.push_back(1.0);
187 arcPositions_j.insert(arcPositions_j.begin(), 0.0);
188 arcPositions_j.push_back(1.0);
189
190 for ( int k = 1; k < int( arcPositions_i.size() ); k++ ) {
191 // Only include segments of finite length
192 if ( fabs(arcPositions_i [ k ] - arcPositions_i [ k - 1 ]) > arcLengthTol ) {
193 //printf("arcPositions.size(): %lu\n", arcPositions.size() );
194
195 DynamicDataReader dataReader("xfem-crack");
196 crack_i->appendInputRecords(dataReader);
197 // ... split crack i at the intersection and add intersection enrichment
198 // fronts at the newly detected intersections.
199
200 int n1 = this->giveNumberOfEnrichmentItems() + 1;
201 // EnrichmentItem *newEI_1 = new Crack(n1, this, this->giveDomain() );
202 auto newCrack = std::make_unique<Crack>( n1, this, this->giveDomain() );
203
204 auto &ir = dataReader.giveInputRecord(DataReader :: IR_enrichItemRec, i);
205 newCrack->initializeFrom(ir);
206 newCrack->instanciateYourself(dataReader);
207
208 PolygonLine *new_pl = dynamic_cast< PolygonLine * >( newCrack->giveGeometry() );
209 // EDCrack *ed = dynamic_cast<EDCrack*>( newEI_1->giveEnrichmentDomain() );
210
211 if ( !new_pl ) {
212 OOFEM_ERROR("Failed to cast PolygonLine *new_pl.")
213 } else {
214 //printf("arcPositions_i[k-1]: %e arcPositions_i[k]: %e\n", arcPositions_i[k-1], arcPositions_i[k] );
215 new_pl->cropPolygon(arcPositions_i [ k - 1 ], arcPositions_i [ k ]);
216
217
218 PolygonLine *polygonLine_j = dynamic_cast< PolygonLine * >( crack_j->giveGeometry() );
219
220 if ( !polygonLine_j ) {
221 OOFEM_ERROR("Failed to cast PolygonLine *polygonLine_j.")
222 }
223
224 PolygonLine *polygonLine_i = dynamic_cast< PolygonLine * >( crack_i->giveGeometry() );
225
226 if ( !polygonLine_i ) {
227 OOFEM_ERROR("Failed to cast PolygonLine *polygonLine_i.")
228 }
229
230 // Take enrichment front tangent direction
231 // as the normal direction of crack_j
232 // EnrichmentDomain_BG *ed_crack_j = dynamic_cast<EnrichmentDomain_BG*>( crack_j->giveEnrichmentDomain() );
233 // if(ed_crack_j == NULL) {
234 // OOFEM_ERROR("Failed to cast EnrichmentDomain_BG *ed_crack_j.")
235 // }
236 //
237 // PolygonLine *polygonLine_j = dynamic_cast<PolygonLine*>( ed_crack_j->bg );
238 // if(polygonLine_j == NULL) {
239 // OOFEM_ERROR("Failed to cast PolygonLine *polygonLine_j.")
240 // }
241 //
242 // EnrichmentDomain_BG *ed_crack_i = dynamic_cast<EnrichmentDomain_BG*>( crack_i->giveEnrichmentDomain() );
243 // if(ed_crack_i == NULL) {
244 // OOFEM_ERROR("Failed to cast EnrichmentDomain_BG *ed_crack_i.")
245 // }
246 //
247 // PolygonLine *polygonLine_i = dynamic_cast<PolygonLine*>( ed_crack_i->bg );
248 // if(polygonLine_i == NULL) {
249 // OOFEM_ERROR("Failed to cast PolygonLine *polygonLine_i.")
250 // }
251
252
253 // Change enrichment fronts
254 if ( k - 1 > 0 ) {
255 FloatArray frontTangent1;
256 polygonLine_j->giveNormal(frontTangent1, arcPositions_j [ k - 1 ]);
257
258 FloatArray crackTangent1;
259 polygonLine_i->giveTangent(crackTangent1, arcPositions_i [ k - 1 ]);
260 crackTangent1.times(-1.0);
261
262 if ( frontTangent1.dotProduct(crackTangent1) < 0.0 ) {
263 frontTangent1.times(-1.0);
264 }
265 auto ef = std::make_unique<EnrFrontIntersection>();
266 ef->setTangent(frontTangent1);
267
268 newCrack->setEnrichmentFrontStart(std::move(ef));
269 }
270
271 if ( k < int( arcPositions_i.size() ) - 1 ) {
272 FloatArray frontTangent1;
273 polygonLine_j->giveNormal(frontTangent1, arcPositions_j [ k ]);
274
275 FloatArray crackTangent1;
276 polygonLine_i->giveTangent(crackTangent1, arcPositions_i [ k ]);
277
278
279 if ( frontTangent1.dotProduct(crackTangent1) < 0.0 ) {
280 frontTangent1.times(-1.0);
281 }
282 auto ef = std::make_unique<EnrFrontIntersection>();
283 ef->setTangent(frontTangent1);
284
285 newCrack->setEnrichmentFrontEnd(std::move(ef));
286 }
287 }
288 //this->enrichmentItemList[i-1] = std :: move(ei);
289
290 this->enrichmentItemList.push_back(nullptr);
291 newCrack->updateGeometry();
292 this->enrichmentItemList [ enrichmentItemList.size() - 1 ] = std :: move(newCrack);
293
294
295 splittedCrack = true;
296 }
297 }
298 }
299 }
300
301 if ( splittedCrack ) {
302 enrichmentItemList.erase(enrichmentItemList.begin() + i - 1);
304 i--;
305 break;
306 }
307 }
308 }
309 }
310
312
313 //printf("After splitting: Number of ei: %d\n", giveNumberOfEnrichmentItems() );
314
316
317 for ( size_t i = 0; i < enrichmentItemList.size(); i++ ) {
318 enrichmentItemList [ i ]->setNumber(i + 1);
319 }
320
321 for ( size_t i = 0; i < enrichmentItemList.size(); i++ ) {
322 enrichmentItemList [ i ]->updateGeometry();
323 }
324}
325
326void XfemStructureManager :: removeShortCracks()
327{
328 //printf("Entering XfemStructureManager :: removeShortCracks()\n");
329
330 //printf("Number of ei before removal: %d\n", giveNumberOfEnrichmentItems());
331
332 double l_tol = mMinCrackLength;
333
334 for ( int i = 1; i <= giveNumberOfEnrichmentItems(); i++ ) {
335 Crack *crack = dynamic_cast< Crack * >( this->giveEnrichmentItem(i) );
336 if ( crack ) {
337 double l = crack->computeLength();
338
339 if ( l < l_tol ) {
340 printf("Removing short crack with l: %e\n", l);
341
342 // Explicitly erasing things is a mess...
343 //crack->removeEnrichedDofs();
344 //enrichmentItemList.erase(enrichmentItemList.begin() + i - 1);
345 //i--;
346
347 // ...therefore, just remove the geometry.
348 PolygonLine *polygonLine = dynamic_cast< PolygonLine * >( crack->giveGeometry() );
349 polygonLine->clear();
350 //FloatArray tmp = {0.0, 0.0, 0.0};
351 FloatArray tmp = Vec3(-1.0e3, -1.0e3, -1.0e3);
352 polygonLine->insertVertexBack(tmp);
353
354 }
355 }
356 }
357
358 //printf("Number of ei after removal: %d\n", giveNumberOfEnrichmentItems());
359
360}
361
362bool XfemStructureManager :: tipsHaveOppositeDirection(EnrichmentFront *iEf1, EnrichmentFront *iEf2)
363{
364 const TipInfo &t1 = iEf1->giveTipInfo();
365 const TipInfo &t2 = iEf2->giveTipInfo();
366
367 return t1.mTangDir.dotProduct( t2.mTangDir ) <= 0.0;
368}
369
370void XfemStructureManager :: mergeCloseCracks()
371{
372 //printf("Entering XfemStructureManager :: mergeCloseCracks().\n");
373
374 const double &dist_tol = mCrackMergeTol;
375
376 // Loop over cracks and check if two crack tips are closer to
377 // each other than a predefined distance. If so, merge the cracks.
378 // Loop over cracks
379 for ( int i = 1; i <= giveNumberOfEnrichmentItems(); i++ ) {
380 Crack *crack_i = dynamic_cast< Crack * >( this->giveEnrichmentItem(i) );
381 if ( crack_i ) {
382
383 BasicGeometry *bg_i = crack_i->giveGeometry();
384 TipInfo startTip_i, endTip_i;
385 bg_i->giveTips(startTip_i, endTip_i) ;
386
387 const FloatArray &ps_i =startTip_i.mGlobalCoord;
388 const FloatArray &pe_i =endTip_i.mGlobalCoord;
389
390 PolygonLine *polygonLine_i = dynamic_cast< PolygonLine * >( crack_i->giveGeometry() );
391
392 if ( !polygonLine_i ) {
393 OOFEM_ERROR("Failed to cast PolygonLine *polygonLine_i.")
394 }
395
396 for ( int j = i+1; j <= giveNumberOfEnrichmentItems(); j++ ) {
397 // TODO: To improve performance, we may wish to use
398 // a tree structure here.
399// bool mergedCrack = false;
400
401 Crack *crack_j = dynamic_cast< Crack * >( this->giveEnrichmentItem(j) );
402
403 if ( crack_i->computeLength() < 1.0e-18 || crack_j->computeLength() < 1.0e-18 ) {
404 continue;
405 }
406
407 if ( crack_j ) {
408 BasicGeometry *bg_j = crack_j->giveGeometry();
409 TipInfo startTip_j, endTip_j;
410 bg_j->giveTips(startTip_j, endTip_j) ;
411
412 const FloatArray &ps_j =startTip_j.mGlobalCoord;
413 const FloatArray &pe_j =endTip_j.mGlobalCoord;
414
415 PolygonLine *polygonLine_j = dynamic_cast< PolygonLine * >( crack_j->giveGeometry() );
416
417 if ( !polygonLine_j ) {
418 OOFEM_ERROR("Failed to cast PolygonLine *polygonLine_j.")
419 }
420
422 if ( distance(ps_i, ps_j) < dist_tol ) {
423 printf("distance(ps_i, ps_j) < dist_tol\n");
424
425 // Merging is not reasonable if the tips are close to parallel
427 printf("Preventing merge due to parallel tips.\n");
428 } else {
429 // Append points to the start of polygonLine_i
430 int n = polygonLine_j->giveNrVertices();
431 for (int k = 1; k <= n; k++) {
432 polygonLine_i->insertVertexFront( polygonLine_j->giveVertex(k) );
433 }
434
435 polygonLine_i->removeDuplicatePoints(1.0e-18);
436
437
438 polygonLine_j->clear();
439 //FloatArray tmp = { 0.0, 0.0, 0.0};
440 FloatArray tmp = Vec3(-1.0e3, -1.0e3, -1.0e3);
441 polygonLine_j->insertVertexBack(tmp);
442
443 // Fix tips
444 EnrichmentFront *ef_tmp = crack_i->giveEnrichmentFrontStart();
445 crack_i->setEnrichmentFrontStart(std::unique_ptr<EnrichmentFront>(crack_j->giveEnrichmentFrontEnd()), false );
446 crack_j->setEnrichmentFrontEnd(std::unique_ptr<EnrichmentFront>(ef_tmp), false);
447
448
449 //mergedCrack = true;
450 break;
451 }
452
453 }
454
456 if ( distance(ps_i, pe_j) < dist_tol ) {
457 printf("distance(ps_i, pe_j) < dist_tol\n");
458
459#if 1
461 printf("Preventing merge due to parallel tips.\n");
462 } else {
463
464 // Append points to the start of polygonLine_i
465 int n = polygonLine_j->giveNrVertices();
466 for(int k = n; k > 0; k--) {
467 polygonLine_i->insertVertexFront( polygonLine_j->giveVertex(k) );
468 }
469
470 polygonLine_i->removeDuplicatePoints(1.0e-18);
471
472
473 polygonLine_j->clear();
474 //FloatArray tmp = {0.0, 0.0, 0.0};
475 FloatArray tmp = Vec3(-1.0e3, -1.0e3, -1.0e3);
476 polygonLine_j->insertVertexBack(tmp);
477
478 // Fix tips
480 EnrichmentFront *ef_tmp = crack_i->giveEnrichmentFrontStart();
481 crack_i->setEnrichmentFrontStart(std::unique_ptr<EnrichmentFront>(crack_j->giveEnrichmentFrontStart()), false );
482 crack_j->setEnrichmentFrontStart(std::unique_ptr<EnrichmentFront>(ef_tmp), false);
483
484 //mergedCrack = true;
485 break;
486 }
487#endif
488 }
489
490
492 if ( distance(pe_i, ps_j) < dist_tol ) {
493 printf("distance(pe_i, ps_j) < dist_tol\n");
494
496 printf("Preventing merge due to parallel tips.\n");
497 } else {
498
499 // Append points to the end of polygonLine_i
500 int n = polygonLine_j->giveNrVertices();
501 for(int k = 1; k <= n; k++) {
502 polygonLine_i->insertVertexBack( polygonLine_j->giveVertex(k) );
503 }
504
505 polygonLine_i->removeDuplicatePoints(1.0e-18);
506
507
508 polygonLine_j->clear();
509 //FloatArray tmp = {0.0, 0.0, 0.0};
510 FloatArray tmp = Vec3(-1.0e3, -1.0e3, -1.0e3);
511 polygonLine_j->insertVertexBack(tmp);
512
513
514 // Fix tips
515 EnrichmentFront *ef_tmp = crack_i->giveEnrichmentFrontEnd();
516 crack_i->setEnrichmentFrontEnd(std::unique_ptr<EnrichmentFront>(crack_j->giveEnrichmentFrontEnd()), false );
517 crack_j->setEnrichmentFrontEnd(std::unique_ptr<EnrichmentFront>(ef_tmp), false);
518
519 //mergedCrack = true;
520 break;
521 }
522 }
523
525 if ( distance(pe_i, pe_j) < dist_tol ) {
526 printf("distance(pe_i, pe_j) < dist_tol\n");
527
529 printf("Preventing merge due to parallel tips.\n");
530 } else {
531
532 // Append points to the end of polygonLine_i
533 int n = polygonLine_j->giveNrVertices();
534 for(int k = n; k > 0; k--) {
535 polygonLine_i->insertVertexBack( polygonLine_j->giveVertex(k) );
536 }
537
538 polygonLine_i->removeDuplicatePoints(1.0e-18);
539
540 polygonLine_j->clear();
541 //FloatArray tmp = {0.0, 0.0, 0.0};
542 FloatArray tmp = Vec3(-1.0e3, -1.0e3, -1.0e3);
543 polygonLine_j->insertVertexBack(tmp);
544
545
546 // Fix tips
547 EnrichmentFront *ef_tmp = crack_i->giveEnrichmentFrontEnd();
548 crack_i->setEnrichmentFrontEnd(std::unique_ptr<EnrichmentFront>(crack_j->giveEnrichmentFrontStart()), false );
549 crack_j->setEnrichmentFrontStart(std::unique_ptr<EnrichmentFront>(ef_tmp), false);
550
551 //mergedCrack = true;
552 break;
553 }
554 }
555 }
556 }
557 }
558 }
559
561
562 for ( size_t i = 0; i < enrichmentItemList.size(); i++ ) {
563 enrichmentItemList [ i ]->setNumber(i + 1);
564 }
565
566
567 for ( size_t i = 0; i < enrichmentItemList.size(); i++ ) {
568 enrichmentItemList [ i ]->updateGeometry();
569 }
570
572
573}
574
575
576double XfemStructureManager :: computeTotalCrackLength()
577{
578 double l_tot = 0.0;
579
580 for ( int i = 1; i <= giveNumberOfEnrichmentItems(); i++ ) {
581 auto crack = dynamic_cast< Crack * >( this->giveEnrichmentItem(i) );
582 if ( crack ) {
583 l_tot += crack->computeLength();
584 }
585 }
586
587 return l_tot;
588}
589
590
591} /* namespace oofem */
#define REGISTER_XfemManager(class)
void insertVertexBack(const FloatArray &iP)
Definition geometry.h:131
const FloatArray & giveVertex(int n) const
Definition geometry.h:124
void insertVertexFront(const FloatArray &iP)
Definition geometry.h:130
virtual bool giveTips(TipInfo &oStartTipInfo, TipInfo &oEndTipInfo) const
Definition geometry.h:179
int giveNrVertices() const
Returns number of Geometry vertices.
Definition geometry.h:144
void removeDuplicatePoints(const double &iTolSquare)
Definition geometry.C:67
void computeCrackIntersectionPoints(Crack &iCrack, std ::vector< FloatArray > &oIntersectionPoints, std ::vector< double > &oArcPositions)
Definition crack.C:100
void computeArcPoints(const std ::vector< FloatArray > &iIntersectionPoints, std ::vector< double > &oArcPositions)
Definition crack.C:127
double computeLength()
Definition crack.C:149
InputRecord & giveInputRecord(InputRecordType, int recordId) override
void setField(int item, InputFieldType id)
const TipInfo & giveTipInfo() const
EnrichmentFront * giveEnrichmentFrontStart()
void setEnrichmentFrontEnd(std::unique_ptr< EnrichmentFront > ipEnrichmentFrontEnd, bool iDeleteOld=true)
EnrichmentFront * giveEnrichmentFrontEnd()
void setEnrichmentFrontStart(std::unique_ptr< EnrichmentFront > ipEnrichmentFrontStart, bool iDeleteOld=true)
double dotProduct(const FloatArray &x) const
Definition floatarray.C:524
void times(double s)
Definition floatarray.C:834
void appendInputRecords(DynamicDataReader &oDR) override
BasicGeometry * giveGeometry()
void giveNormal(FloatArray &oNormal, const double &iArcPosition) const
Definition geometry.C:1181
void cropPolygon(const double &iArcPosStart, const double &iArcPosEnd)
Definition geometry.C:1772
void giveTangent(FloatArray &oTangent, const double &iArcPosition) const override
Computes tangential direction at given local coordinate (arcPos).
Definition geometry.C:1216
FloatArray mTangDir
Definition tipinfo.h:32
FloatArray mGlobalCoord
Definition tipinfo.h:30
virtual void updateYourself(TimeStep *tStep)
std ::vector< std ::unique_ptr< EnrichmentItem > > enrichmentItemList
Enrichment item list.
Domain * giveDomain()
EnrichmentItem * giveEnrichmentItem(int n)
void updateNodeEnrichmentItemMap()
int giveNumberOfEnrichmentItems() const
bool tipsHaveOppositeDirection(EnrichmentFront *iEf1, EnrichmentFront *iEf2)
#define OOFEM_ERROR(...)
Definition error.h:79
#define IR_GIVE_OPTIONAL_FIELD(__ir, __value, __id)
Definition inputrecord.h:75
double distance(const FloatArray &x, const FloatArray &y)
static FloatArray Vec3(const double &a, const double &b, const double &c)
Definition floatarray.h:607
#define _IFT_XfemStructureManager_nonstandardCZ
#define _IFT_XfemStructureManager_crackMergeTol
#define _IFT_XfemStructureManager_minCrackLength
#define _IFT_XfemStructureManager_splitCracks

This page is part of the OOFEM-3.0 documentation. Copyright Copyright (C) 1994-2025 Borek Patzak Bořek Patzák
Project e-mail: oofem@fsv.cvut.cz
Generated at for OOFEM by doxygen 1.15.0 written by Dimitri van Heesch, © 1997-2011