v0.16.3
Loading...
Searching...
No Matches
EshelbianFracture.cpp
Go to the documentation of this file.
1/**
2 * @file EshelbianFracture.cpp
3 * @author your name (you@domain.com)
4 * @brief
5 * @version 0.1
6 * @date 2025-12-21
7 *
8 * @copyright Copyright (c) 2025
9 *
10 */
11
12namespace EshelbianPlasticity {
13
15 const int tag, TS ts, SmartPetscObj<Vec> *adjoint_gradient_vector) {
17
18 constexpr bool debug = false;
19
20 auto get_tags_vec = [&](std::vector<std::pair<std::string, int>> names) {
21 std::vector<Tag> tags;
22 tags.reserve(names.size());
23 auto create_and_clean = [&]() {
25 for (auto n : names) {
26 tags.push_back(Tag());
27 auto &tag = tags.back();
28 auto &moab = mField.get_moab();
29 auto rval = moab.tag_get_handle(n.first.c_str(), tag);
30 if (rval == MB_SUCCESS) {
31 moab.tag_delete(tag);
32 }
33 double def_val[] = {0., 0., 0.};
34 CHKERR moab.tag_get_handle(n.first.c_str(), n.second, MB_TYPE_DOUBLE,
35 tag, MB_TAG_CREAT | MB_TAG_SPARSE, def_val);
36 }
38 };
39 CHK_THROW_MESSAGE(create_and_clean(), "create_and_clean");
40 return tags;
41 };
42
43 enum ExhangeTags {
44 MATERIALFORCE,
45 ADJOINT_MATERIALFORCE,
46 AREAGROWTH,
47 GRIFFITHFORCE,
48 ADJOINT_GRIFFITHFORCE,
49 FACEPRESSURE
50 };
51
52 auto tags = get_tags_vec({{"MaterialForce", 3},
53 {"AdjointMaterialForce", 3},
54 {"AreaGrowth", 3},
55 {"GriffithForce", 1},
56 {"AdjointGriffithForce", 1},
57 {"FacePressure", 1}});
58
59 auto calculate_material_forces = [&]() {
61
62 /**
63 * @brief Create element to integration faces energies
64 */
65 auto get_face_material_force_fe = [&]() {
67 auto fe_ptr = boost::make_shared<FaceEle>(mField);
68 fe_ptr->getRuleHook = [](int, int, int) { return -1; };
69 fe_ptr->setRuleHook =
70 SetIntegrationAtFrontFace(frontVertices, frontAdjEdges);
71 if (ts != PETSC_NULLPTR) {
72 fe_ptr->data_ctx |= PetscData::CTX_SET_TIME;
73 CHKERR TSGetTime(ts, &(fe_ptr->ts_t));
74 CHKERR TSGetTimeStep(ts, &(fe_ptr->ts_dt));
75 }
76 // hybrid disp, evaluated on face first
77 EshelbianPlasticity::AddHOOps<2, 2, 3>::add(
78 fe_ptr->getOpPtrVector(), {L2}, materialH1Positions, frontAdjEdges);
79 fe_ptr->getOpPtrVector().push_back(new OpCalculateVectorFieldValues<3>(
80 hybridSpatialDisp, dataAtPts->getHybridDispAtPts()));
81 fe_ptr->getOpPtrVector().push_back(
82 new OpCalculateVectorFieldGradient<SPACE_DIM, SPACE_DIM>(
83 hybridSpatialDisp, dataAtPts->getGradHybridDispAtPts()));
84 auto op_loop_domain_side =
85 new OpLoopSide<VolumeElementForcesAndSourcesCoreOnSide>(
86 mField, elementVolumeName, SPACE_DIM, Sev::noisy);
87 fe_ptr->getOpPtrVector().push_back(op_loop_domain_side);
88 fe_ptr->getOpPtrVector().push_back(new OpFaceMaterialForce(dataAtPts));
89
90 // evaluated in side domain, that is op_loop_domain_side
91 op_loop_domain_side->getSideFEPtr()->getUserPolynomialBase() =
92 boost::make_shared<CGGUserPolynomialBase>(nullptr, true);
93
94 EshelbianPlasticity::AddHOOps<SPACE_DIM, SPACE_DIM, SPACE_DIM>::add(
95 op_loop_domain_side->getOpPtrVector(), {HDIV, H1, L2},
96 materialH1Positions, frontAdjEdges, nullptr, nullptr, nullptr);
97 op_loop_domain_side->getOpPtrVector().push_back(
98 new OpCalculateHVecTensorField<SPACE_DIM, SPACE_DIM>(
99 piolaStress, dataAtPts->getApproxPAtPts()));
100
101 op_loop_domain_side->getOpPtrVector().push_back(
102 new OpCalculateVectorFieldValues<SPACE_DIM>(
103 rotAxis, dataAtPts->getRotAxisAtPts(), MBTET));
104 CHKERR physicalEquations->pushMaterialForceFields(
105 *this, op_loop_domain_side->getOpPtrVector(), dataAtPts);
106
107 op_loop_domain_side->getOpPtrVector().push_back(
109
110 return fe_ptr;
111 };
112
113 auto integrate_face_material_force_fe = [&](auto &&face_energy_fe) {
115 CHKERR DMoFEMLoopFiniteElementsUpAndLowRank(
116 dM, skeletonElement, face_energy_fe, 0, mField.get_comm_size());
117
118 auto face_exchange = CommInterface::createEntitiesPetscVector(
119 mField.get_comm(), mField.get_moab(), 2, 3, Sev::inform);
120
121 auto print_loc_size = [this](auto v, auto str, auto sev) {
123 int size;
124 CHKERR VecGetLocalSize(v.second, &size);
125 int low, high;
126 CHKERR VecGetOwnershipRange(v.second, &low, &high);
127 MOFEM_LOG("EPSYNC", sev) << str << " local size " << size << " ( "
128 << low << " " << high << " ) ";
129 MOFEM_LOG_SEVERITY_SYNC(mField.get_comm(), sev);
131 };
132 CHKERR print_loc_size(face_exchange, "material face_exchange",
133 Sev::verbose);
134
135 CHKERR CommInterface::updateEntitiesPetscVector(
136 mField.get_moab(), face_exchange, tags[ExhangeTags::MATERIALFORCE]);
137 CHKERR CommInterface::updateEntitiesPetscVector(
138 mField.get_moab(), faceExchange, tags[ExhangeTags::FACEPRESSURE]);
139
140 #ifndef NDEBUG
141 if (debug) {
142 CHKERR save_range(mField.get_moab(),
143 "front_skin_faces_material_force_" +
144 std::to_string(mField.get_comm_rank()) + ".vtk",
145 *skeletonFaces);
146 }
147 #endif
148
150 };
151
152 CHKERR integrate_face_material_force_fe(get_face_material_force_fe());
153
155 };
156
157 auto get_conn = [&](auto e) {
158 Range conn;
159 CHK_MOAB_THROW(mField.get_moab().get_connectivity(&e, 1, conn, true),
160 "get connectivity");
161 return conn;
162 };
163
164 auto get_conn_range = [&](auto e) {
165 Range conn;
166 CHK_MOAB_THROW(mField.get_moab().get_connectivity(e, conn, true),
167 "get connectivity");
168 return conn;
169 };
170
171 auto get_adj = [&](auto e, auto dim) {
172 Range adj;
173 CHK_MOAB_THROW(mField.get_moab().get_adjacencies(&e, 1, dim, true, adj),
174 "get adj");
175 return adj;
176 };
177
178 auto get_adj_range = [&](auto e, auto dim) {
179 Range adj;
180 CHK_MOAB_THROW(mField.get_moab().get_adjacencies(e, dim, true, adj,
181 moab::Interface::UNION),
182 "get adj");
183 return adj;
184 };
185
186 auto get_vector_tag_data = [&](auto r, auto th) {
187 MatrixDouble tag_data(r.size(), 3, false);
189 mField.get_moab().tag_get_data(th, r, tag_data.data().data()),
190 "get vector tag data");
191 return tag_data;
192 };
193
194 auto calculate_edge_direction = [&](auto e) {
195 const EntityHandle *conn;
196 int num_nodes;
198 mField.get_moab().get_connectivity(e, conn, num_nodes, true),
199 "get connectivity");
200 std::array<double, 6> coords;
201 CHK_MOAB_THROW(mField.get_moab().get_coords(conn, num_nodes, coords.data()),
202 "get coords");
204 &coords[0], &coords[1], &coords[2]};
206 &coords[3], &coords[4], &coords[5]};
209 t_dir(i) = t_p1(i) - t_p0(i);
210 return t_dir;
211 };
212
213 auto average_vector_tag_at_edge = [&](auto th) {
215
217
218 for (auto e : *frontEdges) {
219 auto conn = get_conn(e);
220 auto data = get_vector_tag_data(conn, th);
221 auto t_node = getFTensor1FromPtr<SPACE_DIM>(data.data().data());
222 FTensor::Tensor1<double, SPACE_DIM> t_edge_material_force{0., 0., 0.};
223 for (auto n : conn) {
224 NOT_USED(n);
225 t_edge_material_force(I) += t_node(I);
226 ++t_node;
227 }
228 t_edge_material_force(I) /= conn.size();
229
230 FTensor::Tensor1<double, SPACE_DIM> t_edge_direction =
231 calculate_edge_direction(e);
232 t_edge_direction.normalize();
233
234 // Project the averaged vector to the plane normal to the front edge.
236 t_cross(K) = FTensor::levi_civita(I, J, K) * t_edge_direction(I) *
237 t_edge_material_force(J);
238 t_edge_material_force(K) =
239 FTensor::levi_civita(I, J, K) * t_edge_direction(J) * t_cross(I);
240
241 CHKERR mField.get_moab().tag_set_data(th, &e, 1,
242 &t_edge_material_force(0));
243 }
244
246 };
247
248 auto average_material_force_at_edge = [&](auto th) {
250
251 if (mField.get_comm_rank() == 0) {
252 CHKERR average_vector_tag_at_edge(th);
253
254// #ifndef NDEBUG
255// if (debug) {
256 int ts_step;
257 CHKERR TSGetStepNumber(ts, &ts_step);
258 CHKERR save_range(mField.get_moab(),
259 "front_edges_material_force_" +
260 std::to_string(ts_step) + ".vtk",
261 *frontEdges);
262// }
263// #endif
264 }
265
267 };
268
269 auto calculate_force_through_node = [&](auto nb_J_integral_contours) {
271
273
274 if (mField.get_comm_rank() == 0) {
275 auto front_nodes = get_conn_range(*frontEdges);
276 Range all_skin_faces;
277
278 for (auto n : front_nodes) {
279 auto adj_tets = get_adj(n, SPACE_DIM);
280 for (int ll = 0; ll < nb_J_integral_contours; ++ll) {
281 auto conn = get_conn_range(adj_tets);
282 adj_tets = get_adj_range(conn, SPACE_DIM);
283 }
284
285 auto skin_faces = get_skin(mField, adj_tets);
286 auto material_forces =
287 get_vector_tag_data(skin_faces, tags[ExhangeTags::MATERIALFORCE]);
288
289#ifndef NDEBUG
290 if (debug) {
291 all_skin_faces.merge(skin_faces);
292 }
293#endif
294
295 auto t_face_T =
296 getFTensor1FromPtr<SPACE_DIM>(material_forces.data().data());
297 FTensor::Tensor1<double, SPACE_DIM> t_node_force{0., 0., 0.};
298 for (auto face : skin_faces) {
299
300 FTensor::Tensor1<double, SPACE_DIM> t_face_force_tmp{0., 0., 0.};
301 t_face_force_tmp(I) = t_face_T(I);
302 ++t_face_T;
303
304 auto face_tets = intersect(get_adj(face, SPACE_DIM), adj_tets);
305
306 if (face_tets.empty()) {
307 continue;
308 }
309
310 if (face_tets.size() != 1) {
312 "face_tets.size() != 1");
313 }
314
315 int side_number, sense, offset;
316 CHK_MOAB_THROW(mField.get_moab().side_number(face_tets[0], face,
317 side_number, sense,
318 offset),
319 "moab side number");
320 t_face_force_tmp(I) *= sense;
321 t_node_force(I) += t_face_force_tmp(I);
322 }
323
324 t_node_force(I) /= griffithEnergy; // scale all by griffith energy
326 mField.get_moab().tag_set_data(tags[ExhangeTags::MATERIALFORCE],
327 &n, 1, &t_node_force(0)),
328 "set data");
329 }
330
331#ifndef NDEBUG
332 if (debug) {
333 int ts_step;
334 CHKERR TSGetStepNumber(ts, &ts_step);
335 CHKERR save_range(mField.get_moab(),
336 "front_skin_faces_material_force_" +
337 std::to_string(ts_step) + ".vtk",
338 all_skin_faces);
339 }
340#endif
341 }
342
344 };
345
346 auto get_adj_tets_for_contour = [&](auto n, auto nb_J_integral_contours) {
347 auto adj_tets = get_adj(n, SPACE_DIM);
348 for (int ll = 0; ll < nb_J_integral_contours; ++ll) {
349 auto conn = get_conn_range(adj_tets);
350 adj_tets = get_adj_range(conn, SPACE_DIM);
351 }
352 return adj_tets;
353 };
354
355 auto get_front_node_adj_crack_faces = [&](auto n) {
356 return intersect(get_adj(n, SPACE_DIM - 1), *crackFaces);
357 };
358
359 auto calculate_crack_area_growth_face = [&](auto nb_J_integral_contours) {
361
362 FTENSOR_INDEXES(SPACE_DIM, I, J, K, L);
363
364 if (mField.get_comm_rank() == 0) {
365 auto front_nodes = get_conn_range(*frontEdges);
366 auto body_edges = get_range_from_block(mField, "EDGES", 1);
367 Range body_ents;
368 CHKERR mField.get_moab().get_entities_by_dimension(0, SPACE_DIM,
369 body_ents);
370 auto body_skin = get_skin(mField, body_ents);
371 auto body_skin_conn = get_conn_range(body_skin);
372
373 auto calculate_seed_area_growth = [&](auto n, auto &adj_faces) {
374 // if skin is on body surface, project the direction on it
375 FTensor::Tensor1<double, SPACE_DIM> t_project{0., 0., 0.};
376 auto boundary_node = intersect(Range(n, n), body_skin_conn);
377 if (boundary_node.size()) {
378 auto faces = intersect(get_adj(n, SPACE_DIM - 1), body_skin);
379 for (auto f : faces) {
380 FTensor::Tensor1<double, 3> t_normal_face;
381 CHKERR mField.getInterface<Tools>()->getTriNormal(
382 f, &t_normal_face(0));
383 t_project(I) += t_normal_face(I);
384 }
385 t_project.normalize();
386 }
387
388 // calculate surface projection matrix
391 t_Q(I, J) = t_kd(I, J);
392 if (boundary_node.size()) {
393 t_Q(I, J) -= t_project(I) * t_project(J);
394 }
395
396 FTensor::Tensor1<double, 3> t_area_dir{0., 0., 0.};
397 for (auto f : adj_faces) {
398 int num_nodes;
399 const EntityHandle *conn;
400 CHKERR mField.get_moab().get_connectivity(f, conn, num_nodes, true);
401 std::array<double, 9> coords;
402 CHKERR mField.get_moab().get_coords(conn, num_nodes, coords.data());
403 FTensor::Tensor1<double, 3> t_face_normal;
405 CHKERR mField.getInterface<Tools>()->getTriNormal(
406 coords.data(), &t_face_normal(0), &t_d_normal(0, 0, 0));
407 auto n_it = std::find(conn, conn + num_nodes, n);
408 auto n_index = std::distance(conn, n_it);
409
410 FTensor::Tensor2<double, 3, 3> t_face_hessian{
411 t_d_normal(0, n_index, 0), t_d_normal(0, n_index, 1),
412 t_d_normal(0, n_index, 2),
413
414 t_d_normal(1, n_index, 0), t_d_normal(1, n_index, 1),
415 t_d_normal(1, n_index, 2),
416
417 t_d_normal(2, n_index, 0), t_d_normal(2, n_index, 1),
418 t_d_normal(2, n_index, 2)};
419
420 FTensor::Tensor2<double, 3, 3> t_projected_hessian;
421 t_projected_hessian(I, J) =
422 t_Q(I, K) * (t_face_hessian(K, L) * t_Q(L, J));
423 t_face_normal.normalize();
424 t_area_dir(K) += t_face_normal(I) * t_projected_hessian(I, K) / 2.;
425 }
426
427 return t_area_dir;
428 };
429
430 auto get_crack_area_growth_seed_nodes = [&](auto &adj_tets) {
431 // This gets all 1D edges adjacent to the current tetrahedral patch
432 // adj_tets, then keeps only edges that are either crack-front edges or
433 // special body edges from the "EDGES" block. So adj_edges is the local
434 // edge stencil relevant to crack growth near the current front node.
435 auto adj_edges = intersect(get_adj_range(adj_tets, 1),
436 unite(*frontEdges, body_edges));
437
438 // This collects all vertices/nodes connected to those edges. These are
439 // candidate seed nodes whose local crack-area growth contribution will
440 // be accumulated.
441 auto seed_n = get_conn_range(adj_edges);
442 auto skin_adj_edges = get_skin(mField, adj_edges);
443 skin_adj_edges = subtract(skin_adj_edges, body_skin_conn);
444 seed_n = subtract(seed_n, skin_adj_edges);
445
446 return std::make_pair(seed_n, skin_adj_edges);
447 };
448
449 auto calculate_front_node_area_growth = [&](auto &adj_tets) {
450 auto [seed_n, skin_adj_edges] =
451 get_crack_area_growth_seed_nodes(adj_tets);
452
453 FTensor::Tensor1<double, SPACE_DIM> t_area_dir{0., 0., 0.};
454 auto add_area_growth_direction = [&](auto sn, double weight) {
455 auto adj_faces = intersect(get_adj(sn, SPACE_DIM - 1), *crackFaces);
456 if (adj_faces.empty()) {
457 return;
458 }
459
460 auto t_area_dir_sn = calculate_seed_area_growth(sn, adj_faces);
461 t_area_dir(I) += weight * t_area_dir_sn(I);
462 };
463
464 for (auto sn : seed_n) {
465 add_area_growth_direction(sn, 1.);
466 }
467 for (auto sn : skin_adj_edges) {
468 add_area_growth_direction(sn, 0.5);
469 }
470
471 return t_area_dir;
472 };
473
474 for (auto n : front_nodes) {
475 auto front_node_adj_faces = get_front_node_adj_crack_faces(n);
476 if (front_node_adj_faces.empty()) {
477 continue;
478 }
479
480 auto adj_tets = get_adj_tets_for_contour(n, nb_J_integral_contours);
481 auto t_area_dir = calculate_front_node_area_growth(adj_tets);
482
484 mField.get_moab().tag_set_data(tags[ExhangeTags::AREAGROWTH], &n, 1,
485 &t_area_dir(0)),
486 "set data");
487 }
488 }
489
491 };
492
493 auto calculate_crack_area_growth_no_face = [&](auto nb_J_integral_contours,
494 auto material_force_tag) {
496
498
499 if (mField.get_comm_rank() == 0) {
500 auto front_nodes = get_conn_range(*frontEdges);
501 auto body_edges = get_range_from_block(mField, "EDGES", 1);
502 Range body_ents;
503 CHKERR mField.get_moab().get_entities_by_dimension(0, SPACE_DIM,
504 body_ents);
505 auto body_skin = get_skin(mField, body_ents);
506 auto body_skin_conn = get_conn_range(body_skin);
507
508 auto calculate_seed_area_growth = [&](auto n, auto &t_node_force) {
509 auto adj_edges =
510 intersect(get_adj(n, 1), unite(*frontEdges, body_edges));
511 double l = 0;
512 for (auto e : adj_edges) {
513 auto t_dir = calculate_edge_direction(e);
514 l += t_dir.l2();
515 }
516 l /= 2;
517
518 FTensor::Tensor1<double, SPACE_DIM> t_area_dir{0., 0., 0.};
520 t_node_force_tmp(I) = t_node_force(I);
521 t_node_force_tmp.normalize();
522 t_area_dir(I) = -t_node_force_tmp(I);
523 t_area_dir(I) *= l / 2;
524 return t_area_dir;
525 };
526
527 auto get_crack_area_growth_seed_nodes = [&](auto &adj_tets) {
528 auto adj_edges = intersect(get_adj_range(adj_tets, 1),
529 unite(*frontEdges, body_edges));
530 auto seed_n = get_conn_range(adj_edges);
531 auto skin_adj_edges = get_skin(mField, adj_edges);
532 skin_adj_edges = subtract(skin_adj_edges, body_skin_conn);
533 seed_n = subtract(seed_n, skin_adj_edges);
534
535 return std::make_pair(seed_n, skin_adj_edges);
536 };
537
538 auto calculate_front_node_area_growth = [&](auto &adj_tets,
539 auto &t_node_force) {
540 auto [seed_n, skin_adj_edges] =
541 get_crack_area_growth_seed_nodes(adj_tets);
542
543 FTensor::Tensor1<double, SPACE_DIM> t_area_dir{0., 0., 0.};
544 auto add_area_growth_direction = [&](auto sn, double weight) {
545 auto t_area_dir_sn = calculate_seed_area_growth(sn, t_node_force);
546 t_area_dir(I) += weight * t_area_dir_sn(I);
547 };
548
549 for (auto sn : seed_n) {
550 add_area_growth_direction(sn, 1.);
551 }
552 for (auto sn : skin_adj_edges) {
553 add_area_growth_direction(sn, 0.5);
554 }
555
556 return t_area_dir;
557 };
558
559 for (auto n : front_nodes) {
560 auto front_node_adj_faces = get_front_node_adj_crack_faces(n);
561 if (front_node_adj_faces.empty()) {
563 CHKERR mField.get_moab().tag_get_data(tags[material_force_tag], &n, 1,
564 &t_node_force(0));
565
566 auto adj_tets = get_adj_tets_for_contour(n, nb_J_integral_contours);
567 auto t_area_dir =
568 calculate_front_node_area_growth(adj_tets, t_node_force);
569
571 mField.get_moab().tag_set_data(tags[ExhangeTags::AREAGROWTH], &n,
572 1, &t_area_dir(0)),
573 "set data");
574 }
575 }
576 }
577
579 };
580
581 auto update_crack_area_growth_edges = [&]() {
583
584 if (mField.get_comm_rank() == 0) {
585 CHKERR average_vector_tag_at_edge(tags[ExhangeTags::AREAGROWTH]);
586 }
587
588 auto area_growth_edge_exchange = CommInterface::createEntitiesPetscVector(
589 mField.get_comm(), mField.get_moab(), 1, 3, Sev::inform);
590 CHKERR CommInterface::updateEntitiesPetscVector(
591 mField.get_moab(), area_growth_edge_exchange,
592 tags[ExhangeTags::AREAGROWTH]);
593
595 };
596
597 auto calculate_griffith_force = [&](ExhangeTags material_force_tag,
598 ExhangeTags griffith_force_tag) {
600
602
603 if (mField.get_comm_rank() == 0) {
604 auto front_nodes = get_conn_range(*frontEdges);
605 Range all_front_faces;
606
607 for (auto n : front_nodes) {
609 CHKERR mField.get_moab().tag_get_data(tags[material_force_tag], &n, 1,
610 &t_node_force(0));
612 CHKERR mField.get_moab().tag_get_data(tags[ExhangeTags::AREAGROWTH], &n,
613 1, &t_area_dir(0));
614
615 auto griffith =
616 -t_node_force(I) * t_area_dir(I) / (t_area_dir(K) * t_area_dir(K));
617 CHK_MOAB_THROW(mField.get_moab().tag_set_data(
618 tags[griffith_force_tag], &n, 1, &griffith),
619 "set data");
620 }
621
622 for (auto e : *frontEdges) {
624 CHKERR mField.get_moab().tag_get_data(tags[material_force_tag], &e, 1,
625 &t_edge_force(0));
627 CHKERR mField.get_moab().tag_get_data(tags[ExhangeTags::AREAGROWTH], &e,
628 1, &t_edge_area_dir(0));
629 double griffith_energy =
630 -t_edge_force(I) * t_edge_area_dir(I) /
631 (t_edge_area_dir(K) * t_edge_area_dir(K));
632 CHKERR mField.get_moab().tag_set_data(tags[griffith_force_tag], &e, 1,
633 &griffith_energy);
634 }
635
636 for (auto e : *frontEdges) {
637 auto adj_faces = get_adj(e, SPACE_DIM - 1);
638
639 if (debug) {
640 all_front_faces.merge(adj_faces);
641 }
642
644 CHKERR mField.get_moab().tag_get_data(tags[material_force_tag], &e, 1,
645 &t_edge_force(0));
646 FTensor::Tensor1<double, SPACE_DIM> t_edge_direction =
647 calculate_edge_direction(e);
648 t_edge_direction.normalize();
649
651 t_cross(K) = FTensor::levi_civita(I, J, K) * t_edge_direction(I) *
652 t_edge_force(J);
653
654 for (auto f : adj_faces) {
656 CHKERR mField.getInterface<Tools>()->getTriNormal(f, &t_normal(0));
657 t_normal.normalize();
658 int side_number, sense, offset;
659 CHKERR mField.get_moab().side_number(f, e, side_number, sense, offset);
660 auto dot = -sense * t_cross(I) * t_normal(I);
661 CHK_MOAB_THROW(mField.get_moab().tag_set_data(
662 tags[griffith_force_tag], &f, 1, &dot),
663 "set data");
664 }
665 }
666
667#ifndef NDEBUG
668 if (debug) {
669 int ts_step;
670 CHKERR TSGetStepNumber(ts, &ts_step);
671 CHKERR save_range(mField.get_moab(),
672 "front_faces_material_force_" +
673 std::to_string(ts_step) + ".vtk",
674 all_front_faces);
675 }
676#endif
677 }
678
679 auto vector_edge_exchange = CommInterface::createEntitiesPetscVector(
680 mField.get_comm(), mField.get_moab(), 1, 3, Sev::inform);
681 CHKERR CommInterface::updateEntitiesPetscVector(
682 mField.get_moab(), vector_edge_exchange, tags[material_force_tag]);
683 auto &scalar_edge_exchange = edgeExchange;
684 CHKERR CommInterface::updateEntitiesPetscVector(
685 mField.get_moab(), scalar_edge_exchange, tags[griffith_force_tag]);
686
688 };
689
690 auto calculate_griffith_force_simplified = [&](auto material_force_tag,
691 auto griffith_force_tag) {
693
694 if (mField.get_comm_rank() == 0) {
695 auto front_nodes = get_conn_range(*frontEdges);
696
697 for (auto n : front_nodes) {
699 CHKERR mField.get_moab().tag_get_data(tags[material_force_tag], &n, 1,
700 &t_node_force(0));
701
702 auto adj_edges = intersect(get_adj(n, 1), *frontEdges);
703 double adj_edges_length = 0.;
704 for (auto e : adj_edges) {
705 auto t_edge_dir = calculate_edge_direction(e);
706 adj_edges_length += t_edge_dir.l2();
707 }
708
709 const double nodal_front_length = adj_edges_length / 2.;
710 if (nodal_front_length <= 0.) {
712 "Front node has zero adjacent front edge length");
713 }
714
715 double griffith_energy = t_node_force.l2() / nodal_front_length;
716 CHK_MOAB_THROW(mField.get_moab().tag_set_data(tags[griffith_force_tag],
717 &n, 1, &griffith_energy),
718 "set data");
719 }
720 }
721
723 };
724
725 auto calculate_adjoint_material_force = [&]() {
727
728 if (ts == PETSC_NULLPTR) {
729 SETERRQ(mField.get_comm(), MOFEM_DATA_INCONSISTENCY,
730 "TS is required to calculate adjoint material force");
731 }
732
733 auto g = createDMVector(dmMaterial, RowColData::ROW);
734 CHKERR VecZeroEntries(g);
735
736 // Disabled until the topological objective provides a material-compatible
737 // tangent for Neo-Hookean models. Keep g zero so the remaining exchange
738 // path cannot consume uninitialised vector data.
739 // auto topological_tao_ctx = createTopologicalTAOCtx(
740 // this, SetIntegrationAtFrontVolume(frontVertices, frontAdjEdges),
741 // SetIntegrationAtFrontFace(frontVertices, frontAdjEdges),
742 // SmartPetscObj<TS>(ts, true));
743 // double obj_value = 0;
744 // CHKERR evaluateGradient(topological_tao_ctx.get(), &obj_value, g,
745 // ObjectiveModelType::HENCKY_MODEL);
746
747 auto set_vertex_exchange_from_gradient = [&]() {
749
750 CHKERR VecZeroEntries(vertexExchange.second);
751 CHKERR VecGhostUpdateBegin(vertexExchange.second, INSERT_VALUES,
752 SCATTER_FORWARD);
753 CHKERR VecGhostUpdateEnd(vertexExchange.second, INSERT_VALUES,
754 SCATTER_FORWARD);
755
756 auto *problem_ptr = getProblemPtr(dmMaterial);
757 auto &dofs =
758 problem_ptr->getNumeredRowDofsPtr()->get<Unique_mi_tag>();
759 const auto field_bit = mField.get_field_bit_number(materialH1Positions);
760
761 double *g_array;
762 double *exchange_array;
763 CHKERR VecGetArray(g, &g_array);
764 CHKERR VecGetArray(vertexExchange.second, &exchange_array);
765
766 auto ptr = exchange_array; // vector values are arranged as entries, That
767 // is key idea behind vertexExchange vector.
768 for (auto v : vertexExchange.first.first) {
769 std::array<double, SPACE_DIM> values = {0., 0., 0.};
770 auto lo =
771 dofs.lower_bound(DofEntity::getLoFieldEntityUId(field_bit, v));
772 auto hi =
773 dofs.upper_bound(DofEntity::getHiFieldEntityUId(field_bit, v));
774 for (; lo != hi; ++lo) {
775 if (!(*lo)->getHasLocalIndex())
776 continue;
777 const auto coeff = (*lo)->getDofCoeffIdx();
778 if (coeff < SPACE_DIM)
779 values[coeff] = g_array[(*lo)->getPetscLocalDofIdx()];
780 }
781 for (int d = 0; d != SPACE_DIM; ++d, ++ptr) {
782 *ptr = values[d];
783 }
784 }
785
786 CHKERR VecRestoreArray(vertexExchange.second, &exchange_array);
787 CHKERR VecRestoreArray(g, &g_array);
788
789 if (adjoint_gradient_vector != nullptr) {
790 (*adjoint_gradient_vector) = g;
791 }
792
794 };
795
796 CHKERR set_vertex_exchange_from_gradient();
797
798 CHKERR CommInterface::setTagFromVector(
799 mField.get_moab(), vertexExchange,
800 tags[ExhangeTags::ADJOINT_MATERIALFORCE]);
801 CHKERR CommInterface::updateEntitiesPetscVector(
802 mField.get_moab(), vertexExchange,
803 tags[ExhangeTags::ADJOINT_MATERIALFORCE]);
804
806 };
807
808 auto print_results = [&](auto nb_J_integral_conturs, bool print_material,
809 bool print_adjoint) {
811
812 if (!print_material && !print_adjoint) {
814 }
815
816 auto get_conn_range = [&](auto e) {
817 Range conn;
818 CHK_MOAB_THROW(mField.get_moab().get_connectivity(e, conn, true),
819 "get connectivity");
820 return conn;
821 };
822
823 auto get_tag_data = [&](auto &ents, auto tag, auto dim) {
824 std::vector<double> data(ents.size() * dim);
825 CHK_MOAB_THROW(mField.get_moab().tag_get_data(tag, ents, data.data()),
826 "get data");
827 return data;
828 };
829
830 if (mField.get_comm_rank() == 0) {
831 auto at_nodes = [&]() {
833 auto conn = get_conn_range(*frontEdges);
834 std::vector<double> material_force;
835 std::vector<double> adjoint_material_force;
836 auto area_growth = get_tag_data(conn, tags[ExhangeTags::AREAGROWTH], 3);
837 std::vector<double> griffith_force;
838 std::vector<double> adjoint_griffith_force;
839 if (print_material) {
840 material_force =
841 get_tag_data(conn, tags[ExhangeTags::MATERIALFORCE], 3);
842 griffith_force =
843 get_tag_data(conn, tags[ExhangeTags::GRIFFITHFORCE], 1);
844 }
845 if (print_adjoint) {
846 adjoint_material_force =
847 get_tag_data(conn, tags[ExhangeTags::ADJOINT_MATERIALFORCE], 3);
848 adjoint_griffith_force =
849 get_tag_data(conn, tags[ExhangeTags::ADJOINT_GRIFFITHFORCE], 1);
850 }
851 std::vector<double> coords(conn.size() * 3);
852 CHK_MOAB_THROW(mField.get_moab().get_coords(conn, coords.data()),
853 "get coords");
854 MOFEM_LOG("EPSELF", Sev::inform) << "Force results at nodes";
855 MOFEM_LOG("EPSELF", Sev::inform)
856 << std::left << std::setw(10) << "kind" << std::right
857 << std::setw(9) << "node" << std::setw(18) << "coord_x"
858 << std::setw(18) << "coord_y" << std::setw(18) << "coord_z"
859 << std::setw(18) << "force_x" << std::setw(18) << "force_y"
860 << std::setw(18) << "force_z" << std::setw(18) << "area_x"
861 << std::setw(18) << "area_y" << std::setw(18) << "area_z"
862 << std::setw(18) << "griffith" << std::setw(10) << "contour";
863
864 auto print_row = [&](const char *kind, const auto &force,
865 const auto &griffith, const size_t i) {
866 MOFEM_LOG("EPSELF", Sev::inform)
867 << std::left << std::setw(10) << kind << std::right
868 << std::setw(9) << conn[i] << std::scientific
869 << std::setprecision(10) << std::setw(18) << coords[i * 3 + 0]
870 << std::setw(18) << coords[i * 3 + 1] << std::setw(18)
871 << coords[i * 3 + 2] << std::setw(18) << force[i * 3 + 0]
872 << std::setw(18) << force[i * 3 + 1] << std::setw(18)
873 << force[i * 3 + 2] << std::setw(18) << area_growth[i * 3 + 0]
874 << std::setw(18) << area_growth[i * 3 + 1] << std::setw(18)
875 << area_growth[i * 3 + 2] << std::setw(18) << griffith[i]
876 << std::defaultfloat << std::setprecision(6) << std::setw(10)
877 << nb_J_integral_conturs;
878 };
879
880 for (size_t i = 0; i < conn.size(); ++i) {
881 if (print_material) {
882 print_row("material", material_force, griffith_force, i);
883 }
884 if (print_adjoint) {
885 print_row("adjoint", adjoint_material_force, adjoint_griffith_force,
886 i);
887 }
888 }
889
891 };
892
893 at_nodes();
894 }
896 };
897
898 CHKERR calculate_material_forces();
899
900 PetscBool all_contours = PETSC_FALSE;
901 CHKERR PetscOptionsGetBool(PETSC_NULLPTR, "",
902 "-calculate_J_integral_all_levels", &all_contours,
903 PETSC_NULLPTR); // for backward compatibility
904 CHKERR PetscOptionsGetBool(
905 PETSC_NULLPTR, "", "-calculate_J_integral_all_contours", &all_contours,
906 PETSC_NULLPTR); // new name
907
908 if (all_contours == PETSC_TRUE) {
909 for (int l = 0; l < nbJIntegralContours; ++l) {
910 CHKERR calculate_force_through_node(l);
911 CHKERR average_material_force_at_edge(tags[ExhangeTags::MATERIALFORCE]);
912 CHKERR calculate_crack_area_growth_face(l);
913 CHKERR calculate_crack_area_growth_no_face(l, ExhangeTags::MATERIALFORCE);
914 CHKERR update_crack_area_growth_edges();
915 CHKERR calculate_griffith_force(ExhangeTags::MATERIALFORCE,
916 ExhangeTags::GRIFFITHFORCE);
917 CHKERR print_results(l, true, false);
918 }
919 }
920
921 PetscBool has_nonzero_ts_solution = PETSC_FALSE;
922
923 if (ts != PETSC_NULLPTR) {
924 Vec ts_solution = PETSC_NULLPTR;
925 CHKERR TSGetSolution(ts, &ts_solution);
926 if (ts_solution != PETSC_NULLPTR) {
927 PetscReal ts_solution_norm = 0.0;
928 CHKERR VecNorm(ts_solution, NORM_2, &ts_solution_norm);
929 has_nonzero_ts_solution =
930 (ts_solution_norm > PETSC_MACHINE_EPSILON) ? PETSC_TRUE : PETSC_FALSE;
931 }
932
933 if (has_nonzero_ts_solution == PETSC_TRUE) {
934 CHKERR calculate_adjoint_material_force();
935 }
936 }
937
938 CHKERR calculate_force_through_node(nbJIntegralContours);
939 CHKERR average_material_force_at_edge(tags[ExhangeTags::MATERIALFORCE]);
940 CHKERR calculate_crack_area_growth_face(nbJIntegralContours);
941 CHKERR calculate_crack_area_growth_no_face(nbJIntegralContours,
942 ExhangeTags::MATERIALFORCE);
943 CHKERR update_crack_area_growth_edges();
944 CHKERR calculate_griffith_force(ExhangeTags::MATERIALFORCE,
945 ExhangeTags::GRIFFITHFORCE);
946 if (has_nonzero_ts_solution == PETSC_TRUE) {
947 CHKERR calculate_griffith_force(ExhangeTags::ADJOINT_MATERIALFORCE,
948 ExhangeTags::ADJOINT_GRIFFITHFORCE);
949 CHKERR calculate_griffith_force_simplified(
950 ExhangeTags::ADJOINT_MATERIALFORCE, ExhangeTags::ADJOINT_GRIFFITHFORCE);
951 }
952 CHKERR print_results(nbJIntegralContours, true, true);
953
955}
956
957MoFEMErrorCode EshelbianCore::calculateOrientation(const int tag,
958 bool set_orientation) {
960
961 constexpr bool debug = false;
962 (void)debug;
963 constexpr auto sev = Sev::verbose;
964
965 Range body_ents;
966 CHKERR mField.get_moab().get_entities_by_dimension(0, 3, body_ents);
967 auto body_skin = get_skin(mField, body_ents);
968 Range body_skin_edges;
969 CHKERR mField.get_moab().get_adjacencies(body_skin, 1, false, body_skin_edges,
970 moab::Interface::UNION);
971 Range boundary_skin_verts;
972 CHKERR mField.get_moab().get_connectivity(body_skin_edges,
973 boundary_skin_verts, true);
974
975 auto geometry_edges = get_range_from_block(mField, "EDGES", 1);
976 Range geometry_edges_verts;
977 CHKERR mField.get_moab().get_connectivity(geometry_edges,
978 geometry_edges_verts, true);
979 Range crack_faces_verts;
980 CHKERR mField.get_moab().get_connectivity(*crackFaces, crack_faces_verts,
981 true);
982 Range crack_faces_edges;
983 CHKERR mField.get_moab().get_adjacencies(
984 *crackFaces, 1, true, crack_faces_edges, moab::Interface::UNION);
985 Range crack_faces_tets;
986 CHKERR mField.get_moab().get_adjacencies(
987 *crackFaces, 3, true, crack_faces_tets, moab::Interface::UNION);
988
989 Range front_verts;
990 CHKERR mField.get_moab().get_connectivity(*frontEdges, front_verts, true);
991 Range front_faces;
992 CHKERR mField.get_moab().get_adjacencies(*frontEdges, 2, true, front_faces,
993 moab::Interface::UNION);
994 Range front_verts_edges;
995 CHKERR mField.get_moab().get_adjacencies(
996 front_verts, 1, true, front_verts_edges, moab::Interface::UNION);
997
998 auto get_tags_vec = [&](auto tag_name, int dim) {
999 std::vector<Tag> tags(1);
1000
1001 if (dim > 3)
1003
1004 auto create_and_clean = [&]() {
1006 auto &moab = mField.get_moab();
1007 auto rval = moab.tag_get_handle(tag_name, tags[0]);
1008 if (rval == MB_SUCCESS) {
1009 moab.tag_delete(tags[0]);
1010 }
1011 double def_val[] = {0., 0., 0.};
1012 CHKERR moab.tag_get_handle(tag_name, dim, MB_TYPE_DOUBLE, tags[0],
1013 MB_TAG_CREAT | MB_TAG_SPARSE, def_val);
1015 };
1016
1017 CHK_THROW_MESSAGE(create_and_clean(), "create_and_clean");
1018
1019 return tags;
1020 };
1021
1022 auto get_adj_front = [&](bool subtract_crack) {
1023 Range adj_front;
1024 CHKERR mField.get_moab().get_adjacencies(*frontEdges, SPACE_DIM - 1, true,
1025 adj_front, moab::Interface::UNION);
1026 if (subtract_crack)
1027 adj_front = subtract(adj_front, *crackFaces);
1028 return adj_front;
1029 };
1030
1031 MOFEM_LOG_CHANNEL("SELF");
1032
1033 auto th_front_position = get_tags_vec("FrontPosition", 3);
1034 auto th_max_face_energy = get_tags_vec("MaxFaceEnergy", 1);
1035
1036 if (mField.get_comm_rank() == 0) {
1037
1038 auto get_layers_for_sides = [&](auto &side) {
1039 std::vector<Range> layers;
1040 auto get = [&]() {
1042
1043 auto get_adj = [&](auto &r, int dim) {
1044 Range adj;
1045 CHKERR mField.get_moab().get_adjacencies(r, dim, true, adj,
1046 moab::Interface::UNION);
1047 return adj;
1048 };
1049
1050 auto get_tets = [&](auto r) { return get_adj(r, SPACE_DIM); };
1051
1052 Range front_nodes;
1053 CHKERR mField.get_moab().get_connectivity(*frontEdges, front_nodes,
1054 true);
1055 Range front_faces = get_adj(front_nodes, 2);
1056 front_faces = subtract(front_faces, *crackFaces);
1057 auto front_tets = get_tets(front_nodes);
1058 auto front_side = intersect(side, front_tets);
1059 layers.push_back(front_side);
1060 for (;;) {
1061 auto adj_faces = get_skin(mField, layers.back());
1062 adj_faces = intersect(adj_faces, front_faces);
1063 auto adj_faces_tets = get_tets(adj_faces);
1064 adj_faces_tets = intersect(adj_faces_tets, front_tets);
1065 layers.push_back(unite(layers.back(), adj_faces_tets));
1066 if (layers.back().size() == layers[layers.size() - 2].size()) {
1067 break;
1068 }
1069 }
1071 };
1072 CHK_THROW_MESSAGE(get(), "get_layers_for_sides");
1073 return layers;
1074 };
1075
1076 auto sides_pair = get_two_sides_of_crack_surface(mField, *crackFaces);
1077 auto layers_top = get_layers_for_sides(sides_pair.first);
1078 auto layers_bottom = get_layers_for_sides(sides_pair.second);
1079
1080#ifndef NDEBUG
1081 if (debug) {
1082 auto get_crack_adj_tets = [&](auto r) {
1083 Range crack_faces_conn;
1084 CHKERR mField.get_moab().get_connectivity(r, crack_faces_conn);
1085 Range crack_faces_conn_tets;
1086 CHKERR mField.get_moab().get_adjacencies(
1087 crack_faces_conn, SPACE_DIM, true, crack_faces_conn_tets,
1088 moab::Interface::UNION);
1089 return crack_faces_conn_tets;
1090 };
1092 mField.get_moab(),
1093 "crack_tets_" +
1094 boost::lexical_cast<std::string>(mField.get_comm_rank()) + ".vtk",
1095 get_crack_adj_tets(*crackFaces));
1096 CHKERR save_range(mField.get_moab(), "sides_first.vtk", sides_pair.first);
1097 CHKERR save_range(mField.get_moab(), "sides_second.vtk",
1098 sides_pair.second);
1099 MOFEM_LOG("EP", sev) << "Nb. layers " << layers_top.size();
1100 int l = 0;
1101 for (auto &r : layers_top) {
1102 MOFEM_LOG("EP", sev) << "Layer " << l << " size " << r.size();
1104 mField.get_moab(),
1105 "layers_top_" + boost::lexical_cast<std::string>(l) + ".vtk", r);
1106 ++l;
1107 }
1108
1109 l = 0;
1110 for (auto &r : layers_bottom) {
1111 MOFEM_LOG("EP", sev) << "Layer " << l << " size " << r.size();
1113 mField.get_moab(),
1114 "layers_bottom_" + boost::lexical_cast<std::string>(l) + ".vtk", r);
1115 ++l;
1116 }
1117 }
1118#endif
1119
1120 auto get_cross = [&](auto t_dir, auto f) {
1122 CHKERR mField.getInterface<Tools>()->getTriNormal(f, &t_normal(0));
1123 t_normal.normalize();
1128 t_cross(i) = FTensor::levi_civita(i, j, k) * t_normal(j) * t_dir(k);
1129 return t_cross;
1130 };
1131
1132 auto get_sense = [&](auto f, auto e) {
1133 int side, sense, offset;
1134 CHK_MOAB_THROW(mField.get_moab().side_number(f, e, side, sense, offset),
1135 "get sense");
1136 return std::make_tuple(side, sense, offset);
1137 };
1138
1139 auto calculate_edge_direction = [&](auto e, auto normalize = true) {
1140 const EntityHandle *conn;
1141 int num_nodes;
1142 CHKERR mField.get_moab().get_connectivity(e, conn, num_nodes, true);
1143 std::array<double, 6> coords;
1144 CHKERR mField.get_moab().get_coords(conn, num_nodes, coords.data());
1146 &coords[0], &coords[1], &coords[2]};
1148 &coords[3], &coords[4], &coords[5]};
1151 t_dir(i) = t_p1(i) - t_p0(i);
1152 if (normalize)
1153 t_dir.normalize();
1154 return t_dir;
1155 };
1156
1157 auto evaluate_face_energy_and_set_orientation = [&](auto front_edges,
1158 auto front_faces,
1159 auto &sides_pair,
1160 auto th_position) {
1162
1163 Tag th_face_energy;
1164 Tag th_material_force;
1165 switch (energyReleaseSelector) {
1166 case GRIFFITH_FORCE:
1167 case GRIFFITH_SKELETON:
1168 CHKERR mField.get_moab().tag_get_handle("GriffithForce",
1169 th_face_energy);
1170 // CHKERR mField.get_moab().tag_get_handle("MaterialForce",
1171 // th_material_force);
1172 CHKERR mField.get_moab().tag_get_handle("MaterialForce",
1173 th_material_force);
1174
1175 break;
1176 default:
1177 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG,
1178 "Unknown energy release selector");
1179 };
1180
1181 /**
1182 * Iterate over front edges, get adjacent faces, find maximal face energy.
1183 * Maximal face energy is stored in the edge. Maximal face energy is
1184 * magnitude of edge Griffith force.
1185 */
1186 auto find_maximal_face_energy = [&](auto front_edges, auto front_faces,
1187 auto &edge_face_max_energy_map) {
1189
1190 Range body_ents;
1191 CHKERR mField.get_moab().get_entities_by_dimension(0, 3, body_ents);
1192 auto body_skin = get_skin(mField, body_ents);
1193
1194 Range max_faces;
1195
1196 for (auto e : front_edges) {
1197
1198 double griffith_force;
1199 CHKERR mField.get_moab().tag_get_data(th_face_energy, &e, 1,
1200 &griffith_force);
1201
1202 Range faces;
1203 CHKERR mField.get_moab().get_adjacencies(&e, 1, 2, false, faces);
1204 faces = subtract(intersect(faces, front_faces), body_skin);
1205 std::vector<double> face_energy(faces.size());
1206 CHKERR mField.get_moab().tag_get_data(th_face_energy, faces,
1207 face_energy.data());
1208 auto max_energy_it =
1209 std::max_element(face_energy.begin(), face_energy.end());
1210 double max_energy =
1211 max_energy_it != face_energy.end() ? *max_energy_it : 0;
1212
1213 edge_face_max_energy_map[e] =
1214 std::make_tuple(faces[max_energy_it - face_energy.begin()],
1215 griffith_force, static_cast<double>(0));
1216 MOFEM_LOG("EP", Sev::inform)
1217 << "Edge " << e << " griffith force " << griffith_force
1218 << " max face energy " << max_energy << " factor "
1219 << max_energy / griffith_force;
1220
1221 max_faces.insert(faces[max_energy_it - face_energy.begin()]);
1222 }
1223
1224#ifndef NDEBUG
1225 if (debug) {
1227 mField.get_moab(),
1228 "max_faces_" +
1229 boost::lexical_cast<std::string>(mField.get_comm_rank()) +
1230 ".vtk",
1231 max_faces);
1232 }
1233#endif
1234
1236 };
1237
1238 /**
1239 * For each front edge, find maximal face energy and orientation. This is
1240 * by finding angle between edge material force and maximal face normal
1241 *
1242 */
1243 auto calculate_face_orientation = [&](auto &edge_face_max_energy_map) {
1245
1246 auto up_down_face = [&](
1247
1248 auto &face_angle_map_up,
1249 auto &face_angle_map_down
1250
1251 ) {
1253
1254 for (auto &m : edge_face_max_energy_map) {
1255 auto e = m.first;
1256 auto [max_face, energy, opt_angle] = m.second;
1257
1258 Range faces;
1259 CHKERR mField.get_moab().get_adjacencies(&e, 1, 2, false, faces);
1260 faces = intersect(faces, front_faces);
1261 Range adj_tets; // tetrahedrons adjacent to the face
1262 CHKERR mField.get_moab().get_adjacencies(&max_face, 1, SPACE_DIM,
1263 false, adj_tets,
1264 moab::Interface::UNION);
1265 if (adj_tets.size()) {
1266
1267 Range adj_tets; // tetrahedrons adjacent to the face
1268 CHKERR mField.get_moab().get_adjacencies(&max_face, 1, SPACE_DIM,
1269 false, adj_tets,
1270 moab::Interface::UNION);
1271 if (adj_tets.size()) {
1272
1273 Range adj_tets_faces;
1274 // get faces
1275 CHKERR mField.get_moab().get_adjacencies(
1276 adj_tets, SPACE_DIM - 1, false, adj_tets_faces,
1277 moab::Interface::UNION);
1278 adj_tets_faces = intersect(adj_tets_faces, faces);
1280
1281 // cross product of face normal and edge direction
1282 auto t_cross_max =
1283 get_cross(calculate_edge_direction(e, true), max_face);
1284 auto [side_max, sense_max, offset_max] = get_sense(max_face, e);
1285 t_cross_max(i) *= sense_max;
1286
1287 for (auto t : adj_tets) {
1288 Range adj_tets_faces;
1289 CHKERR mField.get_moab().get_adjacencies(
1290 &t, 1, SPACE_DIM - 1, false, adj_tets_faces);
1291 adj_tets_faces = intersect(adj_tets_faces, faces);
1292 adj_tets_faces =
1293 subtract(adj_tets_faces, Range(max_face, max_face));
1294
1295 if (adj_tets_faces.size() == 1) {
1296
1297 // cross product of adjacent face normal and edge
1298 // direction
1299 auto t_cross = get_cross(calculate_edge_direction(e, true),
1300 adj_tets_faces[0]);
1301 auto [side, sense, offset] =
1302 get_sense(adj_tets_faces[0], e);
1303 t_cross(i) *= sense;
1304 double dot = t_cross(i) * t_cross_max(i);
1305 auto angle = std::acos(dot);
1306
1307 double face_energy;
1308 CHKERR mField.get_moab().tag_get_data(
1309 th_face_energy, adj_tets_faces, &face_energy);
1310
1311 auto [side_face, sense_face, offset_face] =
1312 get_sense(t, max_face);
1313
1314 if (sense_face > 0) {
1315 face_angle_map_up[e] = std::make_tuple(face_energy, angle,
1316 adj_tets_faces[0]);
1317
1318 } else {
1319 face_angle_map_down[e] = std::make_tuple(
1320 face_energy, -angle, adj_tets_faces[0]);
1321 }
1322 }
1323 }
1324 }
1325 }
1326 }
1327
1329 };
1330
1331 auto calc_optimal_angle = [&](
1332
1333 auto &face_angle_map_up,
1334 auto &face_angle_map_down
1335
1336 ) {
1338
1339 for (auto &m : edge_face_max_energy_map) {
1340 auto e = m.first;
1341 auto &[max_face, e0, a0] = m.second;
1342
1343 if (std::abs(e0) > std::numeric_limits<double>::epsilon()) {
1344
1345 if (face_angle_map_up.find(e) == face_angle_map_up.end() ||
1346 face_angle_map_down.find(e) == face_angle_map_down.end()) {
1347 // Do nothing
1348 } else {
1349
1350 switch (energyReleaseSelector) {
1351 case GRIFFITH_FORCE:
1352 case GRIFFITH_SKELETON: {
1353
1354 Tag th_material_force;
1355 CHKERR mField.get_moab().tag_get_handle("MaterialForce",
1356 th_material_force);
1357 FTensor::Tensor1<double, SPACE_DIM> t_material_force;
1358 CHKERR mField.get_moab().tag_get_data(
1359 th_material_force, &e, 1, &t_material_force(0));
1360 auto material_force_magnitude = t_material_force.l2();
1361 if (material_force_magnitude <
1362 std::numeric_limits<double>::epsilon()) {
1363 a0 = 0;
1364
1365 } else {
1366
1367 auto t_edge_dir = calculate_edge_direction(e, true);
1368 auto t_cross_max = get_cross(t_edge_dir, max_face);
1369 auto [side, sense, offset] = get_sense(max_face, e);
1370 t_cross_max(sense) *= sense;
1371
1375
1376 t_material_force.normalize();
1377 t_cross_max.normalize();
1379 t_cross(I) = FTensor::levi_civita(I, J, K) *
1380 t_material_force(J) * t_cross_max(K);
1381 a0 = -std::asin(t_cross(I) * t_edge_dir(I));
1382
1383 MOFEM_LOG("EP", sev)
1384 << "Optimal angle " << a0 << " energy " << e0;
1385 }
1386 break;
1387 }
1388 default: {
1389
1390 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG,
1391 "Unknown energy release selector");
1392 }
1393 }
1394 }
1395 }
1396 }
1397
1399 };
1400
1401 std::map<EntityHandle, std::tuple<double, double, EntityHandle>>
1402 face_angle_map_up;
1403 std::map<EntityHandle, std::tuple<double, double, EntityHandle>>
1404 face_angle_map_down;
1405 CHKERR up_down_face(face_angle_map_up, face_angle_map_down);
1406 CHKERR calc_optimal_angle(face_angle_map_up, face_angle_map_down);
1407
1408#ifndef NDEBUG
1409 if (debug) {
1410 auto th_angle = get_tags_vec("Angle", 1);
1411 Range up;
1412 for (auto &m : face_angle_map_up) {
1413 auto [e, a, face] = m.second;
1414 up.insert(face);
1415 CHKERR mField.get_moab().tag_set_data(th_angle[0], &face, 1, &a);
1416 }
1417 Range down;
1418 for (auto &m : face_angle_map_down) {
1419 auto [e, a, face] = m.second;
1420 down.insert(face);
1421 CHKERR mField.get_moab().tag_set_data(th_angle[0], &face, 1, &a);
1422 }
1423
1424 Range max_energy_faces;
1425 for (auto &m : edge_face_max_energy_map) {
1426 auto [face, e, angle] = m.second;
1427 max_energy_faces.insert(face);
1428 CHKERR mField.get_moab().tag_set_data(th_angle[0], &face, 1,
1429 &angle);
1430 }
1431 if (mField.get_comm_rank() == 0) {
1432 CHKERR save_range(mField.get_moab(), "up_faces.vtk", up);
1433 CHKERR save_range(mField.get_moab(), "down_faces.vtk", down);
1434 CHKERR save_range(mField.get_moab(), "max_energy_faces.vtk",
1435 max_energy_faces);
1436 }
1437 }
1438#endif // NDEBUG
1439
1441 };
1442
1443 auto get_conn = [&](auto e) {
1444 Range conn;
1445 CHK_MOAB_THROW(mField.get_moab().get_connectivity(e, conn, true),
1446 "get conn");
1447 return conn;
1448 };
1449
1450 auto get_adj = [&](auto e, auto dim) {
1451 Range adj;
1452 CHK_MOAB_THROW(mField.get_moab().get_adjacencies(
1453 e, dim, false, adj, moab::Interface::UNION),
1454 "get adj");
1455 return adj;
1456 };
1457
1458 auto get_coords = [&](auto v) {
1460 CHK_MOAB_THROW(mField.get_moab().get_coords(v, &t_coords(0)),
1461 "get coords");
1462 return t_coords;
1463 };
1464
1465 // calculate normal of the max energy face
1466 auto get_rotated_normal = [&](auto e, auto f, auto angle) {
1469 auto t_edge_dir = calculate_edge_direction(e, true);
1470 auto [side, sense, offset] = get_sense(f, e);
1471 t_edge_dir(i) *= sense;
1472 t_edge_dir.normalize();
1473 t_edge_dir(i) *= angle;
1474 auto t_R = LieGroups::SO3::exp(t_edge_dir, angle);
1476 mField.getInterface<Tools>()->getTriNormal(f, &t_normal(0));
1477 FTensor::Tensor1<double, SPACE_DIM> t_rotated_normal;
1478 t_rotated_normal(i) = t_R(i, j) * t_normal(j);
1479 return std::make_tuple(t_normal, t_rotated_normal);
1480 };
1481
1482 auto set_coord = [&](auto v, auto &adj_vertex_tets_verts, auto &coords,
1483 auto &t_move, auto gamma) {
1484 auto index = adj_vertex_tets_verts.index(v);
1485 if (index >= 0) {
1486 for (auto ii : {0, 1, 2}) {
1487 coords[3 * index + ii] += gamma * t_move(ii);
1488 }
1489 return true;
1490 }
1491 return false;
1492 };
1493
1494 auto tets_quality = [&](auto quality, auto &adj_vertex_tets_verts,
1495 auto &adj_vertex_tets, auto &coords) {
1496 for (auto t : adj_vertex_tets) {
1497 const EntityHandle *conn;
1498 int num_nodes;
1499 CHKERR mField.get_moab().get_connectivity(t, conn, num_nodes, true);
1500 std::array<double, 12> tet_coords;
1501 for (auto n = 0; n != 4; ++n) {
1502 auto index = adj_vertex_tets_verts.index(conn[n]);
1503 if (index < 0) {
1505 }
1506 for (auto ii = 0; ii != 3; ++ii) {
1507 tet_coords[3 * n + ii] = coords[3 * index + ii];
1508 }
1509 }
1510 double q = Tools::volumeLengthQuality(tet_coords.data());
1511 if (!std::isnormal(q))
1512 q = -2;
1513 quality = std::min(quality, q);
1514 };
1515
1516 return quality;
1517 };
1518
1519 auto calculate_free_face_node_displacement =
1520 [&](auto &edge_face_max_energy_map) {
1521 // get edges adjacent to vertex along which nodes are moving
1522 auto get_vertex_edges = [&](auto vertex) {
1523 Range vertex_edges; // edges adjacent to vertex
1524
1525 auto impl = [&]() {
1527 CHKERR mField.get_moab().get_adjacencies(vertex, 1, false,
1528 vertex_edges);
1529 vertex_edges = subtract(vertex_edges, front_verts_edges);
1530
1531 if (boundary_skin_verts.size() &&
1532 boundary_skin_verts.find(vertex[0]) !=
1533 boundary_skin_verts.end()) {
1534 MOFEM_LOG("EP", sev) << "Boundary vertex";
1535 vertex_edges = intersect(vertex_edges, body_skin_edges);
1536 }
1537 if (geometry_edges_verts.size() &&
1538 geometry_edges_verts.find(vertex[0]) !=
1539 geometry_edges_verts.end()) {
1540 MOFEM_LOG("EP", sev) << "Geometry edge vertex";
1541 vertex_edges = intersect(vertex_edges, geometry_edges);
1542 }
1543 if (crack_faces_verts.size() &&
1544 crack_faces_verts.find(vertex[0]) !=
1545 crack_faces_verts.end()) {
1546 MOFEM_LOG("EP", sev) << "Crack face vertex";
1547 vertex_edges = intersect(vertex_edges, crack_faces_edges);
1548 }
1550 };
1551
1552 CHK_THROW_MESSAGE(impl(), "get_vertex_edges");
1553
1554 return vertex_edges;
1555 };
1556
1557 // vector of rotated faces, edge along node is moved, moved edge,
1558 // moved displacement, quality, cardinality, gamma
1559 using Bundle = std::vector<
1560
1561 std::tuple<EntityHandle, EntityHandle, EntityHandle,
1563
1564 >;
1565 std::map<EntityHandle, Bundle> edge_bundle_map;
1566
1567 for (auto &m : edge_face_max_energy_map) {
1568
1569 auto edge = m.first;
1570 auto &[max_face, energy, opt_angle] = m.second;
1571
1572 // calculate rotation of max energy face
1573 auto [t_normal, t_rotated_normal] =
1574 get_rotated_normal(edge, max_face, opt_angle);
1575
1576 auto front_vertex = get_conn(Range(m.first, m.first));
1577 auto adj_tets = get_adj(Range(max_face, max_face), 3);
1578 auto adj_tets_faces = get_adj(adj_tets, 2);
1579 auto adj_front_faces = subtract(
1580 intersect(get_adj(Range(edge, edge), 2), adj_tets_faces),
1581 *crackFaces);
1582 if (adj_front_faces.size() > 3)
1584 "adj_front_faces.size()>3");
1585
1586 FTensor::Tensor1<double, SPACE_DIM> t_material_force;
1587 CHKERR mField.get_moab().tag_get_data(th_material_force, &edge, 1,
1588 &t_material_force(0));
1589 std::vector<double> griffith_energy(adj_front_faces.size());
1590 CHKERR mField.get_moab().tag_get_data(
1591 th_face_energy, adj_front_faces, griffith_energy.data());
1592
1593 auto set_edge_bundle = [&](auto min_gamma) {
1594 for (auto rotated_f : adj_front_faces) {
1595
1596 double rotated_face_energy =
1597 griffith_energy[adj_front_faces.index(rotated_f)];
1598
1599 auto vertex = subtract(get_conn(Range(rotated_f, rotated_f)),
1600 front_vertex);
1601 if (vertex.size() != 1) {
1603 "Wrong number of vertex to move");
1604 }
1605 auto front_vertex_edges_vertex = get_conn(
1606 intersect(get_adj(front_vertex, 1), crack_faces_edges));
1607 vertex = subtract(
1608 vertex, front_vertex_edges_vertex); // vertex free to move
1609 if (vertex.empty()) {
1610 continue;
1611 }
1612
1613 auto face_cardinality = [&](auto f, auto &seen_front_edges) {
1614 auto whole_front =
1615 unite(*frontEdges,
1616 subtract(body_skin_edges, crack_faces_edges));
1617 auto faces = Range(f, f);
1618 int c = 0;
1619 for (; c < 10; ++c) {
1620 auto front_edges =
1621 subtract(get_adj(faces, 1), seen_front_edges);
1622 if (front_edges.size() == 0) {
1623 return 0;
1624 }
1625 auto front_connected_edges =
1626 intersect(front_edges, whole_front);
1627 if (front_connected_edges.size()) {
1628 seen_front_edges.merge(front_connected_edges);
1629 return c;
1630 }
1631 faces.merge(get_adj(front_edges, 2));
1632 ++c;
1633 }
1634 return c;
1635 };
1636
1637 Range seen_edges = Range(edge, edge);
1638 double rotated_face_cardinality = face_cardinality(
1639 rotated_f,
1640 seen_edges); // add cardinality of max energy
1641 // face to rotated face cardinality
1642 // rotated_face_cardinality +=
1643 // face_cardinality(max_face, seen_edges);
1644 rotated_face_cardinality = std::max(rotated_face_cardinality,
1645 1.); // at least one edge
1646
1647 auto t_vertex_coords = get_coords(vertex);
1648 auto vertex_edges = get_vertex_edges(vertex);
1649
1650 EntityHandle f0 = front_vertex[0];
1651 EntityHandle f1 = front_vertex[1];
1652 FTensor::Tensor1<double, 3> t_v_e0, t_v_e1;
1653 CHKERR mField.get_moab().get_coords(&f0, 1, &t_v_e0(0));
1654 CHKERR mField.get_moab().get_coords(&f1, 1, &t_v_e1(0));
1655
1657 for (auto e_used_to_move_detection : vertex_edges) {
1658 auto edge_conn = get_conn(Range(e_used_to_move_detection,
1659 e_used_to_move_detection));
1660 edge_conn = subtract(edge_conn, vertex);
1661 // Find displacement of the edge such that dot porduct with
1662 // normal is zero.
1663 //
1664 // { (t_v0 - t_vertex_coords) + gamma * (t_v3 -
1665 // t_vertex_coords) } * n = 0
1666 // where t_v0 is the edge vertex, t_v3 is the edge end
1667 // point, n is the rotated normal of the face gamma is the
1668 // factor by which the edge is moved
1670 t_v0(i) = (t_v_e0(i) + t_v_e1(i)) / 2;
1672 CHKERR mField.get_moab().get_coords(edge_conn, &t_v3(0));
1673 auto a =
1674 (t_v0(i) - t_vertex_coords(i)) * t_rotated_normal(i);
1675 auto b =
1676 (t_v3(i) - t_vertex_coords(i)) * t_rotated_normal(i);
1677 auto gamma = a / b;
1678
1679 constexpr double eps =
1680 std::numeric_limits<double>::epsilon();
1681 if (std::isnormal(gamma) && gamma < 1.0 - eps &&
1682 gamma > -0.1) {
1684 t_move(i) = gamma * (t_v3(i) - t_vertex_coords(i));
1685
1686 auto check_rotated_face_directoon = [&]() {
1688 t_delta(i) = t_vertex_coords(i) + t_move(i) - t_v0(i);
1689 t_delta.normalize();
1690 auto dot =
1691 (t_material_force(i) / t_material_force.l2()) *
1692 t_delta(i);
1693 return -dot > 0 ? true : false;
1694 };
1695
1696 if (check_rotated_face_directoon()) {
1697
1698 MOFEM_LOG("EP", Sev::verbose)
1699 << "Crack edge " << edge << " moved face "
1700 << rotated_f
1701 << " edge: " << e_used_to_move_detection
1702 << " face direction/energy " << rotated_face_energy
1703 << " face cardinality " << rotated_face_cardinality
1704 << " gamma: " << gamma;
1705
1706 auto &bundle = edge_bundle_map[edge];
1707 bundle.emplace_back(rotated_f, e_used_to_move_detection,
1708 vertex[0], t_move, 1,
1709 rotated_face_cardinality, gamma);
1710 }
1711 }
1712 }
1713 }
1714 };
1715
1716 set_edge_bundle(std::numeric_limits<double>::epsilon());
1717 if (edge_bundle_map[edge].empty()) {
1718 set_edge_bundle(-1.);
1719 }
1720 }
1721
1722 return edge_bundle_map;
1723 };
1724
1725 auto get_sort_by_energy = [&](auto &edge_face_max_energy_map) {
1726 std::map<double, std::tuple<EntityHandle, EntityHandle, double>>
1727 sort_by_energy;
1728
1729 for (auto &m : edge_face_max_energy_map) {
1730 auto e = m.first;
1731 auto &[max_face, energy, opt_angle] = m.second;
1732 auto abs_energy = std::abs(energy);
1733 sort_by_energy[abs_energy] = std::make_tuple(e, max_face, opt_angle);
1734 }
1735
1736 return sort_by_energy;
1737 };
1738
1739 auto set_tag = [&](auto &&adj_edges_map, auto &&sort_by_energy) {
1741
1742 Tag th_face_pressure;
1744 mField.get_moab().tag_get_handle("FacePressure", th_face_pressure),
1745 "get tag");
1746 auto get_face_pressure = [&](auto face) {
1747 double pressure;
1748 CHK_MOAB_THROW(mField.get_moab().tag_get_data(th_face_pressure, &face,
1749 1, &pressure),
1750 "get rag data");
1751 return pressure;
1752 };
1753
1754 MOFEM_LOG("EPSELF", Sev::inform)
1755 << "Number of edges to check " << sort_by_energy.size();
1756
1757 enum face_energy { POSITIVE, NEGATIVE };
1758 constexpr bool skip_negative = true;
1759
1760 for (auto fe : {face_energy::POSITIVE, face_energy::NEGATIVE}) {
1761
1762 std::vector<double> energies;
1763 double max_pressure = -1;
1764 // check max energies and average all energies along the crack front
1765 // extract max pressure along the crack front
1766 for (auto it = sort_by_energy.rbegin(); it != sort_by_energy.rend();
1767 ++it) {
1768 auto energy = it->first;
1769 auto [max_edge, max_face, opt_angle] = it->second;
1770
1771 auto face_pressure = get_face_pressure(max_face);
1772 MOFEM_LOG("EPSELF", Sev::inform)
1773 << "Faces to check: " << max_face << " energy " << energy
1774 << " face pressure " << face_pressure;
1775
1776 const bool pressure_check =
1777 propagateUnderCompression || face_pressure > crackingAtol;
1778 if (energy > 0 && pressure_check) {
1779 energies.push_back(energy);
1780 }
1781 max_pressure = std::max(max_pressure, face_pressure);
1782 }
1783
1784 double average_energy = 0;
1785 if (!energies.empty()) {
1786 average_energy =
1787 std::accumulate(energies.begin(), energies.end(), 0.) /
1788 energies.size();
1789 }
1790
1791 MOFEM_LOG("EPSELF", Sev::inform)
1792 << "Average energy Griffiths energy of crack front "
1793 << average_energy;
1794
1795 bool positive_pressure_face_found = false;
1796
1797 // iterate edges wih maximal energy, and make them seed. Such edges,
1798 // will most likely will have also smallest node displacement
1799 for (auto it = sort_by_energy.rbegin(); it != sort_by_energy.rend();
1800 ++it) {
1801
1802 auto energy = it->first;
1803 auto [max_edge, max_face, opt_angle] = it->second;
1804
1805 auto face_pressure = get_face_pressure(max_face);
1806 if (skip_negative) {
1807 if (fe == face_energy::POSITIVE) {
1808 if (face_pressure <
1809 -(crackingAtol + crackingRtol * std::abs(max_pressure))) {
1810 MOFEM_LOG("EPSELF", Sev::inform)
1811 << "Skip negative face " << max_face << " with energy "
1812 << energy << " and pressure " << face_pressure;
1813 continue;
1814 }
1815 }
1816 }
1817
1818 if (fe == face_energy::POSITIVE)
1819 positive_pressure_face_found = true;
1820
1821 MOFEM_LOG("EPSELF", Sev::inform)
1822 << "Check face " << max_face << " edge " << max_edge
1823 << " energy " << energy << " optimal angle " << opt_angle
1824 << " face pressure " << face_pressure;
1825
1826 // store energy of max face
1827 if (!average_energy) {
1828 MOFEM_LOG("EPSELF", Sev::warning)
1829 << "Average energy is zero, setting max Griffiths energy to "
1830 "current energy "
1831 << energy;
1832 average_energy = energy;
1833 }
1834 avgGriffithsEnergy = average_energy;
1835 auto jt = adj_edges_map.find(max_edge);
1836 if (jt == adj_edges_map.end()) {
1837 MOFEM_LOG("EPSELF", Sev::warning)
1838 << "Edge " << max_edge << " not found in adj_edges_map";
1839 continue;
1840 }
1841 auto &bundle = jt->second;
1842
1843 auto find_max_in_bundle_impl = [&](auto edge, auto &bundle,
1844 auto gamma) {
1846
1847 EntityHandle vertex_max = 0;
1848 EntityHandle face_max = 0;
1849 EntityHandle move_edge_max = 0;
1850 double max_quality = -2;
1851 double max_quality_evaluated = -2;
1852 double min_cardinality = std::numeric_limits<double>::max();
1853
1854 FTensor::Tensor1<double, SPACE_DIM> t_move_last{0., 0., 0.};
1855
1856 for (auto &b : bundle) {
1857 auto &[face, move_edge, vertex, t_move, quality, cardinality,
1858 edge_gamma] = b;
1859
1860 auto adj_vertex_tets = get_adj(Range(vertex, vertex), 3);
1861 auto adj_vertex_tets_verts = get_conn(adj_vertex_tets);
1862 std::vector<double> coords(3 * adj_vertex_tets_verts.size());
1863 CHK_MOAB_THROW(mField.get_moab().get_coords(
1864 adj_vertex_tets_verts, coords.data()),
1865 "get coords");
1866
1867 set_coord(vertex, adj_vertex_tets_verts, coords, t_move, gamma);
1868 quality = tets_quality(quality, adj_vertex_tets_verts,
1869 adj_vertex_tets, coords);
1870
1871 auto eval_quality = [](auto q, auto c, auto edge_gamma) {
1872 if (q < 0) {
1873 return q;
1874 } else {
1875 return ((edge_gamma < 0) ? (q / 2) : q) / pow(c, 2);
1876 }
1877 };
1878
1879 if (eval_quality(quality, cardinality, edge_gamma) >=
1880 max_quality_evaluated) {
1881 max_quality = quality;
1882 min_cardinality = cardinality;
1883 vertex_max = vertex;
1884 face_max = face;
1885 move_edge_max = move_edge;
1886 t_move_last(i) = t_move(i);
1887 max_quality_evaluated =
1888 eval_quality(max_quality, min_cardinality, edge_gamma);
1889 }
1890 }
1891
1892 return std::make_tuple(vertex_max, face_max, t_move_last,
1893 max_quality, min_cardinality);
1894 };
1895
1896 auto find_max_in_bundle = [&](auto edge, auto &bundle) {
1897 auto b_org_bundle = bundle;
1898 auto r = find_max_in_bundle_impl(edge, bundle, 1.);
1899 auto &[vertex_max, face_max, t_move_last, max_quality,
1900 cardinality] = r;
1901 if (max_quality < 0) {
1902 for (double gamma = 0.95; gamma >= 0.45; gamma -= 0.05) {
1903 bundle = b_org_bundle;
1904 r = find_max_in_bundle_impl(edge, bundle, gamma);
1905 auto &[vertex_max, face_max, t_move_last, max_quality,
1906 cardinality] = r;
1907 MOFEM_LOG("EPSELF", Sev::warning)
1908 << "Back tracking: gamma " << gamma << " edge " << edge
1909 << " quality " << max_quality << " cardinality "
1910 << cardinality;
1911 if (max_quality > 0.01) {
1913 t_move_last(I) *= gamma;
1914 return r;
1915 }
1916 }
1918 t_move_last(I) = 0;
1919 }
1920 return r;
1921 };
1922
1923 // set tags with displacement of node and face energy
1924 auto set_tag_to_vertex_and_face = [&](auto &&r, auto &quality) {
1926 auto &[v, f, t_move, q, cardinality] = r;
1927
1928 if ((q > 0 && std::isnormal(q)) && energy > 0) {
1929
1930 MOFEM_LOG("EPSELF", Sev::inform)
1931 << "Set tag: vertex " << v << " face " << f << " "
1932 << max_edge << " move " << t_move << " energy " << energy
1933 << " quality " << q << " cardinality " << cardinality;
1934 CHKERR mField.get_moab().tag_set_data(th_position[0], &v, 1,
1935 &t_move(0));
1936 CHKERR mField.get_moab().tag_set_data(th_max_face_energy[0], &f,
1937 1, &energy);
1938 }
1939
1940 quality = q;
1942 };
1943
1944 double quality = -2;
1945 CHKERR set_tag_to_vertex_and_face(
1946
1947 find_max_in_bundle(max_edge, bundle),
1948
1949 quality
1950
1951 );
1952
1953 if (quality > 0 && std::isnormal(quality) && energy > 0) {
1954 MOFEM_LOG("EPSELF", Sev::inform)
1955 << "Crack face set with quality: " << quality;
1957 }
1958 }
1959
1960 if (fe == face_energy::POSITIVE && !positive_pressure_face_found) {
1961 if (!propagateUnderCompression) {
1962 potentialCrackArrest = true;
1963 MOFEM_LOG("EPSELF", Sev::warning)
1964 << "POTENTIAL ARREST: No suitable face found with positive "
1965 "face pressure to propagate crack";
1966 } else {
1967 MOFEM_LOG("EPSELF", Sev::warning)
1968 << "POTENTIAL ARREST: No suitable face found with positive "
1969 "face pressure to propagate crack; continuing because "
1970 "propagation under compression is enabled";
1971 }
1972 }
1973
1974 if (!skip_negative)
1975 break;
1976 }
1977
1979 };
1980
1981 // map: {edge, {face, energy, optimal_angle}}
1982 MOFEM_LOG("EP", sev) << "Calculate orientation";
1983 std::map<EntityHandle, std::tuple<EntityHandle, double, double>>
1984 edge_face_max_energy_map;
1985 CHKERR find_maximal_face_energy(front_edges, front_faces,
1986 edge_face_max_energy_map);
1987 CHKERR calculate_face_orientation(edge_face_max_energy_map);
1988
1989 MOFEM_LOG("EP", sev) << "Calculate node positions";
1990 CHKERR set_tag(
1991
1992 calculate_free_face_node_displacement(edge_face_max_energy_map),
1993 get_sort_by_energy(edge_face_max_energy_map)
1994
1995 );
1996
1998 };
1999
2000 auto get_max_griffith_force = [&](auto r) {
2001 auto &moab = mField.get_moab();
2002 std::vector<double> gc(r.size());
2003 Tag th_gc;
2004 CHKERR moab.tag_get_handle("GriffithForce", th_gc);
2005 CHKERR moab.tag_get_data(th_gc, r, gc.data());
2006 double max_griffith_force = 0;
2007 for (size_t i = 0; i < r.size(); ++i) {
2008 max_griffith_force = std::max(max_griffith_force, std::abs(gc[i]));
2009 }
2010 return max_griffith_force;
2011 };
2012
2013 MOFEM_LOG("EP", sev) << "Front edges " << frontEdges->size();
2014 if (std::abs(get_max_griffith_force(get_adj_front(true))) >
2015 std::numeric_limits<double>::epsilon()) {
2016 CHKERR evaluate_face_energy_and_set_orientation(
2017 *frontEdges, get_adj_front(true), sides_pair, th_front_position);
2018 } else {
2019 auto adj_front = get_adj_front(true);
2020 double zero[] = {0., 0., 0.};
2021 CHKERR mField.get_moab().tag_clear_data(th_front_position[0], adj_front,
2022 zero);
2023 }
2024 }
2025
2026 // exchange positions and energies from processor zero to all other
2027 CHKERR VecZeroEntries(vertexExchange.second);
2028 CHKERR VecGhostUpdateBegin(vertexExchange.second, INSERT_VALUES,
2029 SCATTER_FORWARD);
2030 CHKERR VecGhostUpdateEnd(vertexExchange.second, INSERT_VALUES,
2031 SCATTER_FORWARD);
2032 CHKERR mField.getInterface<CommInterface>()->updateEntitiesPetscVector(
2033 mField.get_moab(), vertexExchange, th_front_position[0]);
2034 CHKERR VecZeroEntries(faceExchange.second);
2035 CHKERR VecGhostUpdateBegin(faceExchange.second, INSERT_VALUES,
2036 SCATTER_FORWARD);
2037 CHKERR VecGhostUpdateEnd(faceExchange.second, INSERT_VALUES, SCATTER_FORWARD);
2038 CHKERR mField.getInterface<CommInterface>()->updateEntitiesPetscVector(
2039 mField.get_moab(), faceExchange, th_max_face_energy[0]);
2040
2041 auto get_max_moved_faces = [&]() {
2042 Range max_moved_faces;
2043 auto adj_front = get_adj_front(false);
2044 std::vector<double> face_energy(adj_front.size());
2045 CHKERR mField.get_moab().tag_get_data(th_max_face_energy[0], adj_front,
2046 face_energy.data());
2047 for (int i = 0; i != adj_front.size(); ++i) {
2048 if (face_energy[i] > std::numeric_limits<double>::epsilon()) {
2049 max_moved_faces.insert(adj_front[i]);
2050 }
2051 }
2052
2053 return boost::make_shared<Range>(max_moved_faces);
2054 };
2055
2056 // move all faces with energy larger than 0
2057 maxMovedFaces = get_max_moved_faces();
2058 MOFEM_LOG("EP", sev) << "Number of of moved faces: " << maxMovedFaces->size();
2059
2060#ifndef NDEBUG
2061 if (debug) {
2063 mField.get_moab(),
2064 "max_moved_faces_" +
2065 boost::lexical_cast<std::string>(mField.get_comm_rank()) + ".vtk",
2066 *maxMovedFaces);
2067 }
2068#endif
2069
2071}
2072
2075
2076 if (!maxMovedFaces)
2078
2079 Tag th_front_position;
2080 auto rval =
2081 mField.get_moab().tag_get_handle("FrontPosition", th_front_position);
2082 if (rval == MB_SUCCESS && maxMovedFaces) {
2083 Range verts;
2084 CHKERR mField.get_moab().get_connectivity(*maxMovedFaces, verts, true);
2085 CHKERR mField.getInterface<CommInterface>()->synchroniseEntities(verts);
2086 std::vector<double> coords(3 * verts.size());
2087 CHKERR mField.get_moab().get_coords(verts, coords.data());
2088 std::vector<double> pos(3 * verts.size());
2089 CHKERR mField.get_moab().tag_get_data(th_front_position, verts, pos.data());
2090 for (int i = 0; i != 3 * verts.size(); ++i) {
2091 coords[i] += pos[i];
2092 }
2093 CHKERR mField.get_moab().set_coords(verts, coords.data());
2094 double zero[] = {0., 0., 0.};
2095 CHKERR mField.get_moab().tag_clear_data(th_front_position, verts, zero);
2096 }
2097
2098#ifndef NDEBUG
2099 constexpr bool debug = false;
2100 if (debug) {
2101
2103 mField.get_moab(),
2104 "set_coords_faces_" +
2105 boost::lexical_cast<std::string>(mField.get_comm_rank()) + ".vtk",
2106 *maxMovedFaces);
2107 }
2108#endif
2110}
2111
2112MoFEMErrorCode EshelbianCore::addCrackSurfaces(const bool debug) {
2114
2115 constexpr bool potential_crack_debug = false;
2116 if constexpr (potential_crack_debug) {
2117
2118 auto add_ents = get_range_from_block(mField, "POTENTIAL", SPACE_DIM - 1);
2119 Range crack_front_verts;
2120 CHKERR mField.get_moab().get_connectivity(*frontEdges, crack_front_verts,
2121 true);
2122 CHKERR mField.getInterface<CommInterface>()->synchroniseEntities(
2123 crack_front_verts);
2124 Range crack_front_faces;
2125 CHKERR mField.get_moab().get_adjacencies(crack_front_verts, SPACE_DIM - 1,
2126 true, crack_front_faces,
2127 moab::Interface::UNION);
2128 crack_front_faces = intersect(crack_front_faces, add_ents);
2129 CHKERR mField.getInterface<CommInterface>()->synchroniseEntities(
2130 crack_front_faces);
2131 CHKERR mField.getInterface<MeshsetsManager>()->addEntitiesToMeshset(
2132 BLOCKSET, addCrackMeshsetId, crack_front_faces);
2133 }
2134
2135 auto get_crack_faces = [&]() {
2136 if (maxMovedFaces) {
2137 return unite(*crackFaces, *maxMovedFaces);
2138 } else {
2139 return *crackFaces;
2140 }
2141 };
2142
2143 auto get_extended_crack_faces = [&]() {
2144 auto get_faces_of_crack_front_verts = [&](auto crack_faces_org) {
2145 ParallelComm *pcomm =
2146 ParallelComm::get_pcomm(&mField.get_moab(), MYPCOMM_INDEX);
2147
2148 Range crack_faces;
2149
2150 if (!pcomm->rank()) {
2151
2152 auto get_nodes = [&](auto &&e) {
2153 Range nodes;
2154 CHK_MOAB_THROW(mField.get_moab().get_connectivity(e, nodes, true),
2155 "get connectivity");
2156 return nodes;
2157 };
2158
2159 auto get_adj = [&](auto &&e, auto dim,
2160 auto t = moab::Interface::UNION) {
2161 Range adj;
2163 mField.get_moab().get_adjacencies(e, dim, true, adj, t),
2164 "get adj");
2165 return adj;
2166 };
2167
2168 Range body_ents;
2169 CHKERR mField.get_moab().get_entities_by_dimension(0, SPACE_DIM,
2170 body_ents);
2171 auto body_skin = get_skin(mField, body_ents);
2172 auto body_skin_edges = get_adj(body_skin, 1, moab::Interface::UNION);
2173 auto geometry_edges = get_range_from_block(mField, "EDGES", 1);
2174 auto front_block_edges = get_range_from_block(mField, "FRONT", 1);
2175 auto front_block_nodes = get_nodes(front_block_edges);
2176
2177 size_t s;
2178 do {
2179 s = crack_faces.size();
2180
2181 auto crack_face_nodes = get_nodes(crack_faces_org);
2182 auto crack_faces_edges =
2183 get_adj(crack_faces_org, 1, moab::Interface::UNION);
2184
2185 auto crack_skin = get_skin(mField, crack_faces_org);
2186 front_block_edges = subtract(front_block_edges, crack_skin);
2187 auto crack_skin_nodes = get_nodes(crack_skin);
2188 crack_skin_nodes.merge(front_block_nodes);
2189
2190 auto crack_skin_faces =
2191 get_adj(crack_skin, 2, moab::Interface::UNION);
2192 crack_skin_faces =
2193 subtract(subtract(crack_skin_faces, crack_faces_org), body_skin);
2194
2195 crack_faces = crack_faces_org;
2196 for (auto f : crack_skin_faces) {
2197 auto edges = intersect(
2198 get_adj(Range(f, f), 1, moab::Interface::UNION), crack_skin);
2199
2200 // if other edge is part of body skin, e.g. crack punching through
2201 // body surface
2202 if (edges.size() == 2) {
2203 edges.merge(
2204 intersect(get_adj(Range(f, f), 1, moab::Interface::UNION),
2205 body_skin_edges));
2206 }
2207
2208 if (edges.size() == 2) {
2209 auto edge_conn = get_nodes(Range(edges));
2210 auto faces = intersect(get_adj(edges, 2, moab::Interface::UNION),
2211 crack_faces_org);
2212 if (faces.size() == 2) {
2213 auto edge0_conn = get_nodes(Range(edges[0], edges[0]));
2214 auto edge1_conn = get_nodes(Range(edges[1], edges[1]));
2215 auto edges_conn = intersect(intersect(edge0_conn, edge1_conn),
2216 crack_skin_nodes); // node at apex
2217 if (edges_conn.size() == 1) {
2218
2219 auto node_edges =
2220 subtract(intersect(get_adj(edges_conn, 1,
2221 moab::Interface::INTERSECT),
2222 crack_faces_edges),
2223 crack_skin); // nodes on crack surface, but not
2224 // at the skin
2225
2226 if (node_edges.size()) {
2229 CHKERR mField.get_moab().get_coords(edges_conn, &t_v0(0));
2230
2231 auto get_t_dir = [&](auto e_conn) {
2232 auto other_node = subtract(e_conn, edges_conn);
2234 CHKERR mField.get_moab().get_coords(other_node,
2235 &t_dir(0));
2236 t_dir(i) -= t_v0(i);
2237 return t_dir;
2238 };
2239
2241 t_ave_dir(i) =
2242 get_t_dir(edge0_conn)(i) + get_t_dir(edge1_conn)(i);
2243
2244 FTensor::Tensor1<double, SPACE_DIM> t_crack_surface_ave_dir;
2245 t_crack_surface_ave_dir(i) = 0;
2246 for (auto e : node_edges) {
2247 auto e_conn = get_nodes(Range(e, e));
2248 auto t_dir = get_t_dir(e_conn);
2249 t_crack_surface_ave_dir(i) += t_dir(i);
2250 }
2251
2252 auto dot = t_ave_dir(i) * t_crack_surface_ave_dir(i);
2253 // ave edges is in opposite direction to crack surface, so
2254 // thus crack is not turning back
2255 if (dot < -std::numeric_limits<double>::epsilon()) {
2256 crack_faces.insert(f);
2257 }
2258 } else {
2259 crack_faces.insert(f);
2260 }
2261 }
2262 }
2263 } else if (edges.size() == 3) {
2264 crack_faces.insert(f);
2265 }
2266
2267 // if other edge is part of geometry edge, e.g. keyway
2268 if (edges.size() == 1) {
2269 edges.merge(
2270 intersect(get_adj(Range(f, f), 1, moab::Interface::UNION),
2271 geometry_edges));
2272 edges.merge(
2273 intersect(get_adj(Range(f, f), 1, moab::Interface::UNION),
2274 front_block_edges));
2275 if (edges.size() == 2) {
2276 crack_faces.insert(f);
2277 continue;
2278 }
2279 }
2280 }
2281
2282 crack_faces_org = crack_faces;
2283
2284 } while (s != crack_faces.size());
2285 };
2286
2287 return crack_faces; // send_type(mField, crack_faces, MBTRI);
2288 };
2289
2290 return get_faces_of_crack_front_verts(get_crack_faces());
2291 };
2292
2293 if (debug) {
2294 CHKERR save_range(mField.get_moab(), "new_crack_surface_debug.vtk",
2295 get_extended_crack_faces());
2296 }
2297
2298 auto reconstruct_crack_faces = [&](auto crack_faces) {
2299 ParallelComm *pcomm =
2300 ParallelComm::get_pcomm(&mField.get_moab(), MYPCOMM_INDEX);
2301
2302 auto impl = [&]() {
2304
2305 Range new_crack_faces;
2306 if (!pcomm->rank()) {
2307
2308 auto get_nodes = [&](auto &&e) {
2309 Range nodes;
2310 CHK_MOAB_THROW(mField.get_moab().get_connectivity(e, nodes, true),
2311 "get connectivity");
2312 return nodes;
2313 };
2314
2315 auto get_adj = [&](auto &&e, auto dim,
2316 auto t = moab::Interface::UNION) {
2317 Range adj;
2319 mField.get_moab().get_adjacencies(e, dim, true, adj, t),
2320 "get adj");
2321 return adj;
2322 };
2323
2324 auto get_test_on_crack_surface = [&]() {
2325 auto crack_faces_nodes =
2326 get_nodes(crack_faces); // nodes on crac faces
2327 auto crack_faces_tets =
2328 get_adj(crack_faces_nodes, 3,
2329 moab::Interface::UNION); // adjacent
2330 // tets to
2331 // crack
2332 // faces throug nodes
2333 auto crack_faces_tets_nodes =
2334 get_nodes(crack_faces_tets); // nodes on crack faces tets
2335 crack_faces_tets_nodes =
2336 subtract(crack_faces_tets_nodes, crack_faces_nodes);
2337 crack_faces_tets =
2338 subtract(crack_faces_tets, get_adj(crack_faces_tets_nodes, 3,
2339 moab::Interface::UNION));
2340 new_crack_faces =
2341 get_adj(crack_faces_tets, 2,
2342 moab::Interface::UNION); // adjacency faces to crack
2343 // faces through tets
2344 new_crack_faces.merge(crack_faces); // add original crack faces
2345
2346 return std::make_tuple(new_crack_faces, crack_faces_tets);
2347 };
2348
2349 auto carck_faces_test_edges = [&](auto faces, auto tets) {
2350 auto adj_tets_faces = get_adj(tets, 2, moab::Interface::UNION);
2351 auto adj_faces_edges = get_adj(subtract(faces, adj_tets_faces), 1,
2352 moab::Interface::UNION);
2353 auto adj_tets_edges = get_adj(tets, 1, moab::Interface::UNION);
2354 auto geometry_edges = get_range_from_block(mField, "EDGES", 1);
2355 auto front_block_edges = get_range_from_block(mField, "FRONT", 1);
2356 adj_faces_edges.merge(geometry_edges); // geometry edges
2357 adj_faces_edges.merge(front_block_edges); // front block edges
2358
2359 auto boundary_tets_edges = intersect(adj_tets_edges, adj_faces_edges);
2360 auto boundary_test_nodes = get_nodes(boundary_tets_edges);
2361 auto boundary_test_nodes_edges =
2362 get_adj(boundary_test_nodes, 1, moab::Interface::UNION);
2363 auto boundary_test_nodes_edges_nodes = subtract(
2364 get_nodes(boundary_test_nodes_edges), boundary_test_nodes);
2365
2366 boundary_tets_edges =
2367 subtract(boundary_test_nodes_edges,
2368 get_adj(boundary_test_nodes_edges_nodes, 1,
2369 moab::Interface::UNION));
2370
2371 Range body_ents;
2372 CHKERR mField.get_moab().get_entities_by_dimension(0, SPACE_DIM,
2373 body_ents);
2374 auto body_skin = get_skin(mField, body_ents);
2375
2376 auto body_skin_edges = get_adj(body_skin, 1, moab::Interface::UNION);
2377 body_skin_edges = intersect(get_adj(tets, 1, moab::Interface::UNION),
2378 body_skin_edges);
2379 body_skin = intersect(body_skin, adj_tets_faces);
2380 body_skin_edges = subtract(
2381 body_skin_edges, get_adj(body_skin, 1, moab::Interface::UNION));
2382
2383 save_range(mField.get_moab(), "body_skin_edges.vtk", body_skin_edges);
2384 for (auto e : body_skin_edges) {
2385 auto adj_tet = intersect(
2386 get_adj(Range(e, e), 3, moab::Interface::INTERSECT), tets);
2387 if (adj_tet.size() == 1) {
2388 boundary_tets_edges.insert(e);
2389 }
2390 }
2391
2392 return boundary_tets_edges;
2393 };
2394
2395 auto p = get_test_on_crack_surface();
2396 auto &[new_crack_faces, crack_faces_tets] = p;
2397
2398 if (debug) {
2399 CHKERR save_range(mField.get_moab(), "hole_crack_faces_debug.vtk",
2400 crack_faces);
2401 CHKERR save_range(mField.get_moab(), "new_crack_faces_debug.vtk",
2402 new_crack_faces);
2403 CHKERR save_range(mField.get_moab(), "new_crack_tets_debug.vtk",
2404 crack_faces_tets);
2405 }
2406
2407 auto boundary_tets_edges =
2408 carck_faces_test_edges(new_crack_faces, crack_faces_tets);
2409 CHKERR save_range(mField.get_moab(), "boundary_tets_edges.vtk",
2410 boundary_tets_edges);
2411
2412 auto resolve_surface = [&](auto boundary_tets_edges,
2413 auto crack_faces_tets) {
2414 auto boundary_tets_edges_nodes = get_nodes(boundary_tets_edges);
2415 auto crack_faces_tets_faces =
2416 get_adj(crack_faces_tets, 2, moab::Interface::UNION);
2417
2418 Range all_removed_faces;
2419 Range all_removed_tets;
2420 int counter = 0;
2421
2422 int size = 0;
2423 while (size != crack_faces_tets.size()) {
2424 auto tets_faces =
2425 get_adj(crack_faces_tets, 2, moab::Interface::UNION);
2426 auto skin_tets = get_skin(mField, crack_faces_tets);
2427 auto skin_skin =
2428 get_skin(mField, subtract(crack_faces_tets_faces, tets_faces));
2429 auto skin_skin_nodes = get_nodes(skin_skin);
2430
2431 size = crack_faces_tets.size();
2432 MOFEM_LOG("SELF", Sev::inform)
2433 << "Crack faces tets size " << crack_faces_tets.size()
2434 << " crack faces size " << crack_faces_tets_faces.size();
2435 auto skin_tets_nodes = subtract(
2436 get_nodes(skin_tets),
2437 boundary_tets_edges_nodes); // not remove tets which are
2438 // adjagasent to crack faces nodes
2439 skin_tets_nodes = subtract(skin_tets_nodes, skin_skin_nodes);
2440
2441 Range removed_nodes;
2442 Range tets_to_remove;
2443 Range faces_to_remove;
2444 for (auto n : skin_tets_nodes) {
2445 auto tets =
2446 intersect(get_adj(Range(n, n), 3, moab::Interface::INTERSECT),
2447 crack_faces_tets);
2448 if (tets.size() == 0) {
2449 continue;
2450 }
2451
2452 auto hole_detetction = [&]() {
2453 auto adj_tets =
2454 get_adj(Range(n, n), 3, moab::Interface::INTERSECT);
2455 adj_tets =
2456 subtract(adj_tets,
2457 crack_faces_tets); // tetst adjacent to the node
2458 // but not part of crack surface
2459 if (adj_tets.size() == 0) {
2460 return std::make_pair(
2461 intersect(
2462 get_adj(Range(n, n), 2, moab::Interface::INTERSECT),
2463 tets_faces),
2464 tets);
2465 }
2466
2467 std::vector<Range> tets_groups;
2468 auto test_adj_tets = adj_tets;
2469 while (test_adj_tets.size()) {
2470 auto seed_size = 0;
2471 Range seed = Range(test_adj_tets[0], test_adj_tets[0]);
2472 while (seed.size() != seed_size) {
2473 auto adj_faces =
2474 subtract(get_adj(seed, 2, moab::Interface::UNION),
2475 tets_faces); // edges which are not
2476 // part of the node
2477 seed_size = seed.size();
2478 seed.merge(
2479 intersect(get_adj(adj_faces, 3, moab::Interface::UNION),
2480 test_adj_tets));
2481 }
2482 tets_groups.push_back(seed);
2483 test_adj_tets = subtract(test_adj_tets, seed);
2484 }
2485 if (tets_groups.size() == 1) {
2486
2487 return std::make_pair(
2488 intersect(
2489 get_adj(Range(n, n), 2, moab::Interface::INTERSECT),
2490 tets_faces),
2491 tets);
2492 }
2493
2494 Range tets_to_remove;
2495 Range faces_to_remove;
2496 for (auto &r : tets_groups) {
2497 auto f = get_adj(r, 2, moab::Interface::UNION);
2498 auto t = intersect(get_adj(f, 3, moab::Interface::UNION),
2499 crack_faces_tets); // tets
2500
2501 if (f.size() > faces_to_remove.size() ||
2502 faces_to_remove.size() == 0) {
2503 faces_to_remove = f;
2504 tets_to_remove = t; // largest group of tets
2505 }
2506 }
2507 MOFEM_LOG("EPSELF", Sev::inform)
2508 << "Hole detection: faces to remove "
2509 << faces_to_remove.size() << " tets to remove "
2510 << tets_to_remove.size();
2511 return std::make_pair(faces_to_remove, tets_to_remove);
2512 };
2513
2514 if (tets.size() < tets_to_remove.size() ||
2515 tets_to_remove.size() == 0) {
2516 removed_nodes = Range(n, n);
2517 auto [h_faces_to_remove, h_tets_to_remove] =
2518 hole_detetction(); // find faces and tets to remove
2519 faces_to_remove = h_faces_to_remove;
2520 tets_to_remove = h_tets_to_remove;
2521
2522 // intersect(
2523 // get_adj(Range(n, n), 2, moab::Interface::INTERSECT),
2524 // tets_faces);
2525
2526 } // find tets which is largest adjacencty size, so that it is
2527 // removed first, and then faces are removed
2528 all_removed_faces.merge(faces_to_remove);
2529 all_removed_tets.merge(tets_to_remove);
2530 }
2531
2532 crack_faces_tets = subtract(crack_faces_tets, tets_to_remove);
2533 crack_faces_tets_faces =
2534 subtract(crack_faces_tets_faces, faces_to_remove);
2535
2536 if (debug) {
2537 save_range(mField.get_moab(),
2538 "removed_nodes_" +
2539 boost::lexical_cast<std::string>(counter) + ".vtk",
2540 removed_nodes);
2541 save_range(mField.get_moab(),
2542 "faces_to_remove_" +
2543 boost::lexical_cast<std::string>(counter) + ".vtk",
2544 faces_to_remove);
2545 save_range(mField.get_moab(),
2546 "tets_to_remove_" +
2547 boost::lexical_cast<std::string>(counter) + ".vtk",
2548 tets_to_remove);
2549 save_range(mField.get_moab(),
2550 "crack_faces_tets_faces_" +
2551 boost::lexical_cast<std::string>(counter) + ".vtk",
2552 crack_faces_tets_faces);
2553 save_range(mField.get_moab(),
2554 "crack_faces_tets_" +
2555 boost::lexical_cast<std::string>(counter) + ".vtk",
2556 crack_faces_tets);
2557 }
2558 counter++;
2559 }
2560
2561 auto cese_internal_faces = [&]() {
2563 auto skin_tets = get_skin(mField, crack_faces_tets);
2564 auto adj_faces = get_adj(skin_tets, 2, moab::Interface::UNION);
2565 adj_faces =
2566 subtract(adj_faces, skin_tets); // remove skin tets faces
2567 auto adj_tets = get_adj(adj_faces, 3,
2568 moab::Interface::UNION); // tets which are
2569 // adjacent to skin
2570 crack_faces_tets =
2571 subtract(crack_faces_tets,
2572 adj_tets); // remove tets which are adjacent to
2573 // skin, so that they are not removed
2574 crack_faces_tets_faces =
2575 subtract(crack_faces_tets_faces, adj_faces);
2576
2577 all_removed_faces.merge(adj_faces);
2578 all_removed_tets.merge(adj_tets);
2579
2580 MOFEM_LOG("EPSELF", Sev::inform)
2581 << "Remove internal faces size " << adj_faces.size()
2582 << " tets size " << adj_tets.size();
2584 };
2585
2586 auto case_only_one_free_edge = [&]() {
2588
2589 for (auto t : Range(crack_faces_tets)) {
2590
2591 auto adj_faces = get_adj(
2592 Range(t, t), 2,
2593 moab::Interface::UNION); // faces of tet which can be removed
2594 auto crack_surface_edges =
2595 get_adj(subtract(unite(crack_faces_tets_faces, crack_faces),
2596 adj_faces),
2597 1,
2598 moab::Interface::UNION); // edges not on the tet but
2599 // on crack surface
2600 auto adj_edges =
2601 subtract(get_adj(Range(t, t), 1, moab::Interface::INTERSECT),
2602 crack_surface_edges); // free edges
2603 adj_edges = subtract(
2604 adj_edges,
2605 boundary_tets_edges); // edges which are not part of gemetry
2606
2607 if (adj_edges.size() == 1) {
2608 crack_faces_tets =
2609 subtract(crack_faces_tets,
2610 Range(t, t)); // remove tets which are adjacent to
2611 // skin, so that they are not removed
2612
2613 auto faces_to_remove =
2614 get_adj(adj_edges, 2, moab::Interface::UNION); // faces
2615 // which can
2616 // be removed
2617 crack_faces_tets_faces =
2618 subtract(crack_faces_tets_faces, faces_to_remove);
2619
2620 all_removed_faces.merge(faces_to_remove);
2621 all_removed_tets.merge(Range(t, t));
2622
2623 MOFEM_LOG("EPSELF", Sev::inform) << "Remove free one edges ";
2624 }
2625 }
2626
2627 crack_faces_tets = subtract(crack_faces_tets, all_removed_tets);
2628 crack_faces_tets_faces =
2629 subtract(crack_faces_tets_faces, all_removed_faces);
2630
2632 };
2633
2634 auto cese_flat_tet = [&](auto max_adj_edges) {
2636
2637 Range body_ents;
2638 CHKERR mField.get_moab().get_entities_by_dimension(0, SPACE_DIM,
2639 body_ents);
2640 auto body_skin = get_skin(mField, body_ents);
2641 auto body_skin_edges =
2642 get_adj(body_skin, 1, moab::Interface::UNION);
2643
2644 for (auto t : Range(crack_faces_tets)) {
2645
2646 auto adj_faces = get_adj(
2647 Range(t, t), 2,
2648 moab::Interface::UNION); // faces of tet which can be removed
2649 auto crack_surface_edges =
2650 get_adj(subtract(unite(crack_faces_tets_faces, crack_faces),
2651 adj_faces),
2652 1,
2653 moab::Interface::UNION); // edges not on the tet but
2654 // on crack surface
2655 auto adj_edges =
2656 subtract(get_adj(Range(t, t), 1, moab::Interface::INTERSECT),
2657 crack_surface_edges); // free edges
2658 adj_edges = subtract(adj_edges, body_skin_edges);
2659
2660 auto tet_edges = get_adj(Range(t, t), 1,
2661 moab::Interface::UNION); // edges of
2662 // tet
2663 tet_edges = subtract(tet_edges, adj_edges);
2664
2665 for (auto e : tet_edges) {
2666 constexpr int opposite_edge[] = {5, 3, 4, 1, 2, 0};
2667 auto get_side = [&](auto e) {
2668 int side, sense, offset;
2670 mField.get_moab().side_number(t, e, side, sense, offset),
2671 "get side number failed");
2672 return side;
2673 };
2674 auto get_side_ent = [&](auto side) {
2675 EntityHandle side_edge;
2677 mField.get_moab().side_element(t, 1, side, side_edge),
2678 "get side failed");
2679 return side_edge;
2680 };
2681 adj_edges.erase(get_side_ent(opposite_edge[get_side(e)]));
2682 }
2683
2684 if (adj_edges.size() <= max_adj_edges) {
2685
2686 double dot = 1;
2687 Range faces_to_remove;
2688 for (auto e : adj_edges) {
2689 auto edge_adj_faces =
2690 get_adj(Range(e, e), 2, moab::Interface::UNION);
2691 edge_adj_faces = intersect(edge_adj_faces, adj_faces);
2692 if (edge_adj_faces.size() != 2) {
2694 "Adj faces size is not 2 for edge " +
2695 boost::lexical_cast<std::string>(e));
2696 }
2697
2698 auto get_normal = [&](auto f) {
2701 mField.getInterface<Tools>()->getTriNormal(f, &t_n(0)),
2702 "get tri normal failed");
2703 return t_n;
2704 };
2705 auto t_n0 = get_normal(edge_adj_faces[0]);
2706 auto t_n1 = get_normal(edge_adj_faces[1]);
2707 auto get_sense = [&](auto f) {
2708 int side, sense, offset;
2709 CHK_MOAB_THROW(mField.get_moab().side_number(t, f, side,
2710 sense, offset),
2711 "get side number failed");
2712 return sense;
2713 };
2714 auto sense0 = get_sense(edge_adj_faces[0]);
2715 auto sense1 = get_sense(edge_adj_faces[1]);
2716 t_n0.normalize();
2717 t_n1.normalize();
2718
2720 auto dot_e = (sense0 * sense1) * t_n0(i) * t_n1(i);
2721 if (dot_e < dot || e == adj_edges[0]) {
2722 dot = dot_e;
2723 faces_to_remove = edge_adj_faces;
2724 }
2725 }
2726
2727 all_removed_faces.merge(faces_to_remove);
2728 all_removed_tets.merge(Range(t, t));
2729
2730 MOFEM_LOG("EPSELF", Sev::inform)
2731 << "Remove free edges on flat tet, with considered nb. of "
2732 "edges "
2733 << adj_edges.size();
2734 }
2735 }
2736
2737 crack_faces_tets = subtract(crack_faces_tets, all_removed_tets);
2738 crack_faces_tets_faces =
2739 subtract(crack_faces_tets_faces, all_removed_faces);
2740
2742 };
2743
2744 CHK_THROW_MESSAGE(case_only_one_free_edge(),
2745 "Case only one free edge failed");
2746 for (auto max_adj_edges : {0, 1, 2, 3}) {
2747 CHK_THROW_MESSAGE(cese_flat_tet(max_adj_edges),
2748 "Case only one free edge failed");
2749 }
2750 CHK_THROW_MESSAGE(cese_internal_faces(),
2751 "Case internal faces failed");
2752
2753 if (debug) {
2754 save_range(mField.get_moab(),
2755 "crack_faces_tets_faces_" +
2756 boost::lexical_cast<std::string>(counter) + ".vtk",
2757 crack_faces_tets_faces);
2758 save_range(mField.get_moab(),
2759 "crack_faces_tets_" +
2760 boost::lexical_cast<std::string>(counter) + ".vtk",
2761 crack_faces_tets);
2762 }
2763
2764 return std::make_tuple(crack_faces_tets_faces, crack_faces_tets,
2765 all_removed_faces, all_removed_tets);
2766 };
2767
2768 auto [resolved_faces, resolved_tets, all_removed_faces,
2769 all_removed_tets] =
2770 resolve_surface(boundary_tets_edges, crack_faces_tets);
2771 resolved_faces.merge(subtract(crack_faces, all_removed_faces));
2772 if (debug) {
2773 CHKERR save_range(mField.get_moab(), "resolved_faces.vtk",
2774 resolved_faces);
2775 CHKERR save_range(mField.get_moab(), "resolved_tets.vtk",
2776 resolved_tets);
2777 }
2778
2779 crack_faces = resolved_faces;
2780 }
2781
2783 };
2784
2785 CHK_THROW_MESSAGE(impl(), "resolve new crack surfaces");
2786
2787 return crack_faces; // send_type(mField, crack_faces, MBTRI);
2788 };
2789
2790 auto resolve_consisten_crack_extension = [&]() {
2792 auto crack_meshset =
2793 mField.getInterface<MeshsetsManager>()->getCubitMeshsetPtr(
2794 addCrackMeshsetId, BLOCKSET);
2795 auto meshset = crack_meshset->getMeshset();
2796
2797 if (!mField.get_comm_rank() && !noCrackExtension) {
2798 Range old_crack_faces;
2799 CHKERR mField.get_moab().get_entities_by_type(meshset, MBTRI,
2800 old_crack_faces);
2801 auto extendeded_crack_faces = get_extended_crack_faces();
2802 auto reconstructed_crack_faces =
2803 subtract(reconstruct_crack_faces(extendeded_crack_faces),
2804 subtract(*crackFaces, old_crack_faces));
2805 if (nbCrackFaces >= reconstructed_crack_faces.size()) {
2806 MOFEM_LOG("EPSELF", Sev::warning)
2807 << "No new crack faces to add, skipping adding to meshset";
2808 extendeded_crack_faces = subtract(
2809 extendeded_crack_faces, subtract(*crackFaces, old_crack_faces));
2810 MOFEM_LOG("EPSELF", Sev::inform)
2811 << "Number crack faces size (extended) "
2812 << extendeded_crack_faces.size();
2813 CHKERR mField.get_moab().clear_meshset(&meshset, 1);
2814 CHKERR mField.get_moab().add_entities(meshset, extendeded_crack_faces);
2815 } else {
2816 CHKERR mField.get_moab().clear_meshset(&meshset, 1);
2817 CHKERR mField.get_moab().add_entities(meshset,
2818 reconstructed_crack_faces);
2819 MOFEM_LOG("EPSELF", Sev::inform)
2820 << "Number crack faces size (reconstructed) "
2821 << reconstructed_crack_faces.size();
2822 nbCrackFaces = reconstructed_crack_faces.size();
2823 }
2824 }
2825
2826 Range crack_faces;
2827 if (!mField.get_comm_rank()) {
2828 CHKERR mField.get_moab().get_entities_by_type(meshset, MBTRI,
2829 crack_faces);
2830 }
2831 crack_faces = send_type(mField, crack_faces, MBTRI);
2832 if (mField.get_comm_rank()) {
2833 CHKERR mField.get_moab().clear_meshset(&meshset, 1);
2834 CHKERR mField.get_moab().add_entities(meshset, crack_faces);
2835 }
2836
2838 };
2839
2840 CHKERR resolve_consisten_crack_extension();
2841
2843};
2844
2847 auto meshset_mng = mField.getInterface<MeshsetsManager>();
2848 while (meshset_mng->checkMeshset(addCrackMeshsetId, BLOCKSET))
2849 ++addCrackMeshsetId;
2850 MOFEM_LOG("EP", Sev::inform)
2851 << "Crack added surface meshset " << addCrackMeshsetId;
2852 CHKERR meshset_mng->addMeshset(BLOCKSET, addCrackMeshsetId, "CRACK_COMPUTED");
2854};
2855
2856MoFEMErrorCode
2857EshelbianCore::calculateCrackArea(boost::shared_ptr<double> &area_ptr) {
2859
2860 if (!area_ptr) {
2861 // initialize area
2862 area_ptr = boost::shared_ptr<double>(new double(0.0));
2863 }
2864
2865 int success;
2866 *area_ptr = 0;
2867 if (mField.get_comm_rank() == 0) {
2868 MOFEM_LOG("EP", Sev::inform) << "Calculate crack area";
2869 auto crack_faces = get_range_from_block(mField, "CRACK", SPACE_DIM - 1);
2870 for (auto f : crack_faces) {
2871 *area_ptr += mField.getInterface<Tools>()->getTriArea(f);
2872 }
2873 success = MPI_Bcast(area_ptr.get(), 1, MPI_DOUBLE, 0, mField.get_comm());
2874 } else {
2875 success = MPI_Bcast(area_ptr.get(), 1, MPI_DOUBLE, 0, mField.get_comm());
2876 }
2877 if (success != MPI_SUCCESS) {
2879 }
2881}
2882
2883} // namespace EshelbianPlasticity
boost::shared_ptr< DataAtIntegrationPts > dataAtPts
static auto send_type(MoFEM::Interface &m_field, Range r, const EntityType type)
static auto get_two_sides_of_crack_surface(MoFEM::Interface &m_field, Range crack_faces)
#define MOFEM_LOG_SEVERITY_SYNC(comm, severity)
Synchronise "SYNC" on curtain severity level.
#define FTENSOR_INDEXES(DIM,...)
#define FTENSOR_INDEX(DIM, I)
constexpr double a
static const double eps
constexpr int SPACE_DIM
Kronecker Delta class.
Tensor1< T, Tensor_Dim > normalize()
#define NOT_USED(x)
@ ROW
#define CHK_THROW_MESSAGE(err, msg)
Check and throw MoFEM exception.
#define MoFEMFunctionReturnHot(a)
Last executable line of each PETSc function used for error handling. Replaces return()
#define MYPCOMM_INDEX
default communicator number PCOMM
#define MoFEMFunctionBegin
First executable line of each MoFEM function, used for error handling. Final line of MoFEM functions ...
#define CHK_MOAB_THROW(err, msg)
Check error code of MoAB function and throw MoFEM exception.
@ BLOCKSET
@ MOFEM_OPERATION_UNSUCCESSFUL
Definition definitions.h:34
@ MOFEM_ATOM_TEST_INVALID
Definition definitions.h:40
@ MOFEM_DATA_INCONSISTENCY
Definition definitions.h:31
#define MoFEMFunctionReturn(a)
Last executable line of each PETSc function used for error handling. Replaces return()
#define CHKERR
Inline error check.
static const bool debug
constexpr auto t_kd
#define MOFEM_LOG(channel, severity)
Log.
#define MOFEM_LOG_CHANNEL(channel)
Set and reset channel.
constexpr double a0
FTensor::Index< 'i', SPACE_DIM > i
const double c
speed of light (cm/ns)
const double v
phase velocity of light in medium (cm/ns)
const double n
refractive index of diffusive medium
FTensor::Index< 'J', DIM1 > J
Definition level_set.cpp:30
FTensor::Index< 'l', 3 > l
FTensor::Index< 'j', 3 > j
FTensor::Index< 'k', 3 > k
static auto get_range_from_block(MoFEM::Interface &m_field, const std::string block_name, int dim)
constexpr std::enable_if<(Dim0<=2 &&Dim1<=2), Tensor2_Expr< Levi_Civita< T >, T, Dim0, Dim1, i, j > >::type levi_civita(const Index< i, Dim0 > &, const Index< j, Dim1 > &)
levi_civita functions to make for easy adhoc use
constexpr IntegrationType I
constexpr double t
plate stiffness
Definition plate.cpp:58
double q
constexpr double g
FTensor::Index< 'm', 3 > m
MoFEMErrorCode createCrackSurfaceMeshset()
MoFEMErrorCode calculateCrackArea(boost::shared_ptr< double > &area_ptr)
MoFEMErrorCode calculateOrientation(const int tag, bool set_orientation)
MoFEMErrorCode setNewFrontCoordinates()
MoFEMErrorCode addCrackSurfaces(const bool debug=false)
MoFEMErrorCode calculateFaceMaterialForce(const int tag, TS ts, SmartPetscObj< Vec > *adjoint_gradient_vector=nullptr)
static auto exp(A &&t_w_vee, B &&theta)
Definition Lie.hpp:69
auto save_range