v0.15.5
Loading...
Searching...
No Matches
free_surface.cpp
Go to the documentation of this file.
1/**
2 * \file free_surface.cpp
3 * \example mofem/tutorials/vec-5_free_surface/free_surface.cpp
4 *
5 * Using PipelineManager interface calculate the divergence of base functions,
6 * and integral of flux on the boundary. Since the h-div space is used, volume
7 * integral and boundary integral should give the same result.
8 *
9 * Implementation based on \cite lovric2019low
10 */
11
12#include <MoFEM.hpp>
13#include <petsc/private/petscimpl.h>
14
15using namespace MoFEM;
16
17static char help[] = "...\n\n";
18
19#ifdef PYTHON_INIT_SURFACE
20#include <boost/python.hpp>
21#include <boost/python/def.hpp>
22namespace bp = boost::python;
23
24struct SurfacePython {
25 SurfacePython() = default;
26 virtual ~SurfacePython() = default;
27
28 /**
29 * @brief Initialize Python surface evaluation from file
30 *
31 * @param py_file Path to Python file containing surface function
32 * Must contain a function named "surface" that takes
33 * (x, y, z, eta) coordinates and returns surface value
34 * @return MoFEMErrorCode Success/failure code
35 *
36 * @note The Python file must define a function called "surface" that
37 * evaluates the initial surface configuration at given coordinates
38 */
39 MoFEMErrorCode surfaceInit(const std::string py_file) {
41 try {
42
43 // create main module
44 auto main_module = bp::import("__main__");
45 mainNamespace = main_module.attr("__dict__");
46 bp::exec_file(py_file.c_str(), mainNamespace, mainNamespace);
47 // create a reference to python function
48 surfaceFun = mainNamespace["surface"];
49
50 } catch (bp::error_already_set const &) {
51 // print all other errors to stderr
52 PyErr_Print();
54 }
56 };
57
58 /**
59 * @brief Evaluate surface function at given coordinates
60 *
61 * @param x X-coordinate
62 * @param y Y-coordinate
63 * @param z Z-coordinate
64 * @param eta Interface thickness parameter
65 * @param s Output surface value (phase field value)
66 * @return MoFEMErrorCode Success/failure code
67 *
68 * @note This function calls the Python "surface" function with the given
69 * coordinates and returns the evaluated surface value
70 */
71 MoFEMErrorCode evalSurface(
72
73 double x, double y, double z, double eta, double &s
74
75 ) {
77 try {
78
79 // call python function
80 s = bp::extract<double>(surfaceFun(x, y, z, eta));
81
82 } catch (bp::error_already_set const &) {
83 // print all other errors to stderr
84 PyErr_Print();
86 }
88 }
89
90private:
91 bp::object mainNamespace;
92 bp::object surfaceFun;
93};
94
95static boost::weak_ptr<SurfacePython> surfacePythonWeakPtr;
96
97#endif
98
99int coord_type = EXECUTABLE_COORD_TYPE;
100
101constexpr int BASE_DIM = 1;
102constexpr int SPACE_DIM = 2;
103constexpr int U_FIELD_DIM = SPACE_DIM;
104
105constexpr AssemblyType A = AssemblyType::PETSC; //< selected assembly type
107 IntegrationType::GAUSS; //< selected integration type
108
109template <int DIM>
111
114using DomainEleOp = DomainEle::UserDataOperator;
117using BoundaryEleOp = BoundaryEle::UserDataOperator;
119using SideOp = SideEle::UserDataOperator;
120
124
126
130
139
146
147template <CoordinateTypes COORD_TYPE>
150
151// Flux is applied by Lagrange Multiplier BC
155
160
162
163// mesh refinement
164int order = 3; ///< approximation order
165int nb_levels = 4; //< number of refinement levels
166int refine_overlap = 4; //< mesh overlap while refine
167
168constexpr bool debug = true;
169
170auto get_start_bit = []() {
171 return nb_levels + 1;
172}; //< first refinement level for computational mesh
173auto get_current_bit = []() {
174 return 2 * get_start_bit() + 1;
175}; ///< dofs bit used to do calculations
176auto get_skin_parent_bit = []() { return 2 * get_start_bit() + 2; };
177auto get_skin_child_bit = []() { return 2 * get_start_bit() + 3; };
178auto get_projection_bit = []() { return 2 * get_start_bit() + 4; };
179auto get_skin_projection_bit = []() { return 2 * get_start_bit() + 5; };
180
181// Physical parameters
182double a0 = 980;
183double rho_m = 0.998;
184double mu_m = 0.010101 * 1e2;
185double rho_p = 0.0012;
186double mu_p = 0.000182 * 1e2;
187double lambda = 73; ///< surface tension
188double W = 0.25;
189
190// Model parameters
191double h = 0.03; // mesh size
192double eta = h;
193double eta2 = eta * eta;
194
195// Numerical parameters
196double md = 1e-2; // mobility
197double eps = 1e-12;
198double tol = std::numeric_limits<float>::epsilon();
199
200double rho_ave = (rho_p + rho_m) / 2;
201double rho_diff = (rho_p - rho_m) / 2;
202double mu_ave = (mu_p + mu_m) / 2;
203double mu_diff = (mu_p - mu_m) / 2;
204
205double kappa = (3. / (4. * std::sqrt(2. * W))) * (lambda / eta);
206
207auto integration_rule = [](int, int, int) { return 2 * order + 1; };
208
209//! [cylindrical]
210/**
211 * @brief Coordinate system scaling factor
212 * @param r Radial coordinate
213 * @return Scaling factor for integration
214 *
215 * Returns 2πr for cylindrical coordinates, 1 for Cartesian.
216 * Used to scale integration measures and force contributions.
217 */
218auto cylindrical = [](const double r) {
219 if (coord_type == CYLINDRICAL)
220 return 2 * M_PI * r;
221 else
222 return 1.;
223};
224//! [cylindrical]
225
226//! [wetting_angle_sub_stepping]
227/**
228 * @brief Wetting angle sub-stepping for gradual application
229 * @param ts_step Current time step number
230 * @return Scaling factor [0,1] for gradual wetting angle application
231 *
232 * Gradually applies wetting angle boundary condition over first 16 steps
233 * to avoid sudden changes that could destabilize the solution.
234 */
235auto wetting_angle_sub_stepping = [](auto ts_step) {
236 constexpr int sub_stepping = 16;
237 return std::min(1., static_cast<double>(ts_step) / sub_stepping);
238};
239//! [wetting_angle_sub_stepping]
240
241// Phase field cutoff and smoothing functions
242
243/**
244 * @brief Maximum function with smooth transition
245 * @param x Input value
246 * @return max(x, -1) with smooth transition
247 */
248auto my_max = [](const double x) { return (x - 1 + std::abs(x + 1)) / 2; };
249
250/**
251 * @brief Minimum function with smooth transition
252 * @param x Input value
253 * @return min(x, 1) with smooth transition
254 */
255auto my_min = [](const double x) { return (x + 1 - std::abs(x - 1)) / 2; };
256
257/**
258 * @brief Phase field cutoff function
259 * @param h Phase field value
260 * @return Constrained value in [-1, 1]
261 *
262 * Constrains phase field values to physical range [-1,1] with smooth cutoff
263 */
264auto cut_off = [](const double h) { return my_max(my_min(h)); };
265
266/**
267 * @brief Derivative of cutoff function
268 * @param h Phase field value
269 * @return Derivative of cutoff function
270 */
271auto d_cut_off = [](const double h) {
272 if (h >= -1 && h < 1)
273 return 1.;
274 else
275 return 0.;
276};
277
278/**
279 * @brief Phase-dependent material property interpolation
280 * @param h Phase field value (-1 to 1)
281 * @param diff Difference between phase values (phase_plus - phase_minus)
282 * @param ave Average of phase values (phase_plus + phase_minus)/2
283 * @return Interpolated material property
284 *
285 * Linear interpolation: property = diff * h + ave
286 * Used for density and viscosity interpolation between phases
287 */
288auto phase_function = [](const double h, const double diff, const double ave) {
289 return diff * h + ave;
290};
291
292/**
293 * @brief Derivative of phase function with respect to h
294 * @param h Phase field value (unused in linear case)
295 * @param diff Difference between phase values
296 * @return Derivative value
297 */
298auto d_phase_function_h = [](const double h, const double diff) {
299 return diff;
300};
301
302// Free energy and mobility functions for phase field model
303
304/**
305 * @brief Double-well potential function
306 * @param h Phase field value
307 * @return Free energy density f(h) = 4W*h*(h²-1)
308 *
309 * Double-well potential with minima at h = ±1 (pure phases)
310 * and maximum at h = 0 (interface). Controls interface structure.
311 */
312auto get_f = [](const double h) { return 4 * W * h * (h * h - 1); };
313
314/**
315 * @brief Derivative of double-well potential
316 * @param h Phase field value
317 * @return f'(h) = 4W*(3h²-1)
318 */
319auto get_f_dh = [](const double h) { return 4 * W * (3 * h * h - 1); };
320
321/**
322 * @brief Constant mobility function M₀
323 * @param h Phase field value (unused)
324 * @return Constant mobility value
325 */
326auto get_M0 = [](auto h) { return md; };
327
328/**
329 * @brief Derivative of constant mobility
330 * @param h Phase field value (unused)
331 * @return Zero (constant mobility)
332 */
333auto get_M0_dh = [](auto h) { return 0; };
334
335/**
336 * @brief Degenerate mobility function M₂
337 * @param h Phase field value
338 * @return M₂(h) = md*(1-h²)
339 *
340 * Mobility that vanishes at pure phases (h = ±1)
341 */
342auto get_M2 = [](auto h) {
343 return md * (1 - h * h);
344};
345
346/**
347 * @brief Derivative of degenerate mobility M₂
348 * @param h Phase field value
349 * @return M₂'(h) = -2*md*h
350 */
351auto get_M2_dh = [](auto h) { return -md * 2 * h; };
352
353/**
354 * @brief Non-linear mobility function M₃
355 * @param h Phase field value
356 * @return Piecewise cubic mobility function
357 *
358 * Smooth mobility function with different behavior for h >= 0 and h < 0
359 */
360auto get_M3 = [](auto h) {
361 const double h2 = h * h;
362 const double h3 = h2 * h;
363 if (h >= 0)
364 return md * (2 * h3 - 3 * h2 + 1);
365 else
366 return md * (-2 * h3 - 3 * h2 + 1);
367};
368
369/**
370 * @brief Derivative of non-linear mobility M₃
371 * @param h Phase field value
372 * @return M₃'(h) with sign-dependent cubic behavior
373 */
374auto get_M3_dh = [](auto h) {
375 if (h >= 0)
376 return md * (6 * h * (h - 1));
377 else
378 return md * (-6 * h * (h + 1));
379};
380
381// Select mobility model (currently using constant M₀)
382auto get_M = [](auto h) { return get_M0(h); };
383auto get_M_dh = [](auto h) { return get_M0_dh(h); };
384
385/**
386 * @brief Create deviatoric stress tensor
387 * @param A Coefficient for tensor scaling
388 * @return Fourth-order deviatoric tensor D_ijkl
389 *
390 * Constructs deviatoric part of fourth-order identity tensor:
391 * D_ijkl = A * (δ_ik δ_jl + δ_il δ_jk)/2
392 * Used in viscous stress calculations for fluid flow
393 */
394auto get_D = [](const double A) {
396 t_D(i, j, k, l) = A * ((t_kd(i, k) ^ t_kd(j, l)) / 4.);
397 return t_D;
398};
399
400// Initial condition functions for phase field
401
402/**
403 * @brief Oscillating interface initialization
404 * @param r Radial coordinate
405 * @param y Vertical coordinate
406 * @param unused Z-coordinate (unused in 2D)
407 * @return Phase field value (-1 to 1)
408 *
409 * Creates circular interface with sinusoidal perturbations:
410 * - Base radius R with amplitude A
411 * - n-fold symmetry oscillations
412 * - Uses tanh profile for smooth interface
413 */
414auto kernel_oscillation = [](double r, double y, double) {
415 constexpr int n = 3;
416 constexpr double R = 0.0125;
417 constexpr double A = R * 0.2;
418 const double theta = atan2(r, y);
419 const double w = R + A * cos(n * theta);
420 const double d = std::sqrt(r * r + y * y);
421 return tanh((w - d) / (eta * std::sqrt(2)));
422};
423
424/**
425 * @brief Eye-shaped interface initialization
426 * @param r Radial coordinate
427 * @param y Vertical coordinate
428 * @param unused Z-coordinate (unused in 2D)
429 * @return Phase field value (-1 to 1)
430 *
431 * Creates circular droplet centered at (0, y0) with radius R
432 */
433auto kernel_eye = [](double r, double y, double) {
434 constexpr double y0 = 0.5;
435 constexpr double R = 0.4;
436 y -= y0;
437 const double d = std::sqrt(r * r + y * y);
438 return tanh((R - d) / (eta * std::sqrt(2)));
439};
440
441/**
442 * @brief Capillary tube initialization
443 * @param x X-coordinate (unused)
444 * @param y Y-coordinate
445 * @param z Z-coordinate (unused)
446 * @return Phase field value (-1 to 1)
447 *
448 * Creates horizontal interface at specified water height
449 */
450auto capillary_tube = [](double x, double y, double z) {
451 constexpr double water_height = 0.;
452 return tanh((water_height - y) / (eta * std::sqrt(2)));
453 ;
454};
455
456/**
457 * @brief Bubble device initialization
458 * @param x X-coordinate
459 * @param y Y-coordinate (unused)
460 * @param z Z-coordinate (unused)
461 * @return Phase field value (-1 to 1)
462 *
463 * Creates vertical interface for bubble formation device
464 */
465auto bubble_device = [](double x, double y, double z) {
466 return -tanh((-0.039 - x) / (eta * std::sqrt(2)));
467};
468
469/**
470 * @brief Initialisation function
471 *
472 * @note If UMs are compiled with Python to initialise phase field "H"
473 * surface.py function is used, which has to be present in execution folder.
474 * @note Not used in current code implementation.
475 *
476 */
477auto init_h = [](double r, double y, double theta) {
478#ifdef PYTHON_INIT_SURFACE
479 double s = 1;
480 if (auto ptr = surfacePythonWeakPtr.lock()) {
481 CHK_THROW_MESSAGE(ptr->evalSurface(r, y, theta, eta, s),
482 "error eval python");
483 }
484 return s;
485#else
486 // return bubble_device(r, y, theta);
487 return capillary_tube(r, y, theta);
488 // return kernel_eye(r, y, theta);
489#endif
490};
491
492/**
493 * @brief Wetting angle function (placeholder)
494 * @param water_level Current water level
495 * @return Wetting angle value
496 *
497 * Currently returns input value directly. Can be modified to implement
498 * complex wetting angle dependencies on interface position or time.
499 */
500auto wetting_angle = [](double water_level) { return water_level; };
501
502/**
503 * @brief Create bit reference level
504 * @param b Bit number to set
505 * @return BitRefLevel with specified bit set
506 */
507auto bit = [](auto b) { return BitRefLevel().set(b); };
508
509/**
510 * @brief Create marker bit reference level
511 * @param b Bit number from end
512 * @return BitRefLevel with bit set from end
513 */
514auto marker = [](auto b) { return BitRefLevel().set(BITREFLEVEL_SIZE - b); };
515
516/**
517 * @brief Get bit reference level from finite element
518 * @param fe_ptr Pointer to finite element method
519 * @return Current bit reference level of the element
520 */
521auto get_fe_bit = [](FEMethod *fe_ptr) {
522 return fe_ptr->numeredEntFiniteElementPtr->getBitRefLevel();
523};
524
525/**
526 * @brief Get global size across all processors
527 * @param l_size Local size on current processor
528 * @return Global sum of sizes across all processors
529 */
530auto get_global_size = [](int l_size) {
531 int g_size;
532 MPI_Allreduce(&l_size, &g_size, 1, MPI_INT, MPI_SUM, MPI_COMM_WORLD);
533 return g_size;
534};
535
536/**
537 * @brief Save range of entities to file
538 * @param moab MOAB interface for mesh operations
539 * @param name Output filename
540 * @param r Range of entities to save
541 * @return MoFEMErrorCode Success/failure code
542 *
543 * Saves entities to HDF5 file if range is non-empty globally
544 */
545auto save_range = [](moab::Interface &moab, const std::string name,
546 const Range r) {
548 if (get_global_size(r.size())) {
549 auto out_meshset = get_temp_meshset_ptr(moab);
550 CHKERR moab.add_entities(*out_meshset, r);
551 CHKERR moab.write_file(name.c_str(), "MOAB", "PARALLEL=WRITE_PART",
552 out_meshset->get_ptr(), 1);
553 }
555};
556
557/**
558 * @brief Get entities of DOFs by field name - used for debugging
559 * @param dm PETSc DM object containing the problem
560 * @param field_name Name of field to extract entities for
561 * @return Range of entities containing DOFs for specified field
562 *
563 * Extracts all mesh entities that have degrees of freedom for the
564 * specified field. Useful for debugging and visualization of field
565 * distributions across the mesh.
566 */
567auto get_dofs_ents_by_field_name = [](auto dm, auto field_name) {
568 auto prb_ptr = getProblemPtr(dm);
569 std::vector<EntityHandle> ents_vec;
570
571 MoFEM::Interface *m_field_ptr;
572 CHKERR DMoFEMGetInterfacePtr(dm, &m_field_ptr);
573
574 auto bit_number = m_field_ptr->get_field_bit_number(field_name);
575 auto dofs = prb_ptr->numeredRowDofsPtr;
576 auto lo_it = dofs->lower_bound(FieldEntity::getLoBitNumberUId(bit_number));
577 auto hi_it = dofs->upper_bound(FieldEntity::getHiBitNumberUId(bit_number));
578 ents_vec.reserve(std::distance(lo_it, hi_it));
579
580 for (; lo_it != hi_it; ++lo_it) {
581 ents_vec.push_back((*lo_it)->getEnt());
582 }
583
584 std::sort(ents_vec.begin(), ents_vec.end());
585 auto it = std::unique(ents_vec.begin(), ents_vec.end());
586 Range r;
587 r.insert_list(ents_vec.begin(), it);
588 return r;
589};
590
591/**
592 * @brief Get all entities with DOFs in the problem - used for debugging
593 * @param dm PETSc DM object containing the problem
594 * @return Range of all entities containing any DOFs
595 *
596 * Extracts all mesh entities that have degrees of freedom for any field.
597 * Useful for debugging mesh partitioning and DOF distribution.
598 */
599auto get_dofs_ents_all = [](auto dm) {
600 auto prb_ptr = getProblemPtr(dm);
601 std::vector<EntityHandle> ents_vec;
602
603 MoFEM::Interface *m_field_ptr;
604 CHKERR DMoFEMGetInterfacePtr(dm, &m_field_ptr);
605
606 auto dofs = prb_ptr->numeredRowDofsPtr;
607 ents_vec.reserve(dofs->size());
608
609 for (auto d : *dofs) {
610 ents_vec.push_back(d->getEnt());
611 }
612
613 std::sort(ents_vec.begin(), ents_vec.end());
614 auto it = std::unique(ents_vec.begin(), ents_vec.end());
615 Range r;
616 r.insert_list(ents_vec.begin(), it);
617 return r;
618};
619
620#include <FreeSurfaceOps.hpp>
621using namespace FreeSurfaceOps;
622
623struct FreeSurface;
624
625enum FR { F, R }; // F - forward, and reverse
626
627/**
628 * @brief Set of functions called by PETSc solver used to refine and update
629 * mesh.
630 *
631 * @note Currently theta method is only handled by this code.
632 *
633 */
634struct TSPrePostProc {
635
636 TSPrePostProc() = default;
637 virtual ~TSPrePostProc() = default;
638
639 /**
640 * @brief Used to setup TS solver
641 *
642 * @param ts PETSc time stepping object to configure
643 * @return MoFEMErrorCode Success/failure code
644 *
645 * Sets up time stepping solver with custom preconditioner, monitors,
646 * and callback functions for mesh refinement and solution projection
647 */
649
650 /**
651 * @brief Get scatter context for vector operations
652 *
653 * @param x Local sub-vector
654 * @param y Global vector
655 * @param fr Direction flag (F=forward, R=reverse)
656 * @return SmartPetscObj<VecScatter> Scatter context for vector operations
657 *
658 * Creates scatter context to transfer data between global and sub-problem vectors
659 */
660 SmartPetscObj<VecScatter> getScatter(Vec x, Vec y, enum FR fr);
661
662 /**
663 * @brief Create sub-problem vector
664 *
665 * @return SmartPetscObj<Vec> Vector compatible with sub-problem DM
666 *
667 * Creates a vector with proper size and ghost structure for sub-problem
668 */
670
674
675private:
676 /**
677 * @brief Pre process time step
678 *
679 * Refine mesh and update fields before each time step
680 *
681 * @param ts PETSc time stepping object
682 * @return MoFEMErrorCode Success/failure code
683 *
684 * This function is called before each time step to:
685 * - Apply phase field cutoff constraints
686 * - Refine mesh based on interface location
687 * - Project solution data to new mesh
688 * - Update solver operators
689 */
690 static MoFEMErrorCode tsPreProc(TS ts);
691
692 /**
693 * @brief Post process time step
694 *
695 * Currently this function does not make anything major
696 *
697 * @param ts PETSc time stepping object
698 * @return MoFEMErrorCode Success/failure code
699 *
700 * Called after each time step completion for cleanup operations
701 */
702 static MoFEMErrorCode tsPostProc(TS ts);
703
704 /**
705 * @brief Pre-stage processing for time stepping
706 *
707 * @param ts PETSc time stepping object
708 * @return MoFEMErrorCode Success/failure code
709 */
711
712 /**
713 * @brief Set implicit function for time stepping
714 *
715 * @param ts PETSc time stepping object
716 * @param t Current time
717 * @param u Solution vector at current time
718 * @param u_t Time derivative of solution
719 * @param f Output residual vector F(t,u,u_t)
720 * @param ctx User context (unused)
721 * @return MoFEMErrorCode Success/failure code
722 *
723 * Wrapper that scatters global vectors to sub-problem and evaluates residual
724 */
725 static MoFEMErrorCode tsSetIFunction(TS ts, PetscReal t, Vec u, Vec u_t,
726 Vec f,
727 void *ctx); //< Wrapper for SNES Rhs
728
729 /**
730 * @brief Set implicit Jacobian for time stepping
731 *
732 * @param ts PETSc time stepping object
733 * @param t Current time
734 * @param u Solution vector at current time
735 * @param u_t Time derivative of solution
736 * @param a Shift parameter for implicit methods
737 * @param A Jacobian matrix (input)
738 * @param B Jacobian matrix (output, often same as A)
739 * @param ctx User context (unused)
740 * @return MoFEMErrorCode Success/failure code
741 *
742 * Wrapper that assembles Jacobian matrix for sub-problem
743 */
744 static MoFEMErrorCode tsSetIJacobian(TS ts, PetscReal t, Vec u, Vec u_t,
745 PetscReal a, Mat A, Mat B,
746 void *ctx); ///< Wrapper for SNES Lhs
747
748 /**
749 * @brief Monitor solution during time stepping
750 *
751 * @param ts PETSc time stepping object
752 * @param step Current time step number
753 * @param t Current time value
754 * @param u Current solution vector
755 * @param ctx User context (pointer to FreeSurface)
756 * @return MoFEMErrorCode Success/failure code
757 *
758 * Called after each time step to monitor solution and save output
759 */
760 static MoFEMErrorCode tsMonitor(TS ts, PetscInt step, PetscReal t, Vec u,
761 void *ctx); ///< Wrapper for TS monitor
762
763 /**
764 * @brief Setup preconditioner
765 *
766 * @param pc PETSc preconditioner object
767 * @return MoFEMErrorCode Success/failure code
768 *
769 * Initializes KSP solver for shell preconditioner
770 */
771 static MoFEMErrorCode pcSetup(PC pc);
772
773 /**
774 * @brief Apply preconditioner
775 *
776 * @param pc PETSc preconditioner object
777 * @param pc_f Input vector (right-hand side)
778 * @param pc_x Output vector (preconditioned solution)
779 * @return MoFEMErrorCode Success/failure code
780 *
781 * Applies preconditioner by solving sub-problem with KSP
782 */
783 static MoFEMErrorCode pcApply(PC pc, Vec pc_f, Vec pc_x);
784
785 SmartPetscObj<Vec> globRes; //< global residual
786 SmartPetscObj<Mat> subB; //< sub problem tangent matrix
787 SmartPetscObj<KSP> subKSP; //< sub problem KSP solver
788
789 boost::shared_ptr<SnesCtx>
790 snesCtxPtr; //< internal data (context) for MoFEM SNES functions
791 boost::shared_ptr<TsCtx>
792 tsCtxPtr; //< internal data (context) for MoFEM TS functions.
793};
794
795static boost::weak_ptr<TSPrePostProc> tsPrePostProc;
796
798
799 /**
800 * @brief Constructor
801 *
802 * @param m_field Reference to MoFEM interface for finite element operations
803 */
804 FreeSurface(MoFEM::Interface &m_field) : mField(m_field) {}
805
806 /**
807 * @brief Main function to run the complete free surface simulation
808 *
809 * @return MoFEMErrorCode Success/failure code
810 *
811 * Executes the complete simulation workflow:
812 * 1. Read mesh from file
813 * 2. Setup problem (fields, approximation orders, parameters)
814 * 3. Apply boundary conditions and initialize fields
815 * 4. Assemble system matrices and operators
816 * 5. Solve time-dependent problem
817 */
819
820 /**
821 * @brief Create refined problem for mesh adaptation
822 *
823 * @return MoFEMErrorCode Success/failure code
824 *
825 * Creates mesh refinement data structures needed for adaptive meshing
826 */
828
830
831private:
832 /**
833 * @brief Read mesh from input file
834 *
835 * @return MoFEMErrorCode Success/failure code
836 *
837 * Loads mesh using Simple interface and sets up parent adjacencies
838 * for hierarchical mesh refinement
839 */
841
842 /**
843 * @brief Setup problem fields and parameters
844 *
845 * @return MoFEMErrorCode Success/failure code
846 *
847 * Creates finite element fields:
848 * - U: Velocity field (H1, vector)
849 * - P: Pressure field (H1, scalar)
850 * - H: Phase/order field (H1, scalar)
851 * - G: Chemical potential (H1, scalar)
852 * - L: Lagrange multiplier for boundary conditions (H1, scalar)
853 *
854 * Sets approximation orders and reads physical parameters from command line
855 */
857
858 /**
859 * @brief Apply boundary conditions and initialize fields
860 *
861 * @return MoFEMErrorCode Success/failure code
862 *
863 * - Initializes phase field using analytical or Python functions
864 * - Solves initialization problem for consistent initial conditions
865 * - Performs mesh refinement based on interface location
866 * - Removes DOFs for boundary conditions (symmetry, fixed, etc.)
867 */
869
870 /**
871 * @brief Project solution data between mesh levels
872 *
873 * @return MoFEMErrorCode Success/failure code
874 *
875 * Projects field data from coarse to fine mesh levels during refinement.
876 * Handles both time stepping vectors (for theta method) and regular fields.
877 * Uses L2 projection with mass matrix assembly.
878 */
880
881 /**
882 * @brief Assemble system operators and matrices
883 *
884 * @return MoFEMErrorCode Success/failure code
885 *
886 * Sets up finite element operators for:
887 * - Domain integration (momentum, phase field, mass conservation)
888 * - Boundary integration (surface tension, wetting angle, Lagrange multipliers)
889 * - Parent-child mesh hierarchies for adaptive refinement
890 */
892
893 /**
894 * @brief Solve the time-dependent free surface problem
895 *
896 * @return MoFEMErrorCode Success/failure code
897 *
898 * Creates and configures time stepping solver with:
899 * - Sub-problem DM for refined mesh
900 * - Post-processing for output visualization
901 * - Custom preconditioner and monitors
902 * - TSTheta implicit time integration
903 */
905
906 /**
907 * @brief Find entities on refinement levels
908 * @param overlap Level of overlap around phase interface
909 * @return Vector of entity ranges for each refinement level
910 *
911 * Identifies mesh entities that cross the phase interface by analyzing
912 * phase field values at vertices. Returns entities that need refinement
913 * at each hierarchical level with specified overlap.
914 */
915 std::vector<Range> findEntitiesCrossedByPhaseInterface(size_t overlap);
916
917 /**
918 * @brief Find parent entities that need refinement
919 * @param ents Child entities requiring refinement
920 * @param level Bit level for entity marking
921 * @param mask Bit mask for filtering
922 * @return Range of parent entities to refine
923 *
924 * Traverses mesh hierarchy to find parent entities that should be
925 * refined to accommodate interface tracking requirements.
926 */
928
929 /**
930 * @brief Perform adaptive mesh refinement
931 * @param overlap Number of element layers around interface to refine
932 * @return MoFEMErrorCode Success/failure code
933 *
934 * Performs hierarchical mesh refinement around the phase interface:
935 * - Identifies entities crossing interface
936 * - Creates refinement hierarchy
937 * - Sets up skin elements between levels
938 * - Updates bit level markings for computation
939 */
940 MoFEMErrorCode refineMesh(size_t overlap);
941
942 /**
943 * @brief Create hierarchy of elements run on parent levels
944 * @param fe_top Pipeline element to which hierarchy is attached
945 * @param field_name Name of field for DOF extraction ("" for all fields)
946 * @param op Type of operator OPSPACE, OPROW, OPCOL or OPROWCOL
947 * @param child_ent_bit Bit level marking child entities
948 * @param get_elem Lambda function to create element on hierarchy
949 * @param verbosity Verbosity level for debugging output
950 * @param sev Severity level for logging
951 * @return MoFEMErrorCode Success/failure code
952 *
953 * Sets up parent-child relationships for hierarchical mesh refinement.
954 * Allows access to field data from parent mesh levels during computation
955 * on refined child meshes. Essential for projection and interpolation.
956 */
958 boost::shared_ptr<FEMethod> fe_top, std::string field_name,
960 BitRefLevel child_ent_bit,
961 boost::function<boost::shared_ptr<ForcesAndSourcesCore>()> get_elem,
962 int verbosity, LogManager::SeverityLevel sev);
963
964 friend struct TSPrePostProc;
965
966 /**
967 * @brief Check results for correctness
968 *
969 * @return MoFEMErrorCode Success/failure code
970 */
972};
973
974//! [Run programme]
985//! [Run programme]
986
987//! [Read mesh]
990 MOFEM_LOG("FS", Sev::inform) << "Read mesh for problem";
992
994 simple->getBitRefLevel() = BitRefLevel();
995
996 CHKERR simple->getOptions();
997 CHKERR simple->loadFile();
998
1000}
1001//! [Read mesh]
1002
1003//! [Set up problem]
1006
1007 CHKERR PetscOptionsGetInt(PETSC_NULLPTR, "", "-order", &order, PETSC_NULLPTR);
1008 CHKERR PetscOptionsGetInt(PETSC_NULLPTR, "", "-nb_levels", &nb_levels,
1009 PETSC_NULLPTR);
1010 CHKERR PetscOptionsGetInt(PETSC_NULLPTR, "", "-refine_overlap", &refine_overlap,
1011 PETSC_NULLPTR);
1012
1013 const char *coord_type_names[] = {"cartesian", "polar", "cylindrical",
1014 "spherical"};
1015 CHKERR PetscOptionsGetEList(PETSC_NULLPTR, NULL, "-coords", coord_type_names,
1016 LAST_COORDINATE_SYSTEM, &coord_type, PETSC_NULLPTR);
1017
1018 MOFEM_LOG("FS", Sev::inform) << "Approximation order = " << order;
1019 MOFEM_LOG("FS", Sev::inform)
1020 << "Number of refinement levels nb_levels = " << nb_levels;
1021 nb_levels += 1;
1022
1023 auto simple = mField.getInterface<Simple>();
1024
1025 CHKERR PetscOptionsGetScalar(PETSC_NULLPTR, "", "-a0", &a0, PETSC_NULLPTR); // Acceleration
1026 CHKERR PetscOptionsGetScalar(PETSC_NULLPTR, "", "-rho_m", &rho_m, PETSC_NULLPTR); // Density minus phase
1027 CHKERR PetscOptionsGetScalar(PETSC_NULLPTR, "", "-mu_m", &mu_m, PETSC_NULLPTR); // Viscosity minus phase
1028 CHKERR PetscOptionsGetScalar(PETSC_NULLPTR, "", "-rho_p", &rho_p, PETSC_NULLPTR); // Density plus phase
1029 CHKERR PetscOptionsGetScalar(PETSC_NULLPTR, "", "-mu_p", &mu_p, PETSC_NULLPTR); // Viscosity plus phase
1030 CHKERR PetscOptionsGetScalar(PETSC_NULLPTR, "", "-lambda", &lambda, PETSC_NULLPTR); // Surface tension
1031 CHKERR PetscOptionsGetScalar(PETSC_NULLPTR, "", "-W", &W, PETSC_NULLPTR); // Height of the well in energy functional
1032
1033 rho_ave = (rho_p + rho_m) / 2;
1034 rho_diff = (rho_p - rho_m) / 2;
1035 mu_ave = (mu_p + mu_m) / 2;
1036 mu_diff = (mu_p - mu_m) / 2;
1037
1038 CHKERR PetscOptionsGetScalar(PETSC_NULLPTR, "", "-h", &h, PETSC_NULLPTR);
1039 eta = h;
1040 eta2 = eta * eta;
1041 kappa = (3. / (4. * std::sqrt(2. * W))) * (lambda / eta);
1042
1043 CHKERR PetscOptionsGetScalar(PETSC_NULLPTR, "", "-md", &md, PETSC_NULLPTR);
1044
1045 MOFEM_LOG("FS", Sev::inform) << "Acceleration a0 = " << a0;
1046 MOFEM_LOG("FS", Sev::inform) << "\"Minus\" phase density rho_m = " << rho_m;
1047 MOFEM_LOG("FS", Sev::inform) << "\"Minus\" phase viscosity mu_m = " << mu_m;
1048 MOFEM_LOG("FS", Sev::inform) << "\"Plus\" phase density rho_p = " << rho_p;
1049 MOFEM_LOG("FS", Sev::inform) << "\"Plus\" phase viscosity mu_p = " << mu_p;
1050 MOFEM_LOG("FS", Sev::inform) << "Surface tension lambda = " << lambda;
1051 MOFEM_LOG("FS", Sev::inform)
1052 << "Height of the well in energy functional W = " << W;
1053 MOFEM_LOG("FS", Sev::inform) << "Characteristic mesh size h = " << h;
1054 MOFEM_LOG("FS", Sev::inform) << "Mobility md = " << md;
1055
1056 MOFEM_LOG("FS", Sev::inform) << "Average density rho_ave = " << rho_ave;
1057 MOFEM_LOG("FS", Sev::inform) << "Difference density rho_diff = " << rho_diff;
1058 MOFEM_LOG("FS", Sev::inform) << "Average viscosity mu_ave = " << mu_ave;
1059 MOFEM_LOG("FS", Sev::inform) << "Difference viscosity mu_diff = " << mu_diff;
1060 MOFEM_LOG("FS", Sev::inform) << "kappa = " << kappa;
1061
1062
1063 // Fields on domain
1064
1065 // Velocity field
1066 CHKERR simple->addDomainField("U", H1, AINSWORTH_LEGENDRE_BASE, U_FIELD_DIM);
1067 // Pressure field
1068 CHKERR simple->addDomainField("P", H1, AINSWORTH_LEGENDRE_BASE, 1);
1069 // Order/phase fild
1070 CHKERR simple->addDomainField("H", H1, AINSWORTH_LEGENDRE_BASE, 1);
1071 // Chemical potential
1072 CHKERR simple->addDomainField("G", H1, AINSWORTH_LEGENDRE_BASE, 1);
1073
1074 // Field on boundary
1075 CHKERR simple->addBoundaryField("U", H1, AINSWORTH_LEGENDRE_BASE,
1076 U_FIELD_DIM);
1077 CHKERR simple->addBoundaryField("H", H1, AINSWORTH_LEGENDRE_BASE, 1);
1078 CHKERR simple->addBoundaryField("G", H1, AINSWORTH_LEGENDRE_BASE, 1);
1079 // Lagrange multiplier which constrains slip conditions
1080 CHKERR simple->addBoundaryField("L", H1, AINSWORTH_LEGENDRE_BASE, 1);
1081
1082 CHKERR simple->setFieldOrder("U", order);
1083 CHKERR simple->setFieldOrder("P", order - 1);
1084 CHKERR simple->setFieldOrder("H", order);
1085 CHKERR simple->setFieldOrder("G", order);
1086 CHKERR simple->setFieldOrder("L", order);
1087
1088 // Initialise bit ref levels
1089 auto set_problem_bit = [&]() {
1091 // Set bits to build adjacencies between parents and children. That is
1092 // used by simple interface.
1093 simple->getBitAdjEnt() = BitRefLevel().set();
1094 simple->getBitAdjParent() = BitRefLevel().set();
1095 simple->getBitRefLevel() = BitRefLevel().set();
1096 simple->getBitRefLevelMask() = BitRefLevel().set();
1098 };
1099
1100 CHKERR set_problem_bit();
1101
1102 CHKERR simple->setUp();
1103
1105}
1106//! [Set up problem]
1107
1108//! [Boundary condition]
1111
1112#ifdef PYTHON_INIT_SURFACE
1113 auto get_py_surface_init = []() {
1114 auto py_surf_init = boost::make_shared<SurfacePython>();
1115 CHKERR py_surf_init->surfaceInit("surface.py");
1116 surfacePythonWeakPtr = py_surf_init;
1117 return py_surf_init;
1118 };
1119 auto py_surf_init = get_py_surface_init();
1120#endif
1121
1122 auto simple = mField.getInterface<Simple>();
1123 auto pip_mng = mField.getInterface<PipelineManager>();
1124 auto bc_mng = mField.getInterface<BcManager>();
1125 auto bit_mng = mField.getInterface<BitRefManager>();
1126 auto dm = simple->getDM();
1127
1129
1130 auto reset_bits = [&]() {
1132 BitRefLevel start_mask;
1133 for (auto s = 0; s != get_start_bit(); ++s)
1134 start_mask[s] = true;
1135 // reset bit ref levels
1136 CHKERR bit_mng->lambdaBitRefLevel(
1137 [&](EntityHandle ent, BitRefLevel &bit) { bit &= start_mask; });
1138 Range level0;
1139 CHKERR bit_mng->getEntitiesByRefLevel(bit(0), BitRefLevel().set(), level0);
1140 CHKERR bit_mng->setNthBitRefLevel(level0, get_current_bit(), true);
1141 CHKERR bit_mng->setNthBitRefLevel(level0, get_projection_bit(), true);
1143 };
1144
1145 auto add_parent_field = [&](auto fe, auto op, auto field) {
1146 return setParentDofs(
1147 fe, field, op, bit(get_skin_parent_bit()),
1148
1149 [&]() {
1150 boost::shared_ptr<ForcesAndSourcesCore> fe_parent(
1151 new DomainParentEle(mField));
1152 return fe_parent;
1153 },
1154
1155 QUIET, Sev::noisy);
1156 };
1157
1158 auto h_ptr = boost::make_shared<VectorDouble>();
1159 auto grad_h_ptr = boost::make_shared<MatrixDouble>();
1160 auto g_ptr = boost::make_shared<VectorDouble>();
1161 auto grad_g_ptr = boost::make_shared<MatrixDouble>();
1162
1163 auto set_generic = [&](auto fe) {
1165 auto &pip = fe->getOpPtrVector();
1166
1168
1170 fe, "", UDO::OPSPACE, bit(get_skin_parent_bit()),
1171
1172 [&]() {
1173 boost::shared_ptr<ForcesAndSourcesCore> fe_parent(
1174 new DomainParentEle(mField));
1176 fe_parent->getOpPtrVector(), {H1});
1177 return fe_parent;
1178 },
1179
1180 QUIET, Sev::noisy);
1181
1182 CHKERR add_parent_field(fe, UDO::OPCOL, "H");
1183 pip.push_back(new OpCalculateScalarFieldValues("H", h_ptr));
1184 pip.push_back(
1185 new OpCalculateScalarFieldGradient<SPACE_DIM>("H", grad_h_ptr));
1186
1187 CHKERR add_parent_field(fe, UDO::OPCOL, "G");
1188 pip.push_back(new OpCalculateScalarFieldValues("G", g_ptr));
1189 pip.push_back(
1190 new OpCalculateScalarFieldGradient<SPACE_DIM>("G", grad_g_ptr));
1191
1193 };
1194
1195 auto post_proc = [&](auto exe_test) {
1197 auto post_proc_fe = boost::make_shared<PostProcEleDomain>(mField);
1198 post_proc_fe->exeTestHook = exe_test;
1199
1200 CHKERR set_generic(post_proc_fe);
1201
1203
1204 post_proc_fe->getOpPtrVector().push_back(
1205
1206 new OpPPMap(post_proc_fe->getPostProcMesh(),
1207 post_proc_fe->getMapGaussPts(),
1208
1209 {{"H", h_ptr}, {"G", g_ptr}},
1210
1211 {{"GRAD_H", grad_h_ptr}, {"GRAD_G", grad_g_ptr}},
1212
1213 {},
1214
1215 {}
1216
1217 )
1218
1219 );
1220
1221 CHKERR DMoFEMLoopFiniteElements(dm, "dFE", post_proc_fe);
1222 CHKERR post_proc_fe->writeFile("out_init.h5m");
1223
1225 };
1226
1227 auto solve_init = [&](auto exe_test) {
1229
1230 pip_mng->getOpDomainRhsPipeline().clear();
1231 pip_mng->getOpDomainLhsPipeline().clear();
1232
1233 auto set_domain_rhs = [&](auto fe) {
1235 CHKERR set_generic(fe);
1236 auto &pip = fe->getOpPtrVector();
1237
1238 CHKERR add_parent_field(fe, UDO::OPROW, "H");
1239 pip.push_back(new OpRhsH<true>("H", nullptr, nullptr, h_ptr, grad_h_ptr,
1240 grad_g_ptr));
1241 CHKERR add_parent_field(fe, UDO::OPROW, "G");
1242 pip.push_back(new OpRhsG<true>("G", h_ptr, grad_h_ptr, g_ptr));
1244 };
1245
1246 auto set_domain_lhs = [&](auto fe) {
1248
1249 CHKERR set_generic(fe);
1250 auto &pip = fe->getOpPtrVector();
1251
1252 CHKERR add_parent_field(fe, UDO::OPROW, "H");
1253 CHKERR add_parent_field(fe, UDO::OPCOL, "H");
1254 pip.push_back(new OpLhsH_dH<true>("H", nullptr, h_ptr, grad_g_ptr));
1255
1256 CHKERR add_parent_field(fe, UDO::OPCOL, "G");
1257 pip.push_back(new OpLhsH_dG<true>("H", "G", h_ptr));
1258
1259 CHKERR add_parent_field(fe, UDO::OPROW, "G");
1260 pip.push_back(new OpLhsG_dG("G"));
1261
1262 CHKERR add_parent_field(fe, UDO::OPCOL, "H");
1263 pip.push_back(new OpLhsG_dH<true>("G", "H", h_ptr));
1265 };
1266
1267 auto create_subdm = [&]() {
1268 auto level_ents_ptr = boost::make_shared<Range>();
1269 CHKERR mField.getInterface<BitRefManager>()->getEntitiesByRefLevel(
1270 bit(get_current_bit()), BitRefLevel().set(), *level_ents_ptr);
1271
1272 DM subdm;
1273 CHKERR DMCreate(mField.get_comm(), &subdm);
1274 CHKERR DMSetType(subdm, "DMMOFEM");
1275 CHKERR DMMoFEMCreateSubDM(subdm, dm, "SUB_INIT");
1276 CHKERR DMMoFEMAddElement(subdm, simple->getDomainFEName());
1277 CHKERR DMMoFEMSetSquareProblem(subdm, PETSC_TRUE);
1278 CHKERR DMMoFEMSetDestroyProblem(subdm, PETSC_TRUE);
1279
1280 for (auto f : {"H", "G"}) {
1281 CHKERR DMMoFEMAddSubFieldRow(subdm, f, level_ents_ptr);
1282 CHKERR DMMoFEMAddSubFieldCol(subdm, f, level_ents_ptr);
1283 }
1284 CHKERR DMSetUp(subdm);
1285
1286 if constexpr (debug) {
1287 if (mField.get_comm_size() == 1) {
1288 auto dm_ents = get_dofs_ents_all(SmartPetscObj<DM>(subdm, true));
1289 CHKERR save_range(mField.get_moab(), "sub_dm_init_ents_verts.h5m",
1290 dm_ents.subset_by_type(MBVERTEX));
1291 dm_ents = subtract(dm_ents, dm_ents.subset_by_type(MBVERTEX));
1292 CHKERR save_range(mField.get_moab(), "sub_dm_init_ents.h5m", dm_ents);
1293 }
1294 }
1295
1296 return SmartPetscObj<DM>(subdm);
1297 };
1298
1299 auto subdm = create_subdm();
1300
1301 pip_mng->getDomainRhsFE().reset();
1302 pip_mng->getDomainLhsFE().reset();
1303 CHKERR pip_mng->setDomainRhsIntegrationRule(integration_rule);
1304 CHKERR pip_mng->setDomainLhsIntegrationRule(integration_rule);
1305 pip_mng->getDomainLhsFE()->exeTestHook = exe_test;
1306 pip_mng->getDomainRhsFE()->exeTestHook = exe_test;
1307
1308 CHKERR set_domain_rhs(pip_mng->getCastDomainRhsFE());
1309 CHKERR set_domain_lhs(pip_mng->getCastDomainLhsFE());
1310
1311 auto D = createDMVector(subdm);
1312 auto snes = pip_mng->createSNES(subdm);
1313 auto snes_ctx_ptr = getDMSnesCtx(subdm);
1314
1315 auto set_section_monitor = [&](auto solver) {
1317 CHKERR SNESMonitorSet(solver,
1318 (MoFEMErrorCode(*)(SNES, PetscInt, PetscReal,
1319 void *))MoFEMSNESMonitorFields,
1320 (void *)(snes_ctx_ptr.get()), nullptr);
1321
1323 };
1324
1325 auto print_section_field = [&]() {
1327
1328 auto section =
1329 mField.getInterface<ISManager>()->sectionCreate("SUB_INIT");
1330 PetscInt num_fields;
1331 CHKERR PetscSectionGetNumFields(section, &num_fields);
1332 for (int f = 0; f < num_fields; ++f) {
1333 const char *field_name;
1334 CHKERR PetscSectionGetFieldName(section, f, &field_name);
1335 MOFEM_LOG("FS", Sev::inform)
1336 << "Field " << f << " " << std::string(field_name);
1337 }
1338
1340 };
1341
1342 CHKERR set_section_monitor(snes);
1343 CHKERR print_section_field();
1344
1345 for (auto f : {"U", "P", "H", "G", "L"}) {
1346 CHKERR mField.getInterface<FieldBlas>()->setField(0, f);
1347 }
1348
1349 CHKERR SNESSetFromOptions(snes);
1350 auto B = createDMMatrix(subdm);
1351 CHKERR SNESSetJacobian(snes, B, B,
1352 PETSC_NULLPTR, PETSC_NULLPTR);
1353 CHKERR SNESSolve(snes, PETSC_NULLPTR, D);
1354
1355 CHKERR VecGhostUpdateBegin(D, INSERT_VALUES, SCATTER_FORWARD);
1356 CHKERR VecGhostUpdateEnd(D, INSERT_VALUES, SCATTER_FORWARD);
1357 CHKERR DMoFEMMeshToLocalVector(subdm, D, INSERT_VALUES, SCATTER_REVERSE);
1358
1360 };
1361
1362 CHKERR reset_bits();
1363 CHKERR solve_init(
1364 [](FEMethod *fe_ptr) { return get_fe_bit(fe_ptr).test(0); });
1365 CHKERR refineMesh(refine_overlap);
1366
1367 for (auto f : {"U", "P", "H", "G", "L"}) {
1368 CHKERR mField.getInterface<FieldBlas>()->setField(0, f);
1369 }
1370 CHKERR solve_init([](FEMethod *fe_ptr) {
1371 return get_fe_bit(fe_ptr).test(get_start_bit() + nb_levels - 1);
1372 });
1373
1374 CHKERR post_proc([](FEMethod *fe_ptr) {
1375 return get_fe_bit(fe_ptr).test(get_start_bit() + nb_levels - 1);
1376 });
1377
1378 pip_mng->getOpDomainRhsPipeline().clear();
1379 pip_mng->getOpDomainLhsPipeline().clear();
1380
1381 // Remove DOFs where boundary conditions are set
1382 CHKERR bc_mng->removeBlockDOFsOnEntities(simple->getProblemName(), "SYM_X",
1383 "U", 0, 0);
1384 CHKERR bc_mng->removeBlockDOFsOnEntities(simple->getProblemName(), "SYM_X",
1385 "L", 0, 0);
1386 CHKERR bc_mng->removeBlockDOFsOnEntities(simple->getProblemName(), "SYM_Y",
1387 "U", 1, 1);
1388 CHKERR bc_mng->removeBlockDOFsOnEntities(simple->getProblemName(), "SYM_Y",
1389 "L", 1, 1);
1390 CHKERR bc_mng->removeBlockDOFsOnEntities(simple->getProblemName(), "FIX", "U",
1391 0, SPACE_DIM);
1392 CHKERR bc_mng->removeBlockDOFsOnEntities(simple->getProblemName(), "FIX", "L",
1393 0, 0);
1394 CHKERR bc_mng->removeBlockDOFsOnEntities(simple->getProblemName(), "ZERO",
1395 "L", 0, 0);
1396
1398}
1399//! [Boundary condition]
1400
1401//! [Data projection]
1404
1405 // FIXME: Functionality in this method should be improved (projection should
1406 // be done field by field), generalized, and move to become core
1407 // functionality.
1408
1409 auto simple = mField.getInterface<Simple>();
1410 auto pip_mng = mField.getInterface<PipelineManager>();
1411 auto bit_mng = mField.getInterface<BitRefManager>();
1412 auto field_blas = mField.getInterface<FieldBlas>();
1413
1414 // Store all existing elements pipelines, replace them by data projection
1415 // pipelines, to put them back when projection is done.
1416 auto fe_domain_lhs = pip_mng->getDomainLhsFE();
1417 auto fe_domain_rhs = pip_mng->getDomainRhsFE();
1418 auto fe_bdy_lhs = pip_mng->getBoundaryLhsFE();
1419 auto fe_bdy_rhs = pip_mng->getBoundaryRhsFE();
1420
1421 pip_mng->getDomainLhsFE().reset();
1422 pip_mng->getDomainRhsFE().reset();
1423 pip_mng->getBoundaryLhsFE().reset();
1424 pip_mng->getBoundaryRhsFE().reset();
1425
1427
1428 // extract field data for domain parent element
1429 auto add_parent_field_domain = [&](auto fe, auto op, auto field, auto bit) {
1430 return setParentDofs(
1431 fe, field, op, bit,
1432
1433 [&]() {
1434 boost::shared_ptr<ForcesAndSourcesCore> fe_parent(
1435 new DomainParentEle(mField));
1436 return fe_parent;
1437 },
1438
1439 QUIET, Sev::noisy);
1440 };
1441
1442 // extract field data for boundary parent element
1443 auto add_parent_field_bdy = [&](auto fe, auto op, auto field, auto bit) {
1444 return setParentDofs(
1445 fe, field, op, bit,
1446
1447 [&]() {
1448 boost::shared_ptr<ForcesAndSourcesCore> fe_parent(
1450 return fe_parent;
1451 },
1452
1453 QUIET, Sev::noisy);
1454 };
1455
1456 // run this element on element with given bit, otherwise run other nested
1457 // element
1458 auto create_run_parent_op = [&](auto parent_fe_ptr, auto this_fe_ptr,
1459 auto fe_bit) {
1460 auto parent_mask = fe_bit;
1461 parent_mask.flip();
1462 return new OpRunParent(parent_fe_ptr, BitRefLevel().set(), parent_mask,
1463 this_fe_ptr, fe_bit, BitRefLevel().set(), QUIET,
1464 Sev::inform);
1465 };
1466
1467 // create hierarchy of nested elements
1468 auto get_parents_vel_fe_ptr = [&](auto this_fe_ptr, auto fe_bit) {
1469 std::vector<boost::shared_ptr<DomainParentEle>> parents_elems_ptr_vec;
1470 for (int l = 0; l < nb_levels; ++l)
1471 parents_elems_ptr_vec.emplace_back(
1472 boost::make_shared<DomainParentEle>(mField));
1473 for (auto l = 1; l < nb_levels; ++l) {
1474 parents_elems_ptr_vec[l - 1]->getOpPtrVector().push_back(
1475 create_run_parent_op(parents_elems_ptr_vec[l], this_fe_ptr, fe_bit));
1476 }
1477 return parents_elems_ptr_vec[0];
1478 };
1479
1480 // solve projection problem
1481 auto solve_projection = [&](auto exe_test) {
1483
1484 auto set_domain_rhs = [&](auto fe) {
1486 auto &pip = fe->getOpPtrVector();
1487
1488 auto u_ptr = boost::make_shared<MatrixDouble>();
1489 auto p_ptr = boost::make_shared<VectorDouble>();
1490 auto h_ptr = boost::make_shared<VectorDouble>();
1491 auto g_ptr = boost::make_shared<VectorDouble>();
1492
1493 auto eval_fe_ptr = boost::make_shared<DomainParentEle>(mField);
1494 {
1495 auto &pip = eval_fe_ptr->getOpPtrVector();
1498 eval_fe_ptr, "", UDO::OPSPACE, bit(get_skin_projection_bit()),
1499
1500 [&]() {
1501 boost::shared_ptr<ForcesAndSourcesCore> fe_parent(
1502 new DomainParentEle(mField));
1503 return fe_parent;
1504 },
1505
1506 QUIET, Sev::noisy);
1507 // That can be done much smarter, by block, field by field. For
1508 // simplicity is like that.
1509 CHKERR add_parent_field_domain(eval_fe_ptr, UDO::OPCOL, "U",
1511 pip.push_back(new OpCalculateVectorFieldValues<SPACE_DIM>("U", u_ptr));
1512 CHKERR add_parent_field_domain(eval_fe_ptr, UDO::OPCOL, "P",
1514 pip.push_back(new OpCalculateScalarFieldValues("P", p_ptr));
1515 CHKERR add_parent_field_domain(eval_fe_ptr, UDO::OPCOL, "H",
1517 pip.push_back(new OpCalculateScalarFieldValues("H", h_ptr));
1518 CHKERR add_parent_field_domain(eval_fe_ptr, UDO::OPCOL, "G",
1520 pip.push_back(new OpCalculateScalarFieldValues("G", g_ptr));
1521 }
1522 auto parent_eval_fe_ptr =
1523 get_parents_vel_fe_ptr(eval_fe_ptr, bit(get_projection_bit()));
1524 pip.push_back(create_run_parent_op(parent_eval_fe_ptr, eval_fe_ptr,
1526
1527 auto assemble_fe_ptr = boost::make_shared<DomainParentEle>(mField);
1528 {
1529 auto &pip = assemble_fe_ptr->getOpPtrVector();
1532 assemble_fe_ptr, "", UDO::OPSPACE, bit(get_skin_parent_bit()),
1533
1534 [&]() {
1535 boost::shared_ptr<ForcesAndSourcesCore> fe_parent(
1536 new DomainParentEle(mField));
1537 return fe_parent;
1538 },
1539
1540 QUIET, Sev::noisy);
1541 CHKERR add_parent_field_domain(assemble_fe_ptr, UDO::OPROW, "U",
1543 pip.push_back(new OpDomainAssembleVector("U", u_ptr));
1544 CHKERR add_parent_field_domain(assemble_fe_ptr, UDO::OPROW, "P",
1546 pip.push_back(new OpDomainAssembleScalar("P", p_ptr));
1547 CHKERR add_parent_field_domain(assemble_fe_ptr, UDO::OPROW, "H",
1549 pip.push_back(new OpDomainAssembleScalar("H", h_ptr));
1550 CHKERR add_parent_field_domain(assemble_fe_ptr, UDO::OPROW, "G",
1552 pip.push_back(new OpDomainAssembleScalar("G", g_ptr));
1553 }
1554 auto parent_assemble_fe_ptr =
1555 get_parents_vel_fe_ptr(assemble_fe_ptr, bit(get_current_bit()));
1556 pip.push_back(create_run_parent_op(
1557 parent_assemble_fe_ptr, assemble_fe_ptr, bit(get_current_bit())));
1558
1560 };
1561
1562 auto set_domain_lhs = [&](auto fe) {
1564
1565 auto &pip = fe->getOpPtrVector();
1566
1568
1570 fe, "", UDO::OPSPACE, bit(get_skin_parent_bit()),
1571
1572 [&]() {
1573 boost::shared_ptr<ForcesAndSourcesCore> fe_parent(
1574 new DomainParentEle(mField));
1575 return fe_parent;
1576 },
1577
1578 QUIET, Sev::noisy);
1579
1580 // That can be done much smarter, by block, field by field. For simplicity
1581 // is like that.
1582 CHKERR add_parent_field_domain(fe, UDO::OPROW, "U",
1584 CHKERR add_parent_field_domain(fe, UDO::OPCOL, "U",
1586 pip.push_back(new OpDomainMassU("U", "U"));
1587 CHKERR add_parent_field_domain(fe, UDO::OPROW, "P",
1589 CHKERR add_parent_field_domain(fe, UDO::OPCOL, "P",
1591 pip.push_back(new OpDomainMassP("P", "P"));
1592 CHKERR add_parent_field_domain(fe, UDO::OPROW, "H",
1594 CHKERR add_parent_field_domain(fe, UDO::OPCOL, "H",
1596 pip.push_back(new OpDomainMassH("H", "H"));
1597 CHKERR add_parent_field_domain(fe, UDO::OPROW, "G",
1599 CHKERR add_parent_field_domain(fe, UDO::OPCOL, "G",
1601 pip.push_back(new OpDomainMassG("G", "G"));
1602
1604 };
1605
1606 auto set_bdy_rhs = [&](auto fe) {
1608 auto &pip = fe->getOpPtrVector();
1609
1610 auto l_ptr = boost::make_shared<VectorDouble>();
1611
1612 auto eval_fe_ptr = boost::make_shared<BoundaryParentEle>(mField);
1613 {
1614 auto &pip = eval_fe_ptr->getOpPtrVector();
1616 eval_fe_ptr, "", UDO::OPSPACE, bit(get_skin_projection_bit()),
1617
1618 [&]() {
1619 boost::shared_ptr<ForcesAndSourcesCore> fe_parent(
1621 return fe_parent;
1622 },
1623
1624 QUIET, Sev::noisy);
1625 // That can be done much smarter, by block, field by field. For
1626 // simplicity is like that.
1627 CHKERR add_parent_field_bdy(eval_fe_ptr, UDO::OPCOL, "L",
1629 pip.push_back(new OpCalculateScalarFieldValues("L", l_ptr));
1630 }
1631 auto parent_eval_fe_ptr =
1632 get_parents_vel_fe_ptr(eval_fe_ptr, bit(get_projection_bit()));
1633 pip.push_back(create_run_parent_op(parent_eval_fe_ptr, eval_fe_ptr,
1635
1636 auto assemble_fe_ptr = boost::make_shared<BoundaryParentEle>(mField);
1637 {
1638 auto &pip = assemble_fe_ptr->getOpPtrVector();
1640 assemble_fe_ptr, "", UDO::OPSPACE, bit(get_skin_parent_bit()),
1641
1642 [&]() {
1643 boost::shared_ptr<ForcesAndSourcesCore> fe_parent(
1645 return fe_parent;
1646 },
1647
1648 QUIET, Sev::noisy);
1649
1650 struct OpLSize : public BoundaryEleOp {
1651 OpLSize(boost::shared_ptr<VectorDouble> l_ptr)
1652 : BoundaryEleOp(NOSPACE, DomainEleOp::OPSPACE), lPtr(l_ptr) {}
1653 MoFEMErrorCode doWork(int, EntityType, EntData &) {
1655 if (lPtr->size() != getGaussPts().size2()) {
1656 lPtr->resize(getGaussPts().size2());
1657 lPtr->clear();
1658 }
1660 }
1661
1662 private:
1663 boost::shared_ptr<VectorDouble> lPtr;
1664 };
1665
1666 pip.push_back(new OpLSize(l_ptr));
1667
1668 CHKERR add_parent_field_bdy(assemble_fe_ptr, UDO::OPROW, "L",
1670 pip.push_back(new OpBoundaryAssembleScalar("L", l_ptr));
1671 }
1672 auto parent_assemble_fe_ptr =
1673 get_parents_vel_fe_ptr(assemble_fe_ptr, bit(get_current_bit()));
1674 pip.push_back(create_run_parent_op(
1675 parent_assemble_fe_ptr, assemble_fe_ptr, bit(get_current_bit())));
1676
1678 };
1679
1680 auto set_bdy_lhs = [&](auto fe) {
1682
1683 auto &pip = fe->getOpPtrVector();
1684
1686 fe, "", UDO::OPSPACE, bit(get_skin_parent_bit()),
1687
1688 [&]() {
1689 boost::shared_ptr<ForcesAndSourcesCore> fe_parent(
1691 return fe_parent;
1692 },
1693
1694 QUIET, Sev::noisy);
1695
1696 // That can be done much smarter, by block, field by field. For simplicity
1697 // is like that.
1698 CHKERR add_parent_field_bdy(fe, UDO::OPROW, "L",
1700 CHKERR add_parent_field_bdy(fe, UDO::OPCOL, "L",
1702 pip.push_back(new OpBoundaryMassL("L", "L"));
1703
1705 };
1706
1707 auto create_subdm = [&]() -> SmartPetscObj<DM> {
1708 auto level_ents_ptr = boost::make_shared<Range>();
1709 CHKERR mField.getInterface<BitRefManager>()->getEntitiesByRefLevel(
1710 bit(get_current_bit()), BitRefLevel().set(), *level_ents_ptr);
1711
1712 auto get_prj_ents = [&]() {
1713 Range prj_mesh;
1714 CHKERR bit_mng->getEntitiesByDimAndRefLevel(bit(get_projection_bit()),
1715 BitRefLevel().set(),
1716 SPACE_DIM, prj_mesh);
1717 auto common_ents = intersect(prj_mesh, *level_ents_ptr);
1718 prj_mesh = subtract(unite(*level_ents_ptr, prj_mesh), common_ents)
1719 .subset_by_dimension(SPACE_DIM);
1720
1721 return prj_mesh;
1722 };
1723
1724 auto prj_ents = get_prj_ents();
1725
1726 if (get_global_size(prj_ents.size())) {
1727
1728 auto rebuild = [&]() {
1729 auto prb_mng = mField.getInterface<ProblemsManager>();
1731
1732 std::vector<std::string> fields{"U", "P", "H", "G", "L"};
1733 std::map<std::string, boost::shared_ptr<Range>> range_maps{
1734
1735 {"U", level_ents_ptr},
1736 {"P", level_ents_ptr},
1737 {"H", level_ents_ptr},
1738 {"G", level_ents_ptr},
1739 {"L", level_ents_ptr}
1740
1741 };
1742
1743 CHKERR prb_mng->buildSubProblem("SUB_SOLVER", fields, fields,
1744 simple->getProblemName(), PETSC_TRUE,
1745 &range_maps, &range_maps);
1746
1747 // partition problem
1748 CHKERR prb_mng->partitionFiniteElements("SUB_SOLVER", true, 0,
1750 // set ghost nodes
1751 CHKERR prb_mng->partitionGhostDofsOnDistributedMesh("SUB_SOLVER");
1752
1754 };
1755
1756 MOFEM_LOG("FS", Sev::verbose) << "Create projection problem";
1757
1758 CHKERR rebuild();
1759
1760 auto dm = simple->getDM();
1761 DM subdm;
1762 CHKERR DMCreate(mField.get_comm(), &subdm);
1763 CHKERR DMSetType(subdm, "DMMOFEM");
1764 CHKERR DMMoFEMCreateSubDM(subdm, dm, "SUB_SOLVER");
1765 return SmartPetscObj<DM>(subdm);
1766 }
1767
1768 MOFEM_LOG("FS", Sev::inform) << "Nothing to project";
1769
1770 return SmartPetscObj<DM>();
1771 };
1772
1773 auto create_dummy_dm = [&]() {
1774 auto dummy_dm = createDM(mField.get_comm(), "DMMOFEM");
1776 simple->getProblemName().c_str(),
1778 "create dummy dm");
1779 return dummy_dm;
1780 };
1781
1782 auto subdm = create_subdm();
1783 if (subdm) {
1784
1785 pip_mng->getDomainRhsFE().reset();
1786 pip_mng->getDomainLhsFE().reset();
1787 pip_mng->getBoundaryRhsFE().reset();
1788 pip_mng->getBoundaryLhsFE().reset();
1789 CHKERR pip_mng->setDomainRhsIntegrationRule(integration_rule);
1790 CHKERR pip_mng->setDomainLhsIntegrationRule(integration_rule);
1791 CHKERR pip_mng->setBoundaryRhsIntegrationRule(integration_rule);
1792 CHKERR pip_mng->setBoundaryLhsIntegrationRule(integration_rule);
1793 pip_mng->getDomainLhsFE()->exeTestHook = exe_test;
1794 pip_mng->getDomainRhsFE()->exeTestHook = [](FEMethod *fe_ptr) {
1795 return get_fe_bit(fe_ptr).test(nb_levels - 1);
1796 };
1797 pip_mng->getBoundaryLhsFE()->exeTestHook = exe_test;
1798 pip_mng->getBoundaryRhsFE()->exeTestHook = [](FEMethod *fe_ptr) {
1799 return get_fe_bit(fe_ptr).test(nb_levels - 1);
1800 };
1801
1802 CHKERR set_domain_rhs(pip_mng->getCastDomainRhsFE());
1803 CHKERR set_domain_lhs(pip_mng->getCastDomainLhsFE());
1804 CHKERR set_bdy_rhs(pip_mng->getCastBoundaryRhsFE());
1805 CHKERR set_bdy_lhs(pip_mng->getCastBoundaryLhsFE());
1806
1807 auto D = createDMVector(subdm);
1808 auto F = vectorDuplicate(D);
1809
1810 auto ksp = pip_mng->createKSP(subdm);
1811 CHKERR KSPSetFromOptions(ksp);
1812 CHKERR KSPSetUp(ksp);
1813
1814 // get vector norm
1815 auto get_norm = [&](auto x) {
1816 double nrm;
1817 CHKERR VecNorm(x, NORM_2, &nrm);
1818 return nrm;
1819 };
1820
1821 /**
1822 * @brief Zero DOFs, used by FieldBlas
1823 */
1824 auto zero_dofs = [](boost::shared_ptr<FieldEntity> ent_ptr) {
1826 for (auto &v : ent_ptr->getEntFieldData()) {
1827 v = 0;
1828 }
1830 };
1831
1832 auto solve = [&](auto S) {
1834 CHKERR VecZeroEntries(S);
1835 CHKERR VecZeroEntries(F);
1836 CHKERR VecGhostUpdateBegin(F, INSERT_VALUES, SCATTER_FORWARD);
1837 CHKERR VecGhostUpdateEnd(F, INSERT_VALUES, SCATTER_FORWARD);
1838 CHKERR KSPSolve(ksp, F, S);
1839 CHKERR VecGhostUpdateBegin(S, INSERT_VALUES, SCATTER_FORWARD);
1840 CHKERR VecGhostUpdateEnd(S, INSERT_VALUES, SCATTER_FORWARD);
1841
1842
1843
1845 };
1846
1847 MOFEM_LOG("FS", Sev::inform) << "Solve projection";
1848 CHKERR solve(D);
1849
1850 auto glob_x = createDMVector(simple->getDM());
1851 auto sub_x = createDMVector(subdm);
1852 auto dummy_dm = create_dummy_dm();
1853
1854 /**
1855 * @brief get TSTheta data operators
1856 */
1857 auto apply_restrict = [&]() {
1858 auto get_is = [](auto v) {
1859 IS iy;
1860 auto create = [&]() {
1862 int n, ystart;
1863 CHKERR VecGetLocalSize(v, &n);
1864 CHKERR VecGetOwnershipRange(v, &ystart, NULL);
1865 CHKERR ISCreateStride(PETSC_COMM_SELF, n, ystart, 1, &iy);
1867 };
1868 CHK_THROW_MESSAGE(create(), "create is");
1869 return SmartPetscObj<IS>(iy);
1870 };
1871
1872 auto iy = get_is(glob_x);
1873 auto s = createVecScatter(glob_x, PETSC_NULLPTR, glob_x, iy);
1874
1876 DMSubDomainRestrict(simple->getDM(), s, PETSC_NULLPTR, dummy_dm),
1877 "restrict");
1878 Vec X0, Xdot;
1879 CHK_THROW_MESSAGE(DMGetNamedGlobalVector(dummy_dm, "TSTheta_X0", &X0),
1880 "get X0");
1882 DMGetNamedGlobalVector(dummy_dm, "TSTheta_Xdot", &Xdot),
1883 "get Xdot");
1884
1885 auto forward_ghost = [](auto g) {
1887 CHKERR VecGhostUpdateBegin(g, INSERT_VALUES, SCATTER_FORWARD);
1888 CHKERR VecGhostUpdateEnd(g, INSERT_VALUES, SCATTER_FORWARD);
1890 };
1891
1892 CHK_THROW_MESSAGE(forward_ghost(X0), "");
1893 CHK_THROW_MESSAGE(forward_ghost(Xdot), "");
1894
1895 if constexpr (debug) {
1896 MOFEM_LOG("FS", Sev::inform)
1897 << "Reverse restrict: X0 " << get_norm(X0) << " Xdot "
1898 << get_norm(Xdot);
1899 }
1900
1901 return std::vector<Vec>{X0, Xdot};
1902 };
1903
1904 auto ts_solver_vecs = apply_restrict();
1905
1906 if (ts_solver_vecs.size()) {
1907
1908 for (auto v : ts_solver_vecs) {
1909 MOFEM_LOG("FS", Sev::inform) << "Solve projection vector";
1910
1911 CHKERR DMoFEMMeshToLocalVector(simple->getDM(), v, INSERT_VALUES,
1912 SCATTER_REVERSE);
1913 CHKERR solve(sub_x);
1914
1915 for (auto f : {"U", "P", "H", "G", "L"}) {
1916 MOFEM_LOG("WORLD", Sev::verbose) << "Zero field " << f;
1917 CHKERR field_blas->fieldLambdaOnEntities(zero_dofs, f);
1918 }
1919
1920 CHKERR DMoFEMMeshToLocalVector(subdm, sub_x, INSERT_VALUES,
1921 SCATTER_REVERSE);
1922 CHKERR DMoFEMMeshToLocalVector(simple->getDM(), v, INSERT_VALUES,
1923 SCATTER_FORWARD);
1924
1925 MOFEM_LOG("FS", Sev::inform) << "Norm V " << get_norm(v);
1926 }
1927
1928 CHKERR DMRestoreNamedGlobalVector(dummy_dm, "TSTheta_X0",
1929 &ts_solver_vecs[0]);
1930 CHKERR DMRestoreNamedGlobalVector(dummy_dm, "TSTheta_Xdot",
1931 &ts_solver_vecs[1]);
1932 }
1933
1934 for (auto f : {"U", "P", "H", "G", "L"}) {
1935 MOFEM_LOG("WORLD", Sev::verbose) << "Zero field " << f;
1936 CHKERR field_blas->fieldLambdaOnEntities(zero_dofs, f);
1937 }
1938 CHKERR DMoFEMMeshToLocalVector(subdm, D, INSERT_VALUES, SCATTER_REVERSE);
1939 }
1940
1942 };
1943
1944 // postprocessing (only for debugging purposes)
1945 auto post_proc = [&](auto exe_test) {
1947 auto post_proc_fe = boost::make_shared<PostProcEleDomain>(mField);
1948 auto &pip = post_proc_fe->getOpPtrVector();
1949 post_proc_fe->exeTestHook = exe_test;
1950
1952
1954 post_proc_fe, "", UDO::OPSPACE, bit(get_skin_parent_bit()),
1955
1956 [&]() {
1957 boost::shared_ptr<ForcesAndSourcesCore> fe_parent(
1958 new DomainParentEle(mField));
1960 fe_parent->getOpPtrVector(), {H1});
1961 return fe_parent;
1962 },
1963
1964 QUIET, Sev::noisy);
1965
1967
1968 auto u_ptr = boost::make_shared<MatrixDouble>();
1969 auto p_ptr = boost::make_shared<VectorDouble>();
1970 auto h_ptr = boost::make_shared<VectorDouble>();
1971 auto g_ptr = boost::make_shared<VectorDouble>();
1972
1973 CHKERR add_parent_field_domain(post_proc_fe, UDO::OPCOL, "U",
1975 pip.push_back(new OpCalculateVectorFieldValues<SPACE_DIM>("U", u_ptr));
1976 CHKERR add_parent_field_domain(post_proc_fe, UDO::OPCOL, "P",
1978 pip.push_back(new OpCalculateScalarFieldValues("P", p_ptr));
1979 CHKERR add_parent_field_domain(post_proc_fe, UDO::OPCOL, "H",
1981 pip.push_back(new OpCalculateScalarFieldValues("H", h_ptr));
1982 CHKERR add_parent_field_domain(post_proc_fe, UDO::OPCOL, "G",
1984 pip.push_back(new OpCalculateScalarFieldValues("G", g_ptr));
1985
1986 post_proc_fe->getOpPtrVector().push_back(
1987
1988 new OpPPMap(post_proc_fe->getPostProcMesh(),
1989 post_proc_fe->getMapGaussPts(),
1990
1991 {{"P", p_ptr}, {"H", h_ptr}, {"G", g_ptr}},
1992
1993 {{"U", u_ptr}},
1994
1995 {},
1996
1997 {}
1998
1999 )
2000
2001 );
2002
2003 auto dm = simple->getDM();
2004 CHKERR DMoFEMLoopFiniteElements(dm, "dFE", post_proc_fe);
2005 CHKERR post_proc_fe->writeFile("out_projection.h5m");
2006
2008 };
2009
2010 CHKERR solve_projection([](FEMethod *fe_ptr) {
2011 return get_fe_bit(fe_ptr).test(get_current_bit());
2012 });
2013
2014 if constexpr (debug) {
2015 CHKERR post_proc([](FEMethod *fe_ptr) {
2016 return get_fe_bit(fe_ptr).test(get_current_bit());
2017 });
2018 }
2019
2020 fe_domain_lhs.swap(pip_mng->getDomainLhsFE());
2021 fe_domain_rhs.swap(pip_mng->getDomainRhsFE());
2022 fe_bdy_lhs.swap(pip_mng->getBoundaryLhsFE());
2023 fe_bdy_rhs.swap(pip_mng->getBoundaryRhsFE());
2024
2026}
2027//! [Data projection]
2028
2029//! [Push operators to pip]
2032 auto simple = mField.getInterface<Simple>();
2033
2035
2036 auto add_parent_field_domain = [&](auto fe, auto op, auto field) {
2037 return setParentDofs(
2038 fe, field, op, bit(get_skin_parent_bit()),
2039
2040 [&]() {
2041 boost::shared_ptr<ForcesAndSourcesCore> fe_parent(
2042 new DomainParentEle(mField));
2043 return fe_parent;
2044 },
2045
2046 QUIET, Sev::noisy);
2047 };
2048
2049 auto add_parent_field_bdy = [&](auto fe, auto op, auto field) {
2050 return setParentDofs(
2051 fe, field, op, bit(get_skin_parent_bit()),
2052
2053 [&]() {
2054 boost::shared_ptr<ForcesAndSourcesCore> fe_parent(
2056 return fe_parent;
2057 },
2058
2059 QUIET, Sev::noisy);
2060 };
2061
2062 auto test_bit_child = [](FEMethod *fe_ptr) {
2063 return fe_ptr->numeredEntFiniteElementPtr->getBitRefLevel().test(
2064 get_start_bit() + nb_levels - 1);
2065 };
2066
2067 auto dot_u_ptr = boost::make_shared<MatrixDouble>(); // time derivative of velocity u ie du/dt
2068 auto u_ptr = boost::make_shared<MatrixDouble>(); // velocity u
2069 auto grad_u_ptr = boost::make_shared<MatrixDouble>(); // velocity gradient tensor ie grad(u)
2070 auto dot_h_ptr = boost::make_shared<VectorDouble>(); // time derivative of phase field ie dh/dt
2071 auto h_ptr = boost::make_shared<VectorDouble>(); // phase field variable h
2072 auto grad_h_ptr = boost::make_shared<MatrixDouble>(); // phase field gradient ie grad(h)
2073 auto g_ptr = boost::make_shared<VectorDouble>(); // chemical potential g
2074 auto grad_g_ptr = boost::make_shared<MatrixDouble>(); // chemical potential gradient ie grad(g)
2075 auto lambda_ptr = boost::make_shared<VectorDouble>(); // Lagrange multiplier lambda
2076 auto p_ptr = boost::make_shared<VectorDouble>(); // pressure p
2077 auto div_u_ptr = boost::make_shared<VectorDouble>(); // divergence of velocity ie div(u)
2078
2079 // Push element from reference configuration to current configuration in 3d
2080 // space
2081 auto set_domain_general = [&](auto fe) {
2083 auto &pip = fe->getOpPtrVector();
2084
2086
2088 fe, "", UDO::OPSPACE, bit(get_skin_parent_bit()),
2089
2090 [&]() {
2091 boost::shared_ptr<ForcesAndSourcesCore> fe_parent(
2092 new DomainParentEle(mField));
2094 fe_parent->getOpPtrVector(), {H1});
2095 return fe_parent;
2096 },
2097
2098 QUIET, Sev::noisy);
2099
2100 CHKERR add_parent_field_domain(fe, UDO::OPROW, "U");
2101 CHKERR add_parent_field_domain(fe, UDO::OPCOL, "U");
2102 pip.push_back(
2104 pip.push_back(new OpCalculateVectorFieldValues<U_FIELD_DIM>("U", u_ptr));
2106 "U", grad_u_ptr));
2107
2108 switch (coord_type) {
2109 case CARTESIAN:
2110 pip.push_back(
2112 "U", div_u_ptr));
2113 break;
2114 case CYLINDRICAL:
2115 pip.push_back(
2117 "U", div_u_ptr));
2118 break;
2119 default:
2120 SETERRQ(PETSC_COMM_WORLD, MOFEM_NOT_IMPLEMENTED,
2121 "Coordinate system not implemented");
2122 }
2123
2124 CHKERR add_parent_field_domain(fe, UDO::OPCOL, "H");
2125 pip.push_back(new OpCalculateScalarFieldValuesDot("H", dot_h_ptr));
2126 pip.push_back(new OpCalculateScalarFieldValues("H", h_ptr));
2127 pip.push_back(
2128 new OpCalculateScalarFieldGradient<SPACE_DIM>("H", grad_h_ptr));
2129
2130 CHKERR add_parent_field_domain(fe, UDO::OPCOL, "G");
2131 pip.push_back(new OpCalculateScalarFieldValues("G", g_ptr));
2132 pip.push_back(
2133 new OpCalculateScalarFieldGradient<SPACE_DIM>("G", grad_g_ptr));
2134
2135 CHKERR add_parent_field_domain(fe, UDO::OPCOL, "P");
2136 pip.push_back(new OpCalculateScalarFieldValues("P", p_ptr));
2138 };
2139
2140 auto set_domain_rhs = [&](auto fe) {
2142 auto &pip = fe->getOpPtrVector();
2143
2144 CHKERR set_domain_general(fe);
2145
2146 CHKERR add_parent_field_domain(fe, UDO::OPROW, "U");
2147 pip.push_back(new OpRhsU("U", dot_u_ptr, u_ptr, grad_u_ptr, h_ptr,
2148 grad_h_ptr, g_ptr, p_ptr));
2149
2150 CHKERR add_parent_field_domain(fe, UDO::OPROW, "H");
2151 pip.push_back(new OpRhsH<false>("H", u_ptr, dot_h_ptr, h_ptr, grad_h_ptr,
2152 grad_g_ptr));
2153
2154 CHKERR add_parent_field_domain(fe, UDO::OPROW, "G");
2155 pip.push_back(new OpRhsG<false>("G", h_ptr, grad_h_ptr, g_ptr));
2156
2157 CHKERR add_parent_field_domain(fe, UDO::OPROW, "P");
2158 pip.push_back(new OpDomainAssembleScalar(
2159 "P", div_u_ptr, [](const double r, const double, const double) {
2160 return cylindrical(r);
2161 }));
2162 pip.push_back(new OpDomainAssembleScalar(
2163 "P", p_ptr, [](const double r, const double, const double) {
2164 return eps * cylindrical(r);
2165 }));
2167 };
2168
2169 auto set_domain_lhs = [&](auto fe) {
2171 auto &pip = fe->getOpPtrVector();
2172
2173 CHKERR set_domain_general(fe);
2174
2175 CHKERR add_parent_field_domain(fe, UDO::OPROW, "U");
2176 {
2177 CHKERR add_parent_field_domain(fe, UDO::OPCOL, "U");
2178 pip.push_back(new OpLhsU_dU("U", u_ptr, grad_u_ptr, h_ptr));
2179 CHKERR add_parent_field_domain(fe, UDO::OPCOL, "H");
2180 pip.push_back(
2181 new OpLhsU_dH("U", "H", dot_u_ptr, u_ptr, grad_u_ptr, h_ptr, g_ptr));
2182
2183 CHKERR add_parent_field_domain(fe, UDO::OPCOL, "G");
2184 pip.push_back(new OpLhsU_dG("U", "G", grad_h_ptr));
2185 }
2186
2187 CHKERR add_parent_field_domain(fe, UDO::OPROW, "H");
2188 {
2189 CHKERR add_parent_field_domain(fe, UDO::OPCOL, "U");
2190 pip.push_back(new OpLhsH_dU("H", "U", grad_h_ptr));
2191 CHKERR add_parent_field_domain(fe, UDO::OPCOL, "H");
2192 pip.push_back(new OpLhsH_dH<false>("H", u_ptr, h_ptr, grad_g_ptr));
2193 CHKERR add_parent_field_domain(fe, UDO::OPCOL, "G");
2194 pip.push_back(new OpLhsH_dG<false>("H", "G", h_ptr));
2195 }
2196
2197 CHKERR add_parent_field_domain(fe, UDO::OPROW, "G");
2198 {
2199 CHKERR add_parent_field_domain(fe, UDO::OPCOL, "H");
2200 pip.push_back(new OpLhsG_dH<false>("G", "H", h_ptr));
2201 CHKERR add_parent_field_domain(fe, UDO::OPCOL, "G");
2202 pip.push_back(new OpLhsG_dG("G"));
2203 }
2204
2205 CHKERR add_parent_field_domain(fe, UDO::OPROW, "P");
2206 {
2207 CHKERR add_parent_field_domain(fe, UDO::OPCOL, "U");
2208
2209 switch (coord_type) {
2210 case CARTESIAN:
2211 pip.push_back(new OpMixScalarTimesDiv<CARTESIAN>(
2212 "P", "U",
2213 [](const double r, const double, const double) {
2214 return cylindrical(r);
2215 },
2216 true, false));
2217 break;
2218 case CYLINDRICAL:
2219 pip.push_back(new OpMixScalarTimesDiv<CYLINDRICAL>(
2220 "P", "U",
2221 [](const double r, const double, const double) {
2222 return cylindrical(r);
2223 },
2224 true, false));
2225 break;
2226 default:
2227 SETERRQ(PETSC_COMM_WORLD, MOFEM_NOT_IMPLEMENTED,
2228 "Coordinate system not implemented");
2229 }
2230
2231 CHKERR add_parent_field_domain(fe, UDO::OPCOL, "P");
2232 pip.push_back(new OpDomainMassP("P", "P", [](double r, double, double) {
2233 return eps * cylindrical(r);
2234 }));
2235 }
2236
2238 };
2239
2240 auto get_block_name = [](auto name) {
2241 return boost::format("%s(.*)") % "WETTING_ANGLE";
2242 };
2243
2244 auto get_blocks = [&](auto &&name) {
2245 return mField.getInterface<MeshsetsManager>()->getCubitMeshsetPtr(
2246 std::regex(name.str()));
2247 };
2248
2249 auto set_boundary_rhs = [&](auto fe) {
2251 auto &pip = fe->getOpPtrVector();
2252
2254 fe, "", UDO::OPSPACE, bit(get_skin_parent_bit()),
2255
2256 [&]() {
2257 boost::shared_ptr<ForcesAndSourcesCore> fe_parent(
2259 return fe_parent;
2260 },
2261
2262 QUIET, Sev::noisy);
2263
2264 CHKERR add_parent_field_bdy(fe, UDO::OPCOL, "U");
2265 pip.push_back(new OpCalculateVectorFieldValues<U_FIELD_DIM>("U", u_ptr));
2266
2267 CHKERR add_parent_field_bdy(fe, UDO::OPCOL, "L");
2268 pip.push_back(new OpCalculateScalarFieldValues("L", lambda_ptr));
2269 pip.push_back(new OpNormalConstrainRhs("L", u_ptr));
2270
2272 pip, mField, "L", {}, "INFLUX",
2273 [](double r, double, double) { return cylindrical(r); }, Sev::inform);
2274
2275 CHKERR add_parent_field_bdy(fe, UDO::OPROW, "U");
2276 pip.push_back(new OpNormalForceRhs("U", lambda_ptr));
2277
2278 auto wetting_block = get_blocks(get_block_name("WETTING_ANGLE"));
2279 if (wetting_block.size()) {
2280 // push operators to the side element which is called from op_bdy_side
2281 auto op_bdy_side =
2282 new OpLoopSide<SideEle>(mField, simple->getDomainFEName(), SPACE_DIM);
2283 op_bdy_side->getSideFEPtr()->exeTestHook = test_bit_child;
2284
2286 op_bdy_side->getOpPtrVector(), {H1});
2287
2289 op_bdy_side->getSideFEPtr(), "", UDO::OPSPACE,
2291
2292 [&]() {
2293 boost::shared_ptr<ForcesAndSourcesCore> fe_parent(
2294 new DomainParentEle(mField));
2296 fe_parent->getOpPtrVector(), {H1});
2297 return fe_parent;
2298 },
2299
2300 QUIET, Sev::noisy);
2301
2302 CHKERR add_parent_field_domain(op_bdy_side->getSideFEPtr(), UDO::OPCOL,
2303 "H");
2304 op_bdy_side->getOpPtrVector().push_back(
2305 new OpCalculateScalarFieldGradient<SPACE_DIM>("H", grad_h_ptr));
2306 // push bdy side op
2307 pip.push_back(op_bdy_side);
2308
2309 // push operators for rhs wetting angle
2310 for (auto &b : wetting_block) {
2311 Range force_edges;
2312 std::vector<double> attr_vec;
2313 CHKERR b->getMeshsetIdEntitiesByDimension(
2314 mField.get_moab(), SPACE_DIM - 1, force_edges, true);
2315 b->getAttributes(attr_vec);
2316 if (attr_vec.size() != 1)
2317 SETERRQ(PETSC_COMM_SELF, MOFEM_INVALID_DATA,
2318 "Should be one attribute");
2319 MOFEM_LOG("FS", Sev::inform) << "Wetting angle: " << attr_vec.front();
2320 // need to find the attributes and pass to operator
2321 CHKERR add_parent_field_bdy(fe, UDO::OPROW, "G");
2322 CHKERR add_parent_field_bdy(fe, UDO::OPCOL, "G");
2323 pip.push_back(new OpWettingAngleRhs(
2324 "G", grad_h_ptr, boost::make_shared<Range>(force_edges),
2325 attr_vec.front()));
2326 }
2327 }
2328
2330 };
2331
2332 auto set_boundary_lhs = [&](auto fe) {
2334 auto &pip = fe->getOpPtrVector();
2335
2337 fe, "", UDO::OPSPACE, bit(get_skin_parent_bit()),
2338
2339 [&]() {
2340 boost::shared_ptr<ForcesAndSourcesCore> fe_parent(
2342 return fe_parent;
2343 },
2344
2345 QUIET, Sev::noisy);
2346
2347 CHKERR add_parent_field_bdy(fe, UDO::OPROW, "L");
2348 CHKERR add_parent_field_bdy(fe, UDO::OPCOL, "U");
2349 pip.push_back(new OpNormalConstrainLhs("L", "U"));
2350
2351 auto wetting_block = get_blocks(get_block_name("WETTING_ANGLE"));
2352 if (wetting_block.size()) {
2353 auto col_ind_ptr = boost::make_shared<std::vector<VectorInt>>();
2354 auto col_diff_base_ptr = boost::make_shared<std::vector<MatrixDouble>>();
2355
2356 // push operators to the side element which is called from op_bdy_side
2357 auto op_bdy_side =
2358 new OpLoopSide<SideEle>(mField, simple->getDomainFEName(), SPACE_DIM);
2359 op_bdy_side->getSideFEPtr()->exeTestHook = test_bit_child;
2360
2362 op_bdy_side->getOpPtrVector(), {H1});
2363
2365 op_bdy_side->getSideFEPtr(), "", UDO::OPSPACE,
2367
2368 [&]() {
2369 boost::shared_ptr<ForcesAndSourcesCore> fe_parent(
2370 new DomainParentEle(mField));
2372 fe_parent->getOpPtrVector(), {H1});
2373 return fe_parent;
2374 },
2375
2376 QUIET, Sev::noisy);
2377
2378 CHKERR add_parent_field_domain(op_bdy_side->getSideFEPtr(), UDO::OPROW,
2379 "H");
2380 CHKERR add_parent_field_domain(op_bdy_side->getSideFEPtr(), UDO::OPCOL,
2381 "H");
2382 op_bdy_side->getOpPtrVector().push_back(
2383 new OpCalculateScalarFieldGradient<SPACE_DIM>("H", grad_h_ptr));
2384 op_bdy_side->getOpPtrVector().push_back(
2385 new OpLoopSideGetDataForSideEle("H", col_ind_ptr, col_diff_base_ptr));
2386
2387 // push bdy side op
2388 pip.push_back(op_bdy_side);
2389
2390 // push operators for lhs wetting angle
2391 for (auto &b : wetting_block) {
2392 Range force_edges;
2393 std::vector<double> attr_vec;
2394 CHKERR b->getMeshsetIdEntitiesByDimension(
2395 mField.get_moab(), SPACE_DIM - 1, force_edges, true);
2396 b->getAttributes(attr_vec);
2397 if (attr_vec.size() != 1)
2398 SETERRQ(PETSC_COMM_SELF, MOFEM_INVALID_DATA,
2399 "Should be one attribute");
2400 MOFEM_LOG("FS", Sev::inform)
2401 << "wetting angle edges size " << force_edges.size();
2402
2403 CHKERR add_parent_field_bdy(fe, UDO::OPROW, "G");
2404 CHKERR add_parent_field_bdy(fe, UDO::OPCOL, "G");
2405 pip.push_back(new OpWettingAngleLhs(
2406 "G", grad_h_ptr, col_ind_ptr, col_diff_base_ptr,
2407 boost::make_shared<Range>(force_edges), attr_vec.front()));
2408 }
2409 }
2410
2412 };
2413
2414 auto *pip_mng = mField.getInterface<PipelineManager>();
2415
2416 CHKERR set_domain_rhs(pip_mng->getCastDomainRhsFE());
2417 CHKERR set_domain_lhs(pip_mng->getCastDomainLhsFE());
2418 CHKERR set_boundary_rhs(pip_mng->getCastBoundaryRhsFE());
2419 CHKERR set_boundary_lhs(pip_mng->getCastBoundaryLhsFE());
2420
2421 CHKERR pip_mng->setDomainRhsIntegrationRule(integration_rule);
2422 CHKERR pip_mng->setDomainLhsIntegrationRule(integration_rule);
2423 CHKERR pip_mng->setBoundaryRhsIntegrationRule(integration_rule);
2424 CHKERR pip_mng->setBoundaryLhsIntegrationRule(integration_rule);
2425
2426 pip_mng->getDomainLhsFE()->exeTestHook = test_bit_child;
2427 pip_mng->getDomainRhsFE()->exeTestHook = test_bit_child;
2428 pip_mng->getBoundaryLhsFE()->exeTestHook = test_bit_child;
2429 pip_mng->getBoundaryRhsFE()->exeTestHook = test_bit_child;
2430
2432}
2433//! [Push operators to pip]
2434
2435/**
2436 * @brief Monitor solution
2437 *
2438 * This function is called by TS solver at the end of each step. It is used
2439 */
2440struct Monitor : public FEMethod {
2442 SmartPetscObj<DM> dm, boost::shared_ptr<moab::Core> post_proc_mesh,
2443 boost::shared_ptr<PostProcEleDomainCont> post_proc,
2444 boost::shared_ptr<PostProcEleBdyCont> post_proc_edge,
2445 std::pair<boost::shared_ptr<BoundaryEle>, boost::shared_ptr<VectorDouble>>
2446 p)
2447 : dM(dm), postProcMesh(post_proc_mesh), postProc(post_proc),
2448 postProcEdge(post_proc_edge), liftFE(p.first), liftVec(p.second) {}
2451
2452 MOFEM_LOG("FS", Sev::verbose) << "Monitor";
2453 int save_every_nth_step = 1;
2454 CHKERR PetscOptionsGetInt(NULL, NULL, "-save_every_nth_step",
2455 &save_every_nth_step, NULL);
2456 if (ts_step % save_every_nth_step == 0) {
2457 MOFEM_LOG("FS", Sev::verbose) << "Mesh pre proc";
2458 MoFEM::Interface *m_field_ptr;
2459 CHKERR DMoFEMGetInterfacePtr(dM, &m_field_ptr);
2460 auto post_proc_begin =
2461 boost::make_shared<PostProcBrokenMeshInMoabBaseBegin>(*m_field_ptr,
2462 postProcMesh);
2463 auto post_proc_end = boost::make_shared<PostProcBrokenMeshInMoabBaseEnd>(
2464 *m_field_ptr, postProcMesh);
2465 CHKERR DMoFEMPreProcessFiniteElements(dM, post_proc_begin->getFEMethod());
2467 this->getCacheWeakPtr());
2469 this->getCacheWeakPtr());
2470 CHKERR DMoFEMPostProcessFiniteElements(dM, post_proc_end->getFEMethod());
2471 CHKERR post_proc_end->writeFile(
2472 "out_step_" + boost::lexical_cast<std::string>(ts_step) + ".h5m");
2473 MOFEM_LOG("FS", Sev::verbose) << "Mesh pre proc done";
2474 }
2475
2476 liftVec->resize(SPACE_DIM, false);
2477 liftVec->clear();
2479 MPI_Allreduce(MPI_IN_PLACE, &(*liftVec)[0], SPACE_DIM, MPI_DOUBLE, MPI_SUM,
2480 MPI_COMM_WORLD);
2481 MOFEM_LOG("FS", Sev::inform)
2482 << "Step " << ts_step << " time " << ts_t
2483 << " lift vec x: " << (*liftVec)[0] << " y: " << (*liftVec)[1];
2484
2486 }
2487
2488private:
2490 boost::shared_ptr<moab::Core> postProcMesh;
2491 boost::shared_ptr<PostProcEleDomainCont> postProc;
2492 boost::shared_ptr<PostProcEleBdyCont> postProcEdge;
2493 boost::shared_ptr<BoundaryEle> liftFE;
2494 boost::shared_ptr<VectorDouble> liftVec;
2495};
2496
2497//! [Solve]
2500
2502
2503 auto simple = mField.getInterface<Simple>();
2504 auto pip_mng = mField.getInterface<PipelineManager>();
2505
2506 auto create_solver_dm = [&](auto dm) -> SmartPetscObj<DM> {
2507 DM subdm;
2508
2509 auto setup_subdm = [&](auto dm) {
2511 auto simple = mField.getInterface<Simple>();
2512 auto bit_mng = mField.getInterface<BitRefManager>();
2513 auto dm = simple->getDM();
2514 auto level_ents_ptr = boost::make_shared<Range>();
2515 CHKERR bit_mng->getEntitiesByRefLevel(
2516 bit(get_current_bit()), BitRefLevel().set(), *level_ents_ptr);
2517 CHKERR DMCreate(mField.get_comm(), &subdm);
2518 CHKERR DMSetType(subdm, "DMMOFEM");
2519 CHKERR DMMoFEMCreateSubDM(subdm, dm, "SUB_SOLVER");
2520 CHKERR DMMoFEMAddElement(subdm, simple->getDomainFEName());
2521 CHKERR DMMoFEMAddElement(subdm, simple->getBoundaryFEName());
2522 CHKERR DMMoFEMSetSquareProblem(subdm, PETSC_TRUE);
2523 CHKERR DMMoFEMSetDestroyProblem(subdm, PETSC_FALSE);
2524 for (auto f : {"U", "P", "H", "G", "L"}) {
2525 CHKERR DMMoFEMAddSubFieldRow(subdm, f, level_ents_ptr);
2526 CHKERR DMMoFEMAddSubFieldCol(subdm, f, level_ents_ptr);
2527 }
2528 CHKERR DMSetUp(subdm);
2530 };
2531
2532 CHK_THROW_MESSAGE(setup_subdm(dm), "create subdm");
2533
2534 return SmartPetscObj<DM>(subdm);
2535 };
2536
2537 auto get_fe_post_proc = [&](auto post_proc_mesh) {
2538 auto add_parent_field_domain = [&](auto fe, auto op, auto field) {
2539 return setParentDofs(
2540 fe, field, op, bit(get_skin_parent_bit()),
2541
2542 [&]() {
2543 boost::shared_ptr<ForcesAndSourcesCore> fe_parent(
2544 new DomainParentEle(mField));
2545 return fe_parent;
2546 },
2547
2548 QUIET, Sev::noisy);
2549 };
2550
2551 auto post_proc_fe =
2552 boost::make_shared<PostProcEleDomainCont>(mField, post_proc_mesh);
2553 post_proc_fe->exeTestHook = [](FEMethod *fe_ptr) {
2554 return fe_ptr->numeredEntFiniteElementPtr->getBitRefLevel().test(
2555 get_start_bit() + nb_levels - 1);
2556 };
2557
2558 auto u_ptr = boost::make_shared<MatrixDouble>();
2559 auto grad_u_ptr = boost::make_shared<MatrixDouble>();
2560 auto h_ptr = boost::make_shared<VectorDouble>();
2561 auto grad_h_ptr = boost::make_shared<MatrixDouble>();
2562 auto p_ptr = boost::make_shared<VectorDouble>();
2563 auto g_ptr = boost::make_shared<VectorDouble>();
2564 auto grad_g_ptr = boost::make_shared<MatrixDouble>();
2565
2567 post_proc_fe->getOpPtrVector(), {H1});
2568
2570 post_proc_fe, "", UDO::OPSPACE, bit(get_skin_parent_bit()),
2571
2572 [&]() {
2573 boost::shared_ptr<ForcesAndSourcesCore> fe_parent(
2574 new DomainParentEle(mField));
2576 fe_parent->getOpPtrVector(), {H1});
2577 return fe_parent;
2578 },
2579
2580 QUIET, Sev::noisy);
2581
2582 CHKERR add_parent_field_domain(post_proc_fe, UDO::OPCOL, "U");
2583 post_proc_fe->getOpPtrVector().push_back(
2585 post_proc_fe->getOpPtrVector().push_back(
2587 grad_u_ptr));
2588
2589 CHKERR add_parent_field_domain(post_proc_fe, UDO::OPCOL, "H");
2590 post_proc_fe->getOpPtrVector().push_back(
2591 new OpCalculateScalarFieldValues("H", h_ptr));
2592 post_proc_fe->getOpPtrVector().push_back(
2593 new OpCalculateScalarFieldGradient<SPACE_DIM>("H", grad_h_ptr));
2594
2595 CHKERR add_parent_field_domain(post_proc_fe, UDO::OPCOL, "P");
2596 post_proc_fe->getOpPtrVector().push_back(
2597 new OpCalculateScalarFieldValues("P", p_ptr));
2598
2599 CHKERR add_parent_field_domain(post_proc_fe, UDO::OPCOL, "G");
2600 post_proc_fe->getOpPtrVector().push_back(
2601 new OpCalculateScalarFieldValues("G", g_ptr));
2602 post_proc_fe->getOpPtrVector().push_back(
2603 new OpCalculateScalarFieldGradient<SPACE_DIM>("G", grad_g_ptr));
2604
2606
2607 post_proc_fe->getOpPtrVector().push_back(
2608
2609 new OpPPMap(
2610 post_proc_fe->getPostProcMesh(), post_proc_fe->getMapGaussPts(),
2611
2612 {{"H", h_ptr}, {"P", p_ptr}, {"G", g_ptr}},
2613
2614 {{"U", u_ptr}, {"H_GRAD", grad_h_ptr}, {"G_GRAD", grad_g_ptr}},
2615
2616 {{"GRAD_U", grad_u_ptr}},
2617
2618 {}
2619
2620 )
2621
2622 );
2623
2624 return post_proc_fe;
2625 };
2626
2627 auto get_bdy_post_proc_fe = [&](auto post_proc_mesh) {
2628 auto add_parent_field_bdy = [&](auto fe, auto op, auto field) {
2629 return setParentDofs(
2630 fe, field, op, bit(get_skin_parent_bit()),
2631
2632 [&]() {
2633 boost::shared_ptr<ForcesAndSourcesCore> fe_parent(
2634 new BoundaryParentEle(mField));
2635 return fe_parent;
2636 },
2637
2638 QUIET, Sev::noisy);
2639 };
2640
2641 auto post_proc_fe =
2642 boost::make_shared<PostProcEleBdyCont>(mField, post_proc_mesh);
2643 post_proc_fe->exeTestHook = [](FEMethod *fe_ptr) {
2644 return fe_ptr->numeredEntFiniteElementPtr->getBitRefLevel().test(
2645 get_start_bit() + nb_levels - 1);
2646 };
2647
2648 CHKERR setParentDofs(
2649 post_proc_fe, "", UDO::OPSPACE, bit(get_skin_parent_bit()),
2650
2651 [&]() {
2652 boost::shared_ptr<ForcesAndSourcesCore> fe_parent(
2653 new BoundaryParentEle(mField));
2654 return fe_parent;
2655 },
2656
2657 QUIET, Sev::noisy);
2658
2659 struct OpGetNormal : public BoundaryEleOp {
2660
2661 OpGetNormal(boost::shared_ptr<VectorDouble> l_ptr,
2662 boost::shared_ptr<MatrixDouble> n_ptr)
2663 : BoundaryEleOp(NOSPACE, BoundaryEleOp::OPSPACE), ptrL(l_ptr),
2664 ptrNormal(n_ptr) {}
2665
2666 MoFEMErrorCode doWork(int side, EntityType type,
2669 auto t_l = getFTensor0FromVec(*ptrL);
2670 auto t_n_fe = getFTensor1NormalsAtGaussPts();
2671 ptrNormal->resize(SPACE_DIM, getGaussPts().size2(), false);
2672 auto t_n = getFTensor1FromMat<SPACE_DIM>(*ptrNormal);
2673 for (auto gg = 0; gg != getGaussPts().size2(); ++gg) {
2674 t_n(i) = t_n_fe(i) * t_l / std::sqrt(t_n_fe(i) * t_n_fe(i));
2675 ++t_n_fe;
2676 ++t_l;
2677 ++t_n;
2678 }
2680 };
2681
2682 protected:
2683 boost::shared_ptr<VectorDouble> ptrL;
2684 boost::shared_ptr<MatrixDouble> ptrNormal;
2685 };
2686
2687 auto u_ptr = boost::make_shared<MatrixDouble>();
2688 auto p_ptr = boost::make_shared<VectorDouble>();
2689 auto lambda_ptr = boost::make_shared<VectorDouble>();
2690 auto normal_l_ptr = boost::make_shared<MatrixDouble>();
2691
2692 CHKERR add_parent_field_bdy(post_proc_fe, UDO::OPCOL, "U");
2693 post_proc_fe->getOpPtrVector().push_back(
2695
2696 CHKERR add_parent_field_bdy(post_proc_fe, UDO::OPCOL, "L");
2697 post_proc_fe->getOpPtrVector().push_back(
2698 new OpCalculateScalarFieldValues("L", lambda_ptr));
2699 post_proc_fe->getOpPtrVector().push_back(
2700 new OpGetNormal(lambda_ptr, normal_l_ptr));
2701
2702 CHKERR add_parent_field_bdy(post_proc_fe, UDO::OPCOL, "P");
2703 post_proc_fe->getOpPtrVector().push_back(
2704 new OpCalculateScalarFieldValues("P", p_ptr));
2705
2706 auto op_ptr = new BoundaryEleOp(NOSPACE, BoundaryEleOp::OPSPACE);
2707 op_ptr->doWorkRhsHook = [&](DataOperator *base_op_ptr, int side,
2708 EntityType type,
2712 };
2713
2715
2716 post_proc_fe->getOpPtrVector().push_back(
2717
2718 new OpPPMap(post_proc_fe->getPostProcMesh(),
2719 post_proc_fe->getMapGaussPts(),
2720
2721 OpPPMap::DataMapVec{{"P", p_ptr}},
2722
2723 OpPPMap::DataMapMat{{"U", u_ptr}, {"L", normal_l_ptr}},
2724
2726
2728
2729 )
2730
2731 );
2732
2733 return post_proc_fe;
2734 };
2735
2736 auto get_lift_fe = [&]() {
2737 auto add_parent_field_bdy = [&](auto fe, auto op, auto field) {
2738 return setParentDofs(
2739 fe, field, op, bit(get_skin_parent_bit()),
2740
2741 [&]() {
2742 boost::shared_ptr<ForcesAndSourcesCore> fe_parent(
2743 new BoundaryParentEle(mField));
2744 return fe_parent;
2745 },
2746
2747 QUIET, Sev::noisy);
2748 };
2749
2750 auto fe = boost::make_shared<BoundaryEle>(mField);
2751 fe->exeTestHook = [](FEMethod *fe_ptr) {
2752 return fe_ptr->numeredEntFiniteElementPtr->getBitRefLevel().test(
2753 get_start_bit() + nb_levels - 1);
2754 };
2755
2756 auto lift_ptr = boost::make_shared<VectorDouble>();
2757 auto p_ptr = boost::make_shared<VectorDouble>();
2758 auto ents_ptr = boost::make_shared<Range>();
2759
2760 CHKERR setParentDofs(
2761 fe, "", UDO::OPSPACE, bit(get_skin_parent_bit()),
2762
2763 [&]() {
2764 boost::shared_ptr<ForcesAndSourcesCore> fe_parent(
2765 new BoundaryParentEle(mField));
2766 return fe_parent;
2767 },
2768
2769 QUIET, Sev::noisy);
2770
2771 std::vector<const CubitMeshSets *> vec_ptr;
2772 CHKERR mField.getInterface<MeshsetsManager>()->getCubitMeshsetPtr(
2773 std::regex("LIFT"), vec_ptr);
2774 for (auto m_ptr : vec_ptr) {
2775 auto meshset = m_ptr->getMeshset();
2776 Range ents;
2777 CHKERR mField.get_moab().get_entities_by_dimension(meshset, SPACE_DIM - 1,
2778 ents, true);
2779 ents_ptr->merge(ents);
2780 }
2781
2782 MOFEM_LOG("FS", Sev::noisy) << "Lift ents " << (*ents_ptr);
2783 CHKERR add_parent_field_bdy(fe, UDO::OPROW, "L");
2784 CHKERR add_parent_field_bdy(fe, UDO::OPCOL, "L");
2785 fe->getOpPtrVector().push_back(
2786 new OpCalculateScalarFieldValues("L", p_ptr));
2787 fe->getOpPtrVector().push_back(
2788 new OpCalculateLift("L", p_ptr, lift_ptr, ents_ptr));
2789
2790 return std::make_pair(fe, lift_ptr);
2791 };
2792
2793 auto set_post_proc_monitor = [&](auto ts) {
2795 DM dm;
2796 CHKERR TSGetDM(ts, &dm);
2797 boost::shared_ptr<FEMethod> null_fe;
2798 auto post_proc_mesh = boost::make_shared<moab::Core>();
2799 auto monitor_ptr = boost::make_shared<Monitor>(
2800 SmartPetscObj<DM>(dm, true), post_proc_mesh,
2801 get_fe_post_proc(post_proc_mesh), get_bdy_post_proc_fe(post_proc_mesh),
2802 get_lift_fe());
2803 CHKERR DMMoFEMTSSetMonitor(dm, ts, simple->getDomainFEName(), null_fe,
2804 null_fe, monitor_ptr);
2806 };
2807
2808 auto dm = simple->getDM();
2809 auto ts = createTS(mField.get_comm());
2810 CHKERR TSSetDM(ts, dm);
2811
2812 auto ts_pre_post_proc = boost::make_shared<TSPrePostProc>();
2813 tsPrePostProc = ts_pre_post_proc;
2814
2815 if (auto ptr = tsPrePostProc.lock()) {
2816
2817 ptr->fsRawPtr = this;
2818 ptr->solverSubDM = create_solver_dm(simple->getDM());
2819 ptr->globSol = createDMVector(dm);
2820 CHKERR DMoFEMMeshToLocalVector(dm, ptr->globSol, INSERT_VALUES,
2821 SCATTER_FORWARD);
2822 CHKERR VecAssemblyBegin(ptr->globSol);
2823 CHKERR VecAssemblyEnd(ptr->globSol);
2824
2825 auto sub_ts = pip_mng->createTSIM(ptr->solverSubDM);
2826
2827 CHKERR set_post_proc_monitor(sub_ts);
2828
2829 // Add monitor to time solver
2830 CHKERR TSSetFromOptions(ts);
2831 CHKERR ptr->tsSetUp(ts);
2832 CHKERR TSSetUp(ts);
2833
2834 auto print_fields_in_section = [&]() {
2836 auto section = mField.getInterface<ISManager>()->sectionCreate(
2837 simple->getProblemName());
2838 PetscInt num_fields;
2839 CHKERR PetscSectionGetNumFields(section, &num_fields);
2840 for (int f = 0; f < num_fields; ++f) {
2841 const char *field_name;
2842 CHKERR PetscSectionGetFieldName(section, f, &field_name);
2843 MOFEM_LOG("FS", Sev::inform)
2844 << "Field " << f << " " << std::string(field_name);
2845 }
2847 };
2848
2849 CHKERR print_fields_in_section();
2850
2851 CHKERR TSSolve(ts, ptr->globSol);
2852 }
2853
2855}
2856
2857/**
2858 * @brief Main function for free surface simulation
2859 * @param argc Number of command line arguments
2860 * @param argv Array of command line argument strings
2861 * @return Exit code (0 for success)
2862 *
2863 * Main driver function that:
2864 * 1. Initializes MoFEM/PETSc and MOAB data structures
2865 * 2. Sets up logging channels for debugging output
2866 * 3. Registers MoFEM discrete manager with PETSc
2867 * 4. Creates mesh database (MOAB) and finite element database (MoFEM)
2868 * 5. Runs the complete free surface simulation
2869 * 6. Handles cleanup and finalization
2870 *
2871 * Command line options are read from param_file.petsc and command line.
2872 * Python initialization is optional (controlled by PYTHON_INIT_SURFACE).
2873 */
2874int main(int argc, char *argv[]) {
2875
2876#ifdef PYTHON_INIT_SURFACE
2877 Py_Initialize();
2878#endif
2879
2880 // Initialisation of MoFEM/PETSc and MOAB data structures
2881 const char param_file[] = "param_file.petsc";
2882 MoFEM::Core::Initialize(&argc, &argv, param_file, help);
2883
2884 // Add logging channel for example
2885 auto core_log = logging::core::get();
2886 core_log->add_sink(LogManager::createSink(LogManager::getStrmWorld(), "FS"));
2887 LogManager::setLog("FS");
2888 MOFEM_LOG_TAG("FS", "free surface");
2889
2890 try {
2891
2892 //! [Register MoFEM discrete manager in PETSc]
2893 DMType dm_name = "DMMOFEM";
2894 CHKERR DMRegister_MoFEM(dm_name);
2895 //! [Register MoFEM discrete manager in PETSc
2896
2897 //! [Create MoAB]
2898 moab::Core mb_instance; ///< mesh database
2899 moab::Interface &moab = mb_instance; ///< mesh database interface
2900 //! [Create MoAB]
2901
2902 //! [Create MoFEM]
2903 MoFEM::Core core(moab); ///< finite element database
2904 MoFEM::Interface &m_field = core; ///< finite element database interface
2905 //! [Create MoFEM]
2906
2907 //! [FreeSurface]
2908 FreeSurface ex(m_field);
2909 CHKERR ex.runProblem();
2910 //! [FreeSurface]
2911 }
2913
2915
2916#ifdef PYTHON_INIT_SURFACE
2917 if (Py_FinalizeEx() < 0) {
2918 exit(120);
2919 }
2920#endif
2921}
2922
2923std::vector<Range>
2925
2926 auto &moab = mField.get_moab();
2927 auto bit_mng = mField.getInterface<BitRefManager>();
2928 auto comm_mng = mField.getInterface<CommInterface>();
2929
2930 Range vertices;
2931 CHK_THROW_MESSAGE(bit_mng->getEntitiesByTypeAndRefLevel(
2932 bit(0), BitRefLevel().set(), MBVERTEX, vertices),
2933 "can not get vertices on bit 0");
2934
2935 auto &dofs_mi = mField.get_dofs()->get<Unique_mi_tag>();
2936 auto field_bit_number = mField.get_field_bit_number("H");
2937
2938 Range plus_range, minus_range;
2939 std::vector<EntityHandle> plus, minus;
2940
2941 // get vertices on level 0 on plus and minus side
2942 for (auto p = vertices.pair_begin(); p != vertices.pair_end(); ++p) {
2943
2944 const auto f = p->first;
2945 const auto s = p->second;
2946
2947 // Lowest Dof UId for given field (field bit number) on entity f
2948 const auto lo_uid = DofEntity::getLoFieldEntityUId(field_bit_number, f);
2949 const auto hi_uid = DofEntity::getHiFieldEntityUId(field_bit_number, s);
2950 auto it = dofs_mi.lower_bound(lo_uid);
2951 const auto hi_it = dofs_mi.upper_bound(hi_uid);
2952
2953 plus.clear();
2954 minus.clear();
2955 plus.reserve(std::distance(it, hi_it));
2956 minus.reserve(std::distance(it, hi_it));
2957
2958 for (; it != hi_it; ++it) {
2959 const auto v = (*it)->getFieldData();
2960 if (v > 0)
2961 plus.push_back((*it)->getEnt());
2962 else
2963 minus.push_back((*it)->getEnt());
2964 }
2965
2966 plus_range.insert_list(plus.begin(), plus.end());
2967 minus_range.insert_list(minus.begin(), minus.end());
2968 }
2969
2970 MOFEM_LOG_CHANNEL("SYNC");
2971 MOFEM_TAG_AND_LOG("SYNC", Sev::noisy, "FS")
2972 << "Plus range " << plus_range << endl;
2973 MOFEM_TAG_AND_LOG("SYNC", Sev::noisy, "FS")
2974 << "Minus range " << minus_range << endl;
2976
2977 auto get_elems = [&](auto &ents, auto bit, auto mask) {
2978 Range adj;
2979 CHK_MOAB_THROW(moab.get_adjacencies(ents, SPACE_DIM, false, adj,
2980 moab::Interface::UNION),
2981 "can not get adjacencies");
2982 CHK_THROW_MESSAGE(bit_mng->filterEntitiesByRefLevel(bit, mask, adj),
2983 "can not filter elements with bit 0");
2984 return adj;
2985 };
2986
2987 CHKERR comm_mng->synchroniseEntities(plus_range);
2988 CHKERR comm_mng->synchroniseEntities(minus_range);
2989
2990 std::vector<Range> ele_plus(nb_levels), ele_minus(nb_levels);
2991 ele_plus[0] = get_elems(plus_range, bit(0), BitRefLevel().set());
2992 ele_minus[0] = get_elems(minus_range, bit(0), BitRefLevel().set());
2993 auto common = intersect(ele_plus[0], ele_minus[0]);
2994 ele_plus[0] = subtract(ele_plus[0], common);
2995 ele_minus[0] = subtract(ele_minus[0], common);
2996
2997 auto get_children = [&](auto &p, auto &c) {
2999 CHKERR bit_mng->updateRangeByChildren(p, c);
3000 c = c.subset_by_dimension(SPACE_DIM);
3002 };
3003
3004 for (auto l = 1; l != nb_levels; ++l) {
3005 CHK_THROW_MESSAGE(get_children(ele_plus[l - 1], ele_plus[l]),
3006 "get children");
3007 CHK_THROW_MESSAGE(get_children(ele_minus[l - 1], ele_minus[l]),
3008 "get children");
3009 }
3010
3011 auto get_level = [&](auto &p, auto &m, auto z, auto bit, auto mask) {
3012 Range l;
3014 bit_mng->getEntitiesByDimAndRefLevel(bit, mask, SPACE_DIM, l),
3015 "can not get vertices on bit");
3016 l = subtract(l, p);
3017 l = subtract(l, m);
3018 for (auto f = 0; f != z; ++f) {
3019 Range conn;
3020 CHK_MOAB_THROW(moab.get_connectivity(l, conn, true), "");
3021 CHKERR mField.getInterface<CommInterface>()->synchroniseEntities(conn);
3022 l = get_elems(conn, bit, mask);
3023 }
3024 return l;
3025 };
3026
3027 std::vector<Range> vec_levels(nb_levels);
3028 for (auto l = nb_levels - 1; l >= 0; --l) {
3029 vec_levels[l] = get_level(ele_plus[l], ele_minus[l], 2 * overlap, bit(l),
3030 BitRefLevel().set());
3031 }
3032
3033 if constexpr (debug) {
3034 if (mField.get_comm_size() == 1) {
3035 for (auto l = 0; l != nb_levels; ++l) {
3036 std::string name = (boost::format("out_r%d.h5m") % l).str();
3037 CHK_THROW_MESSAGE(save_range(mField.get_moab(), name, vec_levels[l]),
3038 "save mesh");
3039 }
3040 }
3041 }
3042
3043 return vec_levels;
3044}
3045
3048
3049 auto bit_mng = mField.getInterface<BitRefManager>();
3050
3051 BitRefLevel start_mask;
3052 for (auto s = 0; s != get_start_bit(); ++s)
3053 start_mask[s] = true;
3054
3055 // store prev_level
3056 Range prev_level;
3057 CHKERR bit_mng->getEntitiesByRefLevel(bit(get_current_bit()),
3058 BitRefLevel().set(), prev_level);
3059 Range prev_level_skin;
3060 CHKERR bit_mng->getEntitiesByRefLevel(bit(get_skin_parent_bit()),
3061 BitRefLevel().set(), prev_level_skin);
3062 // reset bit ref levels
3063 CHKERR bit_mng->lambdaBitRefLevel(
3064 [&](EntityHandle ent, BitRefLevel &bit) { bit &= start_mask; });
3065 CHKERR bit_mng->setNthBitRefLevel(prev_level, get_projection_bit(), true);
3066 CHKERR bit_mng->setNthBitRefLevel(prev_level_skin, get_skin_projection_bit(),
3067 true);
3068
3069 // set refinement levels
3070 auto set_levels = [&](auto &&
3071 vec_levels /*entities are refined on each level*/) {
3073
3074 // start with zero level, which is the coarsest mesh
3075 Range level0;
3076 CHKERR bit_mng->getEntitiesByRefLevel(bit(0), BitRefLevel().set(), level0);
3077 CHKERR bit_mng->setNthBitRefLevel(level0, get_start_bit(), true);
3078
3079 // get lower dimension entities
3080 auto get_adj = [&](auto ents) {
3081 Range conn;
3082 CHK_MOAB_THROW(mField.get_moab().get_connectivity(ents, conn, true),
3083 "get conn");
3084 for (auto d = 1; d != SPACE_DIM; ++d) {
3085 CHK_MOAB_THROW(mField.get_moab().get_adjacencies(
3086 ents.subset_by_dimension(SPACE_DIM), d, false, conn,
3087 moab::Interface::UNION),
3088 "get adj");
3089 }
3090 ents.merge(conn);
3091 return ents;
3092 };
3093
3094 // set bit levels
3095 for (auto l = 1; l != nb_levels; ++l) {
3096 Range level_prev;
3097 CHKERR bit_mng->getEntitiesByDimAndRefLevel(bit(get_start_bit() + l - 1),
3098 BitRefLevel().set(),
3099 SPACE_DIM, level_prev);
3100 Range parents;
3101 CHKERR bit_mng->updateRangeByParent(vec_levels[l], parents);
3102 // subtract entities from previous level, which are refined, so should be
3103 // not there
3104 level_prev = subtract(level_prev, parents);
3105 // and instead add their children
3106 level_prev.merge(vec_levels[l]);
3107 // set bit to each level
3108 CHKERR bit_mng->setNthBitRefLevel(level_prev, get_start_bit() + l, true);
3109 }
3110
3111 // set bit levels to lower dimension entities
3112 for (auto l = 1; l != nb_levels; ++l) {
3113 Range level;
3114 CHKERR bit_mng->getEntitiesByDimAndRefLevel(
3115 bit(get_start_bit() + l), BitRefLevel().set(), SPACE_DIM, level);
3116 level = get_adj(level);
3117 CHKERR mField.getInterface<CommInterface>()->synchroniseEntities(level);
3118 CHKERR bit_mng->setNthBitRefLevel(level, get_start_bit() + l, true);
3119 }
3120
3122 };
3123
3124 // resolve skin between refined levels
3125 auto set_skins = [&]() {
3127
3128 moab::Skinner skinner(&mField.get_moab());
3129 ParallelComm *pcomm =
3130 ParallelComm::get_pcomm(&mField.get_moab(), MYPCOMM_INDEX);
3131
3132 // get skin of bit level
3133 auto get_bit_skin = [&](BitRefLevel bit, BitRefLevel mask) {
3134 Range bit_ents;
3137 bit, mask, SPACE_DIM, bit_ents),
3138 "can't get bit level");
3139 Range bit_skin;
3140 CHK_MOAB_THROW(skinner.find_skin(0, bit_ents, false, bit_skin),
3141 "can't get skin");
3142 return bit_skin;
3143 };
3144
3145 auto get_level_skin = [&]() {
3146 Range skin;
3147 BitRefLevel bit_prev;
3148 for (auto l = 1; l != nb_levels; ++l) {
3149 auto skin_level_mesh = get_bit_skin(bit(l), BitRefLevel().set());
3150 // filter (remove) all entities which are on partition borders
3151 CHKERR pcomm->filter_pstatus(skin_level_mesh,
3152 PSTATUS_SHARED | PSTATUS_MULTISHARED,
3153 PSTATUS_NOT, -1, nullptr);
3154 auto skin_level =
3155 get_bit_skin(bit(get_start_bit() + l), BitRefLevel().set());
3156 skin_level = subtract(skin_level,
3157 skin_level_mesh); // get only internal skins, not
3158 // on the body boundary
3159 // get lower dimension adjacencies (FIXME: add edges if 3D)
3160 Range skin_level_verts;
3161 CHKERR mField.get_moab().get_connectivity(skin_level, skin_level_verts,
3162 true);
3163 skin_level.merge(skin_level_verts);
3164
3165 // remove previous level
3166 bit_prev.set(l - 1);
3167 Range level_prev;
3168 CHKERR bit_mng->getEntitiesByRefLevel(bit_prev, BitRefLevel().set(),
3169 level_prev);
3170 skin.merge(subtract(skin_level, level_prev));
3171 }
3172
3173 return skin;
3174 };
3175
3176 auto resolve_shared = [&](auto &&skin) {
3177 Range tmp_skin = skin;
3178
3179 map<int, Range> map_procs;
3180 CHKERR mField.getInterface<CommInterface>()->synchroniseEntities(
3181 tmp_skin, &map_procs);
3182
3183 Range from_other_procs; // entities which also exist on other processors
3184 for (auto &m : map_procs) {
3185 if (m.first != mField.get_comm_rank()) {
3186 from_other_procs.merge(m.second);
3187 }
3188 }
3189
3190 auto common = intersect(
3191 skin, from_other_procs); // entities which are on internal skin
3192 skin.merge(from_other_procs);
3193
3194 // entities which are on processor borders, and several processors are not
3195 // true skin.
3196 if (!common.empty()) {
3197 // skin is internal exist on other procs
3198 skin = subtract(skin, common);
3199 }
3200
3201 return skin;
3202 };
3203
3204 // get parents of entities
3205 auto get_parent_level_skin = [&](auto skin) {
3206 Range skin_parents;
3207 CHKERR bit_mng->updateRangeByParent(
3208 skin.subset_by_dimension(SPACE_DIM - 1), skin_parents);
3209 Range skin_parent_verts;
3210 CHKERR mField.get_moab().get_connectivity(skin_parents, skin_parent_verts,
3211 true);
3212 skin_parents.merge(skin_parent_verts);
3213 CHKERR mField.getInterface<CommInterface>()->synchroniseEntities(
3214 skin_parents);
3215 return skin_parents;
3216 };
3217
3218 auto child_skin = resolve_shared(get_level_skin());
3219 auto parent_skin = get_parent_level_skin(child_skin);
3220
3221 child_skin = subtract(child_skin, parent_skin);
3222 CHKERR bit_mng->setNthBitRefLevel(child_skin, get_skin_child_bit(), true);
3223 CHKERR bit_mng->setNthBitRefLevel(parent_skin, get_skin_parent_bit(), true);
3224
3226 };
3227
3228 // take last level, remove childs on boarder, and set bit
3229 auto set_current = [&]() {
3231 Range last_level;
3232 CHKERR bit_mng->getEntitiesByRefLevel(bit(get_start_bit() + nb_levels - 1),
3233 BitRefLevel().set(), last_level);
3234 Range skin_child;
3235 CHKERR bit_mng->getEntitiesByRefLevel(bit(get_skin_child_bit()),
3236 BitRefLevel().set(), skin_child);
3237
3238 last_level = subtract(last_level, skin_child);
3239 CHKERR bit_mng->setNthBitRefLevel(last_level, get_current_bit(), true);
3241 };
3242
3243 // set bits to levels
3244 CHKERR set_levels(findEntitiesCrossedByPhaseInterface(overlap));
3245 // set bits to skin
3246 CHKERR set_skins();
3247 // set current level bit
3248 CHKERR set_current();
3249
3250 if constexpr (debug) {
3251 if (mField.get_comm_size() == 1) {
3252 for (auto l = 0; l != nb_levels; ++l) {
3253 std::string name = (boost::format("out_level%d.h5m") % l).str();
3254 CHKERR bit_mng->writeBitLevel(BitRefLevel().set(get_start_bit() + l),
3255 BitRefLevel().set(), name.c_str(), "MOAB",
3256 "PARALLEL=WRITE_PART");
3257 }
3258 CHKERR bit_mng->writeBitLevel(BitRefLevel().set(get_current_bit()),
3259 BitRefLevel().set(), "current_bit.h5m",
3260 "MOAB", "PARALLEL=WRITE_PART");
3261 CHKERR bit_mng->writeBitLevel(BitRefLevel().set(get_projection_bit()),
3262 BitRefLevel().set(), "projection_bit.h5m",
3263 "MOAB", "PARALLEL=WRITE_PART");
3264
3265 CHKERR bit_mng->writeBitLevel(BitRefLevel().set(get_skin_child_bit()),
3266 BitRefLevel().set(), "skin_child_bit.h5m",
3267 "MOAB", "PARALLEL=WRITE_PART");
3268 CHKERR bit_mng->writeBitLevel(BitRefLevel().set(get_skin_parent_bit()),
3269 BitRefLevel().set(), "skin_parent_bit.h5m",
3270 "MOAB", "PARALLEL=WRITE_PART");
3271 }
3272 }
3273
3275};
3276
3278 boost::shared_ptr<FEMethod> fe_top, std::string field_name,
3280 BitRefLevel child_ent_bit,
3281 boost::function<boost::shared_ptr<ForcesAndSourcesCore>()> get_elem,
3282 int verbosity, LogManager::SeverityLevel sev) {
3284
3285 /**
3286 * @brief Collect data from parent elements to child
3287 */
3288 boost::function<void(boost::shared_ptr<ForcesAndSourcesCore>, int)>
3289 add_parent_level =
3290 [&](boost::shared_ptr<ForcesAndSourcesCore> parent_fe_pt, int level) {
3291 // Evaluate if not last parent element
3292 if (level > 0) {
3293
3294 // Create domain parent FE
3295 auto fe_ptr_current = get_elem();
3296
3297 // Call next level
3298 add_parent_level(
3299 boost::dynamic_pointer_cast<ForcesAndSourcesCore>(
3300 fe_ptr_current),
3301 level - 1);
3302
3303 // Add data to curent fe level
3305
3306 // Only base
3307 parent_fe_pt->getOpPtrVector().push_back(
3308
3310
3311 H1, op, fe_ptr_current,
3312
3313 BitRefLevel().set(), BitRefLevel().set(),
3314
3315 child_ent_bit, BitRefLevel().set(),
3316
3317 verbosity, sev));
3318
3319 } else {
3320
3321 // Filed data
3322 parent_fe_pt->getOpPtrVector().push_back(
3323
3325
3326 field_name, op, fe_ptr_current,
3327
3328 BitRefLevel().set(), BitRefLevel().set(),
3329
3330 child_ent_bit, BitRefLevel().set(),
3331
3332 verbosity, sev));
3333 }
3334 }
3335 };
3336
3337 add_parent_level(boost::dynamic_pointer_cast<ForcesAndSourcesCore>(fe_top),
3338 nb_levels);
3339
3341}
3342
3345
3346 if (auto ptr = tsPrePostProc.lock()) {
3347
3348 /**
3349 * @brief cut-off values at nodes, i.e. abs("H") <= 1
3350 *
3351 */
3352 auto cut_off_dofs = [&]() {
3354
3355 auto &m_field = ptr->fsRawPtr->mField;
3356
3357 Range current_verts;
3358 auto bit_mng = m_field.getInterface<BitRefManager>();
3360 bit(get_current_bit()), BitRefLevel().set(), MBVERTEX, current_verts);
3361
3362 auto cut_off_verts = [&](boost::shared_ptr<FieldEntity> ent_ptr) {
3364 for (auto &h : ent_ptr->getEntFieldData()) {
3365 h = cut_off(h);
3366 }
3368 };
3369
3370 auto field_blas = m_field.getInterface<FieldBlas>();
3371 CHKERR field_blas->fieldLambdaOnEntities(cut_off_verts, "H",
3372 &current_verts);
3374 };
3375
3376 CHKERR cut_off_dofs();
3377 }
3378
3379 if (auto ptr = tsPrePostProc.lock()) {
3380 MOFEM_LOG("FS", Sev::inform) << "Run step pre proc";
3381
3382 auto &m_field = ptr->fsRawPtr->mField;
3383 auto simple = m_field.getInterface<Simple>();
3384
3385 // get vector norm
3386 auto get_norm = [&](auto x) {
3387 double nrm;
3388 CHKERR VecNorm(x, NORM_2, &nrm);
3389 return nrm;
3390 };
3391
3392 // refine problem and project data, including theta data
3393 auto refine_problem = [&]() {
3395 MOFEM_LOG("FS", Sev::inform) << "Refine problem";
3396 CHKERR ptr->fsRawPtr->refineMesh(refine_overlap);
3397 CHKERR ptr->fsRawPtr->projectData();
3399 };
3400
3401 // set new jacobin operator, since problem and thus tangent matrix size has
3402 // changed
3403 auto set_jacobian_operators = [&]() {
3405 ptr->subB = createDMMatrix(ptr->solverSubDM);
3406 CHKERR KSPReset(ptr->subKSP);
3408 };
3409
3410 // set new solution
3411 auto set_solution = [&]() {
3413 MOFEM_LOG("FS", Sev::inform) << "Set solution";
3414
3415 PetscObjectState state;
3416
3417 // Record the state, and set it again. This is to fool PETSc that solution
3418 // vector is not updated. Otherwise PETSc will treat every step as a first
3419 // step.
3420
3421 // globSol is updated as result mesh refinement - this is not really set
3422 // a new solution.
3423
3424 CHKERR PetscObjectStateGet(getPetscObject(ptr->globSol.get()), &state);
3425 CHKERR DMoFEMMeshToLocalVector(simple->getDM(), ptr->globSol,
3426 INSERT_VALUES, SCATTER_FORWARD);
3427 CHKERR PetscObjectStateSet(getPetscObject(ptr->globSol.get()), state);
3428 MOFEM_LOG("FS", Sev::verbose)
3429 << "Set solution, vector norm " << get_norm(ptr->globSol);
3431 };
3432
3433 PetscBool is_theta;
3434 PetscObjectTypeCompare((PetscObject)ts, TSTHETA, &is_theta);
3435 if (is_theta) {
3436
3437 CHKERR refine_problem(); // refine problem
3438 CHKERR set_jacobian_operators(); // set new jacobian
3439 CHKERR set_solution(); // set solution
3440
3441 } else {
3442 SETERRQ(PETSC_COMM_WORLD, MOFEM_NOT_IMPLEMENTED,
3443 "Sorry, only TSTheta handling is implemented");
3444 }
3445
3446 // Need barriers, some functions in TS solver need are called collectively
3447 // and require the same state of variables
3448 PetscBarrier((PetscObject)ts);
3449
3450 MOFEM_LOG_CHANNEL("SYNC");
3451 MOFEM_TAG_AND_LOG("SYNC", Sev::verbose, "FS") << "PreProc done";
3452 MOFEM_LOG_SEVERITY_SYNC(m_field.get_comm(), Sev::verbose);
3453 }
3454
3456}
3457
3459 if (auto ptr = tsPrePostProc.lock()) {
3460 auto &m_field = ptr->fsRawPtr->mField;
3461 MOFEM_LOG_CHANNEL("SYNC");
3462 MOFEM_TAG_AND_LOG("SYNC", Sev::verbose, "FS") << "PostProc done";
3463 MOFEM_LOG_SEVERITY_SYNC(m_field.get_comm(), Sev::verbose);
3464 }
3465 return 0;
3466}
3467
3468MoFEMErrorCode TSPrePostProc::tsSetIFunction(TS ts, PetscReal t, Vec u, Vec u_t,
3469 Vec f, void *ctx) {
3471 if (auto ptr = tsPrePostProc.lock()) {
3472 auto sub_u = ptr->getSubVector();
3473 auto sub_u_t = vectorDuplicate(sub_u);
3474 auto sub_f = vectorDuplicate(sub_u);
3475 auto scatter = ptr->getScatter(sub_u, u, R);
3476
3477 auto apply_scatter_and_update = [&](auto x, auto sub_x) {
3479 CHKERR VecScatterBegin(scatter, x, sub_x, INSERT_VALUES, SCATTER_REVERSE);
3480 CHKERR VecScatterEnd(scatter, x, sub_x, INSERT_VALUES, SCATTER_REVERSE);
3481 CHKERR VecGhostUpdateBegin(sub_x, INSERT_VALUES, SCATTER_FORWARD);
3482 CHKERR VecGhostUpdateEnd(sub_x, INSERT_VALUES, SCATTER_FORWARD);
3484 };
3485
3486 CHKERR apply_scatter_and_update(u, sub_u);
3487 CHKERR apply_scatter_and_update(u_t, sub_u_t);
3488
3489 CHKERR TsSetIFunction(ts, t, sub_u, sub_u_t, sub_f, ptr->tsCtxPtr.get());
3490 CHKERR VecScatterBegin(scatter, sub_f, f, INSERT_VALUES, SCATTER_FORWARD);
3491 CHKERR VecScatterEnd(scatter, sub_f, f, INSERT_VALUES, SCATTER_FORWARD);
3492 }
3494}
3495
3496MoFEMErrorCode TSPrePostProc::tsSetIJacobian(TS ts, PetscReal t, Vec u, Vec u_t,
3497 PetscReal a, Mat A, Mat B,
3498 void *ctx) {
3500 if (auto ptr = tsPrePostProc.lock()) {
3501 auto sub_u = ptr->getSubVector();
3502 auto sub_u_t = vectorDuplicate(sub_u);
3503 auto scatter = ptr->getScatter(sub_u, u, R);
3504
3505 auto apply_scatter_and_update = [&](auto x, auto sub_x) {
3507 CHKERR VecScatterBegin(scatter, x, sub_x, INSERT_VALUES, SCATTER_REVERSE);
3508 CHKERR VecScatterEnd(scatter, x, sub_x, INSERT_VALUES, SCATTER_REVERSE);
3509 CHKERR VecGhostUpdateBegin(sub_x, INSERT_VALUES, SCATTER_FORWARD);
3510 CHKERR VecGhostUpdateEnd(sub_x, INSERT_VALUES, SCATTER_FORWARD);
3512 };
3513
3514 CHKERR apply_scatter_and_update(u, sub_u);
3515 CHKERR apply_scatter_and_update(u_t, sub_u_t);
3516
3517 CHKERR TsSetIJacobian(ts, t, sub_u, sub_u_t, a, ptr->subB, ptr->subB,
3518 ptr->tsCtxPtr.get());
3519 }
3521}
3522
3523MoFEMErrorCode TSPrePostProc::tsMonitor(TS ts, PetscInt step, PetscReal t,
3524 Vec u, void *ctx) {
3526 if (auto ptr = tsPrePostProc.lock()) {
3527 auto get_norm = [&](auto x) {
3528 double nrm;
3529 CHKERR VecNorm(x, NORM_2, &nrm);
3530 return nrm;
3531 };
3532
3533 auto sub_u = ptr->getSubVector();
3534 auto scatter = ptr->getScatter(sub_u, u, R);
3535 CHKERR VecScatterBegin(scatter, u, sub_u, INSERT_VALUES, SCATTER_REVERSE);
3536 CHKERR VecScatterEnd(scatter, u, sub_u, INSERT_VALUES, SCATTER_REVERSE);
3537 CHKERR VecGhostUpdateBegin(sub_u, INSERT_VALUES, SCATTER_FORWARD);
3538 CHKERR VecGhostUpdateEnd(sub_u, INSERT_VALUES, SCATTER_FORWARD);
3539
3540 MOFEM_LOG("FS", Sev::verbose)
3541 << "u norm " << get_norm(u) << " u sub nom " << get_norm(sub_u);
3542
3543 CHKERR TsMonitorSet(ts, step, t, sub_u, ptr->tsCtxPtr.get());
3544 }
3546}
3547
3550 if (auto ptr = tsPrePostProc.lock()) {
3551 MOFEM_LOG("FS", Sev::verbose) << "SetUP sub PC";
3552 ptr->subKSP = createKSP(ptr->fsRawPtr->mField.get_comm());
3553 CHKERR KSPSetFromOptions(ptr->subKSP);
3554 }
3556};
3557
3558MoFEMErrorCode TSPrePostProc::pcApply(PC pc, Vec pc_f, Vec pc_x) {
3560 if (auto ptr = tsPrePostProc.lock()) {
3561 auto sub_x = ptr->getSubVector();
3562 auto sub_f = vectorDuplicate(sub_x);
3563 auto scatter = ptr->getScatter(sub_x, pc_x, R);
3564 CHKERR VecScatterBegin(scatter, pc_f, sub_f, INSERT_VALUES,
3565 SCATTER_REVERSE);
3566 CHKERR VecScatterEnd(scatter, pc_f, sub_f, INSERT_VALUES, SCATTER_REVERSE);
3567 CHKERR KSPSetOperators(ptr->subKSP, ptr->subB, ptr->subB);
3568 MOFEM_LOG("FS", Sev::verbose) << "PCShell solve";
3569 CHKERR KSPSolve(ptr->subKSP, sub_f, sub_x);
3570 MOFEM_LOG("FS", Sev::verbose) << "PCShell solve <- done";
3571 CHKERR VecScatterBegin(scatter, sub_x, pc_x, INSERT_VALUES,
3572 SCATTER_FORWARD);
3573 CHKERR VecScatterEnd(scatter, sub_x, pc_x, INSERT_VALUES, SCATTER_FORWARD);
3574 }
3576};
3577
3579 if (auto ptr = tsPrePostProc.lock()) {
3580 auto prb_ptr = ptr->fsRawPtr->mField.get_problem("SUB_SOLVER");
3581 if (auto sub_data = prb_ptr->getSubData()) {
3582 auto is = sub_data->getSmartColIs();
3583 VecScatter s;
3584 if (fr == R) {
3585 CHK_THROW_MESSAGE(VecScatterCreate(x, PETSC_NULLPTR, y, is, &s),
3586 "crate scatter");
3587 } else {
3588 CHK_THROW_MESSAGE(VecScatterCreate(x, is, y, PETSC_NULLPTR, &s),
3589 "crate scatter");
3590 }
3591 return SmartPetscObj<VecScatter>(s);
3592 }
3593 }
3596}
3597
3601
3603
3604 auto &m_field = fsRawPtr->mField;
3605 auto simple = m_field.getInterface<Simple>();
3606
3608
3609 auto dm = simple->getDM();
3610
3612 CHKERR TSSetIFunction(ts, globRes, tsSetIFunction, nullptr);
3613 CHKERR TSSetIJacobian(ts, PETSC_NULLPTR, PETSC_NULLPTR, tsSetIJacobian, nullptr);
3614 CHKERR TSMonitorSet(ts, tsMonitor, fsRawPtr, PETSC_NULLPTR);
3615
3616 SNES snes;
3617 CHKERR TSGetSNES(ts, &snes);
3618 auto snes_ctx_ptr = getDMSnesCtx(dm);
3619
3620 auto set_section_monitor = [&](auto snes) {
3622 CHKERR SNESMonitorSet(snes,
3623 (MoFEMErrorCode(*)(SNES, PetscInt, PetscReal,
3624 void *))MoFEMSNESMonitorFields,
3625 (void *)(snes_ctx_ptr.get()), nullptr);
3627 };
3628
3629 CHKERR set_section_monitor(snes);
3630
3631 auto ksp = createKSP(m_field.get_comm());
3632 CHKERR KSPSetType(ksp, KSPPREONLY); // Run KSP internally in ShellPC
3633 auto sub_pc = createPC(fsRawPtr->mField.get_comm());
3634 CHKERR PCSetType(sub_pc, PCSHELL);
3635 CHKERR PCShellSetSetUp(sub_pc, pcSetup);
3636 CHKERR PCShellSetApply(sub_pc, pcApply);
3637 CHKERR KSPSetPC(ksp, sub_pc);
3638 CHKERR SNESSetKSP(snes, ksp);
3639
3642
3643 CHKERR TSSetPreStep(ts, tsPreProc);
3644 CHKERR TSSetPostStep(ts, tsPostProc);
3645
3647}
3648
3649//! [Check]
3652 PetscInt test_nb = 0;
3653 PetscBool test_flg = PETSC_FALSE;
3654 double regression_value = 0.0;
3655 CHKERR PetscOptionsGetInt(PETSC_NULLPTR, "", "-test", &test_nb, &test_flg);
3656 CHKERR PetscOptionsGetScalar(PETSC_NULLPTR, "", "-regression_value", &regression_value, PETSC_NULLPTR);
3657
3658 if (test_flg) {
3659 auto simple = mField.getInterface<Simple>();
3660 auto T = createDMVector(simple->getDM());
3661 CHKERR DMoFEMMeshToLocalVector(simple->getDM(), T, INSERT_VALUES,
3662 SCATTER_FORWARD);
3663 double nrm2;
3664 CHKERR VecNorm(T, NORM_2, &nrm2);
3665 MOFEM_LOG("FS", Sev::verbose) << "Regression norm " << nrm2;
3666 switch (test_nb) {
3667 case 1:
3668 break;
3669 default:
3670 SETERRQ(PETSC_COMM_WORLD, MOFEM_ATOM_TEST_INVALID, "Wrong test number.");
3671 break;
3672 }
3673 if (fabs(nrm2 - regression_value) > 1e-2)
3674 SETERRQ(PETSC_COMM_WORLD, MOFEM_ATOM_TEST_INVALID,
3675 "Regression test field; wrong norm value. %6.4e != %6.4e", nrm2,
3676 regression_value);
3677 }
3679}
3680//! [Check]
Implements operators for assembling residuals and Jacobians for the Navier–Stokes–Cahn–Hilliard free-...
#define MOFEM_LOG_SEVERITY_SYNC(comm, severity)
Synchronise "SYNC" on curtain severity level.
#define MOFEM_TAG_AND_LOG(channel, severity, tag)
Tag and log in channel.
void simple(double P1[], double P2[], double P3[], double c[], const int N)
Definition acoustic.cpp:69
int main()
constexpr double a
ElementsAndOps< SPACE_DIM >::DomainParentEle DomainParentEle
ElementsAndOps< SPACE_DIM >::BoundaryParentEle BoundaryParentEle
constexpr int SPACE_DIM
ElementsAndOps< SPACE_DIM >::DomainEle DomainEle
ElementsAndOps< SPACE_DIM >::BoundaryEle BoundaryEle
BoundaryEle::UserDataOperator BoundaryEleOp
Kronecker Delta class symmetric.
@ QUIET
#define CATCH_ERRORS
Catch errors.
@ AINSWORTH_LEGENDRE_BASE
Ainsworth Cole (Legendre) approx. base .
Definition definitions.h:60
#define BITREFLEVEL_SIZE
max number of refinements
#define CHK_THROW_MESSAGE(err, msg)
Check and throw MoFEM exception.
@ 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_OPERATION_UNSUCCESSFUL
Definition definitions.h:34
@ MOFEM_ATOM_TEST_INVALID
Definition definitions.h:40
@ MOFEM_DATA_INCONSISTENCY
Definition definitions.h:31
@ MOFEM_INVALID_DATA
Definition definitions.h:36
@ MOFEM_NOT_IMPLEMENTED
Definition definitions.h:32
@ LAST_COORDINATE_SYSTEM
@ CYLINDRICAL
@ CARTESIAN
#define MoFEMFunctionReturn(a)
Last executable line of each PETSc function used for error handling. Replaces return()
#define CHKERR
Inline error check.
PostProcEleByDim< SPACE_DIM >::PostProcEleDomain PostProcEleDomain
auto cylindrical
[cylindrical]
double kappa
auto marker
Create marker bit reference level
auto bubble_device
Bubble device initialization.
double mu_diff
ElementsAndOps< SPACE_DIM >::DomainParentEle DomainParentEle
auto save_range
Save range of entities to file.
auto get_M2
Degenerate mobility function M₂
constexpr int U_FIELD_DIM
ElementsAndOps< SPACE_DIM >::BoundaryParentEle BoundaryParentEle
static boost::weak_ptr< TSPrePostProc > tsPrePostProc
FTensor::Index< 'j', SPACE_DIM > j
auto get_M2_dh
Derivative of degenerate mobility M₂
double tol
@ R
@ F
auto integration_rule
static char help[]
auto get_M_dh
auto kernel_eye
Eye-shaped interface initialization
double mu_m
double lambda
surface tension
FTensor::Index< 'k', SPACE_DIM > k
OpDomainMassH OpDomainMassG
auto get_M3_dh
Derivative of non-linear mobility M₃
FTensor::Index< 'i', SPACE_DIM > i
double rho_diff
auto init_h
Initialisation function.
constexpr auto t_kd
auto d_phase_function_h
Derivative of phase function with respect to h.
auto get_fe_bit
Get bit reference level from finite element.
constexpr int SPACE_DIM
double rho_ave
FTensor::Index< 'l', SPACE_DIM > l
int coord_type
double W
auto kernel_oscillation
Oscillating interface initialization.
double eta2
auto get_skin_parent_bit
auto get_M0
Constant mobility function M₀
auto get_f_dh
Derivative of double-well potential.
auto get_M3
Non-linear mobility function M₃
double rho_p
double mu_p
auto get_start_bit
FormsIntegrators< DomainEleOp >::Assembly< A >::LinearForm< I >::OpBaseTimesVector< BASE_DIM, SPACE_DIM, 1 > OpDomainAssembleVector
OpDomainMassH OpDomainMassP
FormsIntegrators< DomainEleOp >::Assembly< A >::BiLinearForm< I >::OpMass< BASE_DIM, 1 > OpDomainMassH
constexpr int BASE_DIM
int nb_levels
auto wetting_angle
Wetting angle function (placeholder)
double h
FormsIntegrators< DomainEleOp >::Assembly< A >::LinearForm< I >::OpBaseTimesScalar< BASE_DIM > OpDomainAssembleScalar
auto get_M
double mu_ave
double eps
auto get_dofs_ents_all
Get all entities with DOFs in the problem - used for debugging.
auto phase_function
Phase-dependent material property interpolation.
constexpr IntegrationType I
auto get_skin_projection_bit
auto get_global_size
Get global size across all processors.
auto get_dofs_ents_by_field_name
Get entities of DOFs by field name - used for debugging.
constexpr AssemblyType A
auto get_D
Create deviatoric stress tensor.
auto wetting_angle_sub_stepping
[cylindrical]
int refine_overlap
auto get_M0_dh
Derivative of constant mobility.
auto my_max
[wetting_angle_sub_stepping]
FormsIntegrators< BoundaryEleOp >::Assembly< A >::BiLinearForm< I >::OpMass< BASE_DIM, 1 > OpBoundaryMassL
auto cut_off
Phase field cutoff function.
auto get_skin_child_bit
int order
approximation order
SideEle::UserDataOperator SideOp
FormsIntegrators< DomainEleOp >::Assembly< A >::BiLinearForm< I >::OpMixScalarTimesDiv< SPACE_DIM, COORD_TYPE > OpMixScalarTimesDiv
FormsIntegrators< BoundaryEleOp >::Assembly< A >::LinearForm< I >::OpBaseTimesScalar< BASE_DIM > OpBoundaryAssembleScalar
double a0
double rho_m
auto get_current_bit
dofs bit used to do calculations
BoundaryEle::UserDataOperator BoundaryEleOp
double eta
auto bit
Create bit reference level.
double md
auto capillary_tube
Capillary tube initialization.
auto get_f
Double-well potential function.
auto get_projection_bit
auto d_cut_off
Derivative of cutoff function.
auto my_min
Minimum function with smooth transition
FormsIntegrators< DomainEleOp >::Assembly< A >::BiLinearForm< I >::OpMass< BASE_DIM, U_FIELD_DIM > OpDomainMassU
PetscErrorCode DMMoFEMCreateSubDM(DM subdm, DM dm, const char problem_name[])
Must be called by user to set Sub DM MoFEM data structures.
Definition DMMoFEM.cpp:215
PetscErrorCode DMMoFEMAddElement(DM dm, std::string fe_name)
add element to dm
Definition DMMoFEM.cpp:488
PetscErrorCode DMMoFEMSetSquareProblem(DM dm, PetscBool square_problem)
set squared problem
Definition DMMoFEM.cpp:450
PetscErrorCode DMMoFEMCreateMoFEM(DM dm, MoFEM::Interface *m_field_ptr, const char problem_name[], const MoFEM::BitRefLevel bit_level, const MoFEM::BitRefLevel bit_mask=MoFEM::BitRefLevel().set())
Must be called by user to set MoFEM data structures.
Definition DMMoFEM.cpp:114
PetscErrorCode DMoFEMPostProcessFiniteElements(DM dm, MoFEM::FEMethod *method)
execute finite element method for each element in dm (problem)
Definition DMMoFEM.cpp:546
PetscErrorCode DMMoFEMAddSubFieldRow(DM dm, const char field_name[])
Definition DMMoFEM.cpp:238
PetscErrorCode DMoFEMMeshToLocalVector(DM dm, Vec l, InsertMode mode, ScatterMode scatter_mode, RowColData rc=RowColData::COL)
set local (or ghosted) vector values on mesh for partition only
Definition DMMoFEM.cpp:514
PetscErrorCode DMRegister_MoFEM(const char sname[])
Register MoFEM problem.
Definition DMMoFEM.cpp:43
PetscErrorCode DMoFEMLoopFiniteElements(DM dm, const char fe_name[], MoFEM::FEMethod *method, CacheTupleWeakPtr cache_ptr=CacheTupleSharedPtr())
Executes FEMethod for finite elements in DM.
Definition DMMoFEM.cpp:576
auto createDMVector(DM dm, RowColData rc=RowColData::COL)
Get smart vector from DM.
Definition DMMoFEM.hpp:1237
PetscErrorCode DMoFEMGetInterfacePtr(DM dm, MoFEM::Interface **m_field_ptr)
Get pointer to MoFEM::Interface.
Definition DMMoFEM.cpp:410
PetscErrorCode DMMoFEMAddSubFieldCol(DM dm, const char field_name[])
Definition DMMoFEM.cpp:280
auto createDMMatrix(DM dm)
Get smart matrix from DM.
Definition DMMoFEM.hpp:1194
PetscErrorCode DMoFEMPreProcessFiniteElements(DM dm, MoFEM::FEMethod *method)
execute finite element method for each element in dm (problem)
Definition DMMoFEM.cpp:536
virtual const DofEntity_multiIndex * get_dofs() const =0
Get the dofs object.
MoFEMErrorCode getEntitiesByDimAndRefLevel(const BitRefLevel bit, const BitRefLevel mask, const int dim, const EntityHandle meshset, int verb=0) const
add all ents from ref level given by bit to meshset
MoFEMErrorCode getEntitiesByTypeAndRefLevel(const BitRefLevel bit, const BitRefLevel mask, const EntityType type, const EntityHandle meshset, int verb=0) const
add all ents from ref level given by bit to meshset
IntegrationType
Form integrator integration types.
AssemblyType
[Storage and set boundary conditions]
static LoggerType & setLog(const std::string channel)
Set ans resset chanel logger.
#define MOFEM_LOG(channel, severity)
Log.
SeverityLevel
Severity levels.
#define MOFEM_LOG_TAG(channel, tag)
Tag channel.
#define MOFEM_LOG_CHANNEL(channel)
Set and reset channel.
MoFEMErrorCode getMeshset(const int ms_id, const unsigned int cubit_bc_type, EntityHandle &meshset) const
get meshset from CUBIT Id and CUBIT type
MoFEMErrorCode buildSubProblem(const std::string out_name, const std::vector< std::string > &fields_row, const std::vector< std::string > &fields_col, const std::string main_problem, const bool square_matrix=true, const map< std::string, boost::shared_ptr< Range > > *entityMapRow=nullptr, const map< std::string, boost::shared_ptr< Range > > *entityMapCol=nullptr, int verb=VERBOSE)
build sub problem
auto test_bit_child
lambda function used to select elements on which finite element pipelines are executed.
auto bit
set bit
FTensor::Index< 'i', SPACE_DIM > i
const double c
speed of light (cm/ns)
double D
const double v
phase velocity of light in medium (cm/ns)
const double n
refractive index of diffusive medium
PetscErrorCode MoFEMErrorCode
MoFEM/PETSc error code.
std::bitset< BITREFLEVEL_SIZE > BitRefLevel
Bit structure attached to each entity identifying to what mesh entity is attached.
Definition Types.hpp:40
implementation of Data Operators for Forces and Sources
Definition Common.hpp:10
auto createKSP(MPI_Comm comm)
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:1046
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:169
PetscErrorCode TsMonitorSet(TS ts, PetscInt step, PetscReal t, Vec u, void *ctx)
Set monitor for TS solver.
Definition TsCtx.cpp:263
auto getDMTsCtx(DM dm)
Get TS context data structure used by DM.
Definition DMMoFEM.hpp:1279
PetscErrorCode DMMoFEMSetDestroyProblem(DM dm, PetscBool destroy_problem)
Definition DMMoFEM.cpp:434
PetscErrorCode PetscOptionsGetInt(PetscOptions *, const char pre[], const char name[], PetscInt *ivalue, PetscBool *set)
static const bool debug
MoFEMErrorCode MoFEMSNESMonitorFields(SNES snes, PetscInt its, PetscReal fgnorm, SnesCtx *ctx)
Sens monitor printing residual field by field.
Definition SnesCtx.cpp:600
PetscErrorCode PetscOptionsGetScalar(PetscOptions *, const char pre[], const char name[], PetscScalar *dval, PetscBool *set)
PetscErrorCode TsSetIFunction(TS ts, PetscReal t, Vec u, Vec u_t, Vec F, void *ctx)
Set IFunction for TS solver.
Definition TsCtx.cpp:56
SmartPetscObj< Vec > vectorDuplicate(Vec vec)
Create duplicate vector of smart vector.
auto createVecScatter(Vec x, IS ix, Vec y, IS iy)
Create a Vec Scatter object.
static PetscErrorCode solve(Mat mat, Vec x, Vec y)
Definition Schur.cpp:1322
auto createTS(MPI_Comm comm)
auto createPC(MPI_Comm comm)
static auto getFTensor0FromVec(ublas::vector< T, A > &data)
Get tensor rank 0 (scalar) form data vector.
OpCalculateScalarFieldValuesFromPetscVecImpl< PetscData::CTX_SET_X_T > OpCalculateScalarFieldValuesDot
PetscErrorCode PetscOptionsGetEList(PetscOptions *, const char pre[], const char name[], const char *const *list, PetscInt next, PetscInt *value, PetscBool *set)
PetscObject getPetscObject(T obj)
auto get_temp_meshset_ptr(moab::Interface &moab)
Create smart pointer to temporary meshset.
auto getDMSnesCtx(DM dm)
Get SNES context data structure used by DM.
Definition DMMoFEM.hpp:1265
auto createDM(MPI_Comm comm, const std::string dm_type_name)
Creates smart DM object.
auto getProblemPtr(DM dm)
get problem pointer from DM
Definition DMMoFEM.hpp:1182
double h
int save_every_nth_step
OpPostProcMapInMoab< SPACE_DIM, SPACE_DIM > OpPPMap
constexpr double t
plate stiffness
Definition plate.cpp:58
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
MoFEM::Interface & mField
Reference to MoFEM interface.
Definition plastic.cpp:226
Calculate lift force on free surface boundary.
Lhs for H dH (Jacobian block ∂R_H/∂H)
Lhs for U dG (Jacobian block ∂R_U/∂G)
Lhs for U dH (Jacobian block ∂R_U/∂H)
Rhs for G (chemical potential residual)
Rhs for H (phase-field residual)
[OpWettingAngleLhs]
Range findParentsToRefine(Range ents, BitRefLevel level, BitRefLevel mask)
Find parent entities that need refinement.
MoFEMErrorCode setupProblem()
Setup problem fields and parameters.
MoFEMErrorCode runProblem()
Main function to run the complete free surface simulation.
FreeSurface(MoFEM::Interface &m_field)
Constructor.
MoFEMErrorCode projectData()
Project solution data between mesh levels.
MoFEMErrorCode boundaryCondition()
Apply boundary conditions and initialize fields.
MoFEMErrorCode readMesh()
Read mesh from input file.
MoFEMErrorCode setParentDofs(boost::shared_ptr< FEMethod > fe_top, std::string field_name, ForcesAndSourcesCore::UserDataOperator::OpType op, BitRefLevel child_ent_bit, boost::function< boost::shared_ptr< ForcesAndSourcesCore >()> get_elem, int verbosity, LogManager::SeverityLevel sev)
Create hierarchy of elements run on parent levels.
std::vector< Range > findEntitiesCrossedByPhaseInterface(size_t overlap)
Find entities on refinement levels.
MoFEMErrorCode assembleSystem()
Assemble system operators and matrices.
MoFEMErrorCode checkResults()
Check results for correctness.
MoFEM::Interface & mField
MoFEMErrorCode refineMesh(size_t overlap)
Perform adaptive mesh refinement.
MoFEMErrorCode makeRefProblem()
Create refined problem for mesh adaptation.
MoFEMErrorCode solveSystem()
Solve the time-dependent free surface problem.
Add operators pushing bases from local to physical configuration.
boost::weak_ptr< CacheTuple > getCacheWeakPtr() const
Get the cache weak pointer object.
Boundary condition manager for finite element problem setup.
Managing BitRefLevels.
Managing BitRefLevels.
virtual int get_comm_size() const =0
virtual FieldBitNumber get_field_bit_number(const std::string name) const =0
get field bit number
virtual moab::Interface & get_moab()=0
virtual MPI_Comm & get_comm() const =0
virtual int get_comm_rank() const =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:118
base operator to do operations at Gauss Pt. level
Deprecated interface functions.
static UId getLoFieldEntityUId(const FieldBitNumber bit, const EntityHandle ent)
static UId getHiFieldEntityUId(const FieldBitNumber bit, const EntityHandle ent)
Data on single entity (This is passed as argument to DataOperator::doWork)
Structure for user loop methods on finite elements.
boost::shared_ptr< const NumeredEntFiniteElement > numeredEntFiniteElementPtr
Shared pointer to finite element database structure.
Basic algebra on fields.
Definition FieldBlas.hpp:21
MoFEMErrorCode fieldLambdaOnEntities(OneFieldFunctionOnEntities lambda, const std::string field_name, Range *ents_ptr=nullptr)
field lambda
Definition FieldBlas.cpp:50
static UId getHiBitNumberUId(const FieldBitNumber bit_number)
static UId getLoBitNumberUId(const FieldBitNumber bit_number)
OpType
Controls loop over entities on element.
@ OPSPACE
operator do Work is execute on space data
Section manager is used to create indexes and sections.
Definition ISManager.hpp:23
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.
Assembly methods.
Definition Natural.hpp:65
Operator to project base functions from parent entity to child.
Calculate divergence of vector field at integration points.
Get field gradients at integration pts for scalar field rank 0, i.e. vector field.
Specialization for double precision scalar field values calculation.
Get field gradients at integration pts for scalar field rank 0, i.e. vector field.
Approximate field values for given petsc vector.
Specialization for double precision vector field values calculation.
Element used to execute operators on side of the element.
Post post-proc data at points from hash maps.
std::map< std::string, boost::shared_ptr< MatrixDouble > > DataMapMat
std::map< std::string, boost::shared_ptr< VectorDouble > > DataMapVec
Operator to execute finite element instance on parent element. This operator is typically used to pro...
Template struct for dimension-specific finite element types.
PipelineManager interface.
Problem manager is used to build and partition problems.
Simple interface for fast problem set-up.
Definition Simple.hpp:27
MoFEMErrorCode getDM(DM *dm)
Get DM.
Definition Simple.cpp:799
bool & getParentAdjacencies()
Get the addParentAdjacencies flag.
Definition Simple.hpp:555
intrusive_ptr for managing petsc objects
PetscReal ts_t
Current time value.
PetscInt ts_step
Current time step number.
MoFEMErrorCode getInterface(IFACE *&iface) const
Get interface reference to pointer of interface.
[Push operators to pipeline]
boost::shared_ptr< moab::Core > postProcMesh
SmartPetscObj< DM > dM
MoFEMErrorCode postProcess()
Post-processing function executed at loop completion.
Monitor(SmartPetscObj< DM > dm, boost::shared_ptr< moab::Core > post_proc_mesh, boost::shared_ptr< PostProcEleDomainCont > post_proc, boost::shared_ptr< PostProcEleBdyCont > post_proc_edge, std::pair< boost::shared_ptr< BoundaryEle >, boost::shared_ptr< VectorDouble > > p)
boost::shared_ptr< VectorDouble > liftVec
boost::shared_ptr< BoundaryEle > liftFE
boost::shared_ptr< PostProcEle > postProc
boost::shared_ptr< PostProcEleBdyCont > postProcEdge
boost::shared_ptr< PostProcEleDomainCont > postProc
Set of functions called by PETSc solver used to refine and update mesh.
boost::shared_ptr< TsCtx > tsCtxPtr
virtual ~TSPrePostProc()=default
SmartPetscObj< Vec > globRes
SmartPetscObj< Mat > subB
SmartPetscObj< Vec > globSol
SmartPetscObj< Vec > getSubVector()
Create sub-problem vector.
static MoFEMErrorCode tsMonitor(TS ts, PetscInt step, PetscReal t, Vec u, void *ctx)
Monitor solution during time stepping.
static MoFEMErrorCode pcApply(PC pc, Vec pc_f, Vec pc_x)
Apply preconditioner.
SmartPetscObj< VecScatter > getScatter(Vec x, Vec y, enum FR fr)
Get scatter context for vector operations.
SmartPetscObj< DM > solverSubDM
static MoFEMErrorCode tsSetIJacobian(TS ts, PetscReal t, Vec u, Vec u_t, PetscReal a, Mat A, Mat B, void *ctx)
Set implicit Jacobian for time stepping.
FreeSurface * fsRawPtr
boost::shared_ptr< SnesCtx > snesCtxPtr
static MoFEMErrorCode tsPreProc(TS ts)
Pre process time step.
TSPrePostProc()=default
static MoFEMErrorCode tsSetIFunction(TS ts, PetscReal t, Vec u, Vec u_t, Vec f, void *ctx)
Set implicit function for time stepping.
static MoFEMErrorCode pcSetup(PC pc)
Setup preconditioner.
static MoFEMErrorCode tsPostProc(TS ts)
Post process time step.
static MoFEMErrorCode tsPreStage(TS ts)
Pre-stage processing for time stepping.
MoFEMErrorCode tsSetUp(TS ts)
Used to setup TS solver.
SmartPetscObj< KSP > subKSP
constexpr CoordinateTypes COORD_TYPE
static boost::weak_ptr< TSPrePostProc > tsPrePostProc
ElementsAndOps< SPACE_DIM >::SideEle SideEle
Definition plastic.cpp:61
constexpr int SPACE_DIM