v0.14.0
Loading...
Searching...
No Matches
heat_equation.cpp
Go to the documentation of this file.
1/**
2 * \file heat_equation.cpp
3 * \example heat_equation.cpp
4 *
5 * \brief Solve the time-dependent Heat Equation
6 \f[
7 \begin{aligned}
8\frac{\partial u(\mathbf{x}, t)}{\partial t}-\Delta u(\mathbf{x}, t)
9&=f(\mathbf{x}, t) & & \forall \mathbf{x} \in \Omega, t \in(0, T), \\
10u(\mathbf{x}, 0) &=u_{0}(\mathbf{x}) & & \forall \mathbf{x} \in \Omega, \\
11u(\mathbf{x}, t) &=g(\mathbf{x}, t) & & \forall \mathbf{x} \in \partial \Omega,
12t \in(0, T). \end{aligned}
13 \f]
14 **/
15
16#include <stdlib.h>
17#include <cmath>
18#include <MoFEM.hpp>
19
20using namespace MoFEM;
21
22static char help[] = "...\n\n";
23
24template <int DIM> struct ElementsAndOps {};
25
26//! [Define dimension]
27constexpr int SPACE_DIM = 2; //< Space dimension of problem, mesh
28//! [Define dimension]
29
32using DomainEleOp = DomainEle::UserDataOperator;
34using BoundaryEleOp = BoundaryEle::UserDataOperator;
36
47
54
55// Capacity
56constexpr double c = 1;
57constexpr double k = 1;
58constexpr double init_u = 0.;
59
60/**
61 * @brief Monitor solution
62 *
63 * This functions is called by TS solver at the end of each step. It is used
64 * to output results to the hard drive.
65 */
66struct Monitor : public FEMethod {
67
68 Monitor(SmartPetscObj<DM> dm, boost::shared_ptr<PostProcEle> post_proc)
69 : dM(dm), postProc(post_proc){};
70
71 MoFEMErrorCode preProcess() { return 0; }
72 MoFEMErrorCode operator()() { return 0; }
73
74 static constexpr int saveEveryNthStep = 1;
75
78 if (ts_step % saveEveryNthStep == 0) {
80 CHKERR postProc->writeFile(
81 "out_level_" + boost::lexical_cast<std::string>(ts_step) + ".h5m");
82 }
84 }
85
86private:
88 boost::shared_ptr<PostProcEle> postProc;
89};
90
92public:
94
95 // Declaration of the main function to run analysis
97
98private:
99 // Declaration of other main functions called in runProgram()
108
109 // Main interfaces
111
112 // Object to mark boundary entities for the assembling of domain elements
113 boost::shared_ptr<std::vector<unsigned char>> boundaryMarker;
114};
115
116HeatEquation::HeatEquation(MoFEM::Interface &m_field) : mField(m_field) {}
117
128
131
132 auto *simple = mField.getInterface<Simple>();
133 CHKERR simple->addDomainField("U", H1, AINSWORTH_LEGENDRE_BASE, 1);
134 CHKERR simple->addBoundaryField("U", H1, AINSWORTH_LEGENDRE_BASE, 1);
135
136 int order = 3;
137 CHKERR PetscOptionsGetInt(PETSC_NULL, "", "-order", &order, PETSC_NULL);
138 CHKERR simple->setFieldOrder("U", order);
139
140 CHKERR simple->setUp();
141
143}
144
147
148 auto integration_rule = [](int o_row, int o_col, int approx_order) {
149 return 2 * approx_order;
150 };
151
152 auto *pipeline_mng = mField.getInterface<PipelineManager>();
154 CHKERR pipeline_mng->setDomainLhsIntegrationRule(integration_rule);
155 CHKERR pipeline_mng->setBoundaryLhsIntegrationRule(integration_rule);
156 CHKERR pipeline_mng->setBoundaryRhsIntegrationRule(integration_rule);
157
159}
160
163
164 // Get surface entities form blockset, set initial values in those
165 // blocksets. To keep it simple, it is assumed that inital values are on
166 // blockset 1
168 Range inner_surface;
169 CHKERR mField.getInterface<MeshsetsManager>()->getEntitiesByDimension(
170 1, BLOCKSET, 2, inner_surface, true);
171 if (!inner_surface.empty()) {
172 Range inner_surface_verts;
173 CHKERR mField.get_moab().get_connectivity(inner_surface,
174 inner_surface_verts, false);
175 CHKERR mField.getInterface<FieldBlas>()->setField(
176 init_u, MBVERTEX, inner_surface_verts, "U");
177 }
178 }
179
181}
182
185
186 auto bc_mng = mField.getInterface<BcManager>();
187 auto *simple = mField.getInterface<Simple>();
188 CHKERR bc_mng->pushMarkDOFsOnEntities(simple->getProblemName(), "ESSENTIAL",
189 "U", 0, 0);
190
191 auto &bc_map = bc_mng->getBcMapByBlockName();
192 boundaryMarker = boost::make_shared<std::vector<char unsigned>>();
193 for (auto b : bc_map) {
194 if (std::regex_match(b.first, std::regex("(.*)ESSENTIAL(.*)"))) {
195 boundaryMarker->resize(b.second->bcMarkers.size(), 0);
196 for (int i = 0; i != b.second->bcMarkers.size(); ++i) {
197 (*boundaryMarker)[i] |= b.second->bcMarkers[i];
198 }
199 }
200 }
201
203}
204
207
208 auto add_domain_lhs_ops = [&](auto &pipeline) {
210 pipeline.push_back(new OpSetBc("U", true, boundaryMarker));
211 pipeline.push_back(new OpDomainGradGrad(
212 "U", "U", [](double, double, double) -> double { return k; }));
213 auto get_c = [this](const double, const double, const double) {
214 auto pipeline_mng = mField.getInterface<PipelineManager>();
215 auto &fe_domain_lhs = pipeline_mng->getDomainLhsFE();
216 return c * fe_domain_lhs->ts_a;
217 };
218 pipeline.push_back(new OpDomainMass("U", "U", get_c));
219 pipeline.push_back(new OpUnSetBc("U"));
220 };
221
222 auto add_domain_rhs_ops = [&](auto &pipeline) {
224 pipeline.push_back(new OpSetBc("U", true, boundaryMarker));
225 auto grad_u_at_gauss_pts = boost::make_shared<MatrixDouble>();
226 auto dot_u_at_gauss_pts = boost::make_shared<VectorDouble>();
227 pipeline.push_back(new OpCalculateScalarFieldGradient<SPACE_DIM>(
228 "U", grad_u_at_gauss_pts));
229 pipeline.push_back(
230 new OpCalculateScalarFieldValuesDot("U", dot_u_at_gauss_pts));
231 pipeline.push_back(new OpDomainGradTimesVec(
232 "U", grad_u_at_gauss_pts,
233 [](double, double, double) -> double { return k; }));
234 pipeline.push_back(new OpDomainTimesScalarField(
235 "U", dot_u_at_gauss_pts,
236 [](const double, const double, const double) { return c; }));
237 auto source_term = [&](const double x, const double y, const double z) {
238 auto pipeline_mng = mField.getInterface<PipelineManager>();
239 auto &fe_domain_lhs = pipeline_mng->getDomainRhsFE();
240 const auto t = fe_domain_lhs->ts_t;
241 return 1e1 * pow(M_E, -M_PI * M_PI * t) * sin(1. * M_PI * x) *
242 sin(2. * M_PI * y);
243 };
244 pipeline.push_back(new OpDomainSource("U", source_term));
245 pipeline.push_back(new OpUnSetBc("U"));
246 };
247
248 auto add_boundary_lhs_ops = [&](auto &pipeline) {
249 pipeline.push_back(new OpSetBc("U", false, boundaryMarker));
250 pipeline.push_back(new OpBoundaryMass(
251 "U", "U", [](const double, const double, const double) { return c; }));
252 pipeline.push_back(new OpUnSetBc("U"));
253 };
254 auto add_boundary_rhs_ops = [&](auto &pipeline) {
255 pipeline.push_back(new OpSetBc("U", false, boundaryMarker));
256 auto u_at_gauss_pts = boost::make_shared<VectorDouble>();
257 auto boundary_function = [&](const double x, const double y,
258 const double z) {
259 auto pipeline_mng = mField.getInterface<PipelineManager>();
260 auto &fe_rhs = pipeline_mng->getBoundaryRhsFE();
261 const auto t = fe_rhs->ts_t;
262 return 0;
263 // abs(0.1 * pow(M_E, -M_PI * M_PI * t) * sin(2. * M_PI * x) *
264 // sin(3. * M_PI * y));
265 };
266 pipeline.push_back(new OpCalculateScalarFieldValues("U", u_at_gauss_pts));
267 pipeline.push_back(new OpBoundaryTimeScalarField(
268 "U", u_at_gauss_pts,
269 [](const double, const double, const double) { return c; }));
270 pipeline.push_back(new OpBoundarySource("U", boundary_function));
271 pipeline.push_back(new OpUnSetBc("U"));
272 };
273
274 auto pipeline_mng = mField.getInterface<PipelineManager>();
275 add_domain_lhs_ops(pipeline_mng->getOpDomainLhsPipeline());
276 add_domain_rhs_ops(pipeline_mng->getOpDomainRhsPipeline());
277 add_boundary_lhs_ops(pipeline_mng->getOpBoundaryLhsPipeline());
278 add_boundary_rhs_ops(pipeline_mng->getOpBoundaryRhsPipeline());
279
281}
282
284 static PetscErrorCode set(TS ts, PetscReal t, Vec u, Vec u_t, PetscReal a,
285 Mat A, Mat B, void *ctx) {
287 if (a != lastA) {
288 lastA = a;
289 CHKERR TsSetIJacobian(ts, t, u, u_t, a, A, B, ctx);
290 }
292 }
293
294private:
295 static double lastA;
296};
297
298double CalcJacobian::lastA = 0;
299
302
303 auto *simple = mField.getInterface<Simple>();
304 auto *pipeline_mng = mField.getInterface<PipelineManager>();
305
306 auto create_post_process_element = [&]() {
307 auto post_proc_fe = boost::make_shared<PostProcEle>(mField);
308 auto det_ptr = boost::make_shared<VectorDouble>();
309 auto jac_ptr = boost::make_shared<MatrixDouble>();
310 auto inv_jac_ptr = boost::make_shared<MatrixDouble>();
311 post_proc_fe->getOpPtrVector().push_back(new OpCalculateHOJac<2>(jac_ptr));
312 post_proc_fe->getOpPtrVector().push_back(
313 new OpInvertMatrix<2>(jac_ptr, det_ptr, inv_jac_ptr));
314 post_proc_fe->getOpPtrVector().push_back(
315 new OpSetHOInvJacToScalarBases<2>(H1, inv_jac_ptr));
316
317 auto u_ptr = boost::make_shared<VectorDouble>();
318 post_proc_fe->getOpPtrVector().push_back(
319 new OpCalculateScalarFieldValues("U", u_ptr));
320
322
323 post_proc_fe->getOpPtrVector().push_back(
324
325 new OpPPMap(post_proc_fe->getPostProcMesh(),
326 post_proc_fe->getMapGaussPts(),
327
328 {{"U", u_ptr}},
329
330 {}, {}, {}
331
332 )
333
334 );
335
336 return post_proc_fe;
337 };
338
339 auto set_time_monitor = [&](auto dm, auto solver) {
341 boost::shared_ptr<Monitor> monitor_ptr(
342 new Monitor(dm, create_post_process_element()));
343 boost::shared_ptr<ForcesAndSourcesCore> null;
344 CHKERR DMMoFEMTSSetMonitor(dm, solver, simple->getDomainFEName(),
345 monitor_ptr, null, null);
347 };
348
349 auto set_fieldsplit_preconditioner = [&](auto solver) {
351 SNES snes;
352 CHKERR TSGetSNES(solver, &snes);
353 KSP ksp;
354 CHKERR SNESGetKSP(snes, &ksp);
355 PC pc;
356 CHKERR KSPGetPC(ksp, &pc);
357 PetscBool is_pcfs = PETSC_FALSE;
358 PetscObjectTypeCompare((PetscObject)pc, PCFIELDSPLIT, &is_pcfs);
359
360 if (is_pcfs == PETSC_TRUE) {
361 auto bc_mng = mField.getInterface<BcManager>();
362 auto name_prb = simple->getProblemName();
363 auto is_all_bc = bc_mng->getBlockIS(name_prb, "ESSENTIAL", "U", 0, 0);
364 int is_all_bc_size;
365 CHKERR ISGetSize(is_all_bc, &is_all_bc_size);
366 MOFEM_LOG("EXAMPLE", Sev::inform)
367 << "Field split block size " << is_all_bc_size;
368 CHKERR PCFieldSplitSetIS(pc, PETSC_NULL,
369 is_all_bc); // boundary block
370 }
372 };
373
374 /**
375 * That to work, you have to create solver, as follows,
376 \code
377 auto solver = // pipeline_mng->createTSIM( simple->getDM());
378 \endcode
379 That is explicitly use use Simple DM to create solver for DM. Pipeline
380 menage by default creat copy of DM, in case several solvers are used the
381 same DM.
382
383 Alternatively you can get dm directly from the solver, i.e.
384 \code
385 DM ts_dm;
386 CHKERR TSGetDM(solver, &ts_dm);
387 CHKERR DMTSSetIJacobian(
388 ts_dm, CalcJacobian::set, getDMTsCtx(ts_dm).get());
389 \endcode
390 */
391 auto set_user_ts_jacobian = [&](auto dm) {
393 CHKERR DMTSSetIJacobian(dm, CalcJacobian::set, getDMTsCtx(dm).get());
395 };
396
397 auto dm = simple->getDM();
398 auto D = createDMVector(dm);
399 CHKERR DMoFEMMeshToLocalVector(dm, D, INSERT_VALUES, SCATTER_FORWARD);
400
401 auto solver = pipeline_mng->createTSIM(
402 simple->getDM()); // Note DM is set as argument. If DM is not, internal
403 // copy of pipeline DM is created.
404 CHKERR set_user_ts_jacobian(dm);
405 CHKERR set_time_monitor(dm, solver);
406 CHKERR TSSetSolution(solver, D);
407 CHKERR TSSetFromOptions(solver);
408 CHKERR set_fieldsplit_preconditioner(solver);
409 CHKERR TSSetUp(solver);
410
411 CHKERR TSSolve(solver, D);
412
414}
415
418
419 // Processes to set output results are integrated in solveSystem()
420
422}
423
438
439int main(int argc, char *argv[]) {
440
441 // Initialisation of MoFEM/PETSc and MOAB data structures
442 const char param_file[] = "param_file.petsc";
443 MoFEM::Core::Initialize(&argc, &argv, param_file, help);
444
445 // Add logging channel for example
446 auto core_log = logging::core::get();
447 core_log->add_sink(
449 LogManager::setLog("EXAMPLE");
450 MOFEM_LOG_TAG("EXAMPLE", "example")
451
452 // Error handling
453 try {
454 // Register MoFEM discrete manager in PETSc
455 DMType dm_name = "DMMOFEM";
456 CHKERR DMRegister_MoFEM(dm_name);
457
458 // Create MOAB instance
459 moab::Core mb_instance; // mesh database
460 moab::Interface &moab = mb_instance; // mesh database interface
461
462 // Create MoFEM instance
463 MoFEM::Core core(moab); // finite element database
464 MoFEM::Interface &m_field = core; // finite element interface
465
466 // Run the main analysis
467 HeatEquation heat_problem(m_field);
468 CHKERR heat_problem.runProgram();
469 }
471
472 // Finish work: cleaning memory, getting statistics, etc.
474
475 return 0;
476}
void simple(double P1[], double P2[], double P3[], double c[], const int N)
Definition acoustic.cpp:69
int main()
constexpr double a
FormsIntegrators< DomainEleOp >::Assembly< PETSC >::LinearForm< GAUSS >::OpSource< 1, FIELD_DIM > OpDomainSource
FormsIntegrators< DomainEleOp >::Assembly< PETSC >::BiLinearForm< GAUSS >::OpMass< 1, FIELD_DIM > OpDomainMass
ElementsAndOps< SPACE_DIM >::BoundaryEle BoundaryEle
ElementsAndOps< SPACE_DIM >::DomainEle DomainEle
[Define dimension]
#define CATCH_ERRORS
Catch errors.
@ AINSWORTH_LEGENDRE_BASE
Ainsworth Cole (Legendre) approx. base nme:nme847.
Definition definitions.h:60
#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 ...
@ BLOCKSET
#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
auto integration_rule
PetscErrorCode DMoFEMMeshToLocalVector(DM dm, Vec l, InsertMode mode, ScatterMode scatter_mode)
set local (or ghosted) vector values on mesh for partition only
Definition DMMoFEM.cpp:523
PetscErrorCode DMRegister_MoFEM(const char sname[])
Register MoFEM problem.
Definition DMMoFEM.cpp:43
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:586
auto createDMVector(DM dm)
Get smart vector from DM.
Definition DMMoFEM.hpp:1099
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.
bool checkMeshset(const int ms_id, const CubitBCType cubit_bc_type) const
check for CUBIT Id and CUBIT type
FTensor::Index< 'i', SPACE_DIM > i
constexpr double init_u
FormsIntegrators< BoundaryEleOp >::Assembly< PETSC >::LinearForm< GAUSS >::OpBaseTimesScalar< 1 > OpBoundaryTimeScalarField
FormsIntegrators< DomainEleOp >::Assembly< PETSC >::LinearForm< GAUSS >::OpSource< 1, 1 > OpDomainSource
static char help[]
constexpr double k
constexpr int SPACE_DIM
[Define dimension]
constexpr double c
FormsIntegrators< DomainEleOp >::Assembly< PETSC >::LinearForm< GAUSS >::OpGradTimesTensor< 1, 1, SPACE_DIM > OpDomainGradTimesVec
FormsIntegrators< BoundaryEleOp >::Assembly< PETSC >::LinearForm< GAUSS >::OpSource< 1, 1 > OpBoundarySource
FormsIntegrators< DomainEleOp >::Assembly< PETSC >::LinearForm< GAUSS >::OpBaseTimesScalar< 1 > OpDomainTimesScalarField
FormsIntegrators< DomainEleOp >::Assembly< PETSC >::BiLinearForm< GAUSS >::OpGradGrad< 1, 1, SPACE_DIM > OpDomainGradGrad
FormsIntegrators< BoundaryEleOp >::Assembly< PETSC >::BiLinearForm< GAUSS >::OpMass< 1, 1 > OpBoundaryMass
FormsIntegrators< DomainEleOp >::Assembly< PETSC >::BiLinearForm< GAUSS >::OpMass< 1, 1 > OpDomainMass
FormsIntegrators< EdgeEleOp >::Assembly< PETSC >::LinearForm< GAUSS >::OpSource< 1, 1 > OpBoundarySource
Definition helmholtz.cpp:30
FormsIntegrators< DomainEleOp >::Assembly< PETSC >::BiLinearForm< GAUSS >::OpGradGrad< 1, 1, SPACE_DIM > OpDomainGradGrad
Definition helmholtz.cpp:24
FormsIntegrators< DomainEleOp >::Assembly< PETSC >::LinearForm< GAUSS >::OpGradTimesTensor< 1, 1, SPACE_DIM > OpDomainGradTimesVec
FormsIntegrators< DomainEleOp >::Assembly< PETSC >::LinearForm< GAUSS >::OpBaseTimesScalar< 1 > OpDomainTimesScalarField
double D
PetscErrorCode MoFEMErrorCode
MoFEM/PETSc error code.
implementation of Data Operators for Forces and Sources
Definition Common.hpp:10
PetscErrorCode DMMoFEMTSSetMonitor(DM dm, TS ts, const std::string fe_name, boost::shared_ptr< MoFEM::FEMethod > method, boost::shared_ptr< MoFEM::BasicMethod > pre_only, boost::shared_ptr< MoFEM::BasicMethod > post_only)
Set Monitor To TS solver.
Definition DMMoFEM.cpp:1056
PetscErrorCode TsSetIJacobian(TS ts, PetscReal t, Vec u, Vec u_t, PetscReal a, Mat A, Mat B, void *ctx)
Set function evaluating jacobian in TS solver.
Definition TsCtx.cpp:165
auto getDMTsCtx(DM dm)
Get TS context data structure used by DM.
Definition DMMoFEM.hpp:1141
PetscErrorCode PetscOptionsGetInt(PetscOptions *, const char pre[], const char name[], PetscInt *ivalue, PetscBool *set)
OpCalculateScalarFieldValuesFromPetscVecImpl< PetscData::CTX_SET_X_T > OpCalculateScalarFieldValuesDot
FormsIntegrators< BoundaryEleOp >::Assembly< PETSC >::LinearForm< GAUSS >::OpBaseTimesScalar< 1 > OpBoundaryTimeScalarField
OpPostProcMapInMoab< SPACE_DIM, SPACE_DIM > OpPPMap
constexpr double t
plate stiffness
Definition plate.cpp:58
static constexpr int approx_order
FormsIntegrators< BoundaryEleOp >::Assembly< PETSC >::BiLinearForm< GAUSS >::OpMass< 1, SPACE_DIM > OpBoundaryMass
[Only used with Hencky/nonlinear material]
Definition seepage.cpp:70
static double lastA
static PetscErrorCode set(TS ts, PetscReal t, Vec u, Vec u_t, PetscReal a, Mat A, Mat B, void *ctx)
MoFEM::Interface & mField
MoFEMErrorCode solveSystem()
MoFEMErrorCode outputResults()
HeatEquation(MoFEM::Interface &m_field)
MoFEMErrorCode setupProblem()
MoFEMErrorCode setIntegrationRules()
MoFEMErrorCode assembleSystem()
MoFEMErrorCode readMesh()
MoFEMErrorCode boundaryCondition()
boost::shared_ptr< std::vector< unsigned char > > boundaryMarker
MoFEMErrorCode runProgram()
MoFEMErrorCode initialCondition()
Add operators pushing bases from local to physical configuration.
Simple interface for fast problem set-up.
Definition BcManager.hpp:25
virtual moab::Interface & get_moab()=0
Core (interface) class.
Definition Core.hpp:82
static MoFEMErrorCode Initialize(int *argc, char ***args, const char file[], const char help[])
Initializes the MoFEM database PETSc, MOAB and MPI.
Definition Core.cpp:72
static MoFEMErrorCode Finalize()
Checks for options to be called at the conclusion of the program.
Definition Core.cpp:112
Deprecated interface functions.
Data on single entity (This is passed as argument to DataOperator::doWork)
structure for User Loop Methods on finite elements
Basic algebra on fields.
Definition FieldBlas.hpp:21
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.
Interface for managing meshsets containing materials and boundary conditions.
Get field gradients at integration pts for scalar filed rank 0, i.e. vector field.
Get value at integration points for scalar field.
Post post-proc data at points from hash maps.
Set indices on entities on finite element.
Set inverse jacobian to base functions.
PipelineManager interface.
boost::shared_ptr< FEMethod > & getDomainRhsFE()
boost::shared_ptr< FEMethod > & getDomainLhsFE()
MoFEM::FaceElementForcesAndSourcesCore FaceEle
MoFEMErrorCode setDomainRhsIntegrationRule(RuleHookFun rule)
MoFEM::EdgeElementForcesAndSourcesCore EdgeEle
boost::shared_ptr< FEMethod > & getBoundaryRhsFE()
Simple interface for fast problem set-up.
Definition Simple.hpp:27
intrusive_ptr for managing petsc objects
PetscInt ts_step
time step number
MoFEMErrorCode getInterface(IFACE *&iface) const
Get interface reference to pointer of interface.
[Push operators to pipeline]
Monitor(SmartPetscObj< DM > dm, boost::shared_ptr< PostProcEle > post_proc)
static constexpr int saveEveryNthStep
SmartPetscObj< DM > dM
MoFEMErrorCode postProcess()
function is run at the end of loop
boost::shared_ptr< PostProcEle > postProc
MoFEMErrorCode operator()()
function is run for every finite element
MoFEMErrorCode preProcess()
function is run at the beginning of loop