v0.16.3
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 // Differentiate the same quadrature-discretised problem that is solved.
138 static int integrationRule(int, int, int order) { return 2 * order + 1; }
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);
469 "", 1, "gradient_fd",
470 [this, tao, x0, g, rank, eval_objective_and_gradient]() mutable {
472 PetscReal eps = 1e-4, design = 0;
473 CHKERR PetscOptionsGetReal(nullptr, nullptr, "-gradient_fd_eps", &eps,
474 nullptr);
475 CHKERR PetscOptionsGetReal(nullptr, nullptr, "-gradient_fd_design",
476 &design, nullptr);
477 CHKERR VecSet(x0, design);
478 PetscReal value;
479 CHKERR eval_objective_and_gradient(tao, x0, &value, g, this);
480 auto test_x =
481 createGhostVector(mField.get_comm(), (!rank) ? modeVecs.size() : 0,
482 modeVecs.size(), 0, nullptr);
483 CHKERR VecCopy(x0, test_x);
484 auto direction = vectorDuplicate(test_x);
485 auto work_gradient = vectorDuplicate(g);
486 auto evaluate = [&](Vec x, PetscReal &f) {
487 return eval_objective_and_gradient(tao, x, &f, work_gradient, this);
488 };
489 auto tester = mField.getInterface<OperatorsTester>();
490 for (PetscInt mode = 0; mode < modeVecs.size(); ++mode) {
491 CHKERR VecZeroEntries(direction);
492 if (!rank)
493 CHKERR VecSetValue(direction, mode, 1., INSERT_VALUES);
494 CHKERR VecAssemblyBegin(direction);
495 CHKERR VecAssemblyEnd(direction);
496 PetscReal fd, analytical;
497 CHKERR tester->checkScalarCentralFiniteDifference(
498 test_x, direction, g, eps, evaluate, fd, analytical);
499 MOFEM_LOG("WORLD", Sev::inform)
500 << "Shape gradient mode " << mode
501 << ": FD=" << std::setprecision(16) << fd
502 << " analytical=" << analytical
503 << " error=" << std::abs(fd - analytical);
505 ->checkValue<TestType::DERIVATIVE>("shape gradient mode " +
506 std::to_string(mode),
507 analytical, fd, {1e-6, 1e-6});
508 }
509 CHKERR eval_objective_and_gradient(tao, x0, &value, g, this);
511 });
512 CHKERR testing->runTests();
513 if (testing->checkTest("gradient_fd"))
515 CHKERR TaoSolve(tao);
517 tao, "shape optimisation adjoint");
518
519 // Optimization complete - results available in solution vectors
520 MOFEM_LOG("WORLD", Sev::inform) << "Topology optimization completed";
521
523}
524//! [Run problem]
525
526//! [Read mesh]
527/**
528 * @brief Read mesh from file and setup material/boundary condition meshsets
529 *
530 * This function loads the finite element mesh from file and processes
531 * associated meshsets that define material properties and boundary conditions.
532 * The mesh is typically generated using CUBIT and exported in .h5m format.
533 *
534 * Meshsets are used to group elements/faces by:
535 * - Material properties (for different material blocks)
536 * - Boundary conditions (for applying loads and constraints)
537 * - Optimization regions (for topology optimization)
538 *
539 * @return MoFEMErrorCode Success or error code
540 */
544 CHKERR simple->getOptions(); // Read command line options
545 CHKERR simple->loadFile(); // Load mesh from file
546 // Add meshsets if config file provided
547 CHKERR mField.getInterface<MeshsetsManager>()->setMeshsetFromFile();
549}
550//! [Read mesh]
551
552//! [Set up problem]
553/**
554 * @brief Setup finite element fields, approximation spaces and degrees of freedom
555 *
556 * This function configures the finite element problem by:
557 * 1. Setting up the displacement field "U" with vector approximation
558 * 2. Setting up the geometry field "GEOMETRY" for mesh deformation
559 * 3. Defining polynomial approximation order and basis functions
560 * 4. Creating degrees of freedom on mesh entities
561 *
562 * The displacement field uses H1 vector space for standard elasticity.
563 * The geometry field allows mesh modification during topology optimization.
564 * Different basis functions (Ainsworth-Legendre vs Demkowicz) can be selected.
565 *
566 * @return MoFEMErrorCode Success or error code
567 */
571
572 // Select basis functions for finite element approximation
573 enum bases { AINSWORTH, DEMKOWICZ, LASBASETOPT };
574 const char *list_bases[LASBASETOPT] = {"ainsworth", "demkowicz"};
575 PetscInt choice_base_value = AINSWORTH;
576 CHKERR PetscOptionsGetEList(PETSC_NULLPTR, NULL, "-base", list_bases,
577 LASBASETOPT, &choice_base_value, PETSC_NULLPTR);
578
579 switch (choice_base_value) {
580 case AINSWORTH:
582 MOFEM_LOG("WORLD", Sev::inform)
583 << "Set AINSWORTH_LEGENDRE_BASE for displacements";
584 break;
585 case DEMKOWICZ:
587 MOFEM_LOG("WORLD", Sev::inform)
588 << "Set DEMKOWICZ_JACOBI_BASE for displacements";
589 break;
590 default:
591 base = LASTBASE;
592 break;
593 }
594
595 // Add finite element fields
596 /**
597 * Setup displacement field "U" - the primary unknown in elasticity
598 * This field represents displacement vector at each node/DOF
599 */
602 CHKERR PetscOptionsGetInt(PETSC_NULLPTR, "", "-order", &fieldOrder,
603 PETSC_NULLPTR);
604
605 /**
606 * Setup geometry field "GEOMETRY" - used for mesh deformation in optimization
607 * This field stores current nodal coordinates and can be modified
608 * during topology optimization to represent design changes
609 */
610 CHKERR simple->addDataField("GEOMETRY", H1, base, SPACE_DIM);
611
612 // Set polynomial approximation order for both fields
616
617 // Project higher-order geometry representation onto geometry field
618 /**
619 * For higher-order elements, this projects the exact geometry
620 * onto the geometry field to maintain curved boundaries accurately
621 */
622 auto project_ho_geometry = [&]() {
623 Projection10NodeCoordsOnField ent_method(mField, "GEOMETRY");
624 return mField.loop_dofs("GEOMETRY", ent_method);
625 };
626 CHKERR project_ho_geometry();
627
628 // Check if plane strain assumption should be used
629 CHKERR PetscOptionsGetBool(PETSC_NULLPTR, "", "-plane_strain",
630 &is_plane_strain, PETSC_NULLPTR);
631
633}
634//! [Set up problem]
635
636//! [Setup adjoint]
637/**
638 * @brief Setup adjoint fields and finite elements for sensitivity analysis
639 *
640 * The adjoint method is used to efficiently compute gradients of the objective
641 * function with respect to design variables. This function sets up:
642 *
643 * 1. ADJOINT_FIELD - stores adjoint variables (Lagrange multipliers)
644 * 2. ADJOINT_DM - data manager for adjoint problem
645 * 3. Adjoint finite elements for domain and boundary
646 *
647 * The adjoint equation is: K^T * λ = ∂f/∂u
648 * where λ are adjoint variables, K is stiffness matrix, f is objective
649 *
650 * @return MoFEMErrorCode Success or error code
651 */
655
656 // Create adjoint data manager and field
657 auto create_adjoint_dm = [&]() {
658 auto adjoint_dm = createDM(mField.get_comm(), "DMMOFEM");
659
660 auto add_field = [&]() {
662 CHKERR mField.add_field("ADJOINT_FIELD", H1, base, SPACE_DIM);
664 "ADJOINT_FIELD");
665 for (auto tt = MBEDGE; tt <= moab::CN::TypeDimensionMap[SPACE_DIM].second;
666 ++tt)
667 CHKERR mField.set_field_order(simple->getMeshset(), tt, "ADJOINT_FIELD",
668 fieldOrder);
670 "ADJOINT_FIELD", 1);
673 };
674
675 auto add_adjoint_fe_impl = [&]() {
677 CHKERR mField.add_finite_element("ADJOINT_DOMAIN_FE");
679 "ADJOINT_FIELD");
681 "ADJOINT_FIELD");
683 "ADJOINT_FIELD");
685 "GEOMETRY");
687 simple->getMeshset(), SPACE_DIM, "ADJOINT_DOMAIN_FE");
688 CHKERR mField.build_finite_elements("ADJOINT_DOMAIN_FE");
689
690 CHKERR mField.add_finite_element("ADJOINT_BOUNDARY_FE");
692 "ADJOINT_FIELD");
694 "ADJOINT_FIELD");
696 "ADJOINT_FIELD");
698 "GEOMETRY");
699
700 auto block_name = "OPTIMISE";
701 auto mesh_mng = mField.getInterface<MeshsetsManager>();
702 auto bcs = mesh_mng->getCubitMeshsetPtr(
703
704 std::regex((boost::format("%s(.*)") % block_name).str())
705
706 );
707
708 for (auto bc : bcs) {
710 bc->getMeshset(), SPACE_DIM - 1, "ADJOINT_BOUNDARY_FE");
711 }
712
713 CHKERR mField.build_finite_elements("ADJOINT_BOUNDARY_FE");
714
717
719 };
720
721 auto set_adjoint_dm_imp = [&]() {
723 CHKERR DMMoFEMCreateMoFEM(adjoint_dm, &mField, "ADJOINT",
726 CHKERR DMMoFEMSetDestroyProblem(adjoint_dm, PETSC_TRUE);
727 CHKERR DMSetFromOptions(adjoint_dm);
728 CHKERR DMMoFEMAddElement(adjoint_dm, "ADJOINT_DOMAIN_FE");
729 CHKERR DMMoFEMAddElement(adjoint_dm, "ADJOINT_BOUNDARY_FE");
730 CHKERR DMMoFEMSetSquareProblem(adjoint_dm, PETSC_TRUE);
731 CHKERR DMMoFEMSetIsPartitioned(adjoint_dm, PETSC_TRUE);
732 mField.getInterface<ProblemsManager>()->buildProblemFromFields =
733 PETSC_TRUE;
734 CHKERR DMSetUp(adjoint_dm);
735 mField.getInterface<ProblemsManager>()->buildProblemFromFields =
736 PETSC_FALSE;
738 };
739
740 CHK_THROW_MESSAGE(add_field(), "add adjoint field");
741 CHK_THROW_MESSAGE(add_adjoint_fe_impl(), "add adjoint fe");
742 CHK_THROW_MESSAGE(set_adjoint_dm_imp(), "set adjoint dm");
743
744 return adjoint_dm;
745 };
746
747 adjointDM = create_adjoint_dm();
748
750}
751//
752
753//! [Boundary condition]
757 auto bc_mng = mField.getInterface<BcManager>();
758
759 CHKERR bc_mng->removeBlockDOFsOnEntities(simple->getProblemName(), "REMOVE_X",
760 "U", 0, 0);
761 CHKERR bc_mng->removeBlockDOFsOnEntities(simple->getProblemName(), "REMOVE_Y",
762 "U", 1, 1);
763 CHKERR bc_mng->removeBlockDOFsOnEntities(simple->getProblemName(), "REMOVE_Z",
764 "U", 2, 2);
765 CHKERR bc_mng->removeBlockDOFsOnEntities(simple->getProblemName(),
766 "REMOVE_ALL", "U", 0, 3);
767 CHKERR bc_mng->pushMarkDOFsOnEntities<DisplacementCubitBcData>(
768 simple->getProblemName(), "U");
769
771}
772//! [Boundary condition]
773
774//! [Adjoint modes]
777
778 auto opt_ents = get_range_from_block(mField, "OPTIMISE", SPACE_DIM - 1);
779 auto subset_dm_bdy = createDM(mField.get_comm(), "DMMOFEM");
780 CHKERR DMMoFEMSetSquareProblem(subset_dm_bdy, PETSC_TRUE);
781 CHKERR DMMoFEMCreateSubDM(subset_dm_bdy, adjointDM, "SUBSET_BDY");
782 CHKERR DMMoFEMAddElement(subset_dm_bdy, "ADJOINT_BOUNDARY_FE");
783 CHKERR DMMoFEMAddSubFieldRow(subset_dm_bdy, "ADJOINT_FIELD",
784 boost::make_shared<Range>(opt_ents));
785 CHKERR DMMoFEMAddSubFieldCol(subset_dm_bdy, "ADJOINT_FIELD",
786 boost::make_shared<Range>(opt_ents));
787 CHKERR DMSetUp(subset_dm_bdy);
788
789 auto subset_dm_domain = createDM(mField.get_comm(), "DMMOFEM");
790 CHKERR DMMoFEMSetSquareProblem(subset_dm_domain, PETSC_TRUE);
791 CHKERR DMMoFEMCreateSubDM(subset_dm_domain, adjointDM, "SUBSET_DOMAIN");
792 CHKERR DMMoFEMAddElement(subset_dm_domain, "ADJOINT_DOMAIN_FE");
793 CHKERR DMMoFEMAddSubFieldRow(subset_dm_domain, "ADJOINT_FIELD");
794 CHKERR DMMoFEMAddSubFieldCol(subset_dm_domain, "ADJOINT_FIELD");
795 CHKERR DMSetUp(subset_dm_domain);
796
797 // remove dofs on boundary of the domain
798 auto remove_dofs = [&]() {
800
801 std::array<Range, 3> remove_dim_ents;
802 remove_dim_ents[0] =
803 get_range_from_block(mField, "OPT_REMOVE_X", SPACE_DIM - 1);
804 remove_dim_ents[1] =
805 get_range_from_block(mField, "OPT_REMOVE_Y", SPACE_DIM - 1);
806 remove_dim_ents[2] =
807 get_range_from_block(mField, "OPT_REMOVE_Z", SPACE_DIM - 1);
808
809 for (int d = 0; d != 3; ++d) {
810 MOFEM_LOG("WORLD", Sev::inform)
811 << "Removing topology modes on block OPT_REMOVE_" << (char)('X' + d)
812 << " with " << remove_dim_ents[d].size() << " entities";
813 }
814
815 Range body_ents;
816 CHKERR mField.get_moab().get_entities_by_dimension(0, SPACE_DIM, body_ents,
817 true);
818 auto skin = moab::Skinner(&mField.get_moab());
819 Range boundary_ents;
820 CHKERR skin.find_skin(0, body_ents, false, boundary_ents);
821 for (int d = 0; d != 3; ++d) {
822 boundary_ents = subtract(boundary_ents, remove_dim_ents[d]);
823 }
824 ParallelComm *pcomm =
825 ParallelComm::get_pcomm(&mField.get_moab(), MYPCOMM_INDEX);
826 CHKERR pcomm->filter_pstatus(boundary_ents,
827 PSTATUS_SHARED | PSTATUS_MULTISHARED,
828 PSTATUS_NOT, -1, nullptr);
829 for (auto d = SPACE_DIM - 2; d >= 0; --d) {
830 if (d >= 0) {
831 Range ents;
832 CHKERR mField.get_moab().get_adjacencies(boundary_ents, d, false, ents,
833 moab::Interface::UNION);
834 boundary_ents.merge(ents);
835 } else {
836 Range verts;
837 CHKERR mField.get_moab().get_connectivity(boundary_ents, verts);
838 boundary_ents.merge(verts);
839 }
840 CHKERR mField.getInterface<CommInterface>()->synchroniseEntities(
841 boundary_ents);
842 }
843 boundary_ents.merge(opt_ents);
844 CHKERR mField.getInterface<ProblemsManager>()->removeDofsOnEntities(
845 "SUBSET_DOMAIN", "ADJOINT_FIELD", boundary_ents);
846 for (int d = 0; d != 3; ++d) {
847 CHKERR mField.getInterface<ProblemsManager>()->removeDofsOnEntities(
848 "SUBSET_DOMAIN", "ADJOINT_FIELD", remove_dim_ents[d], d, d);
849 }
850
851 // #ifndef NDEBUG
852 if (mField.get_comm_rank() == 0) {
853 CHKERR save_range(mField.get_moab(), "topoMode_boundary_ents.vtk",
854 boundary_ents);
855 }
856 // #endif
857
859 };
860
861 CHKERR remove_dofs();
862
863 auto get_lhs_fe = [&]() {
864 auto fe_lhs = boost::make_shared<BoundaryEle>(mField);
865 fe_lhs->getRuleHook = [](int, int, int p_data) {
866 return 2 * p_data + p_data - 1;
867 };
868 auto &pip = fe_lhs->getOpPtrVector();
870 "GEOMETRY");
873 pip.push_back(new OpMass("ADJOINT_FIELD", "ADJOINT_FIELD",
874 [](double, double, double) { return 1.; }));
875 return fe_lhs;
876 };
877
878 auto get_rhs_fe = [&]() {
879 auto fe_rhs = boost::make_shared<BoundaryEle>(mField);
880 fe_rhs->getRuleHook = [](int, int, int p_data) {
881 return 2 * p_data + p_data - 1;
882 };
883 auto &pip = fe_rhs->getOpPtrVector();
885 "GEOMETRY");
886
887 return fe_rhs;
888 };
889
890 auto block_name = "OPTIMISE";
891 auto mesh_mng = mField.getInterface<MeshsetsManager>();
892 auto bcs = mesh_mng->getCubitMeshsetPtr(
893
894 std::regex((boost::format("%s(.*)") % block_name).str())
895
896 );
897
898 for (auto &v : modeVecs) {
899 v = createDMVector(subset_dm_bdy);
900 }
901
903 struct OpMode : public OP {
904 OpMode(const std::string name,
905 boost::shared_ptr<ObjectiveFunctionData> python_ptr, int id,
906 std::vector<SmartPetscObj<Vec>> mode_vecs,
907 std::vector<std::array<double, 3>> mode_centroids,
908 std::vector<std::array<double, 6>> mode_bboxes, int block_counter,
909 int mode_counter, boost::shared_ptr<Range> range = nullptr)
910 : OP(name, name, OP::OPROW, range), pythonPtr(python_ptr), iD(id),
911 modeVecs(mode_vecs), modeCentroids(mode_centroids),
912 modeBboxes(mode_bboxes), blockCounter(block_counter),
913 modeCounter(mode_counter) {}
914
915 MoFEMErrorCode doWork(int side, EntityType type, EntData &data) {
917
918 if (OP::entsPtr) {
919 if (OP::entsPtr->find(this->getFEEntityHandle()) == OP::entsPtr->end())
921 }
922
923 auto nb_rows = data.getIndices().size();
924 if (!nb_rows) {
926 }
927 auto nb_base_functions = data.getN().size2();
928
930 CHKERR pythonPtr->blockModes(iD, OP::getCoordsAtGaussPts(),
931 modeCentroids[blockCounter],
932 modeBboxes[blockCounter], blockModes);
933
934 auto nb_integration_pts = getGaussPts().size2();
935 if (blockModes.size2() != 3 * nb_integration_pts) {
936 MOFEM_LOG("WORLD", Sev::error)
937 << "Number of modes does not match number of integration points: "
938 << blockModes.size2() << "!=" << 3 * nb_integration_pts;
939 CHK_THROW_MESSAGE(MOFEM_DATA_INCONSISTENCY, "modes/integration points");
940 }
941
942 VectorDouble nf(nb_rows);
943
944 int nb_modes = blockModes.size1();
945 for (auto mode = 0; mode != nb_modes; ++mode) {
946 nf.clear();
947 // get mode
948 auto t_mode = getFTensor1FromPtr<3>(&blockModes(mode, 0));
949 // get element volume
950 const double vol = OP::getMeasure();
951 // get integration weights
952 auto t_w = OP::getFTensor0IntegrationWeight();
953 // get base function gradient on rows
954 auto t_base = data.getFTensor0N();
955 // loop over integration points
956 for (int gg = 0; gg != nb_integration_pts; gg++) {
957
958 // take into account Jacobian
959 const double alpha = t_w * vol;
960 // loop over rows base functions
961 auto t_nf = getFTensor1FromPtr<SPACE_DIM>(nf.data().data());
962 int rr = 0;
963 for (; rr != nb_rows / SPACE_DIM; ++rr) {
964 t_nf(i) += alpha * t_base * t_mode(i);
965 ++t_base;
966 ++t_nf;
967 }
968 for (; rr < nb_base_functions; ++rr)
969 ++t_base;
970 ++t_w; // move to another integration weight
971 ++t_mode; // move to another mode
972 }
973 Vec vec = modeVecs[modeCounter + mode];
974 auto size = data.getIndices().size();
975 auto *indices = data.getIndices().data().data();
976 auto *nf_data = nf.data().data();
977 CHKERR VecSetValues(vec, size, indices, nf_data, ADD_VALUES);
978 }
979
981 }
982
983 private:
984 boost::shared_ptr<ObjectiveFunctionData> pythonPtr;
985 MatrixDouble blockModes;
986 std::vector<std::array<double, 3>> modeCentroids;
987 std::vector<std::array<double, 6>> modeBboxes;
988 int iD;
989 std::vector<SmartPetscObj<Vec>> modeVecs;
990 int blockCounter;
991 int modeCounter;
992 };
993
994 auto solve_bdy = [&]() {
996
997 auto fe_lhs = get_lhs_fe();
998 auto fe_rhs = get_rhs_fe();
999 int block_counter = 0;
1000 int mode_counter = 0;
1001 for (auto &bc : bcs) {
1002 auto id = bc->getMeshsetId();
1003 Range ents;
1004 CHKERR mField.get_moab().get_entities_by_handle(bc->getMeshset(), ents,
1005 true);
1006 auto range = boost::make_shared<Range>(ents);
1007 auto &pip_rhs = fe_rhs->getOpPtrVector();
1008 pip_rhs.push_back(new OpMode("ADJOINT_FIELD", pythonPtr, id, modeVecs,
1009 modeCentroids, modeBBoxes, block_counter,
1010 mode_counter, range));
1011 CHKERR DMoFEMLoopFiniteElements(subset_dm_bdy, "ADJOINT_BOUNDARY_FE",
1012 fe_rhs);
1013 pip_rhs.pop_back();
1014 int nb_modes;
1015 CHKERR pythonPtr->numberOfModes(id, nb_modes);
1016 ++block_counter;
1017 mode_counter += nb_modes;
1018 MOFEM_LOG("WORLD", Sev::inform)
1019 << "Setting mode block block: " << bc->getName()
1020 << " with ID: " << bc->getMeshsetId()
1021 << " total modes: " << mode_counter;
1022 }
1023
1024 for (auto &v : modeVecs) {
1025 CHKERR VecAssemblyBegin(v);
1026 CHKERR VecAssemblyEnd(v);
1027 CHKERR VecGhostUpdateBegin(v, ADD_VALUES, SCATTER_REVERSE);
1028 CHKERR VecGhostUpdateEnd(v, ADD_VALUES, SCATTER_REVERSE);
1029 }
1030
1031 auto M = createDMMatrix(subset_dm_bdy);
1032 fe_lhs->B = M;
1033 CHKERR DMoFEMLoopFiniteElements(subset_dm_bdy, "ADJOINT_BOUNDARY_FE",
1034 fe_lhs);
1035 CHKERR MatAssemblyBegin(M, MAT_FINAL_ASSEMBLY);
1036 CHKERR MatAssemblyEnd(M, MAT_FINAL_ASSEMBLY);
1037
1038 auto solver = createKSP(mField.get_comm());
1039 CHKERR KSPSetOperators(solver, M, M);
1040 CHKERR KSPSetFromOptions(solver);
1041 CHKERR KSPSetUp(solver);
1042 auto v = createDMVector(subset_dm_bdy);
1043 for (auto &f : modeVecs) {
1044 CHKERR KSPSolve(solver, f, v);
1045 CHKERR VecSwap(f, v);
1046 }
1047
1048 for (auto &v : modeVecs) {
1049 CHKERR VecGhostUpdateBegin(v, INSERT_VALUES, SCATTER_FORWARD);
1050 CHKERR VecGhostUpdateEnd(v, INSERT_VALUES, SCATTER_FORWARD);
1051 }
1052
1054 };
1055
1056 CHKERR solve_bdy();
1057
1058 auto get_elastic_fe_lhs = [&]() {
1059 auto fe = boost::make_shared<DomainEle>(mField);
1060 fe->getRuleHook = [](int, int, int p_data) {
1061 return 2 * p_data + p_data - 1;
1062 };
1063 auto &pip = fe->getOpPtrVector();
1065 "GEOMETRY");
1066 CHKERR HookeOps::opFactoryDomainLhs<SPACE_DIM, A, I, DomainEleOp>(
1067 mField, pip, "ADJOINT_FIELD", "MAT_ADJOINT", Sev::noisy);
1068 return fe;
1069 };
1070
1071 auto get_elastic_fe_rhs = [&]() {
1072 auto fe = boost::make_shared<DomainEle>(mField);
1073 fe->getRuleHook = [](int, int, int p_data) {
1074 return 2 * p_data + p_data - 1;
1075 };
1076 auto &pip = fe->getOpPtrVector();
1078 "GEOMETRY");
1079 CHKERR HookeOps::opFactoryDomainRhs<SPACE_DIM, A, I, DomainEleOp>(
1080 mField, pip, "ADJOINT_FIELD", "MAT_ADJOINT", Sev::noisy);
1081 return fe;
1082 };
1083
1084 auto adjoint_gradient_postprocess = [&](auto mode) {
1086 auto post_proc_mesh = boost::make_shared<moab::Core>();
1087 auto post_proc_begin =
1088 boost::make_shared<PostProcBrokenMeshInMoabBaseBegin>(mField,
1089 post_proc_mesh);
1090 auto post_proc_end = boost::make_shared<PostProcBrokenMeshInMoabBaseEnd>(
1091 mField, post_proc_mesh);
1092
1093 auto geom_vec = boost::make_shared<MatrixDouble>();
1094
1095 auto post_proc_fe =
1096 boost::make_shared<PostProcEleDomain>(mField, post_proc_mesh);
1098 post_proc_fe->getOpPtrVector(), {H1}, "GEOMETRY");
1099 post_proc_fe->getOpPtrVector().push_back(
1100 new OpCalculateVectorFieldValues<SPACE_DIM>("ADJOINT_FIELD", geom_vec,
1101 modeVecs[mode]));
1102
1104
1105 post_proc_fe->getOpPtrVector().push_back(
1106
1107 new OpPPMap(
1108
1109 post_proc_fe->getPostProcMesh(), post_proc_fe->getMapGaussPts(),
1110
1111 {},
1112
1113 {{"MODE", geom_vec}},
1114
1115 {},
1116
1117 {}
1118
1119 )
1120
1121 );
1122
1124 post_proc_begin->getFEMethod());
1125 CHKERR DMoFEMLoopFiniteElements(adjointDM, "ADJOINT_DOMAIN_FE",
1126 post_proc_fe);
1128 post_proc_begin->getFEMethod());
1129
1130 CHKERR post_proc_end->writeFile("mode_" + std::to_string(mode) + ".h5m");
1131
1133 };
1134
1135 auto solve_domain = [&]() {
1137 auto fe_lhs = get_elastic_fe_lhs();
1138 auto fe_rhs = get_elastic_fe_rhs();
1139 auto v = createDMVector(subset_dm_domain);
1140 auto F = vectorDuplicate(v);
1141 fe_rhs->f = F;
1142
1143 auto M = createDMMatrix(subset_dm_domain);
1144 fe_lhs->B = M;
1145 CHKERR DMoFEMLoopFiniteElements(subset_dm_domain, "ADJOINT_DOMAIN_FE",
1146 fe_lhs);
1147 CHKERR MatAssemblyBegin(M, MAT_FINAL_ASSEMBLY);
1148 CHKERR MatAssemblyEnd(M, MAT_FINAL_ASSEMBLY);
1149
1150 auto solver = createKSP(mField.get_comm());
1151 CHKERR KSPSetOperators(solver, M, M);
1152 CHKERR KSPSetFromOptions(solver);
1153 CHKERR KSPSetUp(solver);
1154
1155 int mode_counter = 0;
1156 for (auto &f : modeVecs) {
1157 CHKERR mField.getInterface<FieldBlas>()->setField(0, "ADJOINT_FIELD");
1158 CHKERR DMoFEMMeshToLocalVector(subset_dm_bdy, f, INSERT_VALUES,
1159 SCATTER_REVERSE);
1160 CHKERR VecZeroEntries(F);
1161 CHKERR DMoFEMLoopFiniteElements(subset_dm_domain, "ADJOINT_DOMAIN_FE",
1162 fe_rhs);
1163 CHKERR VecAssemblyBegin(F);
1164 CHKERR VecAssemblyEnd(F);
1165 CHKERR VecGhostUpdateBegin(F, ADD_VALUES, SCATTER_REVERSE);
1166 CHKERR VecGhostUpdateEnd(F, ADD_VALUES, SCATTER_REVERSE);
1167 CHKERR KSPSolve(solver, F, v);
1168 CHKERR VecGhostUpdateBegin(v, INSERT_VALUES, SCATTER_FORWARD);
1169 CHKERR VecGhostUpdateEnd(v, INSERT_VALUES, SCATTER_FORWARD);
1170 CHKERR DMoFEMMeshToLocalVector(subset_dm_domain, v, INSERT_VALUES,
1171 SCATTER_REVERSE);
1172 auto m = createDMVector(adjointDM);
1173 CHKERR DMoFEMMeshToLocalVector(adjointDM, m, INSERT_VALUES,
1174 SCATTER_FORWARD);
1175 f = m;
1176 ++mode_counter;
1177 }
1179 };
1180
1181 CHKERR solve_domain();
1182
1183 for (int i = 0; i < modeVecs.size(); ++i) {
1184 CHKERR adjoint_gradient_postprocess(i);
1185 }
1186
1188}
1189//! [Adjoint modes]
1190
1191//! [Push operators to pipeline]
1194 auto pip = mField.getInterface<PipelineManager>();
1195
1196 //! [Integration rule]
1198 CHKERR pip->setDomainLhsIntegrationRule(integrationRule);
1199 CHKERR pip->setBoundaryRhsIntegrationRule(integrationRule);
1200 CHKERR pip->setBoundaryLhsIntegrationRule(integrationRule);
1201 //! [Integration rule]
1202
1204 pip->getOpDomainLhsPipeline(), {H1}, "GEOMETRY");
1206 pip->getOpDomainRhsPipeline(), {H1}, "GEOMETRY");
1208 pip->getOpBoundaryRhsPipeline(), {NOSPACE}, "GEOMETRY");
1210 pip->getOpBoundaryLhsPipeline(), {NOSPACE}, "GEOMETRY");
1211
1212 //! [Push domain stiffness matrix to pipeline]
1213 // Add LHS operator for elasticity (stiffness matrix)
1214 CHKERR HookeOps::opFactoryDomainLhs<SPACE_DIM, A, I, DomainEleOp>(
1215 mField, pip->getOpDomainLhsPipeline(), "U", "MAT_ELASTIC", Sev::noisy);
1216 //! [Push domain stiffness matrix to pipeline]
1217
1218 // Add RHS operator for internal forces
1219 CHKERR HookeOps::opFactoryDomainRhs<SPACE_DIM, A, I, DomainEleOp>(
1220 mField, pip->getOpDomainRhsPipeline(), "U", "MAT_ELASTIC", Sev::noisy);
1221
1222 //! [Push Body forces]
1224 pip->getOpDomainRhsPipeline(), mField, "U", Sev::inform);
1225 //! [Push Body forces]
1226
1227 //! [Push natural boundary conditions]
1228 // Add force boundary condition
1230 pip->getOpBoundaryRhsPipeline(), mField, "U", -1, Sev::inform);
1231 // Add case for mix type of BCs
1233 pip->getOpBoundaryLhsPipeline(), mField, "U", Sev::noisy);
1234 //! [Push natural boundary conditions]
1236}
1237//! [Push operators to pipeline]
1238
1239//! [Solve]
1242 auto simple = mField.getInterface<Simple>();
1243 auto dm = simple->getDM();
1244 auto f = createDMVector(dm);
1245 auto d = vectorDuplicate(f);
1246 CHKERR VecZeroEntries(d);
1247 CHKERR DMoFEMMeshToLocalVector(dm, d, INSERT_VALUES, SCATTER_REVERSE);
1248
1249 auto set_essential_bc = [&]() {
1251 // This is low level pushing finite elements (pipelines) to solver
1252
1253 auto ksp_ctx_ptr = getDMKspCtx(dm);
1254 auto pre_proc_rhs = boost::make_shared<FEMethod>();
1255 auto post_proc_rhs = boost::make_shared<FEMethod>();
1256 auto post_proc_lhs = boost::make_shared<FEMethod>();
1257
1258 auto get_pre_proc_hook = [&]() {
1260 {});
1261 };
1262 pre_proc_rhs->preProcessHook = get_pre_proc_hook();
1263
1264 auto get_post_proc_hook_rhs = [this, post_proc_rhs]() {
1266
1268 post_proc_rhs, 1.)();
1270 };
1271
1272 auto get_post_proc_hook_lhs = [this, post_proc_lhs]() {
1274
1276 post_proc_lhs, 1.)();
1278 };
1279
1280 post_proc_rhs->postProcessHook = get_post_proc_hook_rhs;
1281 post_proc_lhs->postProcessHook = get_post_proc_hook_lhs;
1282
1283 ksp_ctx_ptr->getPreProcComputeRhs().push_front(pre_proc_rhs);
1284 ksp_ctx_ptr->getPostProcComputeRhs().push_back(post_proc_rhs);
1285 ksp_ctx_ptr->getPostProcSetOperators().push_back(post_proc_lhs);
1287 };
1288
1289 auto setup_and_solve = [&](auto solver) {
1291 BOOST_LOG_SCOPED_THREAD_ATTR("Timeline", attrs::timer());
1292 MOFEM_LOG("TIMER", Sev::noisy) << "KSPSetUp";
1293 CHKERR KSPSetUp(solver);
1294 MOFEM_LOG("TIMER", Sev::noisy) << "KSPSetUp <= Done";
1295 MOFEM_LOG("TIMER", Sev::noisy) << "KSPSolve";
1296 CHKERR KSPSolve(solver, f, d);
1297 MOFEM_LOG("TIMER", Sev::noisy) << "KSPSolve <= Done";
1299 };
1300
1301 MOFEM_LOG_CHANNEL("TIMER");
1302 MOFEM_LOG_TAG("TIMER", "timer");
1303
1304 CHKERR set_essential_bc();
1305 CHKERR setup_and_solve(kspElastic);
1306
1307 CHKERR VecGhostUpdateBegin(d, INSERT_VALUES, SCATTER_FORWARD);
1308 CHKERR VecGhostUpdateEnd(d, INSERT_VALUES, SCATTER_FORWARD);
1309 CHKERR DMoFEMMeshToLocalVector(dm, d, INSERT_VALUES, SCATTER_REVERSE);
1310
1311 auto evaluate_field_at_the_point = [&]() {
1313
1314 int coords_dim = 3;
1315 std::array<double, 3> field_eval_coords{0.0, 0.0, 0.0};
1316 PetscBool do_eval_field = PETSC_FALSE;
1317 CHKERR PetscOptionsGetRealArray(NULL, NULL, "-field_eval_coords",
1318 field_eval_coords.data(), &coords_dim,
1319 &do_eval_field);
1320
1321 if (do_eval_field) {
1322
1323 auto vector_field_ptr = boost::make_shared<MatrixDouble>();
1324 auto field_eval_data =
1325 mField.getInterface<FieldEvaluatorInterface>()->getData<DomainEle>();
1326
1328 ->buildTree<SPACE_DIM>(field_eval_data, simple->getDomainFEName());
1329
1330 field_eval_data->setEvalPoints(field_eval_coords.data(), 1);
1331 auto no_rule = [](int, int, int) { return -1; };
1332 auto field_eval_fe_ptr = field_eval_data->feMethodPtr;
1333 field_eval_fe_ptr->getRuleHook = no_rule;
1334
1335 field_eval_fe_ptr->getOpPtrVector().push_back(
1336 new OpCalculateVectorFieldValues<SPACE_DIM>("U", vector_field_ptr));
1337
1339 ->evalFEAtThePoint<SPACE_DIM>(
1340 field_eval_coords.data(), 1e-12, simple->getProblemName(),
1341 simple->getDomainFEName(), field_eval_data,
1343 QUIET);
1344
1345 if (vector_field_ptr->size1()) {
1346 auto t_disp = getFTensor1FromMat<SPACE_DIM>(*vector_field_ptr);
1347 if constexpr (SPACE_DIM == 2)
1348 MOFEM_LOG("FieldEvaluator", Sev::inform)
1349 << "U_X: " << t_disp(0) << " U_Y: " << t_disp(1);
1350 else
1351 MOFEM_LOG("FieldEvaluator", Sev::inform)
1352 << "U_X: " << t_disp(0) << " U_Y: " << t_disp(1)
1353 << " U_Z: " << t_disp(2);
1354 }
1355
1357 }
1359 };
1360
1361 CHKERR evaluate_field_at_the_point();
1362
1364}
1365//! [Solve]
1366
1367//! [Postprocess results]
1369 SmartPetscObj<Vec> adjoint_vector) {
1371 auto simple = mField.getInterface<Simple>();
1375 "out_elastic_" + std::to_string(iter) + ".h5m",
1376 {{"ADJOINT", adjoint_vector}}, {}, Sev::noisy);
1378}
1379//! [Postprocess results]
1380
1381//! [calculateGradient]
1382
1384
1385template <int SPACE_DIM>
1387 DomainBaseOp> : public DomainBaseOp {
1388
1390
1392 boost::shared_ptr<HookeOps::CommonData> comm_ptr,
1393 boost::shared_ptr<MatrixDouble> jac,
1394 boost::shared_ptr<MatrixDouble> diff_jac,
1395 boost::shared_ptr<VectorDouble> cof_vals)
1396 : OP(field_name, field_name, DomainEleOp::OPROW), commPtr(comm_ptr),
1397 jac(jac), diffJac(diff_jac), cofVals(cof_vals) {}
1398
1399protected:
1400 boost::shared_ptr<HookeOps::CommonData> commPtr;
1401 boost::shared_ptr<MatrixDouble> jac;
1402 boost::shared_ptr<MatrixDouble> diffJac;
1403 boost::shared_ptr<VectorDouble> cofVals;
1405};
1406
1407template <int SPACE_DIM>
1418
1419 auto t_diff_symm = FTensor::diff_symmetrize<double>();
1420
1421 // get element volume
1422 const double vol = OP::getMeasure();
1423 // get integration weights
1424 auto t_w = OP::getFTensor0IntegrationWeight();
1425 // get Jacobian values
1426 auto t_jac = getFTensor2FromMat<SPACE_DIM, SPACE_DIM>(*(jac));
1427 // get diff Jacobian values
1428 auto t_diff_jac = getFTensor2FromMat<SPACE_DIM, SPACE_DIM>(*(diffJac));
1429 // get cofactor values
1430 auto t_cof = getFTensor0FromVec(*(cofVals));
1431 // get base function gradient on rows
1432 auto t_row_grad = row_data.getFTensor1DiffN<SPACE_DIM>();
1433 // get fradient of the field
1434 auto t_grad_u =
1435 getFTensor2FromMat<SPACE_DIM, SPACE_DIM>(*(commPtr->matGradPtr));
1436 // get field gradient values
1437 auto t_cauchy_stress =
1438 getFTensor2SymmetricFromMat<SPACE_DIM>(*(commPtr->getMatCauchyStress()));
1439 // material stiffness tensor
1440 auto t_D =
1441 getFTensor4DdgFromMat<SPACE_DIM, SPACE_DIM, 0>(*(commPtr->matDPtr));
1442 // loop over integration points
1443 for (int gg = 0; gg != OP::nbIntegrationPts; gg++) {
1444 // take into account Jacobian
1445 const double alpha = t_w * vol;
1446
1447 auto t_det = determinantTensor(t_jac);
1449 CHKERR invertTensor(t_jac, t_det, t_inv_jac);
1450
1451 // Calculate the variation of the gradient due to geometry change
1453 t_diff_inv_jac(i, j) =
1454 -(t_inv_jac(i, I) * t_diff_jac(I, J)) * t_inv_jac(J, j);
1456 t_diff_grad(i, j) = t_grad_u(i, k) * t_diff_inv_jac(k, j);
1457
1458 // Calculate the variation of the strain tensor
1460 t_diff_strain(i, j) = t_diff_symm(i, j, k, l) * t_diff_grad(k, l);
1461
1462 // Calculate the variation of the stress tensor
1464 t_diff_stress(i, j) = t_D(i, j, k, l) * t_diff_strain(k, l);
1465
1466 // get rhs vector
1467 auto t_nf = OP::template getNf<SPACE_DIM>();
1468 // loop over rows base functions
1469 int rr = 0;
1470 for (; rr != OP::nbRows / SPACE_DIM; rr++) {
1471
1473 t_diff_row_grad(k) = t_row_grad(j) * t_diff_inv_jac(j, k);
1474
1475 // Variation due to change in geometry (diff base)
1476 t_nf(j) += alpha * t_diff_row_grad(i) * t_cauchy_stress(i, j);
1477
1478 // Variation due to change in domain (cofactor)
1479 t_nf(j) += (alpha * t_cof) * t_row_grad(i) * t_cauchy_stress(i, j);
1480
1481 // Variation due to change in stress (diff stress)
1482 t_nf(j) += alpha * t_row_grad(i) * t_diff_stress(i, j);
1483
1484 ++t_row_grad;
1485 ++t_nf;
1486 }
1487 for (; rr < OP::nbRowBaseFunctions; ++rr) {
1488 ++t_row_grad;
1489 }
1490
1491 ++t_grad_u;
1492 ++t_cauchy_stress;
1493 ++t_jac;
1494 ++t_diff_jac;
1495 ++t_cof;
1496 ++t_w; // move to another integration weight
1497 }
1499}
1500
1501//J = 1/2 * sigma : epsilon
1502// calculate dJ/du = (dJ/dsigma * dsigma/depsilon + dJ/depsilon) depsilon / dgrad_u * dgrad_u / du
1506 boost::shared_ptr<ObjectiveFunctionData> python_ptr,
1507 boost::shared_ptr<HookeOps::CommonData> comm_ptr,
1508 boost::shared_ptr<MatrixDouble> u_ptr)
1509 : OP(field_name, field_name, DomainEleOp::OPROW), pythonPtr(python_ptr),
1510 commPtr(comm_ptr), uPtr(u_ptr) {}
1511
1518
1519 constexpr auto symm_size = (SPACE_DIM * (SPACE_DIM + 1)) / 2;
1520 auto nb_gauss_pts = getGaussPts().size2();
1521
1522 auto objective_dstress =
1523 boost::make_shared<MatrixDouble>(nb_gauss_pts, symm_size);
1524 auto objective_dstrain =
1525 boost::make_shared<MatrixDouble>(nb_gauss_pts, symm_size);
1526 auto objective_du =
1527 boost::make_shared<MatrixDouble>(nb_gauss_pts, SPACE_DIM);
1528
1529 auto evaluate_python = [&]() {
1531 auto &coords = OP::getCoordsAtGaussPts();
1532 CHKERR pythonPtr->evalInteriorObjectiveGradientStress(
1533 coords, uPtr, commPtr->getMatCauchyStress(), commPtr->getMatStrain(),
1534 objective_dstress);
1535 CHKERR pythonPtr->evalInteriorObjectiveGradientStrain(
1536 coords, uPtr, commPtr->getMatCauchyStress(), commPtr->getMatStrain(),
1537 objective_dstrain);
1538 CHKERR pythonPtr->evalInteriorObjectiveGradientU(
1539 coords, uPtr, commPtr->getMatCauchyStress(), commPtr->getMatStrain(),
1540 objective_du);
1541
1542 auto vol = OP::getMeasure();
1543 auto t_w = OP::getFTensor0IntegrationWeight();
1544
1545 auto t_D =
1546 getFTensor4DdgFromMat<SPACE_DIM, SPACE_DIM, 0>(*(commPtr->matDPtr));
1547 auto t_row_grad = data.getFTensor1DiffN<SPACE_DIM>();
1548 auto t_row_base = data.getFTensor0N();
1549
1550 auto t_obj_dstress =
1551 getFTensor2SymmetricFromMat<SPACE_DIM>(*objective_dstress);
1552 auto t_obj_dstrain =
1553 getFTensor2SymmetricFromMat<SPACE_DIM>(*objective_dstrain);
1554 auto t_obj_du = getFTensor1FromMat<SPACE_DIM>(*objective_du);
1555
1556 for (int gg = 0; gg != nb_gauss_pts; ++gg) {
1557 const double alpha = t_w * vol;
1559 t_adjoint_stress(i, j) =
1560 t_D(i, j, k, l) * t_obj_dstress(k, l) + t_obj_dstrain(i, j);
1561
1562 auto t_nf = OP::template getNf<SPACE_DIM>();
1563 int rr = 0;
1564 for (; rr != OP::nbRows / SPACE_DIM; rr++) {
1565 t_nf(j) += alpha * t_row_grad(i) * t_adjoint_stress(i, j);
1566 t_nf(j) += alpha * t_row_base * t_obj_du(j);
1567
1568 ++t_row_grad;
1569 ++t_row_base;
1570 ++t_nf;
1571 }
1572
1573 for (; rr < OP::nbRowBaseFunctions; ++rr) {
1574 ++t_row_grad;
1575 ++t_row_base;
1576 }
1577 ++t_obj_dstrain;
1578 ++t_obj_dstress;
1579 ++t_obj_du;
1580 ++t_w;
1581 }
1583 };
1584 CHKERR evaluate_python();
1586 }
1587
1588private:
1589 boost::shared_ptr<ObjectiveFunctionData> pythonPtr;
1590 boost::shared_ptr<HookeOps::CommonData> commPtr;
1591 boost::shared_ptr<MatrixDouble> uPtr;
1592};
1595
1596 OpAdJointObjective(boost::shared_ptr<ObjectiveFunctionData> python_ptr,
1597 boost::shared_ptr<HookeOps::CommonData> comm_ptr,
1598 boost::shared_ptr<MatrixDouble> jac_ptr,
1599 boost::shared_ptr<MatrixDouble> diff_jac,
1600 boost::shared_ptr<VectorDouble> cof_vals,
1601 boost::shared_ptr<MatrixDouble> d_grad_ptr,
1602 boost::shared_ptr<MatrixDouble> d_u_ptr,
1603 boost::shared_ptr<MatrixDouble> u_ptr,
1604 boost::shared_ptr<double> glob_objective_ptr,
1605 boost::shared_ptr<double> glob_objective_grad_ptr)
1606 : OP(NOSPACE, OP::OPSPACE), pythonPtr(python_ptr), commPtr(comm_ptr),
1607 jacPtr(jac_ptr), diffJacPtr(diff_jac), cofVals(cof_vals),
1608 dGradPtr(d_grad_ptr), dUPtr(d_u_ptr), uPtr(u_ptr),
1609 globObjectivePtr(glob_objective_ptr),
1610 globObjectiveGradPtr(glob_objective_grad_ptr) {}
1611
1612 /**
1613 * @brief Compute objective function contributions at element level
1614 *
1615 * Evaluates Python objective function with current displacement and stress
1616 * state, and accumulates global objective value and gradients.
1617 */
1618 MoFEMErrorCode doWork(int side, EntityType type,
1621
1622 // Define tensor indices for calculations
1627
1630
1631 constexpr auto symm_size = (SPACE_DIM * (SPACE_DIM + 1)) /
1632 2; // size of symmetric tensor in Voigt notation
1633
1634 auto t_diff_symm = FTensor::diff_symmetrize<double>();
1635 // fourth order tensor for symmetrization to convert from gradient to strain
1636
1637 auto nb_gauss_pts =
1638 getGaussPts().size2(); // number of gauss points in the element
1639 auto objective_ptr = boost::make_shared<MatrixDouble>(
1640 1, nb_gauss_pts); // objective function values at gauss points
1641 auto objective_dstress = boost::make_shared<MatrixDouble>(
1642 nb_gauss_pts,
1643 symm_size); // objective function gradient w.r.t. stress at gauss points
1644 auto objective_dstrain = boost::make_shared<MatrixDouble>(
1645 nb_gauss_pts,
1646 symm_size); // objective function gradient w.r.t. strain at gauss points
1647 auto objective_du =
1648 boost::make_shared<MatrixDouble>(nb_gauss_pts, SPACE_DIM);
1649 // We have dJ/dsigma and dJ/depsilon at gauss points, we need to convert it to dJ/du
1650
1651 auto evaluate_python = [&]() {
1653 auto &coords = OP::getCoordsAtGaussPts();
1654 CHKERR pythonPtr->evalInteriorObjectiveFunction(
1655 coords, uPtr, commPtr->getMatCauchyStress(), commPtr->getMatStrain(),
1656 objective_ptr);
1657 CHKERR pythonPtr->evalInteriorObjectiveGradientStress(
1658 coords, uPtr, commPtr->getMatCauchyStress(), commPtr->getMatStrain(),
1659 objective_dstress);
1660 CHKERR pythonPtr->evalInteriorObjectiveGradientStrain(
1661 coords, uPtr, commPtr->getMatCauchyStress(), commPtr->getMatStrain(),
1662 objective_dstrain);
1663 CHKERR pythonPtr->evalInteriorObjectiveGradientU(
1664 coords, uPtr, commPtr->getMatCauchyStress(), commPtr->getMatStrain(),
1665 objective_du);
1666
1667 auto t_grad_u =
1668 getFTensor2FromMat<SPACE_DIM, SPACE_DIM>(*(commPtr->matGradPtr));
1669 auto t_D =
1670 getFTensor4DdgFromMat<SPACE_DIM, SPACE_DIM, 0>(*(commPtr->matDPtr));
1671 auto t_jac = getFTensor2FromMat<SPACE_DIM, SPACE_DIM>(*(jacPtr));
1672 auto t_diff_jac = getFTensor2FromMat<SPACE_DIM, SPACE_DIM>(*(diffJacPtr));
1673 auto t_cof = getFTensor0FromVec(*(cofVals));
1674 auto t_d_grad = getFTensor2FromMat<SPACE_DIM, SPACE_DIM>(*(dGradPtr));
1675
1676 auto t_obj = getFTensor0FromMat(*objective_ptr);
1677 auto t_obj_dstress =
1678 getFTensor2SymmetricFromMat<SPACE_DIM>(*objective_dstress);
1679 auto t_obj_dstrain =
1680 getFTensor2SymmetricFromMat<SPACE_DIM>(*objective_dstrain);
1681 auto t_obj_du = getFTensor1FromMat<SPACE_DIM>(*objective_du);
1682 auto t_d_u = getFTensor1FromMat<SPACE_DIM>(*dUPtr);
1683
1684 auto vol = OP::getMeasure();
1685 auto t_w = getFTensor0IntegrationWeight();
1686 for (auto gg = 0; gg != nb_gauss_pts; ++gg) {
1687
1688 auto t_det = determinantTensor(t_jac);
1690 CHKERR invertTensor(t_jac, t_det, t_inv_jac);
1691
1693 t_diff_inv_jac(i, j) =
1694 -(t_inv_jac(i, I) * t_diff_jac(I, J)) * t_inv_jac(J, j);
1696 t_diff_grad(i, j) = t_grad_u(i, k) * t_diff_inv_jac(k, j);
1697
1699 t_d_strain(i, j) = t_diff_symm(i, j, k, l) * (
1700
1701 t_d_grad(k, l)
1702
1703 +
1704
1705 t_diff_grad(k, l)
1706
1707 );
1708
1709 auto alpha = t_w * vol;
1710
1711 (*globObjectivePtr) += alpha * t_obj;
1712 (*globObjectiveGradPtr) +=
1713 alpha *
1714 (
1715
1716 t_obj_dstress(i, j) * (t_D(i, j, k, l) * t_d_strain(k, l))
1717
1718 +
1719
1720 t_obj_dstrain(i, j) * t_d_strain(i, j)
1721
1722 +
1723
1724 t_obj_du(i) * t_d_u(i)
1725
1726 +
1727
1728 t_obj * t_cof
1729
1730 );
1731
1732 ++t_w;
1733 ++t_jac;
1734 ++t_diff_jac;
1735 ++t_cof;
1736
1737 ++t_obj;
1738 ++t_obj_dstress;
1739 ++t_obj_dstrain;
1740 ++t_obj_du;
1741
1742 ++t_grad_u;
1743 ++t_d_grad;
1744 ++t_d_u;
1745 }
1747 };
1748
1749 CHKERR evaluate_python();
1750
1752 }
1753
1754private:
1755 boost::shared_ptr<ObjectiveFunctionData> pythonPtr;
1756 boost::shared_ptr<HookeOps::CommonData> commPtr;
1757 boost::shared_ptr<MatrixDouble> jacPtr;
1758 boost::shared_ptr<MatrixDouble> diffJacPtr;
1759 boost::shared_ptr<VectorDouble> cofVals;
1760 boost::shared_ptr<MatrixDouble> dGradPtr;
1761 boost::shared_ptr<MatrixDouble> dUPtr;
1762 boost::shared_ptr<MatrixDouble> uPtr;
1763
1764 boost::shared_ptr<double> globObjectivePtr;
1765 boost::shared_ptr<double> globObjectiveGradPtr;
1766};
1767
1768MoFEMErrorCode Example::calculateGradient(PetscReal *objective_function_value,
1769 Vec objective_function_gradient,
1770 Vec adjoint_vector) {
1771 MOFEM_LOG_CHANNEL("WORLD");
1773 auto simple = mField.getInterface<Simple>();
1774
1775 auto ents = get_range_from_block(mField, "OPTIMISE", SPACE_DIM - 1);
1776
1777 auto get_essential_fe = [this]() {
1778 auto post_proc_rhs = boost::make_shared<FEMethod>();
1779 auto get_post_proc_hook_rhs = [this, post_proc_rhs]() {
1781
1783 post_proc_rhs, 0)();
1785 };
1786 post_proc_rhs->postProcessHook = get_post_proc_hook_rhs;
1787 return post_proc_rhs;
1788 };
1789
1790 auto get_fd_direvative_fe = [&]() {
1791 auto fe = boost::make_shared<DomainEle>(mField);
1792 fe->getRuleHook = integrationRule;
1793 auto &pip = fe->getOpPtrVector();
1795 // Add RHS operators for internal forces
1796 HookeOps::opFactoryDomainRhs<SPACE_DIM, A, I, DomainEleOp>(
1797 mField, pip, "U", "MAT_ELASTIC", Sev::noisy);
1798
1799 return fe;
1800 };
1801
1802 auto calulate_fd_residual = [&](auto eps, auto diff_vec, auto fd_vec) {
1804
1805 constexpr bool debug = false;
1806
1807 auto geom_norm = [](MoFEM::Interface &mField) {
1809 auto field_blas = mField.getInterface<FieldBlas>();
1810 double nrm2 = 0.0;
1811 auto norm2_field = [&](const double val) {
1812 nrm2 += val * val;
1813 return val;
1814 };
1815 CHKERR field_blas->fieldLambdaOnValues(norm2_field, "GEOMETRY");
1816 MPI_Allreduce(MPI_IN_PLACE, &nrm2, 1, MPI_DOUBLE, MPI_SUM,
1817 mField.get_comm());
1818 MOFEM_LOG("WORLD", Sev::inform) << "Geometry norm: " << sqrt(nrm2);
1820 };
1821
1822 if constexpr (debug)
1823 CHKERR geom_norm(mField);
1824
1825 auto initial_current_geometry = createDMVector(adjointDM);
1826 CHKERR mField.getInterface<VecManager>()->setOtherLocalGhostVector(
1827 "ADJOINT", "ADJOINT_FIELD", "GEOMETRY", RowColData::ROW,
1828 initial_current_geometry, INSERT_VALUES, SCATTER_FORWARD);
1829 CHKERR VecAssemblyBegin(initial_current_geometry);
1830 CHKERR VecAssemblyEnd(initial_current_geometry);
1831
1832 if constexpr (debug)
1833 CHKERR geom_norm(mField);
1834
1835 auto perturb_geometry = [&](auto eps, auto diff_vec) {
1837 auto current_geometry = vectorDuplicate(initial_current_geometry);
1838 CHKERR VecCopy(initial_current_geometry, current_geometry);
1839 CHKERR VecAXPY(current_geometry, eps, diff_vec);
1840 CHKERR mField.getInterface<VecManager>()->setOtherLocalGhostVector(
1841 "ADJOINT", "ADJOINT_FIELD", "GEOMETRY", RowColData::ROW,
1842 current_geometry, INSERT_VALUES, SCATTER_REVERSE);
1844 };
1845
1846 auto fe = get_fd_direvative_fe();
1847 auto fp = vectorDuplicate(diff_vec);
1848 auto fm = vectorDuplicate(diff_vec);
1849 auto calc_impl = [&](auto f, auto eps) { // is this finite difference!
1851 CHKERR VecZeroEntries(f);
1852 fe->f = f;
1853 CHKERR perturb_geometry(eps, diff_vec);
1855 simple->getDomainFEName(), fe);
1856 CHKERR VecAssemblyBegin(f);
1857 CHKERR VecAssemblyEnd(f);
1858 CHKERR VecGhostUpdateBegin(f, ADD_VALUES, SCATTER_REVERSE);
1859 CHKERR VecGhostUpdateEnd(f, ADD_VALUES, SCATTER_REVERSE);
1860 auto post_proc_rhs = get_essential_fe();
1861 post_proc_rhs->f = f;
1863 post_proc_rhs.get());
1865 };
1866 CHKERR calc_impl(fp, eps);
1867 CHKERR calc_impl(fm, -eps);
1868 CHKERR VecWAXPY(fd_vec, -1.0, fm, fp);
1869 CHKERR VecScale(fd_vec, 1.0 / (2.0 * eps));
1870
1871 CHKERR mField.getInterface<VecManager>()->setOtherLocalGhostVector(
1872 "ADJOINT", "ADJOINT_FIELD", "GEOMETRY", RowColData::ROW,
1873 initial_current_geometry, INSERT_VALUES, SCATTER_REVERSE);
1874
1875 if constexpr (debug)
1876 CHKERR geom_norm(mField);
1877
1879 };
1880 // here starts the preperation for the derivative
1881 auto get_direvative_fe = [&](auto diff_vec) {
1882 auto fe_adjoint = boost::make_shared<DomainEle>(mField);
1883 fe_adjoint->getRuleHook = integrationRule;
1884 auto &pip = fe_adjoint->getOpPtrVector();
1885
1886 auto jac_ptr = boost::make_shared<MatrixDouble>();
1887 auto det_ptr = boost::make_shared<VectorDouble>();
1888 auto inv_jac_ptr = boost::make_shared<MatrixDouble>();
1889 auto diff_jac_ptr = boost::make_shared<MatrixDouble>();
1890 auto cof_ptr = boost::make_shared<VectorDouble>();
1891
1892 using OpCoFactor =
1893 AdJoint<DomainEleOp>::Integration<GAUSS>::OpGetCoFactor<SPACE_DIM>;
1894
1896
1898 "GEOMETRY", jac_ptr));
1900 "U", diff_jac_ptr, diff_vec));
1901
1902 pip.push_back(new OpCoFactor(jac_ptr, diff_jac_ptr, cof_ptr));
1903
1904 // Add RHS operators for internal forces
1905 auto common_ptr = HookeOps::commonDataFactory<SPACE_DIM, I, DomainEleOp>(
1906 mField, pip, "U", "MAT_ELASTIC", Sev::noisy);
1908 "U", common_ptr, jac_ptr, diff_jac_ptr, cof_ptr));
1909
1910 return fe_adjoint;
1911 };
1912
1913 auto get_objective_fe = [&](auto diff_vec, auto grad_vec,
1914 auto glob_objective_ptr,
1915 auto glob_objective_grad_ptr) {
1916 auto fe_adjoint = boost::make_shared<DomainEle>(mField);
1917 fe_adjoint->getRuleHook = integrationRule;
1918 auto &pip = fe_adjoint->getOpPtrVector();
1920
1921 auto jac_ptr = boost::make_shared<MatrixDouble>();
1922 auto det_ptr = boost::make_shared<VectorDouble>();
1923 auto inv_jac_ptr = boost::make_shared<MatrixDouble>();
1924 auto diff_jac_ptr = boost::make_shared<MatrixDouble>();
1925 auto cof_ptr = boost::make_shared<VectorDouble>();
1926 auto d_grad_ptr = boost::make_shared<MatrixDouble>();
1927 auto d_u_ptr = boost::make_shared<MatrixDouble>();
1928 auto u_ptr = boost::make_shared<MatrixDouble>();
1929
1930 using OpCoFactor =
1931 AdJoint<DomainEleOp>::Integration<GAUSS>::OpGetCoFactor<SPACE_DIM>;
1933 "GEOMETRY", jac_ptr));
1935 "U", diff_jac_ptr,
1936 diff_vec)); // Note: that vector is stored on displacemnt vector, that
1937 // why is used here
1938 pip.push_back(new OpCoFactor(jac_ptr, diff_jac_ptr, cof_ptr));
1940 "U", d_grad_ptr, grad_vec));
1941 pip.push_back(
1942 new OpCalculateVectorFieldValues<SPACE_DIM>("U", d_u_ptr, grad_vec));
1943 pip.push_back(new OpCalculateVectorFieldValues<SPACE_DIM>("U", u_ptr));
1944
1945 auto common_ptr = HookeOps::commonDataFactory<SPACE_DIM, I, DomainEleOp>(
1946 mField, pip, "U", "MAT_ELASTIC", Sev::noisy);
1947 pip.push_back(new OpAdJointObjective(
1948 pythonPtr, common_ptr, jac_ptr, diff_jac_ptr, cof_ptr, d_grad_ptr,
1949 d_u_ptr, u_ptr, glob_objective_ptr, glob_objective_grad_ptr));
1950
1951 return fe_adjoint;
1952 };
1953
1954 auto dm = simple->getDM();
1955 auto f = createDMVector(dm);
1956 auto d = vectorDuplicate(f);
1957 auto dm_diff_vec = vectorDuplicate(d);
1958 auto zero_diff_vec = vectorDuplicate(dm_diff_vec);
1959 auto zero_state_sensitivity = vectorDuplicate(d);
1960 CHKERR VecZeroEntries(zero_diff_vec);
1961 CHKERR VecZeroEntries(zero_state_sensitivity);
1962 CHKERR VecGhostUpdateBegin(zero_diff_vec, INSERT_VALUES, SCATTER_FORWARD);
1963 CHKERR VecGhostUpdateEnd(zero_diff_vec, INSERT_VALUES, SCATTER_FORWARD);
1964 CHKERR VecGhostUpdateBegin(zero_state_sensitivity, INSERT_VALUES,
1965 SCATTER_FORWARD);
1966 CHKERR VecGhostUpdateEnd(zero_state_sensitivity, INSERT_VALUES,
1967 SCATTER_FORWARD);
1968
1969 auto adjoint_fe = get_direvative_fe(dm_diff_vec);
1970 auto objective_ptr_direct = boost::make_shared<double>(0.0);
1971 auto objective_grad_ptr_direct = boost::make_shared<double>(0.0);
1972 auto objective_fe_direct = get_objective_fe(
1973 dm_diff_vec, d, objective_ptr_direct, objective_grad_ptr_direct);
1974 auto objective_ptr_explicit = boost::make_shared<double>(0.0);
1975 auto objective_grad_ptr_explicit = boost::make_shared<double>(0.0);
1976 auto objective_fe_explicit =
1977 get_objective_fe(dm_diff_vec, zero_state_sensitivity,
1978 objective_ptr_explicit, objective_grad_ptr_explicit);
1979 auto objective_ptr_value = boost::make_shared<double>(0.0);
1980 auto objective_grad_ptr_value = boost::make_shared<double>(0.0);
1981 auto objective_fe_value =
1982 get_objective_fe(zero_diff_vec, zero_state_sensitivity,
1983 objective_ptr_value, objective_grad_ptr_value);
1984
1985 auto set_variance_of_geometry =
1986 [&](auto mode, auto mod_vec) { // think of mod_vec as X(tau) = X + tau*v_h
1987 // take the mod_vec and write it into the adjoint DM then copy that
1988 // field to dm_diff_vec using the same layout as U
1990 CHKERR DMoFEMMeshToLocalVector(adjointDM, mod_vec, INSERT_VALUES,
1991 SCATTER_REVERSE);
1992 CHKERR mField.getInterface<VecManager>()->setOtherLocalGhostVector(
1993 simple->getProblemName(), "U", "ADJOINT_FIELD", RowColData::ROW,
1994 dm_diff_vec, INSERT_VALUES, SCATTER_FORWARD);
1995 CHKERR VecGhostUpdateBegin(dm_diff_vec, INSERT_VALUES, SCATTER_FORWARD);
1996 CHKERR VecGhostUpdateEnd(dm_diff_vec, INSERT_VALUES, SCATTER_FORWARD);
1998 };
1999
2000 auto calculate_variance_internal_forces = [&](auto mode, auto mod_vec) {
2002 CHKERR VecZeroEntries(f);
2003 CHKERR VecGhostUpdateBegin(f, INSERT_VALUES, SCATTER_FORWARD);
2004 CHKERR VecGhostUpdateEnd(f, INSERT_VALUES, SCATTER_FORWARD);
2005 adjoint_fe->f = f;
2007 CHKERR VecAssemblyBegin(f);
2008 CHKERR VecAssemblyEnd(f);
2009 CHKERR VecGhostUpdateBegin(f, ADD_VALUES, SCATTER_REVERSE);
2010 CHKERR VecGhostUpdateEnd(f, ADD_VALUES, SCATTER_REVERSE);
2011 auto post_proc_rhs = get_essential_fe();
2012 post_proc_rhs->f = f;
2014 post_proc_rhs.get());
2015 CHKERR VecScale(f, -1.0);
2016
2017#ifndef NDEBUG
2018 constexpr bool debug = true;
2019 if constexpr (debug) {
2020 double norm0;
2021 CHKERR VecNorm(f, NORM_2, &norm0);
2022 auto fd_check = vectorDuplicate(f);
2023 double eps = 1e-5;
2024 CHKERR calulate_fd_residual(eps, dm_diff_vec, fd_check);
2025 double nrm;
2026 CHKERR VecAXPY(fd_check, -1.0, f);
2027 CHKERR VecNorm(fd_check, NORM_2, &nrm);
2028 MOFEM_LOG("WORLD", Sev::inform)
2029 << " FD check for internal forces [ " << mode << " ]: " << nrm
2030 << " / " << norm0 << " ( " << (nrm / norm0) << " )";
2031 }
2032#endif
2033
2035 };
2036
2037 auto calculate_variance_of_displacement = [&](auto mode, auto mod_vec) {
2039 CHKERR KSPSolve(kspElastic, f, d);
2040
2041 CHKERR VecGhostUpdateBegin(d, INSERT_VALUES, SCATTER_FORWARD);
2042 CHKERR VecGhostUpdateEnd(d, INSERT_VALUES, SCATTER_FORWARD);
2044 };
2045
2046 auto evaluate_objective_terms =
2047 [&](auto objective_fe, auto objective_ptr, auto objective_grad_ptr,
2048 double &objective_value, double &objective_gradient) {
2050 *objective_ptr = 0.0;
2051 *objective_grad_ptr = 0.0;
2053 objective_fe);
2054 std::array<double, 2> array = {*objective_ptr, *objective_grad_ptr};
2055 MPI_Allreduce(MPI_IN_PLACE, array.data(), 2, MPI_DOUBLE, MPI_SUM,
2056 mField.get_comm());
2057 objective_value = array[0];
2058 objective_gradient = array[1];
2060 };
2061
2062 auto calculate_objective_value = [&]() {
2064 double objective_value = 0;
2065 double objective_gradient = 0;
2066 CHKERR evaluate_objective_terms(objective_fe_value, objective_ptr_value,
2067 objective_grad_ptr_value, objective_value,
2068 objective_gradient);
2069 *objective_function_value = objective_value;
2071 };
2072
2073 auto calculate_variance_of_objective_function_dJ_du = [&](Vec dJ_du) {
2075
2076 auto fe = boost::make_shared<DomainEle>(mField);
2077 fe->getRuleHook = integrationRule;
2078 auto &pip = fe->getOpPtrVector();
2080
2081 auto u_ptr = boost::make_shared<MatrixDouble>();
2082 pip.push_back(new OpCalculateVectorFieldValues<SPACE_DIM>("U", u_ptr));
2083
2084 auto common_ptr = HookeOps::commonDataFactory<SPACE_DIM, I, DomainEleOp>(
2085 mField, pip, "U", "MAT_ELASTIC", Sev::noisy);
2086 pip.push_back(new OpStateSensitivity("U", pythonPtr, common_ptr, u_ptr));
2087
2088 CHKERR VecZeroEntries(dJ_du);
2089 fe->f = dJ_du;
2091 CHKERR VecAssemblyBegin(dJ_du);
2092 CHKERR VecAssemblyEnd(dJ_du);
2093
2094 auto post_proc_rhs = get_essential_fe();
2095 post_proc_rhs->f = dJ_du;
2097 post_proc_rhs.get());
2098
2100 };
2101
2102 auto calculate_adjoint_lambda = [&](auto lambda, auto dJ_du) {
2104
2105 MOFEM_LOG("WORLD", Sev::inform) << "Solving for adjoint variable lambda";
2106 CHKERR VecZeroEntries(lambda);
2107 CHKERR KSPSolveTranspose(kspElastic, dJ_du, lambda);
2108 CHKERR VecGhostUpdateBegin(lambda, INSERT_VALUES, SCATTER_FORWARD);
2109 CHKERR VecGhostUpdateEnd(lambda, INSERT_VALUES, SCATTER_FORWARD);
2111 };
2112
2113 CHKERR VecZeroEntries(objective_function_gradient);
2114 CHKERR VecZeroEntries(adjoint_vector);
2115
2116 CHKERR calculate_objective_value();
2117 MOFEM_LOG("WORLD", Sev::verbose)
2118 << "Objective function: " << *objective_function_value;
2119
2120 auto direct = [&]() {
2122 int mode = 0;
2123 for (auto mod_vec : modeVecs) {
2124 CHKERR set_variance_of_geometry(mode, mod_vec);
2125 CHKERR calculate_variance_internal_forces(mode, mod_vec);
2126 CHKERR calculate_variance_of_displacement(mode, mod_vec);
2127 double objective_value = 0;
2128 double objective_gradient = 0;
2129 CHKERR evaluate_objective_terms(objective_fe_direct, objective_ptr_direct,
2130 objective_grad_ptr_direct,
2131 objective_value, objective_gradient);
2132 CHKERR VecSetValue(objective_function_gradient, mode, objective_gradient,
2133 INSERT_VALUES);
2134 CHKERR VecAXPY(adjoint_vector, objective_gradient, dm_diff_vec);
2135 ++mode;
2136 }
2138 };
2139
2140 auto lambda = vectorDuplicate(f);
2141 auto dJ_du = vectorDuplicate(f);
2142
2143 auto adjoint = [&]() {
2145
2146 CHKERR calculate_variance_of_objective_function_dJ_du(dJ_du);
2147 CHKERR calculate_adjoint_lambda(lambda, dJ_du);
2148
2149 int mode = 0;
2150
2151 for (auto mod_vec : modeVecs) {
2152
2153 CHKERR set_variance_of_geometry(mode, mod_vec);
2154 CHKERR calculate_variance_internal_forces(mode, mod_vec);
2155 double objective_value = 0;
2156 double objective_gradient_explicit = 0;
2157 CHKERR evaluate_objective_terms(
2158 objective_fe_explicit, objective_ptr_explicit,
2159 objective_grad_ptr_explicit, objective_value,
2160 objective_gradient_explicit);
2161 double lambda_dot_residual_variation = 0;
2162 CHKERR VecDot(lambda, f, &lambda_dot_residual_variation);
2163 const double dJ_dp =
2164 objective_gradient_explicit + lambda_dot_residual_variation;
2165 CHKERR VecSetValue(objective_function_gradient, mode, dJ_dp,
2166 INSERT_VALUES);
2167 CHKERR VecAXPY(adjoint_vector, dJ_dp, dm_diff_vec);
2168 ++mode;
2169 }
2170 CHKERR VecAssemblyBegin(objective_function_gradient);
2171 CHKERR VecAssemblyEnd(objective_function_gradient);
2173 };
2174
2175 switch (derivative_type) {
2176 case DIRECT:
2177 MOFEM_LOG("WORLD", Sev::inform) << "Running Direct Sensitivity...";
2178 CHKERR direct();
2179 break;
2180
2181 case ADJOINT:
2182 MOFEM_LOG("WORLD", Sev::inform) << "Running Adjoint Sensitivity...";
2183 CHKERR adjoint();
2184 break;
2185
2186 default:
2187 SETERRQ(PETSC_COMM_WORLD, MOFEM_DATA_INCONSISTENCY,
2188 "Wrong sensitivity type selected");
2189 }
2190
2191 CHKERR VecAssemblyBegin(objective_function_gradient);
2192 CHKERR VecAssemblyEnd(objective_function_gradient);
2193
2194 CHKERR VecAssemblyBegin(adjoint_vector);
2195 CHKERR VecAssemblyEnd(adjoint_vector);
2196 CHKERR VecGhostUpdateBegin(adjoint_vector, INSERT_VALUES, SCATTER_FORWARD);
2197 CHKERR VecGhostUpdateEnd(adjoint_vector, INSERT_VALUES, SCATTER_FORWARD);
2198
2200}
2201//! [calculateGradient]
2202
2203static char help[] = "...\n\n";
2204
2205/**
2206 * @brief Main function for topology optimization tutorial using adjoint method
2207 *
2208 * This tutorial demonstrates structural topology optimization using:
2209 * - MoFEM finite element library for elasticity analysis
2210 * - Adjoint method for efficient gradient computation
2211 * - TAO optimization library for design optimization
2212 * - Python interface for flexible objective function definition
2213 *
2214 * Workflow:
2215 * 1. Initialize MoFEM, PETSc and Python environments
2216 * 2. Read mesh and setup finite element problem
2217 * 3. Define objective function via Python interface
2218 * 4. Compute topology optimization modes as design variables
2219 * 5. Run gradient-based optimization using adjoint sensitivities
2220 * 6. Post-process optimized design
2221 *
2222 * The adjoint method enables efficient gradient computation with cost
2223 * independent of the number of design variables, making it suitable
2224 * for large-scale topology optimization problems.
2225 *
2226 * Required input files:
2227 * - Mesh file (.h5m format from CUBIT)
2228 * - Parameter file (param_file.petsc)
2229 * - Objective function (objective_function.py)
2230 *
2231 * @param argc Command line argument count
2232 * @param argv Command line argument values
2233 * @return int Exit code (0 for success)
2234 */
2235int main(int argc, char *argv[]) {
2236
2237 // Initialize Python environment for objective function interface
2238 Py_Initialize();
2239 np::initialize();
2240
2241 // Initialize MoFEM/PETSc and MOAB data structures
2242 const char param_file[] = "param_file.petsc";
2243 MoFEM::Core::Initialize(&argc, &argv, param_file, help);
2244
2245 auto core_log = logging::core::get();
2246 core_log->add_sink(
2248
2249 core_log->add_sink(
2250 LogManager::createSink(LogManager::getStrmSync(), "FieldEvaluator"));
2251 LogManager::setLog("FieldEvaluator");
2252 MOFEM_LOG_TAG("FieldEvaluator", "field_eval");
2253
2254 try {
2255
2256 //! [Register MoFEM discrete manager in PETSc]
2257 DMType dm_name = "DMMOFEM";
2258 CHKERR DMRegister_MoFEM(dm_name);
2259 DMType dm_name_mg = "DMMOFEM_MG";
2261 //! [Register MoFEM discrete manager in PETSc
2262
2263 //! [Create MoAB]
2264 moab::Core mb_instance; ///< mesh database
2265 moab::Interface &moab = mb_instance; ///< mesh database interface
2266 //! [Create MoAB]
2267
2268 //! [Create MoFEM]
2269 MoFEM::Core core(moab); ///< finite element database
2270 MoFEM::Interface &m_field = core; ///< finite element database interface
2271 //! [Create MoFEM]
2272
2273 //! [Example]
2274
2275 Example ex(m_field);
2276 CHKERR ex.runProblem();
2277 //! [Example]
2278 }
2280
2282
2283 if (Py_FinalizeEx() < 0) {
2284 exit(120);
2285 }
2286}
2287
2289 const std::string block_name, int dim) {
2290 Range r;
2291
2292 auto mesh_mng = m_field.getInterface<MeshsetsManager>();
2293 auto bcs = mesh_mng->getCubitMeshsetPtr(
2294
2295 std::regex((boost::format("%s(.*)") % block_name).str())
2296
2297 );
2298
2299 for (auto bc : bcs) {
2300 Range faces;
2301 CHK_MOAB_THROW(bc->getMeshsetIdEntitiesByDimension(m_field.get_moab(), dim,
2302 faces, true),
2303 "get meshset ents");
2304 r.merge(faces);
2305 }
2306
2307 for (auto dd = dim - 1; dd >= 0; --dd) {
2308 if (dd >= 0) {
2309 Range ents;
2310 CHK_MOAB_THROW(m_field.get_moab().get_adjacencies(r, dd, false, ents,
2311 moab::Interface::UNION),
2312 "get adjs");
2313 r.merge(ents);
2314 } else {
2315 Range verts;
2316 CHK_MOAB_THROW(m_field.get_moab().get_connectivity(r, verts),
2317 "get verts");
2318 r.merge(verts);
2319 }
2321 m_field.getInterface<CommInterface>()->synchroniseEntities(r), "comm");
2322 }
2323
2324 return r;
2325};
2326
2327MoFEMErrorCode save_range(moab::Interface &moab, const std::string name,
2328 const Range r) {
2330 auto out_meshset = get_temp_meshset_ptr(moab);
2331 CHKERR moab.add_entities(*out_meshset, r);
2332 CHKERR moab.write_file(name.c_str(), "VTK", "", out_meshset->get_ptr(), 1);
2334};
Calculate traction for linear problem.
Implementation of elastic spring bc.
Natural boundary condition applying pressure from fluid.
Implementation of Hookes operator Hookes for linear elastic problems in MoFEM.
std::string type
#define MOFEM_LOG_SYNCHRONISE(comm)
Synchronise "SYNC" channel.
Implementation of natural boundary conditions.
Boundary conditions in domain, i.e. body forces.
Interface for Python-based objective function evaluation in topology optimization.
PetscInt size
#define FTENSOR_INDEX(DIM, I)
std::string name
static char help[]
[calculateGradient]
Definition adjoint.cpp:2203
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
FormsIntegrators< DomainEleOp >::Assembly< A >::OpBase DomainBaseOp
[Postprocess results]
Definition adjoint.cpp:1383
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:2288
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 ...
constexpr int order
static const bool debug
PostProcEleByDim< SPACE_DIM >::PostProcEleDomain PostProcEleDomain
PostProcEleByDim< SPACE_DIM >::PostProcEleBdy PostProcEleBdy
@ F
PetscBool is_plane_strain
Definition gradient.cpp:37
PipelineManager::ElementsAndOpsByDim< SPACE_DIM >::DomainEle DomainEle
Domain finite elements.
Definition gradient.cpp:45
constexpr int SPACE_DIM
[Define dimension]
Definition gradient.cpp:19
PostProcEleByDim< SPACE_DIM >::SideEle SideEle
Definition gradient.cpp:88
SensitivityMethod derivative_type
Definition gradient.cpp:96
constexpr IntegrationType I
Use Gauss quadrature for integration.
Definition gradient.cpp:25
@ DIRECT
Definition gradient.cpp:94
@ ADJOINT
Definition gradient.cpp:94
FormsIntegrators< DomainEleOp >::Assembly< A >::OpBase DomainBaseOp
[Postprocess results]
Definition gradient.cpp:699
constexpr AssemblyType A
[Define dimension]
Definition gradient.cpp:23
PipelineManager::ElementsAndOpsByDim< SPACE_DIM >::BoundaryEle BoundaryEle
Boundary finite elements.
Definition gradient.cpp:47
Range get_range_from_block(MoFEM::Interface &m_field, const std::string block_name, int dim)
PetscErrorCode DMMoFEMSetIsPartitioned(DM dm, PetscBool is_partitioned)
Definition DMMoFEM.cpp:1142
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:244
PetscErrorCode DMMoFEMAddElement(DM dm, std::string fe_name)
add element to dm
Definition DMMoFEM.cpp:517
PetscErrorCode DMMoFEMSetSquareProblem(DM dm, PetscBool square_problem)
set squared problem
Definition DMMoFEM.cpp:479
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:143
PetscErrorCode DMoFEMPostProcessFiniteElements(DM dm, MoFEM::FEMethod *method)
execute finite element method for each element in dm (problem)
Definition DMMoFEM.cpp:575
PetscErrorCode DMMoFEMAddSubFieldRow(DM dm, const char field_name[])
Definition DMMoFEM.cpp:267
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:543
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:605
auto createDMVector(DM dm, RowColData rc=RowColData::COL)
Get smart vector from DM.
Definition DMMoFEM.hpp:1250
PetscErrorCode DMMoFEMAddSubFieldCol(DM dm, const char field_name[])
Definition DMMoFEM.cpp:309
auto createDMMatrix(DM dm)
Get smart matrix from DM.
Definition DMMoFEM.hpp:1207
PetscErrorCode DMoFEMPreProcessFiniteElements(DM dm, MoFEM::FEMethod *method)
execute finite element method for each element in dm (problem)
Definition DMMoFEM.cpp:565
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
PetscBool testing
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)
PetscErrorCode MoFEMErrorCode
MoFEM/PETSc error code.
implementation of Data Operators for Forces and Sources
Definition Common.hpp:10
MoFEMErrorCode TestingInterface::checkValue< TestType::DERIVATIVE >(const std::string &check_name, PetscReal actual, PetscReal expected, Tolerance tolerance, Sev error_severity, Sev success_severity) const
auto createKSP(MPI_Comm comm)
PetscErrorCode DMMoFEMSetDestroyProblem(DM dm, PetscBool destroy_problem)
Definition DMMoFEM.cpp:463
PetscErrorCode PetscOptionsGetInt(PetscOptions *, const char pre[], const char name[], PetscInt *ivalue, PetscBool *set)
PetscErrorCode PetscOptionsGetReal(PetscOptions *, const char pre[], const char name[], PetscReal *dval, PetscBool *set)
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:1264
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)
auto createGhostVector(MPI_Comm comm, PetscInt n, PetscInt N, PetscInt nghost, const PetscInt ghosts[])
Create smart ghost vector.
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:386
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)
OpPostProcMapInMoab< SPACE_DIM, SPACE_DIM > OpPPMap
#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
constexpr auto field_name
FormsIntegrators< DomainEleOp >::Assembly< PETSC >::BiLinearForm< GAUSS >::OpMass< 1, SPACE_DIM > OpMass
[Only used with Hooke equation (linear material model)]
Definition seepage.cpp:56
constexpr double g
FTensor::Index< 'm', 3 > m
Boundary conditions marker.
[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.
static int integrationRule(int, int, int p_data)
Definition radiation.cpp:52
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:775
Simple * simple
SmartPetscObj< KSP > kspElastic
Linear solver for elastic problem.
Definition adjoint.cpp:167
SmartPetscObj< Mat > M
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
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:1768
MoFEMErrorCode setupAdJoint()
Setup adjoint fields and finite elements.
Definition adjoint.cpp:652
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
static int integrationRule(int, int, int order)
Definition adjoint.cpp:138
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:1368
SmartPetscObj< EPS > eps
MoFEMErrorCode solveElastic()
Solve forward elastic problem.
Definition adjoint.cpp:1240
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
int nbRows
number of dofs on rows
int nbRowBaseFunctions
number or row base functions
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.
Calculate directional derivative of the right hand side and compare it with tangent matrix derivative...
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 addDomainField(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_ZERO, int verb=-1)
Add field on domain.
Definition Simple.cpp:261
EntityHandle & getMeshset()
Get the MeshSet object.
Definition Simple.hpp:394
MoFEMErrorCode loadFile(const std::string options, const std::string mesh_file_name, LoadFileFunc loadFunc=defaultLoadFileFunc)
Load mesh file.
Definition Simple.cpp:191
MoFEMErrorCode addBoundaryField(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_ZERO, int verb=-1)
Add field on boundary.
Definition Simple.cpp:355
MoFEMErrorCode getOptions()
get options
Definition Simple.cpp:180
MoFEMErrorCode getDM(DM *dm)
Get DM.
Definition Simple.cpp:799
MoFEMErrorCode setFieldOrder(const std::string field_name, const int order, const Range *ents=NULL)
Set field order.
Definition Simple.cpp:575
BitRefLevel & getBitRefLevelMask()
Get the BitRefLevelMask.
Definition Simple.hpp:422
MoFEMErrorCode addDataField(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_ZERO, int verb=-1)
Add data field.
Definition Simple.cpp:393
MoFEMErrorCode setUp(const PetscBool is_partitioned=PETSC_TRUE)
Setup problem.
Definition Simple.cpp:735
const std::string getProblemName() const
Get the Problem Name.
Definition Simple.hpp:450
const std::string getDomainFEName() const
Get the Domain FE Name.
Definition Simple.hpp:429
BitRefLevel & getBitRefLevel()
Get the BitRefLevel.
Definition Simple.hpp:415
intrusive_ptr for managing petsc objects
virtual MoFEMErrorCode addTestCommandLine(const std::string &prefix, int number, const std::string &name, TestHook hook)=0
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:1391
Forward declaration of operator for gradient times symmetric tensor operations.
Definition adjoint.cpp:105
boost::shared_ptr< MatrixDouble > dGradPtr
Definition adjoint.cpp:1760
boost::shared_ptr< double > globObjectiveGradPtr
Definition adjoint.cpp:1765
boost::shared_ptr< HookeOps::CommonData > commPtr
Definition adjoint.cpp:1756
ForcesAndSourcesCore::UserDataOperator OP
Definition adjoint.cpp:1594
boost::shared_ptr< double > globObjectivePtr
Definition adjoint.cpp:1764
boost::shared_ptr< ObjectiveFunctionData > pythonPtr
Definition adjoint.cpp:1755
MoFEMErrorCode doWork(int side, EntityType type, EntitiesFieldData::EntData &data)
Compute objective function contributions at element level.
Definition adjoint.cpp:1618
boost::shared_ptr< VectorDouble > cofVals
Definition adjoint.cpp:1759
boost::shared_ptr< MatrixDouble > uPtr
Definition adjoint.cpp:1762
boost::shared_ptr< MatrixDouble > jacPtr
Definition adjoint.cpp:1757
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:1596
boost::shared_ptr< MatrixDouble > diffJacPtr
Definition adjoint.cpp:1758
boost::shared_ptr< MatrixDouble > dUPtr
Definition adjoint.cpp:1761
boost::shared_ptr< HookeOps::CommonData > commPtr
Definition adjoint.cpp:1590
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:1505
MoFEMErrorCode iNtegrate(EntitiesFieldData::EntData &data)
Class dedicated to integrate operator.
Definition adjoint.cpp:1512
boost::shared_ptr< MatrixDouble > uPtr
Definition adjoint.cpp:1591
boost::shared_ptr< ObjectiveFunctionData > pythonPtr
Definition adjoint.cpp:1589
PipelineManager::ElementsAndOpsByDim< 2 >::FaceSideEle SideEle
PipelineManager::ElementsAndOpsByDim< 3 >::FaceSideEle SideEle
auto save_range