v0.15.5
Loading...
Searching...
No Matches
ArcLengthExample.hpp
Go to the documentation of this file.
1/**
2 * @file ArcLengthExample.hpp
3 * @example mofem/tutorials/vec-9_arc_length/src/ArcLengthExample.hpp
4 * @date 2025-10-5
5 *
6 * @copyright Anonymous authors (c) 2025 under the MIT license (see LICENSE for
7 * details)
8 *
9 */
10
12
14
17
18 MoFEMErrorCode runProblem();
19
20protected:
21 MoFEMErrorCode ArcLengthSolve();
22 MoFEMErrorCode checkResults();
23};
24
25//! [Run problem]
28
32
33 PetscBool test_op = PETSC_FALSE;
34 CHKERR PetscOptionsGetBool(PETSC_NULLPTR, "", "-test_op", &test_op,
35 PETSC_NULLPTR);
36
37 if (useAdolcMaterial == PETSC_TRUE) {
39 if (test_op == PETSC_TRUE) {
40 CHKERR opTest();
41 }
42 } else {
44 if (test_op == PETSC_TRUE) {
45 CHKERR opTest();
46 }
47 }
48
53
54 ExampleTimeScale::snesPtr.reset();
55
57}
58//! [Run problem]
59
60//! [Arc Length Solve]
63 auto *simple = mField.getInterface<Simple>();
64 auto *pipeline_mng = mField.getInterface<PipelineManager>();
65
66 auto dm = simple->getDM();
67 auto snes = pipeline_mng->createSNES();
68 ExampleTimeScale::snesPtr = snes;
69
70 PetscBool post_proc_vol;
71 PetscBool post_proc_skin;
72
73 if constexpr (SPACE_DIM == 2) {
74 post_proc_vol = PETSC_TRUE;
75 post_proc_skin = PETSC_FALSE;
76 } else {
77 post_proc_vol = PETSC_FALSE;
78 post_proc_skin = PETSC_TRUE;
79 }
80 CHKERR PetscOptionsGetBool(PETSC_NULLPTR, "", "-post_proc_vol",
81 &post_proc_vol, PETSC_NULLPTR);
82 CHKERR PetscOptionsGetBool(PETSC_NULLPTR, "", "-post_proc_skin",
83 &post_proc_skin, PETSC_NULLPTR);
84
85 // Setup postprocessing
86 auto create_post_proc_fe = [dm, this, simple, post_proc_vol,
87 post_proc_skin]() {
88 auto post_proc_ele_domain = [dm, this](auto &pip_domain) {
90 "GEOMETRY");
91 auto common_ptr = commonDataFactory<SPACE_DIM, GAUSS, DomainEleOp>(
92 mField, pip_domain, "U", "MAT_ELASTIC", Sev::inform);
93 return common_ptr;
94 };
95
96 auto post_proc_map = [this](auto &pip, auto u_ptr, auto common_ptr) {
98
99 using OpPPMap = OpPostProcMapInMoab<SPACE_DIM, SPACE_DIM>;
100
101 auto X_ptr = boost::make_shared<MatrixDouble>();
102
103 pip->getOpPtrVector().push_back(
104 new OpCalculateVectorFieldValues<SPACE_DIM>("GEOMETRY", X_ptr));
105
106 pip->getOpPtrVector().push_back(
107
108 new OpPPMap(pip->getPostProcMesh(), pip->getMapGaussPts(), {},
109 {{"U", u_ptr}, {"GEOMETRY", X_ptr}},
110 {{"GRAD", common_ptr->matGradPtr},
111 {"FIRST_PIOLA", common_ptr->getMatFirstPiolaStress()}},
112 {}));
114 };
115
116 auto push_post_proc_bdy = [dm, this, simple, post_proc_skin,
117 &post_proc_ele_domain,
118 &post_proc_map](auto &pip_bdy) {
119 if (post_proc_skin == PETSC_FALSE)
120 return boost::shared_ptr<PostProcEleBdy>();
121
122 auto u_ptr = boost::make_shared<MatrixDouble>();
123 pip_bdy->getOpPtrVector().push_back(
124 new OpCalculateVectorFieldValues<SPACE_DIM>("U", u_ptr));
125 auto op_loop_side =
126 new OpLoopSide<SideEle>(mField, simple->getDomainFEName(), SPACE_DIM);
127 auto common_ptr = post_proc_ele_domain(op_loop_side->getOpPtrVector());
128 pip_bdy->getOpPtrVector().push_back(op_loop_side);
129
130 CHKERR post_proc_map(pip_bdy, u_ptr, common_ptr);
131
132 return pip_bdy;
133 };
134
135 auto push_post_proc_domain = [dm, this, simple, post_proc_vol,
136 &post_proc_ele_domain,
137 &post_proc_map](auto &pip_domain) {
138 if (post_proc_vol == PETSC_FALSE)
139 return boost::shared_ptr<PostProcEleDomain>();
140
141 auto u_ptr = boost::make_shared<MatrixDouble>();
142 pip_domain->getOpPtrVector().push_back(
143 new OpCalculateVectorFieldValues<SPACE_DIM>("U", u_ptr));
144 auto common_ptr = post_proc_ele_domain(pip_domain->getOpPtrVector());
145
146 CHKERR post_proc_map(pip_domain, u_ptr, common_ptr);
147
148 return pip_domain;
149 };
150
151 auto post_proc_fe_domain = boost::make_shared<PostProcEleDomain>(mField);
152 auto post_proc_fe_bdy = boost::make_shared<PostProcEleBdy>(mField);
153
154 return std::make_pair(push_post_proc_domain(post_proc_fe_domain),
155 push_post_proc_bdy(post_proc_fe_bdy));
156 };
157
158 auto add_arc_length_load_tangent_operator = [&](auto snes) {
160 auto bdy_pipeline = boost::make_shared<BoundaryEle>(mField);
161 auto domain_pipeline = boost::make_shared<DomainEle>(mField);
162
163 auto integration_rule = [](int, int, int approx_order) {
164 return 2 * (approx_order - 1);
165 };
166 bdy_pipeline->getRuleHook = integration_rule;
167 domain_pipeline->getRuleHook = integration_rule;
168
169 struct TimeOne : TimeScale {
170 double getScale(const double) override { return -1.; }
171 };
172 auto time_one = boost::make_shared<TimeOne>();
173
174 // The trick is here, that is assumed that load is applied linearly
175 // proportional to the load factor (time in this case). So derivative of the
176 // load with respect to the load factor is the same as the load at time
177 // = 1.0
178
179 CHKERR BoundaryNaturalBC::AddFluxToPipeline<OpForce>::add(
180 bdy_pipeline->getOpPtrVector(), mField, "U", {time_one}, "FORCE",
181 "PRESSURE", Sev::inform);
182 //! [Define gravity vector]
183 CHKERR DomainNaturalBC::AddFluxToPipeline<OpBodyForce>::add(
184 domain_pipeline->getOpPtrVector(), mField, "U", {time_one},
185 "BODY_FORCE", Sev::inform);
186
187 auto snes_ctx_ptr = getDMSnesCtx(simple->getDM());
188 snes_ctx_ptr->getLoadTangent().push_back(
189 {simple->getBoundaryFEName(), bdy_pipeline});
190 snes_ctx_ptr->getLoadTangent().push_back(
191 {simple->getDomainFEName(), domain_pipeline});
192
193 auto time_scale = boost::make_shared<ExampleTimeScale>();
194
195 auto pre_proc_ptr = boost::make_shared<FEMethod>();
196 auto get_bc_hook_rhs = [this, pre_proc_ptr, time_scale]() {
198 CHKERR EssentialPreProc<DisplacementCubitBcData>(mField, pre_proc_ptr,
199 {time_scale}, false)();
201 };
202 pre_proc_ptr->preProcessHook = get_bc_hook_rhs;
203 snes_ctx_ptr->getPreProcLoadTangent().push_back(pre_proc_ptr);
204 auto post_proc_rhs_ptr = boost::make_shared<FEMethod>();
205 auto get_post_proc_hook_rhs = [this, post_proc_rhs_ptr]() {
207 CHKERR EssentialPostProcRhs<DisplacementCubitBcData>(
208 mField, post_proc_rhs_ptr, 0.)();
210 };
211 post_proc_rhs_ptr->postProcessHook = get_post_proc_hook_rhs;
212 snes_ctx_ptr->getPostProcLoadTangent().push_back(post_proc_rhs_ptr);
213
214 CHKERR SNESNewtonALSetFunction(snes, SnesLoadTangent, snes_ctx_ptr.get());
215
217 };
218
219 auto add_extra_finite_elements_to_solver_pipelines = [&]() {
221
222 auto pre_proc_ptr = boost::make_shared<FEMethod>();
223 auto post_proc_rhs_ptr = boost::make_shared<FEMethod>();
224 auto post_proc_lhs_ptr = boost::make_shared<FEMethod>();
225
226 auto time_scale = boost::make_shared<ExampleTimeScale>();
227
228 auto get_bc_hook_rhs = [this, pre_proc_ptr, time_scale]() {
230 CHKERR EssentialPreProc<DisplacementCubitBcData>(mField, pre_proc_ptr,
231 {time_scale}, false)();
233 };
234
235 pre_proc_ptr->preProcessHook = get_bc_hook_rhs;
236
237 auto get_post_proc_hook_rhs = [this, post_proc_rhs_ptr]() {
239 CHKERR EssentialPreProcReaction<DisplacementCubitBcData>(
240 mField, post_proc_rhs_ptr, nullptr, Sev::verbose)();
241 CHKERR EssentialPostProcRhs<DisplacementCubitBcData>(
242 mField, post_proc_rhs_ptr, 1.)();
244 };
245 auto get_post_proc_hook_lhs = [this, post_proc_lhs_ptr]() {
247 CHKERR EssentialPostProcLhs<DisplacementCubitBcData>(
248 mField, post_proc_lhs_ptr, 1.)();
250 };
251 post_proc_rhs_ptr->postProcessHook = get_post_proc_hook_rhs;
252 post_proc_lhs_ptr->postProcessHook = get_post_proc_hook_lhs;
253
254 // This is low level pushing finite elements (pipelines) to solver
255 auto snes_ctx_ptr = getDMSnesCtx(simple->getDM());
256 snes_ctx_ptr->getPreProcComputeRhs().push_front(pre_proc_ptr);
257 snes_ctx_ptr->getPreProcSetOperators().push_front(pre_proc_ptr);
258 snes_ctx_ptr->getPostProcComputeRhs().push_back(post_proc_rhs_ptr);
259 snes_ctx_ptr->getPostProcSetOperators().push_back(post_proc_lhs_ptr);
261 };
262
263 auto set_log_and_post_hook = [this](auto post_proc_fe, auto post_proc_bdy) {
265
266 auto make_vtk = [this, post_proc_fe, post_proc_bdy](auto iter,
267 auto cache_ptr) {
269 auto simple = mField.getInterface<Simple>();
270 auto dm = simple->getDM();
271 if (post_proc_fe) {
272 CHKERR DMoFEMLoopFiniteElements(dm, "dFE", post_proc_fe, cache_ptr);
273 CHKERR post_proc_fe->writeFile(
274 "out_step_" + boost::lexical_cast<std::string>(iter) + ".h5m");
275 }
276 if (post_proc_bdy) {
277 CHKERR DMoFEMLoopFiniteElements(dm, "bFE", post_proc_bdy, cache_ptr);
278 CHKERR post_proc_bdy->writeFile(
279 "out_skin_" + boost::lexical_cast<std::string>(iter) + ".h5m");
280 }
282 };
283
284 auto hook = [this, make_vtk](SNES snes, Vec x, Vec F,
285 boost::shared_ptr<CacheTuple> cache_ptr,
286 void *ctx) -> MoFEMErrorCode {
288
289 double atol, rtol, stol;
290 int maxit, maxf;
291 CHKERR SNESGetTolerances(snes, &atol, &rtol, &stol, &maxit, &maxf);
292
293 double lambda;
294 CHKERR SNESNewtonALGetLoadParameter(snes, &lambda);
295
296 double nrm;
297 CHKERR VecNorm(F, NORM_2, &nrm);
298
299 int iter;
300 CHKERR SNESGetIterationNumber(snes, &iter);
301 SNESConvergedReason reason;
302 CHKERR SNESGetConvergedReason(snes, &reason);
303 MOFEM_LOG_C("EXAMPLE", Sev::inform,
304 "SNES iteration %d, reason %d nrm2 %g lambda %g", iter,
305 reason, nrm, lambda);
306
307 CHKERR make_vtk(iter, cache_ptr);
308
310 };
311
312 auto simple = mField.getInterface<Simple>();
313 auto snes_ctx_ptr = getDMSnesCtx(simple->getDM());
314 snes_ctx_ptr->getRhsHook() = hook;
315
317 };
318
319 // Set arc length solver
320 auto D = createDMVector(simple->getDM());
321 CHKERR SNESSetType(snes, SNESNEWTONLS);
322 CHKERR SNESSetFromOptions(snes);
323
324 auto B = createDMMatrix(dm);
325 CHKERR SNESSetJacobian(snes, B, B, PETSC_NULLPTR, PETSC_NULLPTR);
326 // Add extra finite elements to SNES solver pipelines to resolve essential
327 // boundary conditions
328 CHKERR add_extra_finite_elements_to_solver_pipelines();
329 CHKERR add_arc_length_load_tangent_operator(snes);
330
331 auto [post_proc_fe, post_proc_bdy] = create_post_proc_fe();
332 CHKERR set_log_and_post_hook(post_proc_fe, post_proc_bdy);
333
334 CHKERR SNESSolve(snes, PETSC_NULLPTR, D);
335
337}
338//! [Arc Length Solve]
339
340//! [Check]
343 PetscInt test_nb = 0;
344 PetscBool test_flg = PETSC_FALSE;
345 CHKERR PetscOptionsGetInt(PETSC_NULLPTR, "", "-test", &test_nb, &test_flg);
346
347 if (test_flg) {
348 auto simple = mField.getInterface<Simple>();
349 auto T = createDMVector(simple->getDM());
350 CHKERR DMoFEMMeshToLocalVector(simple->getDM(), T, INSERT_VALUES,
351 SCATTER_FORWARD);
352 double nrm2;
353 CHKERR VecNorm(T, NORM_2, &nrm2);
354 MOFEM_LOG("EXAMPLE", Sev::verbose) << "Regression norm " << nrm2;
355 double regression_value = 0;
356 switch (test_nb) {
357 case 1:
358 regression_value = 1.215134e-04;
359 break;
360 default:
361 SETERRQ(PETSC_COMM_WORLD, MOFEM_ATOM_TEST_INVALID, "Wrong test number.");
362 break;
363 }
364 if (fabs(nrm2 - regression_value) > 1e-2)
365 SETERRQ(PETSC_COMM_WORLD, MOFEM_ATOM_TEST_INVALID,
366 "Regression test field; wrong norm value. %6.4e != %6.4e", nrm2,
367 regression_value);
368 }
370}
371//! [Check]
#define MOFEM_LOG_C(channel, severity, format,...)
void simple(double P1[], double P2[], double P3[], double c[], const int N)
Definition acoustic.cpp:69
constexpr int SPACE_DIM
#define MoFEMFunctionReturnHot(a)
Last executable line of each PETSc function used for error handling. Replaces return()
@ H1
continuous field
Definition definitions.h:85
#define MoFEMFunctionBegin
First executable line of each MoFEM function, used for error handling. Final line of MoFEM functions ...
@ MOFEM_ATOM_TEST_INVALID
Definition definitions.h:40
#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 ...
@ F
auto integration_rule
PetscErrorCode DMoFEMLoopFiniteElements(DM dm, const char fe_name[], MoFEM::FEMethod *method, CacheTupleWeakPtr cache_ptr=CacheTupleSharedPtr())
Executes FEMethod for finite elements in DM.
Definition DMMoFEM.cpp:576
auto createDMVector(DM dm, RowColData rc=RowColData::COL)
Get smart vector from DM.
Definition DMMoFEM.hpp:1237
auto createDMMatrix(DM dm)
Get smart matrix from DM.
Definition DMMoFEM.hpp:1194
#define MOFEM_LOG(channel, severity)
Log.
static double lambda
double D
const FTensor::Tensor2< T, Dim, Dim > Vec
auto getDMSnesCtx(DM dm)
Get SNES context data structure used by DM.
Definition DMMoFEM.hpp:1265
OpPostProcMapInMoab< SPACE_DIM, SPACE_DIM > OpPPMap
static constexpr int approx_order
MoFEMErrorCode checkResults()
[Arc Length Solve]
MoFEMErrorCode ArcLengthSolve()
[Run problem]
ArcLengthExample(MoFEM::Interface &m_field)
MoFEMErrorCode runProblem()
[Run problem]
Deprecated interface functions.
Post post-proc data at points from hash maps.
MoFEMErrorCode getInterface(IFACE *&iface) const
Get interface reference to pointer of interface.
MoFEMErrorCode setupProblem()
[Read mesh]
MoFEMErrorCode readMesh()
[Run problem]
MoFEMErrorCode assembleSystemHencky()
[Boundary condition]
MoFEMErrorCode boundaryCondition()
[Set up problem]
MoFEMErrorCode opTest()
[Push operators to pipeline]
MoFEMErrorCode outputResults()
[Getting norms]
MoFEMErrorCode gettingNorms()
[TS Solve]