v0.16.0
Loading...
Searching...
No Matches
adjoint.cpp
Go to the documentation of this file.
1/**
2 * @file adjoint.cpp
3 * @brief Topology optimization using adjoint method with Python objective functions
4 * @details This tutorial demonstrates:
5 * - Adjoint-based sensitivity analysis for structural topology optimization
6 * - Integration with Python for flexible objective function definition
7 * - Higher-order finite element analysis with geometry fields
8 * - Automatic differentiation using MoFEM's adjoint capabilities
9 * @author Anonymous author(s) committing under MIT license
10 * @example mofem/tutorials/vec-7_shape_optimisation/adjoint.cpp
11 *
12 */
13
14#include <boost/python.hpp>
15#include <boost/python/def.hpp>
16#include <boost/python/numpy.hpp>
17namespace bp = boost::python;
18namespace np = boost::python::numpy;
19
20#include <MoFEM.hpp>
21
22using namespace MoFEM;
23
24//! [Constants and material properties]
25constexpr int BASE_DIM = 1; ///< Dimension of the base functions
26
27//! [Define dimension]
28constexpr int SPACE_DIM =
29 EXECUTABLE_DIMENSION; ///< Space dimension of problem (2D or 3D), set at compile time
30
31//! [Define dimension]
32constexpr AssemblyType A =
33 AssemblyType::PETSC; ///< Use PETSc for matrix/vector assembly
34constexpr IntegrationType I =
35 IntegrationType::GAUSS; ///< Use Gauss quadrature for integration
36
37//! [Material properties for linear elasticity]
38constexpr double young_modulus = 1; ///< Young's modulus E
39constexpr double poisson_ratio = 0.3; ///< Poisson's ratio ν
40constexpr double bulk_modulus_K =
42 (3 * (1 - 2 * poisson_ratio)); ///< Bulk modulus K = E/(3(1-2ν))
43constexpr double shear_modulus_G =
44 young_modulus / (2 * (1 + poisson_ratio)); ///< Shear modulus G = E/(2(1+ν))
45
46PetscBool is_plane_strain =
47 PETSC_FALSE; ///< Flag for plane strain vs plane stress in 2D
48//! [Constants and material properties]
49
50//! [Define finite element types and operators]
51using EntData =
52 EntitiesFieldData::EntData; ///< Entity data for field operations
54 SPACE_DIM>::DomainEle; ///< Domain finite elements
56 SPACE_DIM>::BoundaryEle; ///< Boundary finite elements
57using DomainEleOp = DomainEle::UserDataOperator; ///< Domain element operators
59 BoundaryEle::UserDataOperator; ///< Boundary element operators
60//! [Define finite element types and operators]
61
62//! [Boundary condition types]
63struct DomainBCs {}; ///< Domain boundary conditions marker
64struct BoundaryBCs {}; ///< Boundary conditions marker
65
66//! [Natural boundary condition operators]
68 I>; ///< Domain RHS natural BCs
70 DomainRhsBCs::OpFlux<DomainBCs, 1, SPACE_DIM>; ///< Domain flux operator
72 I>; ///< Boundary RHS natural BCs
75 SPACE_DIM>; ///< Boundary flux operator
77 I>; ///< Boundary LHS natural BCs
80 SPACE_DIM>; ///< Boundary LHS flux operator
81
82template <int DIM> struct PostProcEleByDim;
83
84template <> struct PostProcEleByDim<2> {
88};
89
90template <> struct PostProcEleByDim<3> {
94};
95// Here you can see how the template is being used
99// Forward declaration
100template <int SPACE_DIM, IntegrationType I, typename OpBase>
102
103/// Forward declaration of operator for gradient times symmetric tensor operations
104template <int SPACE_DIM, IntegrationType I, typename OpBase>
106
108
110
111#include <ElasticSpring.hpp>
112#include <FluidLevel.hpp>
113#include <CalculateTraction.hpp>
114#include <NaturalDomainBC.hpp>
115#include <NaturalBoundaryBC.hpp>
116#include <HookeOps.hpp>
117#include <ElasticPostProc.hpp>
118
120using namespace ShapeOptimization;
121
123 const std::string block_name, int dim);
124
125MoFEMErrorCode save_range(moab::Interface &moab, const std::string name,
126 const Range r);
127struct Example {
128
129 Example(MoFEM::Interface &m_field) : mField(m_field) {}
130
131 /// Main driver function for the optimization process
133
134private:
135 MoFEM::Interface &mField; ///< Reference to MoFEM interface
136
137 boost::shared_ptr<MatrixDouble> vectorFieldPtr =
138 nullptr; ///< Field values at evaluation points
139
140 // Problem setup methods
141 MoFEMErrorCode readMesh(); ///< Read mesh from file and setup meshsets
143 setupProblem(); ///< Setup fields, approximation spaces and DOFs
144 MoFEMErrorCode setupAdJoint(); ///< Setup adjoint fields and finite elements
145 MoFEMErrorCode boundaryCondition(); ///< Apply essential boundary conditions
146 MoFEMErrorCode topologyModes(); ///< Compute topology optimization modes
148 assembleSystem(); ///< Setup operators in finite element pipeline
149
150 // Analysis methods
151 MoFEMErrorCode solveElastic(); ///< Solve forward elastic problem
153 postprocessElastic(int iter,
154 SmartPetscObj<Vec> adjoint_vector =
155 nullptr); ///< Post-process and output results
156
157 /// Calculate objective function gradient using adjoint method
158 MoFEMErrorCode calculateGradient(PetscReal *objective_function_value,
159 Vec objective_function_gradient,
160 Vec adjoint_vector);
161
162 // Problem configuration
163 FieldApproximationBase base; ///< Choice of finite element basis functions
164 int fieldOrder = 2; ///< Polynomial order for approximation
165
166 // Solver and data management
167 SmartPetscObj<KSP> kspElastic; ///< Linear solver for elastic problem
168 SmartPetscObj<DM> adjointDM; ///< Data manager for adjoint problem
169 boost::shared_ptr<ObjectiveFunctionData>
170 pythonPtr; ///< Interface to Python objective function
171
172 // Topology optimization data
173 std::vector<SmartPetscObj<Vec>>
174 modeVecs; ///< Topology mode vectors (design variables)
175 std::vector<std::array<double, 3>>
176 modeCentroids; ///< Centroids of optimization blocks
177 std::vector<std::array<double, 6>>
178 modeBBoxes; ///< Bounding boxes of optimization blocks
179 SmartPetscObj<Vec> initialGeometry; ///< Initial geometry field
180};
181
182//! [Run topology optimization problem]
183/**
184 * @brief Main driver for topology optimization using adjoint sensitivity analysis
185 *
186 * This function orchestrates the complete topology optimization workflow:
187 * 1. Initialize Python objective function interface
188 * 2. Setup finite element problems (forward and adjoint)
189 * 3. Compute initial elastic solution
190 * 4. Generate topology optimization modes
191 * 5. Run TAO optimization loop with adjoint-based gradients
192 *
193 * The optimization uses TAO (Toolkit for Advanced Optimization) with L-BFGS
194 * algorithm. At each iteration:
195 * - Update geometry based on current design variables
196 * - Solve forward elastic problem
197 * - Compute objective function and gradients using adjoint method
198 * - Post-process results
199 *
200 * @return MoFEMErrorCode Success or error code
201 */
204
205 // Read objective function from Python file
206 char objective_function_file_name[255] = "objective_function.py";
208 PETSC_NULLPTR, PETSC_NULLPTR, "-objective_function",
209 objective_function_file_name, 255, PETSC_NULLPTR);
210
211 // Verify that the Python objective function file exists
212 auto file_exists = [](std::string myfile) {
213 std::ifstream file(myfile.c_str());
214 if (file) {
215 return true;
216 }
217 return false;
218 };
219 if (!file_exists(objective_function_file_name)) {
220 MOFEM_LOG("WORLD", Sev::error) << "Objective function file NOT found: "
221 << objective_function_file_name;
222 CHK_THROW_MESSAGE(MOFEM_NOT_FOUND, "file NOT found");
223 }
224
225 // Create Python interface for objective function
226 pythonPtr = create_python_objective_function(objective_function_file_name);
227
228 char sensitivity_method_name[32] = "adjoint";
229 CHKERR PetscOptionsGetString(PETSC_NULLPTR, PETSC_NULLPTR,
230 "-sensitivity_method", sensitivity_method_name,
231 sizeof(sensitivity_method_name), PETSC_NULLPTR);
232 std::string sensitivity_method = sensitivity_method_name;
233 std::transform(sensitivity_method.begin(), sensitivity_method.end(),
234 sensitivity_method.begin(),
235 [](unsigned char c) { return std::tolower(c); });
236 if (sensitivity_method == "direct") {
238 } else if (sensitivity_method == "adjoint") {
240 } else {
241 SETERRQ(PETSC_COMM_WORLD, MOFEM_DATA_INCONSISTENCY,
242 "Unknown -sensitivity_method. Use 'direct' or 'adjoint'.");
243 }
244 MOFEM_LOG("WORLD", Sev::inform)
245 << "Sensitivity method: " << sensitivity_method;
246
247 // Setup finite element problems
248 CHKERR readMesh(); // Read mesh and meshsets
249 CHKERR setupProblem(); // Setup displacement field and geometry field
250 CHKERR setupAdJoint(); // Setup adjoint field for sensitivity analysis
251 CHKERR boundaryCondition(); // Apply essential boundary conditions
252 CHKERR assembleSystem(); // Setup finite element operators
253
254 // Create linear solver for elastic problem
255 auto pip = mField.getInterface<PipelineManager>();
256 kspElastic = pip->createKSP();
257 CHKERR KSPSetFromOptions(kspElastic);
258
259 // Solve initial elastic problem
261 CHKERR postprocessElastic(-1); // Post-process initial solution
262
263 auto create_vec_modes = [&](auto block_name) {
265 auto mesh_mng = mField.getInterface<MeshsetsManager>();
266 auto bcs = mesh_mng->getCubitMeshsetPtr(
267
268 std::regex((boost::format("%s(.*)") % block_name).str())
269
270 );
271
272 int nb_total_modes = 0;
273 for (auto &bc : bcs) {
274 auto id = bc->getMeshsetId();
275 int nb_modes;
276 CHKERR pythonPtr->numberOfModes(id, nb_modes);
277 nb_total_modes += nb_modes;
278 }
279
280 MOFEM_LOG("WORLD", Sev::inform)
281 << "Total number of modes to apply: " << nb_total_modes;
282
283 modeVecs.resize(nb_total_modes);
284
286 };
287
288 auto get_modes_bounding_boxes = [&](auto block_name) {
290 auto mesh_mng = mField.getInterface<MeshsetsManager>();
291 auto bcs = mesh_mng->getCubitMeshsetPtr(
292
293 std::regex((boost::format("%s(.*)") % block_name).str())
294
295 );
296
297 for (auto &bc : bcs) {
298 auto meshset = bc->getMeshset();
299 Range ents;
300 CHKERR mField.get_moab().get_entities_by_handle(meshset, ents, true);
301 Range verts;
302 CHKERR mField.get_moab().get_connectivity(ents, verts, false);
303 std::vector<double> x(verts.size());
304 std::vector<double> y(verts.size());
305 std::vector<double> z(verts.size());
306 CHKERR mField.get_moab().get_coords(verts, x.data(), y.data(), z.data());
307 std::array<double, 3> centroid = {0, 0, 0};
308 for (int i = 0; i != verts.size(); ++i) {
309 centroid[0] += x[i];
310 centroid[1] += y[i];
311 centroid[2] += z[i];
312 }
313 MPI_Allreduce(MPI_IN_PLACE, centroid.data(), 3, MPI_DOUBLE, MPI_SUM,
314 mField.get_comm());
315 int nb_vertex = verts.size();
316 MPI_Allreduce(MPI_IN_PLACE, &nb_vertex, 1, MPI_INT, MPI_SUM,
317 mField.get_comm());
318 if (nb_vertex) {
319 centroid[0] /= nb_vertex;
320 centroid[1] /= nb_vertex;
321 centroid[2] /= nb_vertex;
322 }
323 std::array<double, 6> bbox = {centroid[0], centroid[1], centroid[2],
324 centroid[0], centroid[1], centroid[2]};
325 for (int i = 0; i != verts.size(); ++i) {
326 bbox[0] = std::min(bbox[0], x[i]);
327 bbox[1] = std::min(bbox[1], y[i]);
328 bbox[2] = std::min(bbox[2], z[i]);
329 bbox[3] = std::max(bbox[3], x[i]);
330 bbox[4] = std::max(bbox[4], y[i]);
331 bbox[5] = std::max(bbox[5], z[i]);
332 }
333 MPI_Allreduce(MPI_IN_PLACE, &bbox[0], 3, MPI_DOUBLE, MPI_MIN,
334 mField.get_comm());
335 MPI_Allreduce(MPI_IN_PLACE, &bbox[3], 3, MPI_DOUBLE, MPI_MAX,
336 mField.get_comm());
337
338 MOFEM_LOG("WORLD", Sev::inform)
339 << "Block: " << bc->getName() << " centroid: " << centroid[0] << " "
340 << centroid[1] << " " << centroid[2];
341 MOFEM_LOG("WORLD", Sev::inform)
342 << "Block: " << bc->getName() << " bbox: " << bbox[0] << " "
343 << bbox[1] << " " << bbox[2] << " " << bbox[3] << " " << bbox[4]
344 << " " << bbox[5];
345
346 modeCentroids.push_back(centroid);
347 modeBBoxes.push_back(bbox);
348 }
349
351 };
352
353 auto eval_objective_and_gradient = [](Tao tao, Vec x, PetscReal *f, Vec g,
354 void *ctx) -> PetscErrorCode {
356 auto *ex_ptr = (Example *)ctx;
357
358 int iter;
359 CHKERR TaoGetIterationNumber(tao, &iter);
360
361 auto set_geometry = [&](Vec x) {
363
364 VecScatter ctx;
365 Vec x_local;
366 CHKERR VecScatterCreateToAll(x, &ctx, &x_local);
367 // scatter as many times as you need
368 CHKERR VecScatterBegin(ctx, x, x_local, INSERT_VALUES, SCATTER_FORWARD);
369 CHKERR VecScatterEnd(ctx, x, x_local, INSERT_VALUES, SCATTER_FORWARD);
370 // destroy scatter context and local vector when no longer needed
371 CHKERR VecScatterDestroy(&ctx);
372
373 auto current_geometry = vectorDuplicate(ex_ptr->initialGeometry);
374 CHKERR VecCopy(ex_ptr->initialGeometry, current_geometry);
375 const double *a;
376 CHKERR VecGetArrayRead(x_local, &a);
377 const double *coeff = a;
378 for (auto &mode_vec : ex_ptr->modeVecs) {
379 MOFEM_LOG("WORLD", Sev::verbose)
380 << "Adding mode with coeff: " << *coeff;
381 CHKERR VecAXPY(current_geometry, (*coeff), mode_vec);
382 ++coeff;
383 }
384 CHKERR VecRestoreArrayRead(x_local, &a);
385 CHKERR VecGhostUpdateBegin(current_geometry, INSERT_VALUES,
386 SCATTER_FORWARD);
387 CHKERR VecGhostUpdateEnd(current_geometry, INSERT_VALUES,
388 SCATTER_FORWARD);
389 CHKERR ex_ptr->mField.getInterface<VecManager>()
390 ->setOtherLocalGhostVector("ADJOINT", "ADJOINT_FIELD", "GEOMETRY",
391 RowColData::ROW, current_geometry,
392 INSERT_VALUES, SCATTER_REVERSE);
393
394 CHKERR VecDestroy(&x_local);
396 };
397
398 CHKERR set_geometry(x);
399 CHKERR KSPReset(ex_ptr->kspElastic);
400 CHKERR ex_ptr->solveElastic();
401 auto simple = ex_ptr->mField.getInterface<Simple>();
402 auto adjoint_vector = createDMVector(simple->getDM());
403 CHKERR ex_ptr->calculateGradient(f, g, adjoint_vector);
404 CHKERR ex_ptr->postprocessElastic(iter, adjoint_vector);
405
407 };
408
409 // Create topology optimization modes and setup TAO solver
410 CHKERR create_vec_modes("OPTIMISE");
411 CHKERR get_modes_bounding_boxes("OPTIMISE");
412
413 /**
414 * Setup TAO (Toolkit for Advanced Optimization) solver for topology optimization
415 *
416 * TAO provides various optimization algorithms. Here we use TAOLMVM (Limited Memory
417 * Variable Metric) which is a quasi-Newton method suitable for unconstrained
418 * optimization with gradient information.
419 *
420 * The optimization variables are coefficients for the topology modes, and
421 * gradients are computed using the adjoint method for efficiency.
422 */
423 auto tao = createTao(mField.get_comm());
424 CHKERR TaoSetType(tao, TAOLMVM);
425
426 auto rank = mField.get_comm_rank();
427 auto g = createVectorMPI(mField.get_comm(), (!rank) ? modeVecs.size() : 0,
428 PETSC_DECIDE);
429 CHKERR TaoSetObjectiveAndGradient(tao, g, eval_objective_and_gradient,
430 (void *)this);
431
432 // Store initial geometry for reference during optimization
434 CHKERR mField.getInterface<VecManager>()->setOtherLocalGhostVector(
435 "ADJOINT", "ADJOINT_FIELD", "GEOMETRY", RowColData::ROW, initialGeometry,
436 INSERT_VALUES, SCATTER_FORWARD);
437
438 // Setup topology optimization modes
439 /**
440 * Generate modes for topology optimization design parameterization
441 *
442 * These modes represent perturbations to the geometry that can be used
443 * as design variables in topology optimization. The modes are defined
444 * through the Python interface and provide spatial basis functions
445 * for shape modifications.
446 */
448
449 // Initialize optimization variables and run solver
450 /**
451 * Start optimization with zero initial guess for design variables
452 *
453 * The TAO solver will iteratively:
454 * 1. Evaluate objective function at current design point
455 * 2. Compute gradients using adjoint sensitivity analysis
456 * 3. Update design variables using L-BFGS algorithm
457 * 4. Check convergence criteria
458 *
459 * Each function evaluation involves solving the forward elastic problem
460 * and the adjoint problem to compute sensitivities efficiently.
461 */
462 auto x0 = vectorDuplicate(g);
463
464 CHKERR VecSet(x0, 0.0);
465 CHKERR TaoSetSolution(tao, x0);
466 CHKERR TaoSetFromOptions(tao);
467 CHKERR TaoSolve(tao);
468
469 // Optimization complete - results available in solution vectors
470 MOFEM_LOG("WORLD", Sev::inform) << "Topology optimization completed";
471
473}
474//! [Run problem]
475
476//! [Read mesh]
477/**
478 * @brief Read mesh from file and setup material/boundary condition meshsets
479 *
480 * This function loads the finite element mesh from file and processes
481 * associated meshsets that define material properties and boundary conditions.
482 * The mesh is typically generated using CUBIT and exported in .h5m format.
483 *
484 * Meshsets are used to group elements/faces by:
485 * - Material properties (for different material blocks)
486 * - Boundary conditions (for applying loads and constraints)
487 * - Optimization regions (for topology optimization)
488 *
489 * @return MoFEMErrorCode Success or error code
490 */
494 CHKERR simple->getOptions(); // Read command line options
495 CHKERR simple->loadFile(); // Load mesh from file
496 // Add meshsets if config file provided
497 CHKERR mField.getInterface<MeshsetsManager>()->setMeshsetFromFile();
499}
500//! [Read mesh]
501
502//! [Set up problem]
503/**
504 * @brief Setup finite element fields, approximation spaces and degrees of freedom
505 *
506 * This function configures the finite element problem by:
507 * 1. Setting up the displacement field "U" with vector approximation
508 * 2. Setting up the geometry field "GEOMETRY" for mesh deformation
509 * 3. Defining polynomial approximation order and basis functions
510 * 4. Creating degrees of freedom on mesh entities
511 *
512 * The displacement field uses H1 vector space for standard elasticity.
513 * The geometry field allows mesh modification during topology optimization.
514 * Different basis functions (Ainsworth-Legendre vs Demkowicz) can be selected.
515 *
516 * @return MoFEMErrorCode Success or error code
517 */
521
522 // Select basis functions for finite element approximation
523 enum bases { AINSWORTH, DEMKOWICZ, LASBASETOPT };
524 const char *list_bases[LASBASETOPT] = {"ainsworth", "demkowicz"};
525 PetscInt choice_base_value = AINSWORTH;
526 CHKERR PetscOptionsGetEList(PETSC_NULLPTR, NULL, "-base", list_bases,
527 LASBASETOPT, &choice_base_value, PETSC_NULLPTR);
528
529 switch (choice_base_value) {
530 case AINSWORTH:
532 MOFEM_LOG("WORLD", Sev::inform)
533 << "Set AINSWORTH_LEGENDRE_BASE for displacements";
534 break;
535 case DEMKOWICZ:
537 MOFEM_LOG("WORLD", Sev::inform)
538 << "Set DEMKOWICZ_JACOBI_BASE for displacements";
539 break;
540 default:
541 base = LASTBASE;
542 break;
543 }
544
545 // Add finite element fields
546 /**
547 * Setup displacement field "U" - the primary unknown in elasticity
548 * This field represents displacement vector at each node/DOF
549 */
550 CHKERR simple->addDomainField("U", H1, base, SPACE_DIM);
551 CHKERR simple->addBoundaryField("U", H1, base, SPACE_DIM);
552 CHKERR PetscOptionsGetInt(PETSC_NULLPTR, "", "-order", &fieldOrder,
553 PETSC_NULLPTR);
554
555 /**
556 * Setup geometry field "GEOMETRY" - used for mesh deformation in optimization
557 * This field stores current nodal coordinates and can be modified
558 * during topology optimization to represent design changes
559 */
560 CHKERR simple->addDataField("GEOMETRY", H1, base, SPACE_DIM);
561
562 // Set polynomial approximation order for both fields
563 CHKERR simple->setFieldOrder("U", fieldOrder);
564 CHKERR simple->setFieldOrder("GEOMETRY", fieldOrder);
565 CHKERR simple->setUp();
566
567 // Project higher-order geometry representation onto geometry field
568 /**
569 * For higher-order elements, this projects the exact geometry
570 * onto the geometry field to maintain curved boundaries accurately
571 */
572 auto project_ho_geometry = [&]() {
573 Projection10NodeCoordsOnField ent_method(mField, "GEOMETRY");
574 return mField.loop_dofs("GEOMETRY", ent_method);
575 };
576 CHKERR project_ho_geometry();
577
578 // Check if plane strain assumption should be used
579 CHKERR PetscOptionsGetBool(PETSC_NULLPTR, "", "-plane_strain",
580 &is_plane_strain, PETSC_NULLPTR);
581
583}
584//! [Set up problem]
585
586//! [Setup adjoint]
587/**
588 * @brief Setup adjoint fields and finite elements for sensitivity analysis
589 *
590 * The adjoint method is used to efficiently compute gradients of the objective
591 * function with respect to design variables. This function sets up:
592 *
593 * 1. ADJOINT_FIELD - stores adjoint variables (Lagrange multipliers)
594 * 2. ADJOINT_DM - data manager for adjoint problem
595 * 3. Adjoint finite elements for domain and boundary
596 *
597 * The adjoint equation is: K^T * λ = ∂f/∂u
598 * where λ are adjoint variables, K is stiffness matrix, f is objective
599 *
600 * @return MoFEMErrorCode Success or error code
601 */
605
606 // Create adjoint data manager and field
607 auto create_adjoint_dm = [&]() {
608 auto adjoint_dm = createDM(mField.get_comm(), "DMMOFEM");
609
610 auto add_field = [&]() {
612 CHKERR mField.add_field("ADJOINT_FIELD", H1, base, SPACE_DIM);
614 "ADJOINT_FIELD");
615 for (auto tt = MBEDGE; tt <= moab::CN::TypeDimensionMap[SPACE_DIM].second;
616 ++tt)
617 CHKERR mField.set_field_order(simple->getMeshset(), tt, "ADJOINT_FIELD",
618 fieldOrder);
619 CHKERR mField.set_field_order(simple->getMeshset(), MBVERTEX,
620 "ADJOINT_FIELD", 1);
623 };
624
625 auto add_adjoint_fe_impl = [&]() {
627 CHKERR mField.add_finite_element("ADJOINT_DOMAIN_FE");
629 "ADJOINT_FIELD");
631 "ADJOINT_FIELD");
633 "ADJOINT_FIELD");
635 "GEOMETRY");
637 simple->getMeshset(), SPACE_DIM, "ADJOINT_DOMAIN_FE");
638 CHKERR mField.build_finite_elements("ADJOINT_DOMAIN_FE");
639
640 CHKERR mField.add_finite_element("ADJOINT_BOUNDARY_FE");
642 "ADJOINT_FIELD");
644 "ADJOINT_FIELD");
646 "ADJOINT_FIELD");
648 "GEOMETRY");
649
650 auto block_name = "OPTIMISE";
651 auto mesh_mng = mField.getInterface<MeshsetsManager>();
652 auto bcs = mesh_mng->getCubitMeshsetPtr(
653
654 std::regex((boost::format("%s(.*)") % block_name).str())
655
656 );
657
658 for (auto bc : bcs) {
660 bc->getMeshset(), SPACE_DIM - 1, "ADJOINT_BOUNDARY_FE");
661 }
662
663 CHKERR mField.build_finite_elements("ADJOINT_BOUNDARY_FE");
664
665 CHKERR mField.build_adjacencies(simple->getBitRefLevel(),
666 simple->getBitRefLevelMask());
667
669 };
670
671 auto set_adjoint_dm_imp = [&]() {
673 CHKERR DMMoFEMCreateMoFEM(adjoint_dm, &mField, "ADJOINT",
674 simple->getBitRefLevel(),
675 simple->getBitRefLevelMask());
676 CHKERR DMMoFEMSetDestroyProblem(adjoint_dm, PETSC_TRUE);
677 CHKERR DMSetFromOptions(adjoint_dm);
678 CHKERR DMMoFEMAddElement(adjoint_dm, "ADJOINT_DOMAIN_FE");
679 CHKERR DMMoFEMAddElement(adjoint_dm, "ADJOINT_BOUNDARY_FE");
680 CHKERR DMMoFEMSetSquareProblem(adjoint_dm, PETSC_TRUE);
681 CHKERR DMMoFEMSetIsPartitioned(adjoint_dm, PETSC_TRUE);
682 mField.getInterface<ProblemsManager>()->buildProblemFromFields =
683 PETSC_TRUE;
684 CHKERR DMSetUp(adjoint_dm);
685 mField.getInterface<ProblemsManager>()->buildProblemFromFields =
686 PETSC_FALSE;
688 };
689
690 CHK_THROW_MESSAGE(add_field(), "add adjoint field");
691 CHK_THROW_MESSAGE(add_adjoint_fe_impl(), "add adjoint fe");
692 CHK_THROW_MESSAGE(set_adjoint_dm_imp(), "set adjoint dm");
693
694 return adjoint_dm;
695 };
696
697 adjointDM = create_adjoint_dm();
698
700}
701//
702
703//! [Boundary condition]
707 auto bc_mng = mField.getInterface<BcManager>();
708
709 CHKERR bc_mng->removeBlockDOFsOnEntities(simple->getProblemName(), "REMOVE_X",
710 "U", 0, 0);
711 CHKERR bc_mng->removeBlockDOFsOnEntities(simple->getProblemName(), "REMOVE_Y",
712 "U", 1, 1);
713 CHKERR bc_mng->removeBlockDOFsOnEntities(simple->getProblemName(), "REMOVE_Z",
714 "U", 2, 2);
715 CHKERR bc_mng->removeBlockDOFsOnEntities(simple->getProblemName(),
716 "REMOVE_ALL", "U", 0, 3);
717 CHKERR bc_mng->pushMarkDOFsOnEntities<DisplacementCubitBcData>(
718 simple->getProblemName(), "U");
719
721}
722//! [Boundary condition]
723
724//! [Adjoint modes]
727
728 auto opt_ents = get_range_from_block(mField, "OPTIMISE", SPACE_DIM - 1);
729 auto subset_dm_bdy = createDM(mField.get_comm(), "DMMOFEM");
730 CHKERR DMMoFEMSetSquareProblem(subset_dm_bdy, PETSC_TRUE);
731 CHKERR DMMoFEMCreateSubDM(subset_dm_bdy, adjointDM, "SUBSET_BDY");
732 CHKERR DMMoFEMAddElement(subset_dm_bdy, "ADJOINT_BOUNDARY_FE");
733 CHKERR DMMoFEMAddSubFieldRow(subset_dm_bdy, "ADJOINT_FIELD",
734 boost::make_shared<Range>(opt_ents));
735 CHKERR DMMoFEMAddSubFieldCol(subset_dm_bdy, "ADJOINT_FIELD",
736 boost::make_shared<Range>(opt_ents));
737 CHKERR DMSetUp(subset_dm_bdy);
738
739 auto subset_dm_domain = createDM(mField.get_comm(), "DMMOFEM");
740 CHKERR DMMoFEMSetSquareProblem(subset_dm_domain, PETSC_TRUE);
741 CHKERR DMMoFEMCreateSubDM(subset_dm_domain, adjointDM, "SUBSET_DOMAIN");
742 CHKERR DMMoFEMAddElement(subset_dm_domain, "ADJOINT_DOMAIN_FE");
743 CHKERR DMMoFEMAddSubFieldRow(subset_dm_domain, "ADJOINT_FIELD");
744 CHKERR DMMoFEMAddSubFieldCol(subset_dm_domain, "ADJOINT_FIELD");
745 CHKERR DMSetUp(subset_dm_domain);
746
747 // remove dofs on boundary of the domain
748 auto remove_dofs = [&]() {
750
751 std::array<Range, 3> remove_dim_ents;
752 remove_dim_ents[0] =
753 get_range_from_block(mField, "OPT_REMOVE_X", SPACE_DIM - 1);
754 remove_dim_ents[1] =
755 get_range_from_block(mField, "OPT_REMOVE_Y", SPACE_DIM - 1);
756 remove_dim_ents[2] =
757 get_range_from_block(mField, "OPT_REMOVE_Z", SPACE_DIM - 1);
758
759 for (int d = 0; d != 3; ++d) {
760 MOFEM_LOG("WORLD", Sev::inform)
761 << "Removing topology modes on block OPT_REMOVE_" << (char)('X' + d)
762 << " with " << remove_dim_ents[d].size() << " entities";
763 }
764
765 Range body_ents;
766 CHKERR mField.get_moab().get_entities_by_dimension(0, SPACE_DIM, body_ents,
767 true);
768 auto skin = moab::Skinner(&mField.get_moab());
769 Range boundary_ents;
770 CHKERR skin.find_skin(0, body_ents, false, boundary_ents);
771 for (int d = 0; d != 3; ++d) {
772 boundary_ents = subtract(boundary_ents, remove_dim_ents[d]);
773 }
774 ParallelComm *pcomm =
775 ParallelComm::get_pcomm(&mField.get_moab(), MYPCOMM_INDEX);
776 CHKERR pcomm->filter_pstatus(boundary_ents,
777 PSTATUS_SHARED | PSTATUS_MULTISHARED,
778 PSTATUS_NOT, -1, nullptr);
779 for (auto d = SPACE_DIM - 2; d >= 0; --d) {
780 if (d >= 0) {
781 Range ents;
782 CHKERR mField.get_moab().get_adjacencies(boundary_ents, d, false, ents,
783 moab::Interface::UNION);
784 boundary_ents.merge(ents);
785 } else {
786 Range verts;
787 CHKERR mField.get_moab().get_connectivity(boundary_ents, verts);
788 boundary_ents.merge(verts);
789 }
790 CHKERR mField.getInterface<CommInterface>()->synchroniseEntities(
791 boundary_ents);
792 }
793 boundary_ents.merge(opt_ents);
794 CHKERR mField.getInterface<ProblemsManager>()->removeDofsOnEntities(
795 "SUBSET_DOMAIN", "ADJOINT_FIELD", boundary_ents);
796 for (int d = 0; d != 3; ++d) {
797 CHKERR mField.getInterface<ProblemsManager>()->removeDofsOnEntities(
798 "SUBSET_DOMAIN", "ADJOINT_FIELD", remove_dim_ents[d], d, d);
799 }
800
801 // #ifndef NDEBUG
802 if (mField.get_comm_rank() == 0) {
803 CHKERR save_range(mField.get_moab(), "topoMode_boundary_ents.vtk",
804 boundary_ents);
805 }
806 // #endif
807
809 };
810
811 CHKERR remove_dofs();
812
813 auto get_lhs_fe = [&]() {
814 auto fe_lhs = boost::make_shared<BoundaryEle>(mField);
815 fe_lhs->getRuleHook = [](int, int, int p_data) {
816 return 2 * p_data + p_data - 1;
817 };
818 auto &pip = fe_lhs->getOpPtrVector();
820 "GEOMETRY");
823 pip.push_back(new OpMass("ADJOINT_FIELD", "ADJOINT_FIELD",
824 [](double, double, double) { return 1.; }));
825 return fe_lhs;
826 };
827
828 auto get_rhs_fe = [&]() {
829 auto fe_rhs = boost::make_shared<BoundaryEle>(mField);
830 fe_rhs->getRuleHook = [](int, int, int p_data) {
831 return 2 * p_data + p_data - 1;
832 };
833 auto &pip = fe_rhs->getOpPtrVector();
835 "GEOMETRY");
836
837 return fe_rhs;
838 };
839
840 auto block_name = "OPTIMISE";
841 auto mesh_mng = mField.getInterface<MeshsetsManager>();
842 auto bcs = mesh_mng->getCubitMeshsetPtr(
843
844 std::regex((boost::format("%s(.*)") % block_name).str())
845
846 );
847
848 for (auto &v : modeVecs) {
849 v = createDMVector(subset_dm_bdy);
850 }
851
853 struct OpMode : public OP {
854 OpMode(const std::string name,
855 boost::shared_ptr<ObjectiveFunctionData> python_ptr, int id,
856 std::vector<SmartPetscObj<Vec>> mode_vecs,
857 std::vector<std::array<double, 3>> mode_centroids,
858 std::vector<std::array<double, 6>> mode_bboxes, int block_counter,
859 int mode_counter, boost::shared_ptr<Range> range = nullptr)
860 : OP(name, name, OP::OPROW, range), pythonPtr(python_ptr), iD(id),
861 modeVecs(mode_vecs), modeCentroids(mode_centroids),
862 modeBboxes(mode_bboxes), blockCounter(block_counter),
863 modeCounter(mode_counter) {}
864
865 MoFEMErrorCode doWork(int side, EntityType type, EntData &data) {
867
868 if (OP::entsPtr) {
869 if (OP::entsPtr->find(this->getFEEntityHandle()) == OP::entsPtr->end())
871 }
872
873 auto nb_rows = data.getIndices().size();
874 if (!nb_rows) {
876 }
877 auto nb_base_functions = data.getN().size2();
878
880 CHKERR pythonPtr->blockModes(iD, OP::getCoordsAtGaussPts(),
881 modeCentroids[blockCounter],
882 modeBboxes[blockCounter], blockModes);
883
884 auto nb_integration_pts = getGaussPts().size2();
885 if (blockModes.size2() != 3 * nb_integration_pts) {
886 MOFEM_LOG("WORLD", Sev::error)
887 << "Number of modes does not match number of integration points: "
888 << blockModes.size2() << "!=" << 3 * nb_integration_pts;
889 CHK_THROW_MESSAGE(MOFEM_DATA_INCONSISTENCY, "modes/integration points");
890 }
891
892 VectorDouble nf(nb_rows);
893
894 int nb_modes = blockModes.size1();
895 for (auto mode = 0; mode != nb_modes; ++mode) {
896 nf.clear();
897 // get mode
898 auto t_mode = getFTensor1FromPtr<3>(&blockModes(mode, 0));
899 // get element volume
900 const double vol = OP::getMeasure();
901 // get integration weights
902 auto t_w = OP::getFTensor0IntegrationWeight();
903 // get base function gradient on rows
904 auto t_base = data.getFTensor0N();
905 // loop over integration points
906 for (int gg = 0; gg != nb_integration_pts; gg++) {
907
908 // take into account Jacobian
909 const double alpha = t_w * vol;
910 // loop over rows base functions
911 auto t_nf = getFTensor1FromPtr<SPACE_DIM>(nf.data().data());
912 int rr = 0;
913 for (; rr != nb_rows / SPACE_DIM; ++rr) {
914 t_nf(i) += alpha * t_base * t_mode(i);
915 ++t_base;
916 ++t_nf;
917 }
918 for (; rr < nb_base_functions; ++rr)
919 ++t_base;
920 ++t_w; // move to another integration weight
921 ++t_mode; // move to another mode
922 }
923 Vec vec = modeVecs[modeCounter + mode];
924 auto size = data.getIndices().size();
925 auto *indices = data.getIndices().data().data();
926 auto *nf_data = nf.data().data();
927 CHKERR VecSetValues(vec, size, indices, nf_data, ADD_VALUES);
928 }
929
931 }
932
933 private:
934 boost::shared_ptr<ObjectiveFunctionData> pythonPtr;
935 MatrixDouble blockModes;
936 std::vector<std::array<double, 3>> modeCentroids;
937 std::vector<std::array<double, 6>> modeBboxes;
938 int iD;
939 std::vector<SmartPetscObj<Vec>> modeVecs;
940 int blockCounter;
941 int modeCounter;
942 };
943
944 auto solve_bdy = [&]() {
946
947 auto fe_lhs = get_lhs_fe();
948 auto fe_rhs = get_rhs_fe();
949 int block_counter = 0;
950 int mode_counter = 0;
951 for (auto &bc : bcs) {
952 auto id = bc->getMeshsetId();
953 Range ents;
954 CHKERR mField.get_moab().get_entities_by_handle(bc->getMeshset(), ents,
955 true);
956 auto range = boost::make_shared<Range>(ents);
957 auto &pip_rhs = fe_rhs->getOpPtrVector();
958 pip_rhs.push_back(new OpMode("ADJOINT_FIELD", pythonPtr, id, modeVecs,
959 modeCentroids, modeBBoxes, block_counter,
960 mode_counter, range));
961 CHKERR DMoFEMLoopFiniteElements(subset_dm_bdy, "ADJOINT_BOUNDARY_FE",
962 fe_rhs);
963 pip_rhs.pop_back();
964 int nb_modes;
965 CHKERR pythonPtr->numberOfModes(id, nb_modes);
966 ++block_counter;
967 mode_counter += nb_modes;
968 MOFEM_LOG("WORLD", Sev::inform)
969 << "Setting mode block block: " << bc->getName()
970 << " with ID: " << bc->getMeshsetId()
971 << " total modes: " << mode_counter;
972 }
973
974 for (auto &v : modeVecs) {
975 CHKERR VecAssemblyBegin(v);
976 CHKERR VecAssemblyEnd(v);
977 CHKERR VecGhostUpdateBegin(v, ADD_VALUES, SCATTER_REVERSE);
978 CHKERR VecGhostUpdateEnd(v, ADD_VALUES, SCATTER_REVERSE);
979 }
980
981 auto M = createDMMatrix(subset_dm_bdy);
982 fe_lhs->B = M;
983 CHKERR DMoFEMLoopFiniteElements(subset_dm_bdy, "ADJOINT_BOUNDARY_FE",
984 fe_lhs);
985 CHKERR MatAssemblyBegin(M, MAT_FINAL_ASSEMBLY);
986 CHKERR MatAssemblyEnd(M, MAT_FINAL_ASSEMBLY);
987
988 auto solver = createKSP(mField.get_comm());
989 CHKERR KSPSetOperators(solver, M, M);
990 CHKERR KSPSetFromOptions(solver);
991 CHKERR KSPSetUp(solver);
992 auto v = createDMVector(subset_dm_bdy);
993 for (auto &f : modeVecs) {
994 CHKERR KSPSolve(solver, f, v);
995 CHKERR VecSwap(f, v);
996 }
997
998 for (auto &v : modeVecs) {
999 CHKERR VecGhostUpdateBegin(v, INSERT_VALUES, SCATTER_FORWARD);
1000 CHKERR VecGhostUpdateEnd(v, INSERT_VALUES, SCATTER_FORWARD);
1001 }
1002
1004 };
1005
1006 CHKERR solve_bdy();
1007
1008 auto get_elastic_fe_lhs = [&]() {
1009 auto fe = boost::make_shared<DomainEle>(mField);
1010 fe->getRuleHook = [](int, int, int p_data) {
1011 return 2 * p_data + p_data - 1;
1012 };
1013 auto &pip = fe->getOpPtrVector();
1015 "GEOMETRY");
1016 CHKERR HookeOps::opFactoryDomainLhs<SPACE_DIM, A, I, DomainEleOp>(
1017 mField, pip, "ADJOINT_FIELD", "MAT_ADJOINT", Sev::noisy);
1018 return fe;
1019 };
1020
1021 auto get_elastic_fe_rhs = [&]() {
1022 auto fe = boost::make_shared<DomainEle>(mField);
1023 fe->getRuleHook = [](int, int, int p_data) {
1024 return 2 * p_data + p_data - 1;
1025 };
1026 auto &pip = fe->getOpPtrVector();
1028 "GEOMETRY");
1029 CHKERR HookeOps::opFactoryDomainRhs<SPACE_DIM, A, I, DomainEleOp>(
1030 mField, pip, "ADJOINT_FIELD", "MAT_ADJOINT", Sev::noisy);
1031 return fe;
1032 };
1033
1034 auto adjoint_gradient_postprocess = [&](auto mode) {
1036 auto post_proc_mesh = boost::make_shared<moab::Core>();
1037 auto post_proc_begin =
1038 boost::make_shared<PostProcBrokenMeshInMoabBaseBegin>(mField,
1039 post_proc_mesh);
1040 auto post_proc_end = boost::make_shared<PostProcBrokenMeshInMoabBaseEnd>(
1041 mField, post_proc_mesh);
1042
1043 auto geom_vec = boost::make_shared<MatrixDouble>();
1044
1045 auto post_proc_fe =
1046 boost::make_shared<PostProcEleDomain>(mField, post_proc_mesh);
1048 post_proc_fe->getOpPtrVector(), {H1}, "GEOMETRY");
1049 post_proc_fe->getOpPtrVector().push_back(
1050 new OpCalculateVectorFieldValues<SPACE_DIM>("ADJOINT_FIELD", geom_vec,
1051 modeVecs[mode]));
1052
1054
1055 post_proc_fe->getOpPtrVector().push_back(
1056
1057 new OpPPMap(
1058
1059 post_proc_fe->getPostProcMesh(), post_proc_fe->getMapGaussPts(),
1060
1061 {},
1062
1063 {{"MODE", geom_vec}},
1064
1065 {},
1066
1067 {}
1068
1069 )
1070
1071 );
1072
1074 post_proc_begin->getFEMethod());
1075 CHKERR DMoFEMLoopFiniteElements(adjointDM, "ADJOINT_DOMAIN_FE",
1076 post_proc_fe);
1078 post_proc_begin->getFEMethod());
1079
1080 CHKERR post_proc_end->writeFile("mode_" + std::to_string(mode) + ".h5m");
1081
1083 };
1084
1085 auto solve_domain = [&]() {
1087 auto fe_lhs = get_elastic_fe_lhs();
1088 auto fe_rhs = get_elastic_fe_rhs();
1089 auto v = createDMVector(subset_dm_domain);
1090 auto F = vectorDuplicate(v);
1091 fe_rhs->f = F;
1092
1093 auto M = createDMMatrix(subset_dm_domain);
1094 fe_lhs->B = M;
1095 CHKERR DMoFEMLoopFiniteElements(subset_dm_domain, "ADJOINT_DOMAIN_FE",
1096 fe_lhs);
1097 CHKERR MatAssemblyBegin(M, MAT_FINAL_ASSEMBLY);
1098 CHKERR MatAssemblyEnd(M, MAT_FINAL_ASSEMBLY);
1099
1100 auto solver = createKSP(mField.get_comm());
1101 CHKERR KSPSetOperators(solver, M, M);
1102 CHKERR KSPSetFromOptions(solver);
1103 CHKERR KSPSetUp(solver);
1104
1105 int mode_counter = 0;
1106 for (auto &f : modeVecs) {
1107 CHKERR mField.getInterface<FieldBlas>()->setField(0, "ADJOINT_FIELD");
1108 CHKERR DMoFEMMeshToLocalVector(subset_dm_bdy, f, INSERT_VALUES,
1109 SCATTER_REVERSE);
1110 CHKERR VecZeroEntries(F);
1111 CHKERR DMoFEMLoopFiniteElements(subset_dm_domain, "ADJOINT_DOMAIN_FE",
1112 fe_rhs);
1113 CHKERR VecAssemblyBegin(F);
1114 CHKERR VecAssemblyEnd(F);
1115 CHKERR VecGhostUpdateBegin(F, ADD_VALUES, SCATTER_REVERSE);
1116 CHKERR VecGhostUpdateEnd(F, ADD_VALUES, SCATTER_REVERSE);
1117 CHKERR KSPSolve(solver, F, v);
1118 CHKERR VecGhostUpdateBegin(v, INSERT_VALUES, SCATTER_FORWARD);
1119 CHKERR VecGhostUpdateEnd(v, INSERT_VALUES, SCATTER_FORWARD);
1120 CHKERR DMoFEMMeshToLocalVector(subset_dm_domain, v, INSERT_VALUES,
1121 SCATTER_REVERSE);
1122 auto m = createDMVector(adjointDM);
1123 CHKERR DMoFEMMeshToLocalVector(adjointDM, m, INSERT_VALUES,
1124 SCATTER_FORWARD);
1125 f = m;
1126 ++mode_counter;
1127 }
1129 };
1130
1131 CHKERR solve_domain();
1132
1133 for (int i = 0; i < modeVecs.size(); ++i) {
1134 CHKERR adjoint_gradient_postprocess(i);
1135 }
1136
1138}
1139//! [Adjoint modes]
1140
1141//! [Push operators to pipeline]
1144 auto pip = mField.getInterface<PipelineManager>();
1145
1146 //! [Integration rule]
1147 auto integration_rule = [](int, int, int approx_order) {
1148 return 2 * approx_order + 1;
1149 };
1150
1151 auto integration_rule_bc = [](int, int, int approx_order) {
1152 return 2 * approx_order + 1;
1153 };
1154
1156 CHKERR pip->setDomainLhsIntegrationRule(integration_rule);
1157 CHKERR pip->setBoundaryRhsIntegrationRule(integration_rule_bc);
1158 CHKERR pip->setBoundaryLhsIntegrationRule(integration_rule_bc);
1159 //! [Integration rule]
1160
1162 pip->getOpDomainLhsPipeline(), {H1}, "GEOMETRY");
1164 pip->getOpDomainRhsPipeline(), {H1}, "GEOMETRY");
1166 pip->getOpBoundaryRhsPipeline(), {NOSPACE}, "GEOMETRY");
1168 pip->getOpBoundaryLhsPipeline(), {NOSPACE}, "GEOMETRY");
1169
1170 //! [Push domain stiffness matrix to pipeline]
1171 // Add LHS operator for elasticity (stiffness matrix)
1172 CHKERR HookeOps::opFactoryDomainLhs<SPACE_DIM, A, I, DomainEleOp>(
1173 mField, pip->getOpDomainLhsPipeline(), "U", "MAT_ELASTIC", Sev::noisy);
1174 //! [Push domain stiffness matrix to pipeline]
1175
1176 // Add RHS operator for internal forces
1177 CHKERR HookeOps::opFactoryDomainRhs<SPACE_DIM, A, I, DomainEleOp>(
1178 mField, pip->getOpDomainRhsPipeline(), "U", "MAT_ELASTIC", Sev::noisy);
1179
1180 //! [Push Body forces]
1182 pip->getOpDomainRhsPipeline(), mField, "U", Sev::inform);
1183 //! [Push Body forces]
1184
1185 //! [Push natural boundary conditions]
1186 // Add force boundary condition
1188 pip->getOpBoundaryRhsPipeline(), mField, "U", -1, Sev::inform);
1189 // Add case for mix type of BCs
1191 pip->getOpBoundaryLhsPipeline(), mField, "U", Sev::noisy);
1192 //! [Push natural boundary conditions]
1194}
1195//! [Push operators to pipeline]
1196
1197//! [Solve]
1200 auto simple = mField.getInterface<Simple>();
1201 auto dm = simple->getDM();
1202 auto f = createDMVector(dm);
1203 auto d = vectorDuplicate(f);
1204 CHKERR VecZeroEntries(d);
1205 CHKERR DMoFEMMeshToLocalVector(dm, d, INSERT_VALUES, SCATTER_REVERSE);
1206
1207 auto set_essential_bc = [&]() {
1209 // This is low level pushing finite elements (pipelines) to solver
1210
1211 auto ksp_ctx_ptr = getDMKspCtx(dm);
1212 auto pre_proc_rhs = boost::make_shared<FEMethod>();
1213 auto post_proc_rhs = boost::make_shared<FEMethod>();
1214 auto post_proc_lhs = boost::make_shared<FEMethod>();
1215
1216 auto get_pre_proc_hook = [&]() {
1218 {});
1219 };
1220 pre_proc_rhs->preProcessHook = get_pre_proc_hook();
1221
1222 auto get_post_proc_hook_rhs = [this, post_proc_rhs]() {
1224
1226 post_proc_rhs, 1.)();
1228 };
1229
1230 auto get_post_proc_hook_lhs = [this, post_proc_lhs]() {
1232
1234 post_proc_lhs, 1.)();
1236 };
1237
1238 post_proc_rhs->postProcessHook = get_post_proc_hook_rhs;
1239 post_proc_lhs->postProcessHook = get_post_proc_hook_lhs;
1240
1241 ksp_ctx_ptr->getPreProcComputeRhs().push_front(pre_proc_rhs);
1242 ksp_ctx_ptr->getPostProcComputeRhs().push_back(post_proc_rhs);
1243 ksp_ctx_ptr->getPostProcSetOperators().push_back(post_proc_lhs);
1245 };
1246
1247 auto setup_and_solve = [&](auto solver) {
1249 BOOST_LOG_SCOPED_THREAD_ATTR("Timeline", attrs::timer());
1250 MOFEM_LOG("TIMER", Sev::noisy) << "KSPSetUp";
1251 CHKERR KSPSetUp(solver);
1252 MOFEM_LOG("TIMER", Sev::noisy) << "KSPSetUp <= Done";
1253 MOFEM_LOG("TIMER", Sev::noisy) << "KSPSolve";
1254 CHKERR KSPSolve(solver, f, d);
1255 MOFEM_LOG("TIMER", Sev::noisy) << "KSPSolve <= Done";
1257 };
1258
1259 MOFEM_LOG_CHANNEL("TIMER");
1260 MOFEM_LOG_TAG("TIMER", "timer");
1261
1262 CHKERR set_essential_bc();
1263 CHKERR setup_and_solve(kspElastic);
1264
1265 CHKERR VecGhostUpdateBegin(d, INSERT_VALUES, SCATTER_FORWARD);
1266 CHKERR VecGhostUpdateEnd(d, INSERT_VALUES, SCATTER_FORWARD);
1267 CHKERR DMoFEMMeshToLocalVector(dm, d, INSERT_VALUES, SCATTER_REVERSE);
1268
1269 auto evaluate_field_at_the_point = [&]() {
1271
1272 int coords_dim = 3;
1273 std::array<double, 3> field_eval_coords{0.0, 0.0, 0.0};
1274 PetscBool do_eval_field = PETSC_FALSE;
1275 CHKERR PetscOptionsGetRealArray(NULL, NULL, "-field_eval_coords",
1276 field_eval_coords.data(), &coords_dim,
1277 &do_eval_field);
1278
1279 if (do_eval_field) {
1280
1281 vectorFieldPtr = boost::make_shared<MatrixDouble>();
1282 auto field_eval_data =
1283 mField.getInterface<FieldEvaluatorInterface>()->getData<DomainEle>();
1284
1286 ->buildTree<SPACE_DIM>(field_eval_data, simple->getDomainFEName());
1287
1288 field_eval_data->setEvalPoints(field_eval_coords.data(), 1);
1289 auto no_rule = [](int, int, int) { return -1; };
1290 auto field_eval_fe_ptr = field_eval_data->feMethodPtr;
1291 field_eval_fe_ptr->getRuleHook = no_rule;
1292
1293 field_eval_fe_ptr->getOpPtrVector().push_back(
1295
1297 ->evalFEAtThePoint<SPACE_DIM>(
1298 field_eval_coords.data(), 1e-12, simple->getProblemName(),
1299 simple->getDomainFEName(), field_eval_data,
1301 QUIET);
1302
1303 if (vectorFieldPtr->size1()) {
1304 auto t_disp = getFTensor1FromMat<SPACE_DIM>(*vectorFieldPtr);
1305 if constexpr (SPACE_DIM == 2)
1306 MOFEM_LOG("FieldEvaluator", Sev::inform)
1307 << "U_X: " << t_disp(0) << " U_Y: " << t_disp(1);
1308 else
1309 MOFEM_LOG("FieldEvaluator", Sev::inform)
1310 << "U_X: " << t_disp(0) << " U_Y: " << t_disp(1)
1311 << " U_Z: " << t_disp(2);
1312 }
1313
1315 }
1317 };
1318
1319 CHKERR evaluate_field_at_the_point();
1320
1322}
1323//! [Solve]
1324
1325//! [Postprocess results]
1327 SmartPetscObj<Vec> adjoint_vector) {
1329 auto simple = mField.getInterface<Simple>();
1332 mField, simple->getDM(), simple->getDomainFEName(),
1333 "out_elastic_" + std::to_string(iter) + ".h5m",
1334 {{"ADJOINT", adjoint_vector}}, {}, Sev::noisy);
1336}
1337//! [Postprocess results]
1338
1339//! [calculateGradient]
1340
1342
1343template <int SPACE_DIM>
1345 DomainBaseOp> : public DomainBaseOp {
1346
1348
1350 boost::shared_ptr<HookeOps::CommonData> comm_ptr,
1351 boost::shared_ptr<MatrixDouble> jac,
1352 boost::shared_ptr<MatrixDouble> diff_jac,
1353 boost::shared_ptr<VectorDouble> cof_vals)
1354 : OP(field_name, field_name, DomainEleOp::OPROW), commPtr(comm_ptr),
1355 jac(jac), diffJac(diff_jac), cofVals(cof_vals) {}
1356
1357protected:
1358 boost::shared_ptr<HookeOps::CommonData> commPtr;
1359 boost::shared_ptr<MatrixDouble> jac;
1360 boost::shared_ptr<MatrixDouble> diffJac;
1361 boost::shared_ptr<VectorDouble> cofVals;
1363};
1364
1365template <int DIM> inline auto diff_symmetrize(FTensor::Number<DIM>) {
1366
1367 FTensor::Index<'i', DIM> i;
1368 FTensor::Index<'j', DIM> j;
1369 FTensor::Index<'k', DIM> k;
1370 FTensor::Index<'l', DIM> l;
1371
1373
1374 t_diff(i, j, k, l) = 0;
1375 t_diff(0, 0, 0, 0) = 1;
1376 t_diff(1, 1, 1, 1) = 1;
1377
1378 t_diff(1, 0, 1, 0) = 0.5;
1379 t_diff(1, 0, 0, 1) = 0.5;
1380
1381 t_diff(0, 1, 0, 1) = 0.5;
1382 t_diff(0, 1, 1, 0) = 0.5;
1383
1384 if constexpr (DIM == 3) {
1385 t_diff(2, 2, 2, 2) = 1;
1386
1387 t_diff(2, 0, 2, 0) = 0.5;
1388 t_diff(2, 0, 0, 2) = 0.5;
1389 t_diff(0, 2, 0, 2) = 0.5;
1390 t_diff(0, 2, 2, 0) = 0.5;
1391
1392 t_diff(2, 1, 2, 1) = 0.5;
1393 t_diff(2, 1, 1, 2) = 0.5;
1394 t_diff(1, 2, 1, 2) = 0.5;
1395 t_diff(1, 2, 2, 1) = 0.5;
1396 }
1397
1398 return t_diff;
1399};
1400
1401template <int SPACE_DIM>
1412
1413 auto t_diff_symm = diff_symmetrize(FTensor::Number<SPACE_DIM>());
1414
1415 // get element volume
1416 const double vol = OP::getMeasure();
1417 // get integration weights
1418 auto t_w = OP::getFTensor0IntegrationWeight();
1419 // get Jacobian values
1420 auto t_jac = getFTensor2FromMat<SPACE_DIM, SPACE_DIM>(*(jac));
1421 // get diff Jacobian values
1422 auto t_diff_jac = getFTensor2FromMat<SPACE_DIM, SPACE_DIM>(*(diffJac));
1423 // get cofactor values
1424 auto t_cof = getFTensor0FromVec(*(cofVals));
1425 // get base function gradient on rows
1426 auto t_row_grad = row_data.getFTensor1DiffN<SPACE_DIM>();
1427 // get fradient of the field
1428 auto t_grad_u =
1429 getFTensor2FromMat<SPACE_DIM, SPACE_DIM>(*(commPtr->matGradPtr));
1430 // get field gradient values
1431 auto t_cauchy_stress =
1432 getFTensor2SymmetricFromMat<SPACE_DIM>(*(commPtr->getMatCauchyStress()));
1433 // material stiffness tensor
1434 auto t_D =
1435 getFTensor4DdgFromMat<SPACE_DIM, SPACE_DIM, 0>(*(commPtr->matDPtr));
1436 // loop over integration points
1437 for (int gg = 0; gg != OP::nbIntegrationPts; gg++) {
1438 // take into account Jacobian
1439 const double alpha = t_w * vol;
1440
1441 auto t_det = determinantTensor(t_jac);
1443 CHKERR invertTensor(t_jac, t_det, t_inv_jac);
1444
1445 // Calculate the variation of the gradient due to geometry change
1447 t_diff_inv_jac(i, j) =
1448 -(t_inv_jac(i, I) * t_diff_jac(I, J)) * t_inv_jac(J, j);
1450 t_diff_grad(i, j) = t_grad_u(i, k) * t_diff_inv_jac(k, j);
1451
1452 // Calculate the variation of the strain tensor
1454 t_diff_strain(i, j) = t_diff_symm(i, j, k, l) * t_diff_grad(k, l);
1455
1456 // Calculate the variation of the stress tensor
1458 t_diff_stress(i, j) = t_D(i, j, k, l) * t_diff_strain(k, l);
1459
1460 // get rhs vector
1461 auto t_nf = OP::template getNf<SPACE_DIM>();
1462 // loop over rows base functions
1463 int rr = 0;
1464 for (; rr != OP::nbRows / SPACE_DIM; rr++) {
1465
1467 t_diff_row_grad(k) = t_row_grad(j) * t_diff_inv_jac(j, k);
1468
1469 // Variation due to change in geometry (diff base)
1470 t_nf(j) += alpha * t_diff_row_grad(i) * t_cauchy_stress(i, j);
1471
1472 // Variation due to change in domain (cofactor)
1473 t_nf(j) += (alpha * t_cof) * t_row_grad(i) * t_cauchy_stress(i, j);
1474
1475 // Variation due to change in stress (diff stress)
1476 t_nf(j) += alpha * t_row_grad(i) * t_diff_stress(i, j);
1477
1478 ++t_row_grad;
1479 ++t_nf;
1480 }
1481 for (; rr < OP::nbRowBaseFunctions; ++rr) {
1482 ++t_row_grad;
1483 }
1484
1485 ++t_grad_u;
1486 ++t_cauchy_stress;
1487 ++t_jac;
1488 ++t_diff_jac;
1489 ++t_cof;
1490 ++t_w; // move to another integration weight
1491 }
1493}
1494
1495//J = 1/2 * sigma : epsilon
1496// calculate dJ/du = (dJ/dsigma * dsigma/depsilon + dJ/depsilon) depsilon / dgrad_u * dgrad_u / du
1500 boost::shared_ptr<ObjectiveFunctionData> python_ptr,
1501 boost::shared_ptr<HookeOps::CommonData> comm_ptr,
1502 boost::shared_ptr<MatrixDouble> u_ptr)
1503 : OP(field_name, field_name, DomainEleOp::OPROW), pythonPtr(python_ptr),
1504 commPtr(comm_ptr), uPtr(u_ptr) {}
1505
1512
1513 constexpr auto symm_size = (SPACE_DIM * (SPACE_DIM + 1)) / 2;
1514 auto nb_gauss_pts = getGaussPts().size2();
1515
1516 auto objective_dstress =
1517 boost::make_shared<MatrixDouble>(nb_gauss_pts, symm_size);
1518 auto objective_dstrain =
1519 boost::make_shared<MatrixDouble>(nb_gauss_pts, symm_size);
1520 auto objective_du =
1521 boost::make_shared<MatrixDouble>(nb_gauss_pts, SPACE_DIM);
1522
1523 auto evaluate_python = [&]() {
1525 auto &coords = OP::getCoordsAtGaussPts();
1526 CHKERR pythonPtr->evalInteriorObjectiveGradientStress(
1527 coords, uPtr, commPtr->getMatCauchyStress(), commPtr->getMatStrain(),
1528 objective_dstress);
1529 CHKERR pythonPtr->evalInteriorObjectiveGradientStrain(
1530 coords, uPtr, commPtr->getMatCauchyStress(), commPtr->getMatStrain(),
1531 objective_dstrain);
1532 CHKERR pythonPtr->evalInteriorObjectiveGradientU(
1533 coords, uPtr, commPtr->getMatCauchyStress(), commPtr->getMatStrain(),
1534 objective_du);
1535
1536 auto vol = OP::getMeasure();
1537 auto t_w = OP::getFTensor0IntegrationWeight();
1538
1539 auto t_D =
1540 getFTensor4DdgFromMat<SPACE_DIM, SPACE_DIM, 0>(*(commPtr->matDPtr));
1541 auto t_row_grad = data.getFTensor1DiffN<SPACE_DIM>();
1542 auto t_row_base = data.getFTensor0N();
1543
1544 auto t_obj_dstress =
1545 getFTensor2SymmetricFromMat<SPACE_DIM>(*objective_dstress);
1546 auto t_obj_dstrain =
1547 getFTensor2SymmetricFromMat<SPACE_DIM>(*objective_dstrain);
1548 auto t_obj_du = getFTensor1FromMat<SPACE_DIM>(*objective_du);
1549
1550 for (int gg = 0; gg != nb_gauss_pts; ++gg) {
1551 const double alpha = t_w * vol;
1553 t_adjoint_stress(i, j) =
1554 t_D(i, j, k, l) * t_obj_dstress(k, l) + t_obj_dstrain(i, j);
1555
1556 auto t_nf = OP::template getNf<SPACE_DIM>();
1557 int rr = 0;
1558 for (; rr != OP::nbRows / SPACE_DIM; rr++) {
1559 t_nf(j) += alpha * t_row_grad(i) * t_adjoint_stress(i, j);
1560 t_nf(j) += alpha * t_row_base * t_obj_du(j);
1561
1562 ++t_row_grad;
1563 ++t_row_base;
1564 ++t_nf;
1565 }
1566
1567 for (; rr < OP::nbRowBaseFunctions; ++rr) {
1568 ++t_row_grad;
1569 ++t_row_base;
1570 }
1571 ++t_obj_dstrain;
1572 ++t_obj_dstress;
1573 ++t_obj_du;
1574 ++t_w;
1575 }
1577 };
1578 CHKERR evaluate_python();
1580 }
1581
1582private:
1583 boost::shared_ptr<ObjectiveFunctionData> pythonPtr;
1584 boost::shared_ptr<HookeOps::CommonData> commPtr;
1585 boost::shared_ptr<MatrixDouble> uPtr;
1586};
1589
1590 OpAdJointObjective(boost::shared_ptr<ObjectiveFunctionData> python_ptr,
1591 boost::shared_ptr<HookeOps::CommonData> comm_ptr,
1592 boost::shared_ptr<MatrixDouble> jac_ptr,
1593 boost::shared_ptr<MatrixDouble> diff_jac,
1594 boost::shared_ptr<VectorDouble> cof_vals,
1595 boost::shared_ptr<MatrixDouble> d_grad_ptr,
1596 boost::shared_ptr<MatrixDouble> d_u_ptr,
1597 boost::shared_ptr<MatrixDouble> u_ptr,
1598 boost::shared_ptr<double> glob_objective_ptr,
1599 boost::shared_ptr<double> glob_objective_grad_ptr)
1600 : OP(NOSPACE, OP::OPSPACE), pythonPtr(python_ptr), commPtr(comm_ptr),
1601 jacPtr(jac_ptr), diffJacPtr(diff_jac), cofVals(cof_vals),
1602 dGradPtr(d_grad_ptr), dUPtr(d_u_ptr), uPtr(u_ptr),
1603 globObjectivePtr(glob_objective_ptr),
1604 globObjectiveGradPtr(glob_objective_grad_ptr) {}
1605
1606 /**
1607 * @brief Compute objective function contributions at element level
1608 *
1609 * Evaluates Python objective function with current displacement and stress
1610 * state, and accumulates global objective value and gradients.
1611 */
1612 MoFEMErrorCode doWork(int side, EntityType type,
1615
1616 // Define tensor indices for calculations
1621
1624
1625 constexpr auto symm_size = (SPACE_DIM * (SPACE_DIM + 1)) /
1626 2; // size of symmetric tensor in Voigt notation
1627
1628 auto t_diff_symm = diff_symmetrize(
1630 SPACE_DIM>()); // fourth order tensor for symmetrization to convert from gradient to strain
1631
1632 auto nb_gauss_pts =
1633 getGaussPts().size2(); // number of gauss points in the element
1634 auto objective_ptr = boost::make_shared<MatrixDouble>(
1635 1, nb_gauss_pts); // objective function values at gauss points
1636 auto objective_dstress = boost::make_shared<MatrixDouble>(
1637 nb_gauss_pts,
1638 symm_size); // objective function gradient w.r.t. stress at gauss points
1639 auto objective_dstrain = boost::make_shared<MatrixDouble>(
1640 nb_gauss_pts,
1641 symm_size); // objective function gradient w.r.t. strain at gauss points
1642 auto objective_du =
1643 boost::make_shared<MatrixDouble>(nb_gauss_pts, SPACE_DIM);
1644 // We have dJ/dsigma and dJ/depsilon at gauss points, we need to convert it to dJ/du
1645
1646 auto evaluate_python = [&]() {
1648 auto &coords = OP::getCoordsAtGaussPts();
1649 CHKERR pythonPtr->evalInteriorObjectiveFunction(
1650 coords, uPtr, commPtr->getMatCauchyStress(), commPtr->getMatStrain(),
1651 objective_ptr);
1652 CHKERR pythonPtr->evalInteriorObjectiveGradientStress(
1653 coords, uPtr, commPtr->getMatCauchyStress(), commPtr->getMatStrain(),
1654 objective_dstress);
1655 CHKERR pythonPtr->evalInteriorObjectiveGradientStrain(
1656 coords, uPtr, commPtr->getMatCauchyStress(), commPtr->getMatStrain(),
1657 objective_dstrain);
1658 CHKERR pythonPtr->evalInteriorObjectiveGradientU(
1659 coords, uPtr, commPtr->getMatCauchyStress(), commPtr->getMatStrain(),
1660 objective_du);
1661
1662 auto t_grad_u =
1663 getFTensor2FromMat<SPACE_DIM, SPACE_DIM>(*(commPtr->matGradPtr));
1664 auto t_D =
1665 getFTensor4DdgFromMat<SPACE_DIM, SPACE_DIM, 0>(*(commPtr->matDPtr));
1666 auto t_jac = getFTensor2FromMat<SPACE_DIM, SPACE_DIM>(*(jacPtr));
1667 auto t_diff_jac = getFTensor2FromMat<SPACE_DIM, SPACE_DIM>(*(diffJacPtr));
1668 auto t_cof = getFTensor0FromVec(*(cofVals));
1669 auto t_d_grad = getFTensor2FromMat<SPACE_DIM, SPACE_DIM>(*(dGradPtr));
1670
1671 auto t_obj = getFTensor0FromMat(*objective_ptr);
1672 auto t_obj_dstress =
1673 getFTensor2SymmetricFromMat<SPACE_DIM>(*objective_dstress);
1674 auto t_obj_dstrain =
1675 getFTensor2SymmetricFromMat<SPACE_DIM>(*objective_dstrain);
1676 auto t_obj_du = getFTensor1FromMat<SPACE_DIM>(*objective_du);
1677 auto t_d_u = getFTensor1FromMat<SPACE_DIM>(*dUPtr);
1678
1679 auto vol = OP::getMeasure();
1680 auto t_w = getFTensor0IntegrationWeight();
1681 for (auto gg = 0; gg != nb_gauss_pts; ++gg) {
1682
1683 auto t_det = determinantTensor(t_jac);
1685 CHKERR invertTensor(t_jac, t_det, t_inv_jac);
1686
1688 t_diff_inv_jac(i, j) =
1689 -(t_inv_jac(i, I) * t_diff_jac(I, J)) * t_inv_jac(J, j);
1691 t_diff_grad(i, j) = t_grad_u(i, k) * t_diff_inv_jac(k, j);
1692
1694 t_d_strain(i, j) = t_diff_symm(i, j, k, l) * (
1695
1696 t_d_grad(k, l)
1697
1698 +
1699
1700 t_diff_grad(k, l)
1701
1702 );
1703
1704 auto alpha = t_w * vol;
1705
1706 (*globObjectivePtr) += alpha * t_obj;
1707 (*globObjectiveGradPtr) +=
1708 alpha *
1709 (
1710
1711 t_obj_dstress(i, j) * (t_D(i, j, k, l) * t_d_strain(k, l))
1712
1713 +
1714
1715 t_obj_dstrain(i, j) * t_d_strain(i, j)
1716
1717 +
1718
1719 t_obj_du(i) * t_d_u(i)
1720
1721 +
1722
1723 t_obj * t_cof
1724
1725 );
1726
1727 ++t_w;
1728 ++t_jac;
1729 ++t_diff_jac;
1730 ++t_cof;
1731
1732 ++t_obj;
1733 ++t_obj_dstress;
1734 ++t_obj_dstrain;
1735 ++t_obj_du;
1736
1737 ++t_grad_u;
1738 ++t_d_grad;
1739 ++t_d_u;
1740 }
1742 };
1743
1744 CHKERR evaluate_python();
1745
1747 }
1748
1749private:
1750 boost::shared_ptr<ObjectiveFunctionData> pythonPtr;
1751 boost::shared_ptr<HookeOps::CommonData> commPtr;
1752 boost::shared_ptr<MatrixDouble> jacPtr;
1753 boost::shared_ptr<MatrixDouble> diffJacPtr;
1754 boost::shared_ptr<VectorDouble> cofVals;
1755 boost::shared_ptr<MatrixDouble> dGradPtr;
1756 boost::shared_ptr<MatrixDouble> dUPtr;
1757 boost::shared_ptr<MatrixDouble> uPtr;
1758
1759 boost::shared_ptr<double> globObjectivePtr;
1760 boost::shared_ptr<double> globObjectiveGradPtr;
1761};
1762
1763MoFEMErrorCode Example::calculateGradient(PetscReal *objective_function_value,
1764 Vec objective_function_gradient,
1765 Vec adjoint_vector) {
1766 MOFEM_LOG_CHANNEL("WORLD");
1768 auto simple = mField.getInterface<Simple>();
1769
1770 auto ents = get_range_from_block(mField, "OPTIMISE", SPACE_DIM - 1);
1771
1772 auto get_essential_fe = [this]() {
1773 auto post_proc_rhs = boost::make_shared<FEMethod>();
1774 auto get_post_proc_hook_rhs = [this, post_proc_rhs]() {
1776
1778 post_proc_rhs, 0)();
1780 };
1781 post_proc_rhs->postProcessHook = get_post_proc_hook_rhs;
1782 return post_proc_rhs;
1783 };
1784
1785 auto get_fd_direvative_fe = [&]() {
1786 auto fe = boost::make_shared<DomainEle>(mField);
1787 fe->getRuleHook = [](int, int, int p_data) {
1788 return 2 * p_data + p_data - 1;
1789 };
1790 auto &pip = fe->getOpPtrVector();
1792 // Add RHS operators for internal forces
1793 HookeOps::opFactoryDomainRhs<SPACE_DIM, A, I, DomainEleOp>(
1794 mField, pip, "U", "MAT_ELASTIC", Sev::noisy);
1795
1796 return fe;
1797 };
1798
1799 auto calulate_fd_residual = [&](auto eps, auto diff_vec, auto fd_vec) {
1801
1802 constexpr bool debug = false;
1803
1804 auto geom_norm = [](MoFEM::Interface &mField) {
1806 auto field_blas = mField.getInterface<FieldBlas>();
1807 double nrm2 = 0.0;
1808 auto norm2_field = [&](const double val) {
1809 nrm2 += val * val;
1810 return val;
1811 };
1812 CHKERR field_blas->fieldLambdaOnValues(norm2_field, "GEOMETRY");
1813 MPI_Allreduce(MPI_IN_PLACE, &nrm2, 1, MPI_DOUBLE, MPI_SUM,
1814 mField.get_comm());
1815 MOFEM_LOG("WORLD", Sev::inform) << "Geometry norm: " << sqrt(nrm2);
1817 };
1818
1819 if constexpr (debug)
1820 CHKERR geom_norm(mField);
1821
1822 auto initial_current_geometry = createDMVector(adjointDM);
1823 CHKERR mField.getInterface<VecManager>()->setOtherLocalGhostVector(
1824 "ADJOINT", "ADJOINT_FIELD", "GEOMETRY", RowColData::ROW,
1825 initial_current_geometry, INSERT_VALUES, SCATTER_FORWARD);
1826 CHKERR VecAssemblyBegin(initial_current_geometry);
1827 CHKERR VecAssemblyEnd(initial_current_geometry);
1828
1829 if constexpr (debug)
1830 CHKERR geom_norm(mField);
1831
1832 auto perturb_geometry = [&](auto eps, auto diff_vec) {
1834 auto current_geometry = vectorDuplicate(initial_current_geometry);
1835 CHKERR VecCopy(initial_current_geometry, current_geometry);
1836 CHKERR VecAXPY(current_geometry, eps, diff_vec);
1837 CHKERR mField.getInterface<VecManager>()->setOtherLocalGhostVector(
1838 "ADJOINT", "ADJOINT_FIELD", "GEOMETRY", RowColData::ROW,
1839 current_geometry, INSERT_VALUES, SCATTER_REVERSE);
1841 };
1842
1843 auto fe = get_fd_direvative_fe();
1844 auto fp = vectorDuplicate(diff_vec);
1845 auto fm = vectorDuplicate(diff_vec);
1846 auto calc_impl = [&](auto f, auto eps) { // is this finite difference!
1848 CHKERR VecZeroEntries(f);
1849 fe->f = f;
1850 CHKERR perturb_geometry(eps, diff_vec);
1852 simple->getDomainFEName(), fe);
1853 CHKERR VecAssemblyBegin(f);
1854 CHKERR VecAssemblyEnd(f);
1855 CHKERR VecGhostUpdateBegin(f, ADD_VALUES, SCATTER_REVERSE);
1856 CHKERR VecGhostUpdateEnd(f, ADD_VALUES, SCATTER_REVERSE);
1857 auto post_proc_rhs = get_essential_fe();
1858 post_proc_rhs->f = f;
1860 post_proc_rhs.get());
1862 };
1863 CHKERR calc_impl(fp, eps);
1864 CHKERR calc_impl(fm, -eps);
1865 CHKERR VecWAXPY(fd_vec, -1.0, fm, fp);
1866 CHKERR VecScale(fd_vec, 1.0 / (2.0 * eps));
1867
1868 CHKERR mField.getInterface<VecManager>()->setOtherLocalGhostVector(
1869 "ADJOINT", "ADJOINT_FIELD", "GEOMETRY", RowColData::ROW,
1870 initial_current_geometry, INSERT_VALUES, SCATTER_REVERSE);
1871
1872 if constexpr (debug)
1873 CHKERR geom_norm(mField);
1874
1876 };
1877 // here starts the preperation for the derivative
1878 auto get_direvative_fe = [&](auto diff_vec) {
1879 auto fe_adjoint = boost::make_shared<DomainEle>(mField);
1880 fe_adjoint->getRuleHook = [](int, int, int p_data) {
1881 return 2 * p_data + p_data - 1;
1882 };
1883 auto &pip = fe_adjoint->getOpPtrVector();
1884
1885 auto jac_ptr = boost::make_shared<MatrixDouble>();
1886 auto det_ptr = boost::make_shared<VectorDouble>();
1887 auto inv_jac_ptr = boost::make_shared<MatrixDouble>();
1888 auto diff_jac_ptr = boost::make_shared<MatrixDouble>();
1889 auto cof_ptr = boost::make_shared<VectorDouble>();
1890
1891 using OpCoFactor =
1892 AdJoint<DomainEleOp>::Integration<GAUSS>::OpGetCoFactor<SPACE_DIM>;
1893
1895
1897 "GEOMETRY", jac_ptr));
1899 "U", diff_jac_ptr, diff_vec));
1900
1901 pip.push_back(new OpCoFactor(jac_ptr, diff_jac_ptr, cof_ptr));
1902
1903 // Add RHS operators for internal forces
1904 auto common_ptr = HookeOps::commonDataFactory<SPACE_DIM, I, DomainEleOp>(
1905 mField, pip, "U", "MAT_ELASTIC", Sev::noisy);
1907 "U", common_ptr, jac_ptr, diff_jac_ptr, cof_ptr));
1908
1909 return fe_adjoint;
1910 };
1911
1912 auto get_objective_fe = [&](auto diff_vec, auto grad_vec,
1913 auto glob_objective_ptr,
1914 auto glob_objective_grad_ptr) {
1915 auto fe_adjoint = boost::make_shared<DomainEle>(mField);
1916 fe_adjoint->getRuleHook = [](int, int, int p_data) {
1917 return 2 * p_data + p_data - 1;
1918 };
1919 auto &pip = fe_adjoint->getOpPtrVector();
1921
1922 auto jac_ptr = boost::make_shared<MatrixDouble>();
1923 auto det_ptr = boost::make_shared<VectorDouble>();
1924 auto inv_jac_ptr = boost::make_shared<MatrixDouble>();
1925 auto diff_jac_ptr = boost::make_shared<MatrixDouble>();
1926 auto cof_ptr = boost::make_shared<VectorDouble>();
1927 auto d_grad_ptr = boost::make_shared<MatrixDouble>();
1928 auto d_u_ptr = boost::make_shared<MatrixDouble>();
1929 auto u_ptr = boost::make_shared<MatrixDouble>();
1930
1931 using OpCoFactor =
1932 AdJoint<DomainEleOp>::Integration<GAUSS>::OpGetCoFactor<SPACE_DIM>;
1934 "GEOMETRY", jac_ptr));
1936 "U", diff_jac_ptr,
1937 diff_vec)); // Note: that vector is stored on displacemnt vector, that
1938 // why is used here
1939 pip.push_back(new OpCoFactor(jac_ptr, diff_jac_ptr, cof_ptr));
1941 "U", d_grad_ptr, grad_vec));
1942 pip.push_back(
1943 new OpCalculateVectorFieldValues<SPACE_DIM>("U", d_u_ptr, grad_vec));
1944 pip.push_back(new OpCalculateVectorFieldValues<SPACE_DIM>("U", u_ptr));
1945
1946 auto common_ptr = HookeOps::commonDataFactory<SPACE_DIM, I, DomainEleOp>(
1947 mField, pip, "U", "MAT_ELASTIC", Sev::noisy);
1948 pip.push_back(new OpAdJointObjective(
1949 pythonPtr, common_ptr, jac_ptr, diff_jac_ptr, cof_ptr, d_grad_ptr,
1950 d_u_ptr, u_ptr, glob_objective_ptr, glob_objective_grad_ptr));
1951
1952 return fe_adjoint;
1953 };
1954
1955 auto dm = simple->getDM();
1956 auto f = createDMVector(dm);
1957 auto d = vectorDuplicate(f);
1958 auto dm_diff_vec = vectorDuplicate(d);
1959 auto zero_diff_vec = vectorDuplicate(dm_diff_vec);
1960 auto zero_state_sensitivity = vectorDuplicate(d);
1961 CHKERR VecZeroEntries(zero_diff_vec);
1962 CHKERR VecZeroEntries(zero_state_sensitivity);
1963 CHKERR VecGhostUpdateBegin(zero_diff_vec, INSERT_VALUES, SCATTER_FORWARD);
1964 CHKERR VecGhostUpdateEnd(zero_diff_vec, INSERT_VALUES, SCATTER_FORWARD);
1965 CHKERR VecGhostUpdateBegin(zero_state_sensitivity, INSERT_VALUES,
1966 SCATTER_FORWARD);
1967 CHKERR VecGhostUpdateEnd(zero_state_sensitivity, INSERT_VALUES,
1968 SCATTER_FORWARD);
1969
1970 auto adjoint_fe = get_direvative_fe(dm_diff_vec);
1971 auto objective_ptr_direct = boost::make_shared<double>(0.0);
1972 auto objective_grad_ptr_direct = boost::make_shared<double>(0.0);
1973 auto objective_fe_direct = get_objective_fe(
1974 dm_diff_vec, d, objective_ptr_direct, objective_grad_ptr_direct);
1975 auto objective_ptr_explicit = boost::make_shared<double>(0.0);
1976 auto objective_grad_ptr_explicit = boost::make_shared<double>(0.0);
1977 auto objective_fe_explicit =
1978 get_objective_fe(dm_diff_vec, zero_state_sensitivity,
1979 objective_ptr_explicit, objective_grad_ptr_explicit);
1980 auto objective_ptr_value = boost::make_shared<double>(0.0);
1981 auto objective_grad_ptr_value = boost::make_shared<double>(0.0);
1982 auto objective_fe_value =
1983 get_objective_fe(zero_diff_vec, zero_state_sensitivity,
1984 objective_ptr_value, objective_grad_ptr_value);
1985
1986 auto set_variance_of_geometry =
1987 [&](auto mode, auto mod_vec) { // think of mod_vec as X(tau) = X + tau*v_h
1988 // take the mod_vec and write it into the adjoint DM then copy that
1989 // field to dm_diff_vec using the same layout as U
1991 CHKERR DMoFEMMeshToLocalVector(adjointDM, mod_vec, INSERT_VALUES,
1992 SCATTER_REVERSE);
1993 CHKERR mField.getInterface<VecManager>()->setOtherLocalGhostVector(
1994 simple->getProblemName(), "U", "ADJOINT_FIELD", RowColData::ROW,
1995 dm_diff_vec, INSERT_VALUES, SCATTER_FORWARD);
1996 CHKERR VecGhostUpdateBegin(dm_diff_vec, INSERT_VALUES, SCATTER_FORWARD);
1997 CHKERR VecGhostUpdateEnd(dm_diff_vec, INSERT_VALUES, SCATTER_FORWARD);
1999 };
2000
2001 auto calculate_variance_internal_forces = [&](auto mode, auto mod_vec) {
2003 CHKERR VecZeroEntries(f);
2004 CHKERR VecGhostUpdateBegin(f, INSERT_VALUES, SCATTER_FORWARD);
2005 CHKERR VecGhostUpdateEnd(f, INSERT_VALUES, SCATTER_FORWARD);
2006 adjoint_fe->f = f;
2007 CHKERR DMoFEMLoopFiniteElements(dm, simple->getDomainFEName(), adjoint_fe);
2008 CHKERR VecAssemblyBegin(f);
2009 CHKERR VecAssemblyEnd(f);
2010 CHKERR VecGhostUpdateBegin(f, ADD_VALUES, SCATTER_REVERSE);
2011 CHKERR VecGhostUpdateEnd(f, ADD_VALUES, SCATTER_REVERSE);
2012 auto post_proc_rhs = get_essential_fe();
2013 post_proc_rhs->f = f;
2015 post_proc_rhs.get());
2016 CHKERR VecScale(f, -1.0);
2017
2018#ifndef NDEBUG
2019 constexpr bool debug = true;
2020 if constexpr (debug) {
2021 double norm0;
2022 CHKERR VecNorm(f, NORM_2, &norm0);
2023 auto fd_check = vectorDuplicate(f);
2024 double eps = 1e-5;
2025 CHKERR calulate_fd_residual(eps, dm_diff_vec, fd_check);
2026 double nrm;
2027 CHKERR VecAXPY(fd_check, -1.0, f);
2028 CHKERR VecNorm(fd_check, NORM_2, &nrm);
2029 MOFEM_LOG("WORLD", Sev::inform)
2030 << " FD check for internal forces [ " << mode << " ]: " << nrm
2031 << " / " << norm0 << " ( " << (nrm / norm0) << " )";
2032 }
2033#endif
2034
2036 };
2037
2038 auto calculate_variance_of_displacement = [&](auto mode, auto mod_vec) {
2040 CHKERR KSPSolve(kspElastic, f, d);
2041
2042 CHKERR VecGhostUpdateBegin(d, INSERT_VALUES, SCATTER_FORWARD);
2043 CHKERR VecGhostUpdateEnd(d, INSERT_VALUES, SCATTER_FORWARD);
2045 };
2046
2047 auto evaluate_objective_terms =
2048 [&](auto objective_fe, auto objective_ptr, auto objective_grad_ptr,
2049 double &objective_value, double &objective_gradient) {
2051 *objective_ptr = 0.0;
2052 *objective_grad_ptr = 0.0;
2053 CHKERR DMoFEMLoopFiniteElements(dm, simple->getDomainFEName(),
2054 objective_fe);
2055 std::array<double, 2> array = {*objective_ptr, *objective_grad_ptr};
2056 MPI_Allreduce(MPI_IN_PLACE, array.data(), 2, MPI_DOUBLE, MPI_SUM,
2057 mField.get_comm());
2058 objective_value = array[0];
2059 objective_gradient = array[1];
2061 };
2062
2063 auto calculate_objective_value = [&]() {
2065 double objective_value = 0;
2066 double objective_gradient = 0;
2067 CHKERR evaluate_objective_terms(objective_fe_value, objective_ptr_value,
2068 objective_grad_ptr_value, objective_value,
2069 objective_gradient);
2070 *objective_function_value = objective_value;
2072 };
2073
2074 auto calculate_variance_of_objective_function_dJ_du = [&](Vec dJ_du) {
2076
2077 auto fe = boost::make_shared<DomainEle>(mField);
2078 fe->getRuleHook = [](int, int, int p_data) {
2079 return 2 * p_data + p_data - 1;
2080 };
2081 auto &pip = fe->getOpPtrVector();
2083
2084 auto u_ptr = boost::make_shared<MatrixDouble>();
2085 pip.push_back(new OpCalculateVectorFieldValues<SPACE_DIM>("U", u_ptr));
2086
2087 auto common_ptr = HookeOps::commonDataFactory<SPACE_DIM, I, DomainEleOp>(
2088 mField, pip, "U", "MAT_ELASTIC", Sev::noisy);
2089 pip.push_back(new OpStateSensitivity("U", pythonPtr, common_ptr, u_ptr));
2090
2091 CHKERR VecZeroEntries(dJ_du);
2092 fe->f = dJ_du;
2093 CHKERR DMoFEMLoopFiniteElements(dm, simple->getDomainFEName(), fe);
2094 CHKERR VecAssemblyBegin(dJ_du);
2095 CHKERR VecAssemblyEnd(dJ_du);
2096
2097 auto post_proc_rhs = get_essential_fe();
2098 post_proc_rhs->f = dJ_du;
2100 post_proc_rhs.get());
2101
2103 };
2104
2105 auto calculate_adjoint_lambda = [&](auto lambda, auto dJ_du) {
2107
2108 MOFEM_LOG("WORLD", Sev::inform) << "Solving for adjoint variable lambda";
2109 CHKERR VecZeroEntries(lambda);
2110 CHKERR KSPSolveTranspose(kspElastic, dJ_du, lambda);
2111 CHKERR VecGhostUpdateBegin(lambda, INSERT_VALUES, SCATTER_FORWARD);
2112 CHKERR VecGhostUpdateEnd(lambda, INSERT_VALUES, SCATTER_FORWARD);
2114 };
2115
2116 CHKERR VecZeroEntries(objective_function_gradient);
2117 CHKERR VecZeroEntries(adjoint_vector);
2118
2119 CHKERR calculate_objective_value();
2120 MOFEM_LOG("WORLD", Sev::verbose)
2121 << "Objective function: " << *objective_function_value;
2122
2123 auto direct = [&]() {
2125 int mode = 0;
2126 for (auto mod_vec : modeVecs) {
2127 CHKERR set_variance_of_geometry(mode, mod_vec);
2128 CHKERR calculate_variance_internal_forces(mode, mod_vec);
2129 CHKERR calculate_variance_of_displacement(mode, mod_vec);
2130 double objective_value = 0;
2131 double objective_gradient = 0;
2132 CHKERR evaluate_objective_terms(objective_fe_direct, objective_ptr_direct,
2133 objective_grad_ptr_direct,
2134 objective_value, objective_gradient);
2135 CHKERR VecSetValue(objective_function_gradient, mode, objective_gradient,
2136 INSERT_VALUES);
2137 CHKERR VecAXPY(adjoint_vector, objective_gradient, dm_diff_vec);
2138 ++mode;
2139 }
2141 };
2142
2143 auto lambda = vectorDuplicate(f);
2144 auto dJ_du = vectorDuplicate(f);
2145
2146 auto adjoint = [&]() {
2148
2149 CHKERR calculate_variance_of_objective_function_dJ_du(dJ_du);
2150 CHKERR calculate_adjoint_lambda(lambda, dJ_du);
2151
2152 int mode = 0;
2153
2154 for (auto mod_vec : modeVecs) {
2155
2156 CHKERR set_variance_of_geometry(mode, mod_vec);
2157 CHKERR calculate_variance_internal_forces(mode, mod_vec);
2158 double objective_value = 0;
2159 double objective_gradient_explicit = 0;
2160 CHKERR evaluate_objective_terms(
2161 objective_fe_explicit, objective_ptr_explicit,
2162 objective_grad_ptr_explicit, objective_value,
2163 objective_gradient_explicit);
2164 double lambda_dot_residual_variation = 0;
2165 CHKERR VecDot(lambda, f, &lambda_dot_residual_variation);
2166 const double dJ_dp =
2167 objective_gradient_explicit + lambda_dot_residual_variation;
2168 CHKERR VecSetValue(objective_function_gradient, mode, dJ_dp,
2169 INSERT_VALUES);
2170 CHKERR VecAXPY(adjoint_vector, dJ_dp, dm_diff_vec);
2171 ++mode;
2172 }
2173 CHKERR VecAssemblyBegin(objective_function_gradient);
2174 CHKERR VecAssemblyEnd(objective_function_gradient);
2176 };
2177
2178 switch (derivative_type) {
2179 case DIRECT:
2180 MOFEM_LOG("WORLD", Sev::inform) << "Running Direct Sensitivity...";
2181 CHKERR direct();
2182 break;
2183
2184 case ADJOINT:
2185 MOFEM_LOG("WORLD", Sev::inform) << "Running Adjoint Sensitivity...";
2186 CHKERR adjoint();
2187 break;
2188
2189 default:
2190 SETERRQ(PETSC_COMM_WORLD, MOFEM_DATA_INCONSISTENCY,
2191 "Wrong sensitivity type selected");
2192 }
2193
2194 CHKERR VecAssemblyBegin(objective_function_gradient);
2195 CHKERR VecAssemblyEnd(objective_function_gradient);
2196
2197 CHKERR VecAssemblyBegin(adjoint_vector);
2198 CHKERR VecAssemblyEnd(adjoint_vector);
2199 CHKERR VecGhostUpdateBegin(adjoint_vector, INSERT_VALUES, SCATTER_FORWARD);
2200 CHKERR VecGhostUpdateEnd(adjoint_vector, INSERT_VALUES, SCATTER_FORWARD);
2201
2203}
2204//! [calculateGradient]
2205
2206static char help[] = "...\n\n";
2207
2208/**
2209 * @brief Main function for topology optimization tutorial using adjoint method
2210 *
2211 * This tutorial demonstrates structural topology optimization using:
2212 * - MoFEM finite element library for elasticity analysis
2213 * - Adjoint method for efficient gradient computation
2214 * - TAO optimization library for design optimization
2215 * - Python interface for flexible objective function definition
2216 *
2217 * Workflow:
2218 * 1. Initialize MoFEM, PETSc and Python environments
2219 * 2. Read mesh and setup finite element problem
2220 * 3. Define objective function via Python interface
2221 * 4. Compute topology optimization modes as design variables
2222 * 5. Run gradient-based optimization using adjoint sensitivities
2223 * 6. Post-process optimized design
2224 *
2225 * The adjoint method enables efficient gradient computation with cost
2226 * independent of the number of design variables, making it suitable
2227 * for large-scale topology optimization problems.
2228 *
2229 * Required input files:
2230 * - Mesh file (.h5m format from CUBIT)
2231 * - Parameter file (param_file.petsc)
2232 * - Objective function (objective_function.py)
2233 *
2234 * @param argc Command line argument count
2235 * @param argv Command line argument values
2236 * @return int Exit code (0 for success)
2237 */
2238int main(int argc, char *argv[]) {
2239
2240 // Initialize Python environment for objective function interface
2241 Py_Initialize();
2242 np::initialize();
2243
2244 // Initialize MoFEM/PETSc and MOAB data structures
2245 const char param_file[] = "param_file.petsc";
2246 MoFEM::Core::Initialize(&argc, &argv, param_file, help);
2247
2248 auto core_log = logging::core::get();
2249 core_log->add_sink(
2251
2252 core_log->add_sink(
2253 LogManager::createSink(LogManager::getStrmSync(), "FieldEvaluator"));
2254 LogManager::setLog("FieldEvaluator");
2255 MOFEM_LOG_TAG("FieldEvaluator", "field_eval");
2256
2257 try {
2258
2259 //! [Register MoFEM discrete manager in PETSc]
2260 DMType dm_name = "DMMOFEM";
2261 CHKERR DMRegister_MoFEM(dm_name);
2262 DMType dm_name_mg = "DMMOFEM_MG";
2264 //! [Register MoFEM discrete manager in PETSc
2265
2266 //! [Create MoAB]
2267 moab::Core mb_instance; ///< mesh database
2268 moab::Interface &moab = mb_instance; ///< mesh database interface
2269 //! [Create MoAB]
2270
2271 //! [Create MoFEM]
2272 MoFEM::Core core(moab); ///< finite element database
2273 MoFEM::Interface &m_field = core; ///< finite element database interface
2274 //! [Create MoFEM]
2275
2276 //! [Example]
2277
2278 Example ex(m_field);
2279 CHKERR ex.runProblem();
2280 //! [Example]
2281 }
2283
2285
2286 if (Py_FinalizeEx() < 0) {
2287 exit(120);
2288 }
2289}
2290
2292 const std::string block_name, int dim) {
2293 Range r;
2294
2295 auto mesh_mng = m_field.getInterface<MeshsetsManager>();
2296 auto bcs = mesh_mng->getCubitMeshsetPtr(
2297
2298 std::regex((boost::format("%s(.*)") % block_name).str())
2299
2300 );
2301
2302 for (auto bc : bcs) {
2303 Range faces;
2304 CHK_MOAB_THROW(bc->getMeshsetIdEntitiesByDimension(m_field.get_moab(), dim,
2305 faces, true),
2306 "get meshset ents");
2307 r.merge(faces);
2308 }
2309
2310 for (auto dd = dim - 1; dd >= 0; --dd) {
2311 if (dd >= 0) {
2312 Range ents;
2313 CHK_MOAB_THROW(m_field.get_moab().get_adjacencies(r, dd, false, ents,
2314 moab::Interface::UNION),
2315 "get adjs");
2316 r.merge(ents);
2317 } else {
2318 Range verts;
2319 CHK_MOAB_THROW(m_field.get_moab().get_connectivity(r, verts),
2320 "get verts");
2321 r.merge(verts);
2322 }
2324 m_field.getInterface<CommInterface>()->synchroniseEntities(r), "comm");
2325 }
2326
2327 return r;
2328};
2329
2330MoFEMErrorCode save_range(moab::Interface &moab, const std::string name,
2331 const Range r) {
2333 auto out_meshset = get_temp_meshset_ptr(moab);
2334 CHKERR moab.add_entities(*out_meshset, r);
2335 CHKERR moab.write_file(name.c_str(), "VTK", "", out_meshset->get_ptr(), 1);
2337};
std::string type
#define MOFEM_LOG_SYNCHRONISE(comm)
Synchronise "SYNC" channel.
Interface for Python-based objective function evaluation in topology optimization.
#define FTENSOR_INDEX(DIM, I)
void simple(double P1[], double P2[], double P3[], double c[], const int N)
Definition acoustic.cpp:69
auto diff_symmetrize(FTensor::Number< DIM >)
Definition adjoint.cpp:1365
static char help[]
[calculateGradient]
Definition adjoint.cpp:2206
PetscBool is_plane_strain
Definition adjoint.cpp:46
PipelineManager::ElementsAndOpsByDim< SPACE_DIM >::DomainEle DomainEle
Domain finite elements.
Definition adjoint.cpp:54
constexpr int SPACE_DIM
[Define dimension]
Definition adjoint.cpp:28
SensitivityMethod derivative_type
Definition adjoint.cpp:109
constexpr double poisson_ratio
Poisson's ratio ν
Definition adjoint.cpp:39
constexpr int BASE_DIM
[Constants and material properties]
Definition adjoint.cpp:25
constexpr double shear_modulus_G
Shear modulus G = E/(2(1+ν))
Definition adjoint.cpp:43
constexpr IntegrationType I
Use Gauss quadrature for integration.
Definition adjoint.cpp:34
constexpr double bulk_modulus_K
Bulk modulus K = E/(3(1-2ν))
Definition adjoint.cpp:40
SensitivityMethod
Definition adjoint.cpp:107
@ DIRECT
Definition adjoint.cpp:107
@ ADJOINT
Definition adjoint.cpp:107
constexpr AssemblyType A
[Define dimension]
Definition adjoint.cpp:32
PipelineManager::ElementsAndOpsByDim< SPACE_DIM >::BoundaryEle BoundaryEle
Boundary finite elements.
Definition adjoint.cpp:56
Range get_range_from_block(MoFEM::Interface &m_field, const std::string block_name, int dim)
Definition adjoint.cpp:2291
constexpr double young_modulus
[Material properties for linear elasticity]
Definition adjoint.cpp:38
int main()
constexpr double a
constexpr int SPACE_DIM
ElementsAndOps< SPACE_DIM >::DomainEle DomainEle
ElementsAndOps< SPACE_DIM >::BoundaryEle BoundaryEle
@ QUIET
@ ROW
#define CATCH_ERRORS
Catch errors.
@ MF_EXIST
FieldApproximationBase
approximation base
Definition definitions.h:58
@ LASTBASE
Definition definitions.h:69
@ AINSWORTH_LEGENDRE_BASE
Ainsworth Cole (Legendre) approx. base .
Definition definitions.h:60
@ DEMKOWICZ_JACOBI_BASE
Definition definitions.h:66
#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()
@ H1
continuous field
Definition definitions.h:85
@ NOSPACE
Definition definitions.h:83
#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.
@ MOFEM_NOT_FOUND
Definition definitions.h:33
@ 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.
#define MoFEMFunctionBeginHot
First executable line of each MoFEM function, used for error handling. Final line of MoFEM functions ...
PostProcEleByDim< SPACE_DIM >::PostProcEleDomain PostProcEleDomain
PostProcEleByDim< SPACE_DIM >::PostProcEleBdy PostProcEleBdy
@ F
auto integration_rule
auto diff_symmetrize(FTensor::Number< DIM >)
Definition gradient.cpp:706
constexpr int SPACE_DIM
[Define dimension]
Definition gradient.cpp:19
constexpr IntegrationType I
Use Gauss quadrature for integration.
Definition gradient.cpp:25
FormsIntegrators< DomainEleOp >::Assembly< A >::OpBase DomainBaseOp
[Postprocess results]
Definition gradient.cpp:703
PetscErrorCode DMMoFEMSetIsPartitioned(DM dm, PetscBool is_partitioned)
Definition DMMoFEM.cpp:1113
PetscErrorCode DMMoFEMCreateSubDM(DM subdm, DM dm, const char problem_name[])
Must be called by user to set Sub DM MoFEM data structures.
Definition DMMoFEM.cpp:215
PetscErrorCode DMMoFEMAddElement(DM dm, std::string fe_name)
add element to dm
Definition DMMoFEM.cpp:488
PetscErrorCode DMMoFEMSetSquareProblem(DM dm, PetscBool square_problem)
set squared problem
Definition DMMoFEM.cpp:450
PetscErrorCode DMMoFEMCreateMoFEM(DM dm, MoFEM::Interface *m_field_ptr, const char problem_name[], const MoFEM::BitRefLevel bit_level, const MoFEM::BitRefLevel bit_mask=MoFEM::BitRefLevel().set())
Must be called by user to set MoFEM data structures.
Definition DMMoFEM.cpp:114
PetscErrorCode DMoFEMPostProcessFiniteElements(DM dm, MoFEM::FEMethod *method)
execute finite element method for each element in dm (problem)
Definition DMMoFEM.cpp:546
PetscErrorCode DMMoFEMAddSubFieldRow(DM dm, const char field_name[])
Definition DMMoFEM.cpp:238
PetscErrorCode DMoFEMMeshToLocalVector(DM dm, Vec l, InsertMode mode, ScatterMode scatter_mode, RowColData rc=RowColData::COL)
set local (or ghosted) vector values on mesh for partition only
Definition DMMoFEM.cpp:514
PetscErrorCode DMRegister_MoFEM(const char sname[])
Register MoFEM problem.
Definition DMMoFEM.cpp:43
MoFEMErrorCode DMRegister_MGViaApproxOrders(const char sname[])
Register DM for Multi-Grid via approximation orders.
PetscErrorCode DMoFEMLoopFiniteElements(DM dm, const char fe_name[], MoFEM::FEMethod *method, CacheTupleWeakPtr cache_ptr=CacheTupleSharedPtr())
Executes FEMethod for finite elements in DM.
Definition DMMoFEM.cpp:576
auto createDMVector(DM dm, RowColData rc=RowColData::COL)
Get smart vector from DM.
Definition DMMoFEM.hpp:1237
PetscErrorCode DMMoFEMAddSubFieldCol(DM dm, const char field_name[])
Definition DMMoFEM.cpp:280
auto createDMMatrix(DM dm)
Get smart matrix from DM.
Definition DMMoFEM.hpp:1194
PetscErrorCode DMoFEMPreProcessFiniteElements(DM dm, MoFEM::FEMethod *method)
execute finite element method for each element in dm (problem)
Definition DMMoFEM.cpp:536
virtual MoFEMErrorCode add_ents_to_finite_element_by_dim(const EntityHandle entities, const int dim, const std::string name, const bool recursive=true)=0
add entities to finite element
virtual MoFEMErrorCode add_finite_element(const std::string &fe_name, enum MoFEMTypes bh=MF_EXCL, int verb=DEFAULT_VERBOSITY)=0
add finite element
virtual MoFEMErrorCode build_finite_elements(int verb=DEFAULT_VERBOSITY)=0
Build finite elements.
virtual MoFEMErrorCode modify_finite_element_add_field_col(const std::string &fe_name, const std::string name_row)=0
set field col which finite element use
virtual MoFEMErrorCode modify_finite_element_add_field_row(const std::string &fe_name, const std::string name_row)=0
set field row which finite element use
virtual MoFEMErrorCode modify_finite_element_add_field_data(const std::string &fe_name, const std::string name_field)=0
set finite element field data
virtual MoFEMErrorCode build_fields(int verb=DEFAULT_VERBOSITY)=0
virtual MoFEMErrorCode add_ents_to_field_by_dim(const Range &ents, const int dim, const std::string &name, int verb=DEFAULT_VERBOSITY)=0
Add entities to field meshset.
virtual MoFEMErrorCode set_field_order(const EntityHandle meshset, const EntityType type, const std::string &name, const ApproximationOrder order, int verb=DEFAULT_VERBOSITY)=0
Set order approximation of the entities in the field.
IntegrationType
Form integrator integration types.
AssemblyType
[Storage and set boundary conditions]
@ GAUSS
Gaussian quadrature integration.
static LoggerType & setLog(const std::string channel)
Set ans resset chanel logger.
#define MOFEM_LOG(channel, severity)
Log.
#define MOFEM_LOG_TAG(channel, tag)
Tag channel.
#define MOFEM_LOG_CHANNEL(channel)
Set and reset channel.
virtual MoFEMErrorCode loop_dofs(const Problem *problem_ptr, const std::string &field_name, RowColData rc, DofMethod &method, int lower_rank, int upper_rank, int verb=DEFAULT_VERBOSITY)=0
Make a loop over dofs.
MoFEMErrorCode getCubitMeshsetPtr(const int ms_id, const CubitBCType cubit_bc_type, const CubitMeshSets **cubit_meshset_ptr) const
get cubit meshset
FTensor::Index< 'i', SPACE_DIM > i
static double lambda
const double c
speed of light (cm/ns)
const double v
phase velocity of light in medium (cm/ns)
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
const FTensor::Tensor2< T, Dim, Dim > Vec
MoFEMErrorCode postProcessElasticResults(MoFEM::Interface &mField, SmartPetscObj< DM > dm, const std::string &domain_fe_name, const std::string &out_file_name, std::vector< std::pair< std::string, SmartPetscObj< Vec > > > extra_vectors={}, const std::vector< std::string > &tags_to_transfer={}, const Sev hooke_ops_sev=Sev::verbose)
const double eps
Definition HenckyOps.hpp:13
PetscErrorCode MoFEMErrorCode
MoFEM/PETSc error code.
implementation of Data Operators for Forces and Sources
Definition Common.hpp:10
auto createKSP(MPI_Comm comm)
PetscErrorCode DMMoFEMSetDestroyProblem(DM dm, PetscBool destroy_problem)
Definition DMMoFEM.cpp:434
PetscErrorCode PetscOptionsGetInt(PetscOptions *, const char pre[], const char name[], PetscInt *ivalue, PetscBool *set)
static const bool debug
PetscErrorCode PetscOptionsGetBool(PetscOptions *, const char pre[], const char name[], PetscBool *bval, PetscBool *set)
SmartPetscObj< Vec > vectorDuplicate(Vec vec)
Create duplicate vector of smart vector.
PetscErrorCode PetscOptionsGetRealArray(PetscOptions *, const char pre[], const char name[], PetscReal dval[], PetscInt *nmax, PetscBool *set)
auto createVectorMPI(MPI_Comm comm, PetscInt n, PetscInt N)
Create MPI Vector.
auto getDMKspCtx(DM dm)
Get KSP context data structure used by DM.
Definition DMMoFEM.hpp:1251
static MoFEMErrorCode invertTensor(FTensor::Tensor2< T1, DIM, DIM > &t, T2 &det, FTensor::Tensor2< T3, DIM, DIM > &inv_t)
static auto determinantTensor(FTensor::Tensor2< T, DIM, DIM > &t)
Calculate the determinant of a tensor of rank DIM.
PetscErrorCode PetscOptionsGetEList(PetscOptions *, const char pre[], const char name[], const char *const *list, PetscInt next, PetscInt *value, PetscBool *set)
PetscErrorCode PetscOptionsGetString(PetscOptions *, const char pre[], const char name[], char str[], size_t size, PetscBool *set)
auto getFTensor0FromMat(M &data)
Get tensor rank 0 (scalar) form data vector.
auto get_temp_meshset_ptr(moab::Interface &moab)
Create smart pointer to temporary meshset.
PetscErrorCode TaoSetObjectiveAndGradient(Tao tao, Vec x, PetscReal *f, Vec g, void *ctx)
Sets the objective function value and gradient for a TAO optimization solver.
Definition TaoCtx.cpp:178
static auto getFTensor0FromVec(V &data)
Get tensor rank 0 (scalar) form data vector.
auto createDM(MPI_Comm comm, const std::string dm_type_name)
Creates smart DM object.
MoFEMErrorCode VecSetValues(Vec V, const EntitiesFieldData::EntData &data, const double *ptr, InsertMode iora)
Assemble PETSc vector.
auto createTao(MPI_Comm comm)
boost::shared_ptr< ObjectiveFunctionData > create_python_objective_function(std::string)
constexpr IntegrationType I
constexpr AssemblyType A
OpPostProcMapInMoab< SPACE_DIM, SPACE_DIM > OpPPMap
constexpr auto field_name
static constexpr int approx_order
FormsIntegrators< DomainEleOp >::Assembly< PETSC >::BiLinearForm< GAUSS >::OpMass< 1, SPACE_DIM > OpMass
[Only used with Hooke equation (linear material model)]
Definition seepage.cpp:56
PetscBool is_plane_strain
Definition seepage.cpp:177
constexpr double g
FTensor::Index< 'm', 3 > m
Boundary conditions marker.
Definition elastic.cpp:39
[Define entities]
Definition elastic.cpp:38
[Example]
Definition plastic.cpp:217
MoFEMErrorCode boundaryCondition()
Apply essential boundary conditions.
MoFEMErrorCode assembleSystem()
Setup operators in finite element pipeline.
MoFEMErrorCode readMesh()
Read mesh from file and setup meshsets.
boost::shared_ptr< ObjectiveFunctionData > pythonPtr
Interface to Python objective function.
Definition adjoint.cpp:170
std::vector< SmartPetscObj< Vec > > modeVecs
Topology mode vectors (design variables)
Definition adjoint.cpp:174
SmartPetscObj< DM > adjointDM
Data manager for adjoint problem.
Definition adjoint.cpp:168
FieldApproximationBase base
Choice of finite element basis functions.
Definition plot_base.cpp:68
std::vector< std::array< double, 3 > > modeCentroids
Centroids of optimization blocks.
Definition adjoint.cpp:176
MoFEMErrorCode topologyModes()
Compute topology optimization modes.
Definition adjoint.cpp:725
SmartPetscObj< KSP > kspElastic
Linear solver for elastic problem.
Definition adjoint.cpp:167
SmartPetscObj< Vec > initialGeometry
Initial geometry field.
Definition adjoint.cpp:179
int fieldOrder
Polynomial order for approximation.
Definition adjoint.cpp:164
Example(MoFEM::Interface &m_field)
Definition adjoint.cpp:129
SmartPetscObj< Mat > M
MoFEMErrorCode runProblem()
Main driver function for the optimization process.
MoFEMErrorCode calculateGradient(PetscReal *objective_function_value, Vec objective_function_gradient, Vec adjoint_vector)
Calculate objective function gradient using adjoint method.
Definition adjoint.cpp:1763
MoFEMErrorCode setupAdJoint()
Setup adjoint fields and finite elements.
Definition adjoint.cpp:602
MoFEM::Interface & mField
Reference to MoFEM interface.
Definition plastic.cpp:227
std::vector< std::array< double, 6 > > modeBBoxes
Bounding boxes of optimization blocks.
Definition adjoint.cpp:178
MoFEMErrorCode setupProblem()
Setup fields, approximation spaces and DOFs.
MoFEMErrorCode postprocessElastic(int iter, SmartPetscObj< Vec > adjoint_vector=nullptr)
Post-process and output results.
Definition adjoint.cpp:1326
MoFEMErrorCode solveElastic()
Solve forward elastic problem.
Definition adjoint.cpp:1198
boost::shared_ptr< MatrixDouble > vectorFieldPtr
Field values at evaluation points.
Definition adjoint.cpp:137
Add operators pushing bases from local to physical configuration.
Boundary condition manager for finite element problem setup.
Managing BitRefLevels.
MoFEMErrorCode synchroniseEntities(Range &ent, std::map< int, Range > *received_ents, int verb=DEFAULT_VERBOSITY)
synchronize entity range on processors (collective)
virtual moab::Interface & get_moab()=0
virtual MoFEMErrorCode build_adjacencies(const Range &ents, int verb=DEFAULT_VERBOSITY)=0
build adjacencies
virtual MoFEMErrorCode add_field(const std::string name, const FieldSpace space, const FieldApproximationBase base, const FieldCoefficientsNumber nb_of_coefficients, const TagType tag_type=MB_TAG_SPARSE, const enum MoFEMTypes bh=MF_EXCL, int verb=DEFAULT_VERBOSITY)=0
Add field.
virtual MPI_Comm & get_comm() const =0
virtual int get_comm_rank() const =0
Core (interface) class.
Definition Core.hpp:83
static MoFEMErrorCode Initialize(int *argc, char ***args, const char file[], const char help[])
Initializes the MoFEM database PETSc, MOAB and MPI.
Definition Core.cpp:68
static MoFEMErrorCode Finalize()
Checks for options to be called at the conclusion of the program.
Definition Core.cpp:123
Deprecated interface functions.
Definition of the displacement bc data structure.
Definition BCData.hpp:72
Data on single entity (This is passed as argument to DataOperator::doWork)
FTensor::Tensor0< FTensor::PackPtr< double *, 1 > > getFTensor0N(const FieldApproximationBase base)
Get base function as Tensor0.
auto getFTensor1DiffN(const FieldApproximationBase base)
Get derivatives of base functions.
MatrixDouble & getN(const FieldApproximationBase base)
get base functions this return matrix (nb. of rows is equal to nb. of Gauss pts, nb....
const VectorInt & getIndices() const
Get global indices of degrees of freedom on entity.
Class (Function) to enforce essential constrains on the left hand side diagonal.
Definition Essential.hpp:33
Class (Function) to enforce essential constrains on the right hand side diagonal.
Definition Essential.hpp:41
Class (Function) to enforce essential constrains.
Definition Essential.hpp:25
Basic algebra on fields.
Definition FieldBlas.hpp:21
MoFEMErrorCode fieldLambdaOnValues(OneFieldFunctionOnValues lambda, const std::string field_name, Range *ents_ptr=nullptr)
field lambda
Definition FieldBlas.cpp:21
Field evaluator interface.
static boost::shared_ptr< SinkType > createSink(boost::shared_ptr< std::ostream > stream_ptr, std::string comm_filter)
Create a sink object.
static boost::shared_ptr< std::ostream > getStrmWorld()
Get the strm world object.
static boost::shared_ptr< std::ostream > getStrmSync()
Get the strm sync object.
Interface for managing meshsets containing materials and boundary conditions.
Assembly methods.
Definition Natural.hpp:65
Get field gradients at integration pts for scalar field rank 0, i.e. vector field.
Specialization for MatrixDouble vector field values calculation.
Post post-proc data at points from hash maps.
Template struct for dimension-specific finite element types.
PipelineManager interface.
MoFEMErrorCode setDomainRhsIntegrationRule(RuleHookFun rule)
Set integration rule for domain right-hand side finite element.
Problem manager is used to build and partition problems.
Projection of edge entities with one mid-node on hierarchical basis.
Simple interface for fast problem set-up.
Definition Simple.hpp:27
MoFEMErrorCode getOptions()
get options
Definition Simple.cpp:180
MoFEMErrorCode getDM(DM *dm)
Get DM.
Definition Simple.cpp:799
intrusive_ptr for managing petsc objects
MoFEMErrorCode getInterface(IFACE *&iface) const
Get interface reference to pointer of interface.
Vector manager is used to create vectors \mofem_vectors.
OpAdJointGradTimesSymTensor(const std::string field_name, boost::shared_ptr< HookeOps::CommonData > comm_ptr, boost::shared_ptr< MatrixDouble > jac, boost::shared_ptr< MatrixDouble > diff_jac, boost::shared_ptr< VectorDouble > cof_vals)
Definition adjoint.cpp:1349
Forward declaration of operator for gradient times symmetric tensor operations.
Definition adjoint.cpp:105
boost::shared_ptr< MatrixDouble > dGradPtr
Definition adjoint.cpp:1755
boost::shared_ptr< double > globObjectiveGradPtr
Definition adjoint.cpp:1760
boost::shared_ptr< HookeOps::CommonData > commPtr
Definition adjoint.cpp:1751
ForcesAndSourcesCore::UserDataOperator OP
Definition adjoint.cpp:1588
boost::shared_ptr< double > globObjectivePtr
Definition adjoint.cpp:1759
boost::shared_ptr< ObjectiveFunctionData > pythonPtr
Definition adjoint.cpp:1750
MoFEMErrorCode doWork(int side, EntityType type, EntitiesFieldData::EntData &data)
Compute objective function contributions at element level.
Definition adjoint.cpp:1612
boost::shared_ptr< VectorDouble > cofVals
Definition adjoint.cpp:1754
boost::shared_ptr< MatrixDouble > uPtr
Definition adjoint.cpp:1757
boost::shared_ptr< MatrixDouble > jacPtr
Definition adjoint.cpp:1752
OpAdJointObjective(boost::shared_ptr< ObjectiveFunctionData > python_ptr, boost::shared_ptr< HookeOps::CommonData > comm_ptr, boost::shared_ptr< MatrixDouble > jac_ptr, boost::shared_ptr< MatrixDouble > diff_jac, boost::shared_ptr< VectorDouble > cof_vals, boost::shared_ptr< MatrixDouble > d_grad_ptr, boost::shared_ptr< MatrixDouble > d_u_ptr, boost::shared_ptr< MatrixDouble > u_ptr, boost::shared_ptr< double > glob_objective_ptr, boost::shared_ptr< double > glob_objective_grad_ptr)
Definition adjoint.cpp:1590
boost::shared_ptr< MatrixDouble > diffJacPtr
Definition adjoint.cpp:1753
boost::shared_ptr< MatrixDouble > dUPtr
Definition adjoint.cpp:1756
boost::shared_ptr< HookeOps::CommonData > commPtr
Definition adjoint.cpp:1584
OpStateSensitivity(const std::string field_name, boost::shared_ptr< ObjectiveFunctionData > python_ptr, boost::shared_ptr< HookeOps::CommonData > comm_ptr, boost::shared_ptr< MatrixDouble > u_ptr)
Definition adjoint.cpp:1499
MoFEMErrorCode iNtegrate(EntitiesFieldData::EntData &data)
Definition adjoint.cpp:1506
boost::shared_ptr< MatrixDouble > uPtr
Definition adjoint.cpp:1585
boost::shared_ptr< ObjectiveFunctionData > pythonPtr
Definition adjoint.cpp:1583
PipelineManager::ElementsAndOpsByDim< 2 >::FaceSideEle SideEle
PipelineManager::ElementsAndOpsByDim< 3 >::FaceSideEle SideEle
#define EXECUTABLE_DIMENSION
Definition plastic.cpp:13
PetscBool do_eval_field
Evaluate field.
Definition plastic.cpp:120
ElementsAndOps< SPACE_DIM >::SideEle SideEle
Definition plastic.cpp:62
auto save_range
constexpr int SPACE_DIM
PipelineManager::ElementsAndOpsByDim< SPACE_DIM >::BoundaryEle BoundaryEle
PostProcEleByDim< SPACE_DIM >::SideEle SideEle
PipelineManager::ElementsAndOpsByDim< SPACE_DIM >::DomainEle DomainEle