v0.16.0
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 */
271[[maybe_unused]] auto 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 */
342[[maybe_unused]] auto 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 */
351[[maybe_unused]] auto 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 */
360[[maybe_unused]] auto 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 */
374[[maybe_unused]] auto 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 */
414[[maybe_unused]] auto 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 */
433[[maybe_unused]] auto 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 */
450[[maybe_unused]] auto 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 */
465[[maybe_unused]] auto 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 */
500[[maybe_unused]] auto wetting_angle = [](double water_level) {
501 return water_level;
502};
503
504/**
505 * @brief Create bit reference level
506 * @param b Bit number to set
507 * @return BitRefLevel with specified bit set
508 */
509auto bit = [](auto b) { return BitRefLevel().set(b); };
510
511/**
512 * @brief Create marker bit reference level
513 * @param b Bit number from end
514 * @return BitRefLevel with bit set from end
515 */
516[[maybe_unused]] auto marker = [](auto b) {
517 return BitRefLevel().set(BITREFLEVEL_SIZE - b);
518};
519
520/**
521 * @brief Get bit reference level from finite element
522 * @param fe_ptr Pointer to finite element method
523 * @return Current bit reference level of the element
524 */
525auto get_fe_bit = [](FEMethod *fe_ptr) {
526 return fe_ptr->numeredEntFiniteElementPtr->getBitRefLevel();
527};
528
529/**
530 * @brief Get global size across all processors
531 * @param l_size Local size on current processor
532 * @return Global sum of sizes across all processors
533 */
534auto get_global_size = [](int l_size) {
535 int g_size;
536 MPI_Allreduce(&l_size, &g_size, 1, MPI_INT, MPI_SUM, MPI_COMM_WORLD);
537 return g_size;
538};
539
540/**
541 * @brief Save range of entities to file
542 * @param moab MOAB interface for mesh operations
543 * @param name Output filename
544 * @param r Range of entities to save
545 * @return MoFEMErrorCode Success/failure code
546 *
547 * Saves entities to HDF5 file if range is non-empty globally
548 */
549auto save_range = [](moab::Interface &moab, const std::string name,
550 const Range r) {
552 if (get_global_size(r.size())) {
553 auto out_meshset = get_temp_meshset_ptr(moab);
554 CHKERR moab.add_entities(*out_meshset, r);
555 CHKERR moab.write_file(name.c_str(), "MOAB", "PARALLEL=WRITE_PART",
556 out_meshset->get_ptr(), 1);
557 }
559};
560
561/**
562 * @brief Get entities of DOFs by field name - used for debugging
563 * @param dm PETSc DM object containing the problem
564 * @param field_name Name of field to extract entities for
565 * @return Range of entities containing DOFs for specified field
566 *
567 * Extracts all mesh entities that have degrees of freedom for the
568 * specified field. Useful for debugging and visualization of field
569 * distributions across the mesh.
570 */
571[[maybe_unused]] auto get_dofs_ents_by_field_name = [](auto dm,
572 auto field_name) {
573 auto prb_ptr = getProblemPtr(dm);
574 std::vector<EntityHandle> ents_vec;
575
576 MoFEM::Interface *m_field_ptr;
577 CHKERR DMoFEMGetInterfacePtr(dm, &m_field_ptr);
578
579 auto bit_number = m_field_ptr->get_field_bit_number(field_name);
580 auto dofs = prb_ptr->numeredRowDofsPtr;
581 auto lo_it = dofs->lower_bound(FieldEntity::getLoBitNumberUId(bit_number));
582 auto hi_it = dofs->upper_bound(FieldEntity::getHiBitNumberUId(bit_number));
583 ents_vec.reserve(std::distance(lo_it, hi_it));
584
585 for (; lo_it != hi_it; ++lo_it) {
586 ents_vec.push_back((*lo_it)->getEnt());
587 }
588
589 std::sort(ents_vec.begin(), ents_vec.end());
590 auto it = std::unique(ents_vec.begin(), ents_vec.end());
591 Range r;
592 r.insert_list(ents_vec.begin(), it);
593 return r;
594};
595
596/**
597 * @brief Get all entities with DOFs in the problem - used for debugging
598 * @param dm PETSc DM object containing the problem
599 * @return Range of all entities containing any DOFs
600 *
601 * Extracts all mesh entities that have degrees of freedom for any field.
602 * Useful for debugging mesh partitioning and DOF distribution.
603 */
604auto get_dofs_ents_all = [](auto dm) {
605 auto prb_ptr = getProblemPtr(dm);
606 std::vector<EntityHandle> ents_vec;
607
608 MoFEM::Interface *m_field_ptr;
609 CHKERR DMoFEMGetInterfacePtr(dm, &m_field_ptr);
610
611 auto dofs = prb_ptr->numeredRowDofsPtr;
612 ents_vec.reserve(dofs->size());
613
614 for (auto d : *dofs) {
615 ents_vec.push_back(d->getEnt());
616 }
617
618 std::sort(ents_vec.begin(), ents_vec.end());
619 auto it = std::unique(ents_vec.begin(), ents_vec.end());
620 Range r;
621 r.insert_list(ents_vec.begin(), it);
622 return r;
623};
624
625#include <FreeSurfaceOps.hpp>
626using namespace FreeSurfaceOps;
627
628struct FreeSurface;
629
630enum FR { F, R }; // F - forward, and reverse
631
632/**
633 * @brief Set of functions called by PETSc solver used to refine and update
634 * mesh.
635 *
636 * @note Currently theta method is only handled by this code.
637 *
638 */
639struct TSPrePostProc {
640
641 TSPrePostProc() = default;
642 virtual ~TSPrePostProc() = default;
643
644 /**
645 * @brief Used to setup TS solver
646 *
647 * @param ts PETSc time stepping object to configure
648 * @return MoFEMErrorCode Success/failure code
649 *
650 * Sets up time stepping solver with custom preconditioner, monitors,
651 * and callback functions for mesh refinement and solution projection
652 */
654
655 /**
656 * @brief Get scatter context for vector operations
657 *
658 * @param x Local sub-vector
659 * @param y Global vector
660 * @param fr Direction flag (F=forward, R=reverse)
661 * @return SmartPetscObj<VecScatter> Scatter context for vector operations
662 *
663 * Creates scatter context to transfer data between global and sub-problem vectors
664 */
665 SmartPetscObj<VecScatter> getScatter(Vec x, Vec y, enum FR fr);
666
667 /**
668 * @brief Create sub-problem vector
669 *
670 * @return SmartPetscObj<Vec> Vector compatible with sub-problem DM
671 *
672 * Creates a vector with proper size and ghost structure for sub-problem
673 */
675
679
680private:
681 /**
682 * @brief Pre process time step
683 *
684 * Refine mesh and update fields before each time step
685 *
686 * @param ts PETSc time stepping object
687 * @return MoFEMErrorCode Success/failure code
688 *
689 * This function is called before each time step to:
690 * - Apply phase field cutoff constraints
691 * - Refine mesh based on interface location
692 * - Project solution data to new mesh
693 * - Update solver operators
694 */
695 static MoFEMErrorCode tsPreProc(TS ts);
696
697 /**
698 * @brief Post process time step
699 *
700 * Currently this function does not make anything major
701 *
702 * @param ts PETSc time stepping object
703 * @return MoFEMErrorCode Success/failure code
704 *
705 * Called after each time step completion for cleanup operations
706 */
707 static MoFEMErrorCode tsPostProc(TS ts);
708
709 /**
710 * @brief Pre-stage processing for time stepping
711 *
712 * @param ts PETSc time stepping object
713 * @return MoFEMErrorCode Success/failure code
714 */
716
717 /**
718 * @brief Set implicit function for time stepping
719 *
720 * @param ts PETSc time stepping object
721 * @param t Current time
722 * @param u Solution vector at current time
723 * @param u_t Time derivative of solution
724 * @param f Output residual vector F(t,u,u_t)
725 * @param ctx User context (unused)
726 * @return MoFEMErrorCode Success/failure code
727 *
728 * Wrapper that scatters global vectors to sub-problem and evaluates residual
729 */
730 static MoFEMErrorCode tsSetIFunction(TS ts, PetscReal t, Vec u, Vec u_t,
731 Vec f,
732 void *ctx); //< Wrapper for SNES Rhs
733
734 /**
735 * @brief Set implicit Jacobian for time stepping
736 *
737 * @param ts PETSc time stepping object
738 * @param t Current time
739 * @param u Solution vector at current time
740 * @param u_t Time derivative of solution
741 * @param a Shift parameter for implicit methods
742 * @param A Jacobian matrix (input)
743 * @param B Jacobian matrix (output, often same as A)
744 * @param ctx User context (unused)
745 * @return MoFEMErrorCode Success/failure code
746 *
747 * Wrapper that assembles Jacobian matrix for sub-problem
748 */
749 static MoFEMErrorCode tsSetIJacobian(TS ts, PetscReal t, Vec u, Vec u_t,
750 PetscReal a, Mat A, Mat B,
751 void *ctx); ///< Wrapper for SNES Lhs
752
753 /**
754 * @brief Monitor solution during time stepping
755 *
756 * @param ts PETSc time stepping object
757 * @param step Current time step number
758 * @param t Current time value
759 * @param u Current solution vector
760 * @param ctx User context (pointer to FreeSurface)
761 * @return MoFEMErrorCode Success/failure code
762 *
763 * Called after each time step to monitor solution and save output
764 */
765 static MoFEMErrorCode tsMonitor(TS ts, PetscInt step, PetscReal t, Vec u,
766 void *ctx); ///< Wrapper for TS monitor
767
768 /**
769 * @brief Setup preconditioner
770 *
771 * @param pc PETSc preconditioner object
772 * @return MoFEMErrorCode Success/failure code
773 *
774 * Initializes KSP solver for shell preconditioner
775 */
776 static MoFEMErrorCode pcSetup(PC pc);
777
778 /**
779 * @brief Apply preconditioner
780 *
781 * @param pc PETSc preconditioner object
782 * @param pc_f Input vector (right-hand side)
783 * @param pc_x Output vector (preconditioned solution)
784 * @return MoFEMErrorCode Success/failure code
785 *
786 * Applies preconditioner by solving sub-problem with KSP
787 */
788 static MoFEMErrorCode pcApply(PC pc, Vec pc_f, Vec pc_x);
789
790 SmartPetscObj<Vec> globRes; //< global residual
791 SmartPetscObj<Mat> subB; //< sub problem tangent matrix
792 SmartPetscObj<KSP> subKSP; //< sub problem KSP solver
793
794 boost::shared_ptr<SnesCtx>
795 snesCtxPtr; //< internal data (context) for MoFEM SNES functions
796 boost::shared_ptr<TsCtx>
797 tsCtxPtr; //< internal data (context) for MoFEM TS functions.
798};
799
800static boost::weak_ptr<TSPrePostProc> tsPrePostProc;
801
803
804 /**
805 * @brief Constructor
806 *
807 * @param m_field Reference to MoFEM interface for finite element operations
808 */
809 FreeSurface(MoFEM::Interface &m_field) : mField(m_field) {}
810
811 /**
812 * @brief Main function to run the complete free surface simulation
813 *
814 * @return MoFEMErrorCode Success/failure code
815 *
816 * Executes the complete simulation workflow:
817 * 1. Read mesh from file
818 * 2. Setup problem (fields, approximation orders, parameters)
819 * 3. Apply boundary conditions and initialize fields
820 * 4. Assemble system matrices and operators
821 * 5. Solve time-dependent problem
822 */
824
825 /**
826 * @brief Create refined problem for mesh adaptation
827 *
828 * @return MoFEMErrorCode Success/failure code
829 *
830 * Creates mesh refinement data structures needed for adaptive meshing
831 */
833
835
836private:
837 /**
838 * @brief Read mesh from input file
839 *
840 * @return MoFEMErrorCode Success/failure code
841 *
842 * Loads mesh using Simple interface and sets up parent adjacencies
843 * for hierarchical mesh refinement
844 */
846
847 /**
848 * @brief Setup problem fields and parameters
849 *
850 * @return MoFEMErrorCode Success/failure code
851 *
852 * Creates finite element fields:
853 * - U: Velocity field (H1, vector)
854 * - P: Pressure field (H1, scalar)
855 * - H: Phase/order field (H1, scalar)
856 * - G: Chemical potential (H1, scalar)
857 * - L: Lagrange multiplier for boundary conditions (H1, scalar)
858 *
859 * Sets approximation orders and reads physical parameters from command line
860 */
862
863 /**
864 * @brief Apply boundary conditions and initialize fields
865 *
866 * @return MoFEMErrorCode Success/failure code
867 *
868 * - Initializes phase field using analytical or Python functions
869 * - Solves initialization problem for consistent initial conditions
870 * - Performs mesh refinement based on interface location
871 * - Removes DOFs for boundary conditions (symmetry, fixed, etc.)
872 */
874
875 /**
876 * @brief Project solution data between mesh levels
877 *
878 * @return MoFEMErrorCode Success/failure code
879 *
880 * Projects field data from coarse to fine mesh levels during refinement.
881 * Handles both time stepping vectors (for theta method) and regular fields.
882 * Uses L2 projection with mass matrix assembly.
883 */
885
886 /**
887 * @brief Assemble system operators and matrices
888 *
889 * @return MoFEMErrorCode Success/failure code
890 *
891 * Sets up finite element operators for:
892 * - Domain integration (momentum, phase field, mass conservation)
893 * - Boundary integration (surface tension, wetting angle, Lagrange multipliers)
894 * - Parent-child mesh hierarchies for adaptive refinement
895 */
897
898 /**
899 * @brief Solve the time-dependent free surface problem
900 *
901 * @return MoFEMErrorCode Success/failure code
902 *
903 * Creates and configures time stepping solver with:
904 * - Sub-problem DM for refined mesh
905 * - Post-processing for output visualization
906 * - Custom preconditioner and monitors
907 * - TSTheta implicit time integration
908 */
910
911 /**
912 * @brief Find entities on refinement levels
913 * @param overlap Level of overlap around phase interface
914 * @return Vector of entity ranges for each refinement level
915 *
916 * Identifies mesh entities that cross the phase interface by analyzing
917 * phase field values at vertices. Returns entities that need refinement
918 * at each hierarchical level with specified overlap.
919 */
920 std::vector<Range> findEntitiesCrossedByPhaseInterface(size_t overlap);
921
922 /**
923 * @brief Find parent entities that need refinement
924 * @param ents Child entities requiring refinement
925 * @param level Bit level for entity marking
926 * @param mask Bit mask for filtering
927 * @return Range of parent entities to refine
928 *
929 * Traverses mesh hierarchy to find parent entities that should be
930 * refined to accommodate interface tracking requirements.
931 */
933
934 /**
935 * @brief Perform adaptive mesh refinement
936 * @param overlap Number of element layers around interface to refine
937 * @return MoFEMErrorCode Success/failure code
938 *
939 * Performs hierarchical mesh refinement around the phase interface:
940 * - Identifies entities crossing interface
941 * - Creates refinement hierarchy
942 * - Sets up skin elements between levels
943 * - Updates bit level markings for computation
944 */
945 MoFEMErrorCode refineMesh(size_t overlap);
946
947 /**
948 * @brief Create hierarchy of elements run on parent levels
949 * @param fe_top Pipeline element to which hierarchy is attached
950 * @param field_name Name of field for DOF extraction ("" for all fields)
951 * @param op Type of operator OPSPACE, OPROW, OPCOL or OPROWCOL
952 * @param child_ent_bit Bit level marking child entities
953 * @param get_elem Lambda function to create element on hierarchy
954 * @param verbosity Verbosity level for debugging output
955 * @param sev Severity level for logging
956 * @return MoFEMErrorCode Success/failure code
957 *
958 * Sets up parent-child relationships for hierarchical mesh refinement.
959 * Allows access to field data from parent mesh levels during computation
960 * on refined child meshes. Essential for projection and interpolation.
961 */
963 boost::shared_ptr<FEMethod> fe_top, std::string field_name,
965 BitRefLevel child_ent_bit,
966 boost::function<boost::shared_ptr<ForcesAndSourcesCore>()> get_elem,
967 int verbosity, LogManager::SeverityLevel sev);
968
969 friend struct TSPrePostProc;
970
971 /**
972 * @brief Check results for correctness
973 *
974 * @return MoFEMErrorCode Success/failure code
975 */
977};
978
979//! [Run programme]
990//! [Run programme]
991
992//! [Read mesh]
995 MOFEM_LOG("FS", Sev::inform) << "Read mesh for problem";
997
999 simple->getBitRefLevel() = BitRefLevel();
1000
1001 CHKERR simple->getOptions();
1002 CHKERR simple->loadFile();
1003
1005}
1006//! [Read mesh]
1007
1008//! [Set up problem]
1011
1012 CHKERR PetscOptionsGetInt(PETSC_NULLPTR, "", "-order", &order, PETSC_NULLPTR);
1013 CHKERR PetscOptionsGetInt(PETSC_NULLPTR, "", "-nb_levels", &nb_levels,
1014 PETSC_NULLPTR);
1015 CHKERR PetscOptionsGetInt(PETSC_NULLPTR, "", "-refine_overlap", &refine_overlap,
1016 PETSC_NULLPTR);
1017
1018 const char *coord_type_names[] = {"cartesian", "polar", "cylindrical",
1019 "spherical"};
1020 CHKERR PetscOptionsGetEList(PETSC_NULLPTR, NULL, "-coords", coord_type_names,
1021 LAST_COORDINATE_SYSTEM, &coord_type, PETSC_NULLPTR);
1022
1023 MOFEM_LOG("FS", Sev::inform) << "Approximation order = " << order;
1024 MOFEM_LOG("FS", Sev::inform)
1025 << "Number of refinement levels nb_levels = " << nb_levels;
1026 nb_levels += 1;
1027
1028 auto simple = mField.getInterface<Simple>();
1029
1030 CHKERR PetscOptionsGetScalar(PETSC_NULLPTR, "", "-a0", &a0, PETSC_NULLPTR); // Acceleration
1031 CHKERR PetscOptionsGetScalar(PETSC_NULLPTR, "", "-rho_m", &rho_m, PETSC_NULLPTR); // Density minus phase
1032 CHKERR PetscOptionsGetScalar(PETSC_NULLPTR, "", "-mu_m", &mu_m, PETSC_NULLPTR); // Viscosity minus phase
1033 CHKERR PetscOptionsGetScalar(PETSC_NULLPTR, "", "-rho_p", &rho_p, PETSC_NULLPTR); // Density plus phase
1034 CHKERR PetscOptionsGetScalar(PETSC_NULLPTR, "", "-mu_p", &mu_p, PETSC_NULLPTR); // Viscosity plus phase
1035 CHKERR PetscOptionsGetScalar(PETSC_NULLPTR, "", "-lambda", &lambda, PETSC_NULLPTR); // Surface tension
1036 CHKERR PetscOptionsGetScalar(PETSC_NULLPTR, "", "-W", &W, PETSC_NULLPTR); // Height of the well in energy functional
1037
1038 rho_ave = (rho_p + rho_m) / 2;
1039 rho_diff = (rho_p - rho_m) / 2;
1040 mu_ave = (mu_p + mu_m) / 2;
1041 mu_diff = (mu_p - mu_m) / 2;
1042
1043 CHKERR PetscOptionsGetScalar(PETSC_NULLPTR, "", "-h", &h, PETSC_NULLPTR);
1044 eta = h;
1045 eta2 = eta * eta;
1046 kappa = (3. / (4. * std::sqrt(2. * W))) * (lambda / eta);
1047
1048 CHKERR PetscOptionsGetScalar(PETSC_NULLPTR, "", "-md", &md, PETSC_NULLPTR);
1049
1050 MOFEM_LOG("FS", Sev::inform) << "Acceleration a0 = " << a0;
1051 MOFEM_LOG("FS", Sev::inform) << "\"Minus\" phase density rho_m = " << rho_m;
1052 MOFEM_LOG("FS", Sev::inform) << "\"Minus\" phase viscosity mu_m = " << mu_m;
1053 MOFEM_LOG("FS", Sev::inform) << "\"Plus\" phase density rho_p = " << rho_p;
1054 MOFEM_LOG("FS", Sev::inform) << "\"Plus\" phase viscosity mu_p = " << mu_p;
1055 MOFEM_LOG("FS", Sev::inform) << "Surface tension lambda = " << lambda;
1056 MOFEM_LOG("FS", Sev::inform)
1057 << "Height of the well in energy functional W = " << W;
1058 MOFEM_LOG("FS", Sev::inform) << "Characteristic mesh size h = " << h;
1059 MOFEM_LOG("FS", Sev::inform) << "Mobility md = " << md;
1060
1061 MOFEM_LOG("FS", Sev::inform) << "Average density rho_ave = " << rho_ave;
1062 MOFEM_LOG("FS", Sev::inform) << "Difference density rho_diff = " << rho_diff;
1063 MOFEM_LOG("FS", Sev::inform) << "Average viscosity mu_ave = " << mu_ave;
1064 MOFEM_LOG("FS", Sev::inform) << "Difference viscosity mu_diff = " << mu_diff;
1065 MOFEM_LOG("FS", Sev::inform) << "kappa = " << kappa;
1066
1067
1068 // Fields on domain
1069
1070 // Velocity field
1071 CHKERR simple->addDomainField("U", H1, AINSWORTH_LEGENDRE_BASE, U_FIELD_DIM);
1072 // Pressure field
1073 CHKERR simple->addDomainField("P", H1, AINSWORTH_LEGENDRE_BASE, 1);
1074 // Order/phase fild
1075 CHKERR simple->addDomainField("H", H1, AINSWORTH_LEGENDRE_BASE, 1);
1076 // Chemical potential
1077 CHKERR simple->addDomainField("G", H1, AINSWORTH_LEGENDRE_BASE, 1);
1078
1079 // Field on boundary
1080 CHKERR simple->addBoundaryField("U", H1, AINSWORTH_LEGENDRE_BASE,
1081 U_FIELD_DIM);
1082 CHKERR simple->addBoundaryField("H", H1, AINSWORTH_LEGENDRE_BASE, 1);
1083 CHKERR simple->addBoundaryField("G", H1, AINSWORTH_LEGENDRE_BASE, 1);
1084 // Lagrange multiplier which constrains slip conditions
1085 CHKERR simple->addBoundaryField("L", H1, AINSWORTH_LEGENDRE_BASE, 1);
1086
1087 CHKERR simple->setFieldOrder("U", order);
1088 CHKERR simple->setFieldOrder("P", order - 1);
1089 CHKERR simple->setFieldOrder("H", order);
1090 CHKERR simple->setFieldOrder("G", order);
1091 CHKERR simple->setFieldOrder("L", order);
1092
1093 // Initialise bit ref levels
1094 auto set_problem_bit = [&]() {
1096 // Set bits to build adjacencies between parents and children. That is
1097 // used by simple interface.
1098 simple->getBitAdjEnt() = BitRefLevel().set();
1099 simple->getBitAdjParent() = BitRefLevel().set();
1100 simple->getBitRefLevel() = BitRefLevel().set();
1101 simple->getBitRefLevelMask() = BitRefLevel().set();
1103 };
1104
1105 CHKERR set_problem_bit();
1106
1107 CHKERR simple->setUp();
1108
1110}
1111//! [Set up problem]
1112
1113//! [Boundary condition]
1116
1117#ifdef PYTHON_INIT_SURFACE
1118 auto get_py_surface_init = []() {
1119 auto py_surf_init = boost::make_shared<SurfacePython>();
1120 CHKERR py_surf_init->surfaceInit("surface.py");
1121 surfacePythonWeakPtr = py_surf_init;
1122 return py_surf_init;
1123 };
1124 auto py_surf_init = get_py_surface_init();
1125#endif
1126
1127 auto simple = mField.getInterface<Simple>();
1128 auto pip_mng = mField.getInterface<PipelineManager>();
1129 auto bc_mng = mField.getInterface<BcManager>();
1130 auto bit_mng = mField.getInterface<BitRefManager>();
1131 auto dm = simple->getDM();
1132
1134
1135 auto reset_bits = [&]() {
1137 BitRefLevel start_mask;
1138 for (auto s = 0; s != get_start_bit(); ++s)
1139 start_mask[s] = true;
1140 // reset bit ref levels
1141 CHKERR bit_mng->lambdaBitRefLevel(
1142 [&](EntityHandle ent, BitRefLevel &bit) { bit &= start_mask; });
1143 Range level0;
1144 CHKERR bit_mng->getEntitiesByRefLevel(bit(0), BitRefLevel().set(), level0);
1145 CHKERR bit_mng->setNthBitRefLevel(level0, get_current_bit(), true);
1146 CHKERR bit_mng->setNthBitRefLevel(level0, get_projection_bit(), true);
1148 };
1149
1150 auto add_parent_field = [&](auto fe, auto op, auto field) {
1151 return setParentDofs(
1152 fe, field, op, bit(get_skin_parent_bit()),
1153
1154 [&]() {
1155 boost::shared_ptr<ForcesAndSourcesCore> fe_parent(
1156 new DomainParentEle(mField));
1157 return fe_parent;
1158 },
1159
1160 QUIET, Sev::noisy);
1161 };
1162
1163 auto h_ptr = boost::make_shared<VectorDouble>();
1164 auto grad_h_ptr = boost::make_shared<MatrixDouble>();
1165 auto g_ptr = boost::make_shared<VectorDouble>();
1166 auto grad_g_ptr = boost::make_shared<MatrixDouble>();
1167
1168 auto set_generic = [&](auto fe) {
1170 auto &pip = fe->getOpPtrVector();
1171
1173
1175 fe, "", UDO::OPSPACE, bit(get_skin_parent_bit()),
1176
1177 [&]() {
1178 boost::shared_ptr<ForcesAndSourcesCore> fe_parent(
1179 new DomainParentEle(mField));
1181 fe_parent->getOpPtrVector(), {H1});
1182 return fe_parent;
1183 },
1184
1185 QUIET, Sev::noisy);
1186
1187 CHKERR add_parent_field(fe, UDO::OPCOL, "H");
1188 pip.push_back(new OpCalculateScalarFieldValues("H", h_ptr));
1189 pip.push_back(
1190 new OpCalculateScalarFieldGradient<SPACE_DIM>("H", grad_h_ptr));
1191
1192 CHKERR add_parent_field(fe, UDO::OPCOL, "G");
1193 pip.push_back(new OpCalculateScalarFieldValues("G", g_ptr));
1194 pip.push_back(
1195 new OpCalculateScalarFieldGradient<SPACE_DIM>("G", grad_g_ptr));
1196
1198 };
1199
1200 auto post_proc = [&](auto exe_test) {
1202 auto post_proc_fe = boost::make_shared<PostProcEleDomain>(mField);
1203 post_proc_fe->exeTestHook = exe_test;
1204
1205 CHKERR set_generic(post_proc_fe);
1206
1208
1209 post_proc_fe->getOpPtrVector().push_back(
1210
1211 new OpPPMap(post_proc_fe->getPostProcMesh(),
1212 post_proc_fe->getMapGaussPts(),
1213
1214 {{"H", h_ptr}, {"G", g_ptr}},
1215
1216 {{"GRAD_H", grad_h_ptr}, {"GRAD_G", grad_g_ptr}},
1217
1218 {},
1219
1220 {}
1221
1222 )
1223
1224 );
1225
1226 CHKERR DMoFEMLoopFiniteElements(dm, "dFE", post_proc_fe);
1227 CHKERR post_proc_fe->writeFile("out_init.h5m");
1228
1230 };
1231
1232 auto solve_init = [&](auto exe_test) {
1234
1235 pip_mng->getOpDomainRhsPipeline().clear();
1236 pip_mng->getOpDomainLhsPipeline().clear();
1237
1238 auto set_domain_rhs = [&](auto fe) {
1240 CHKERR set_generic(fe);
1241 auto &pip = fe->getOpPtrVector();
1242
1243 CHKERR add_parent_field(fe, UDO::OPROW, "H");
1244 pip.push_back(new OpRhsH<true>("H", nullptr, nullptr, h_ptr, grad_h_ptr,
1245 grad_g_ptr));
1246 CHKERR add_parent_field(fe, UDO::OPROW, "G");
1247 pip.push_back(new OpRhsG<true>("G", h_ptr, grad_h_ptr, g_ptr));
1249 };
1250
1251 auto set_domain_lhs = [&](auto fe) {
1253
1254 CHKERR set_generic(fe);
1255 auto &pip = fe->getOpPtrVector();
1256
1257 CHKERR add_parent_field(fe, UDO::OPROW, "H");
1258 CHKERR add_parent_field(fe, UDO::OPCOL, "H");
1259 pip.push_back(new OpLhsH_dH<true>("H", nullptr, h_ptr, grad_g_ptr));
1260
1261 CHKERR add_parent_field(fe, UDO::OPCOL, "G");
1262 pip.push_back(new OpLhsH_dG<true>("H", "G", h_ptr));
1263
1264 CHKERR add_parent_field(fe, UDO::OPROW, "G");
1265 pip.push_back(new OpLhsG_dG("G"));
1266
1267 CHKERR add_parent_field(fe, UDO::OPCOL, "H");
1268 pip.push_back(new OpLhsG_dH<true>("G", "H", h_ptr));
1270 };
1271
1272 auto create_subdm = [&]() {
1273 auto level_ents_ptr = boost::make_shared<Range>();
1274 CHKERR mField.getInterface<BitRefManager>()->getEntitiesByRefLevel(
1275 bit(get_current_bit()), BitRefLevel().set(), *level_ents_ptr);
1276
1277 DM subdm;
1278 CHKERR DMCreate(mField.get_comm(), &subdm);
1279 CHKERR DMSetType(subdm, "DMMOFEM");
1280 CHKERR DMMoFEMCreateSubDM(subdm, dm, "SUB_INIT");
1281 CHKERR DMMoFEMAddElement(subdm, simple->getDomainFEName());
1282 CHKERR DMMoFEMSetSquareProblem(subdm, PETSC_TRUE);
1283 CHKERR DMMoFEMSetDestroyProblem(subdm, PETSC_TRUE);
1284
1285 for (auto f : {"H", "G"}) {
1286 CHKERR DMMoFEMAddSubFieldRow(subdm, f, level_ents_ptr);
1287 CHKERR DMMoFEMAddSubFieldCol(subdm, f, level_ents_ptr);
1288 }
1289 CHKERR DMSetUp(subdm);
1290
1291 if constexpr (debug) {
1292 if (mField.get_comm_size() == 1) {
1293 auto dm_ents = get_dofs_ents_all(SmartPetscObj<DM>(subdm, true));
1294 CHKERR save_range(mField.get_moab(), "sub_dm_init_ents_verts.h5m",
1295 dm_ents.subset_by_type(MBVERTEX));
1296 dm_ents = subtract(dm_ents, dm_ents.subset_by_type(MBVERTEX));
1297 CHKERR save_range(mField.get_moab(), "sub_dm_init_ents.h5m", dm_ents);
1298 }
1299 }
1300
1301 return SmartPetscObj<DM>(subdm);
1302 };
1303
1304 auto subdm = create_subdm();
1305
1306 pip_mng->getDomainRhsFE().reset();
1307 pip_mng->getDomainLhsFE().reset();
1308 CHKERR pip_mng->setDomainRhsIntegrationRule(integration_rule);
1309 CHKERR pip_mng->setDomainLhsIntegrationRule(integration_rule);
1310 pip_mng->getDomainLhsFE()->exeTestHook = exe_test;
1311 pip_mng->getDomainRhsFE()->exeTestHook = exe_test;
1312
1313 CHKERR set_domain_rhs(pip_mng->getCastDomainRhsFE());
1314 CHKERR set_domain_lhs(pip_mng->getCastDomainLhsFE());
1315
1316 auto D = createDMVector(subdm);
1317 auto snes = pip_mng->createSNES(subdm);
1318 auto snes_ctx_ptr = getDMSnesCtx(subdm);
1319
1320 auto set_section_monitor = [&](auto solver) {
1322 CHKERR SNESMonitorSet(solver,
1323 (MoFEMErrorCode(*)(SNES, PetscInt, PetscReal,
1324 void *))MoFEMSNESMonitorFields,
1325 (void *)(snes_ctx_ptr.get()), nullptr);
1326
1328 };
1329
1330 auto print_section_field = [&]() {
1332
1333 auto section =
1334 mField.getInterface<ISManager>()->sectionCreate("SUB_INIT");
1335 PetscInt num_fields;
1336 CHKERR PetscSectionGetNumFields(section, &num_fields);
1337 for (int f = 0; f < num_fields; ++f) {
1338 const char *field_name;
1339 CHKERR PetscSectionGetFieldName(section, f, &field_name);
1340 MOFEM_LOG("FS", Sev::inform)
1341 << "Field " << f << " " << std::string(field_name);
1342 }
1343
1345 };
1346
1347 CHKERR set_section_monitor(snes);
1348 CHKERR print_section_field();
1349
1350 for (auto f : {"U", "P", "H", "G", "L"}) {
1351 CHKERR mField.getInterface<FieldBlas>()->setField(0, f);
1352 }
1353
1354 CHKERR SNESSetFromOptions(snes);
1355 auto B = createDMMatrix(subdm);
1356 CHKERR SNESSetJacobian(snes, B, B,
1357 PETSC_NULLPTR, PETSC_NULLPTR);
1358 CHKERR SNESSolve(snes, PETSC_NULLPTR, D);
1359
1360 CHKERR VecGhostUpdateBegin(D, INSERT_VALUES, SCATTER_FORWARD);
1361 CHKERR VecGhostUpdateEnd(D, INSERT_VALUES, SCATTER_FORWARD);
1362 CHKERR DMoFEMMeshToLocalVector(subdm, D, INSERT_VALUES, SCATTER_REVERSE);
1363
1365 };
1366
1367 CHKERR reset_bits();
1368 CHKERR solve_init(
1369 [](FEMethod *fe_ptr) { return get_fe_bit(fe_ptr).test(0); });
1370 CHKERR refineMesh(refine_overlap);
1371
1372 for (auto f : {"U", "P", "H", "G", "L"}) {
1373 CHKERR mField.getInterface<FieldBlas>()->setField(0, f);
1374 }
1375 CHKERR solve_init([](FEMethod *fe_ptr) {
1376 return get_fe_bit(fe_ptr).test(get_start_bit() + nb_levels - 1);
1377 });
1378
1379 CHKERR post_proc([](FEMethod *fe_ptr) {
1380 return get_fe_bit(fe_ptr).test(get_start_bit() + nb_levels - 1);
1381 });
1382
1383 pip_mng->getOpDomainRhsPipeline().clear();
1384 pip_mng->getOpDomainLhsPipeline().clear();
1385
1386 // Remove DOFs where boundary conditions are set
1387 CHKERR bc_mng->removeBlockDOFsOnEntities(simple->getProblemName(), "SYM_X",
1388 "U", 0, 0);
1389 CHKERR bc_mng->removeBlockDOFsOnEntities(simple->getProblemName(), "SYM_X",
1390 "L", 0, 0);
1391 CHKERR bc_mng->removeBlockDOFsOnEntities(simple->getProblemName(), "SYM_Y",
1392 "U", 1, 1);
1393 CHKERR bc_mng->removeBlockDOFsOnEntities(simple->getProblemName(), "SYM_Y",
1394 "L", 1, 1);
1395 CHKERR bc_mng->removeBlockDOFsOnEntities(simple->getProblemName(), "FIX", "U",
1396 0, SPACE_DIM);
1397 CHKERR bc_mng->removeBlockDOFsOnEntities(simple->getProblemName(), "FIX", "L",
1398 0, 0);
1399 CHKERR bc_mng->removeBlockDOFsOnEntities(simple->getProblemName(), "ZERO",
1400 "L", 0, 0);
1401
1403}
1404//! [Boundary condition]
1405
1406//! [Data projection]
1409
1410 // FIXME: Functionality in this method should be improved (projection should
1411 // be done field by field), generalized, and move to become core
1412 // functionality.
1413
1414 auto simple = mField.getInterface<Simple>();
1415 auto pip_mng = mField.getInterface<PipelineManager>();
1416 auto bit_mng = mField.getInterface<BitRefManager>();
1417 auto field_blas = mField.getInterface<FieldBlas>();
1418
1419 // Store all existing elements pipelines, replace them by data projection
1420 // pipelines, to put them back when projection is done.
1421 auto fe_domain_lhs = pip_mng->getDomainLhsFE();
1422 auto fe_domain_rhs = pip_mng->getDomainRhsFE();
1423 auto fe_bdy_lhs = pip_mng->getBoundaryLhsFE();
1424 auto fe_bdy_rhs = pip_mng->getBoundaryRhsFE();
1425
1426 pip_mng->getDomainLhsFE().reset();
1427 pip_mng->getDomainRhsFE().reset();
1428 pip_mng->getBoundaryLhsFE().reset();
1429 pip_mng->getBoundaryRhsFE().reset();
1430
1432
1433 // extract field data for domain parent element
1434 auto add_parent_field_domain = [&](auto fe, auto op, auto field, auto bit) {
1435 return setParentDofs(
1436 fe, field, op, bit,
1437
1438 [&]() {
1439 boost::shared_ptr<ForcesAndSourcesCore> fe_parent(
1440 new DomainParentEle(mField));
1441 return fe_parent;
1442 },
1443
1444 QUIET, Sev::noisy);
1445 };
1446
1447 // extract field data for boundary parent element
1448 auto add_parent_field_bdy = [&](auto fe, auto op, auto field, auto bit) {
1449 return setParentDofs(
1450 fe, field, op, bit,
1451
1452 [&]() {
1453 boost::shared_ptr<ForcesAndSourcesCore> fe_parent(
1455 return fe_parent;
1456 },
1457
1458 QUIET, Sev::noisy);
1459 };
1460
1461 // run this element on element with given bit, otherwise run other nested
1462 // element
1463 auto create_run_parent_op = [&](auto parent_fe_ptr, auto this_fe_ptr,
1464 auto fe_bit) {
1465 auto parent_mask = fe_bit;
1466 parent_mask.flip();
1467 return new OpRunParent(parent_fe_ptr, BitRefLevel().set(), parent_mask,
1468 this_fe_ptr, fe_bit, BitRefLevel().set(), QUIET,
1469 Sev::inform);
1470 };
1471
1472 // create hierarchy of nested elements
1473 auto get_parents_vel_fe_ptr = [&](auto this_fe_ptr, auto fe_bit) {
1474 std::vector<boost::shared_ptr<DomainParentEle>> parents_elems_ptr_vec;
1475 for (int l = 0; l < nb_levels; ++l)
1476 parents_elems_ptr_vec.emplace_back(
1477 boost::make_shared<DomainParentEle>(mField));
1478 for (auto l = 1; l < nb_levels; ++l) {
1479 parents_elems_ptr_vec[l - 1]->getOpPtrVector().push_back(
1480 create_run_parent_op(parents_elems_ptr_vec[l], this_fe_ptr, fe_bit));
1481 }
1482 return parents_elems_ptr_vec[0];
1483 };
1484
1485 // solve projection problem
1486 auto solve_projection = [&](auto exe_test) {
1488
1489 auto set_domain_rhs = [&](auto fe) {
1491 auto &pip = fe->getOpPtrVector();
1492
1493 auto u_ptr = boost::make_shared<MatrixDouble>();
1494 auto p_ptr = boost::make_shared<VectorDouble>();
1495 auto h_ptr = boost::make_shared<VectorDouble>();
1496 auto g_ptr = boost::make_shared<VectorDouble>();
1497
1498 auto eval_fe_ptr = boost::make_shared<DomainParentEle>(mField);
1499 {
1500 auto &pip = eval_fe_ptr->getOpPtrVector();
1503 eval_fe_ptr, "", UDO::OPSPACE, bit(get_skin_projection_bit()),
1504
1505 [&]() {
1506 boost::shared_ptr<ForcesAndSourcesCore> fe_parent(
1507 new DomainParentEle(mField));
1508 return fe_parent;
1509 },
1510
1511 QUIET, Sev::noisy);
1512 // That can be done much smarter, by block, field by field. For
1513 // simplicity is like that.
1514 CHKERR add_parent_field_domain(eval_fe_ptr, UDO::OPCOL, "U",
1516 pip.push_back(new OpCalculateVectorFieldValues<SPACE_DIM>("U", u_ptr));
1517 CHKERR add_parent_field_domain(eval_fe_ptr, UDO::OPCOL, "P",
1519 pip.push_back(new OpCalculateScalarFieldValues("P", p_ptr));
1520 CHKERR add_parent_field_domain(eval_fe_ptr, UDO::OPCOL, "H",
1522 pip.push_back(new OpCalculateScalarFieldValues("H", h_ptr));
1523 CHKERR add_parent_field_domain(eval_fe_ptr, UDO::OPCOL, "G",
1525 pip.push_back(new OpCalculateScalarFieldValues("G", g_ptr));
1526 }
1527 auto parent_eval_fe_ptr =
1528 get_parents_vel_fe_ptr(eval_fe_ptr, bit(get_projection_bit()));
1529 pip.push_back(create_run_parent_op(parent_eval_fe_ptr, eval_fe_ptr,
1531
1532 auto assemble_fe_ptr = boost::make_shared<DomainParentEle>(mField);
1533 {
1534 auto &pip = assemble_fe_ptr->getOpPtrVector();
1537 assemble_fe_ptr, "", UDO::OPSPACE, bit(get_skin_parent_bit()),
1538
1539 [&]() {
1540 boost::shared_ptr<ForcesAndSourcesCore> fe_parent(
1541 new DomainParentEle(mField));
1542 return fe_parent;
1543 },
1544
1545 QUIET, Sev::noisy);
1546 CHKERR add_parent_field_domain(assemble_fe_ptr, UDO::OPROW, "U",
1548 pip.push_back(new OpDomainAssembleVector("U", u_ptr));
1549 CHKERR add_parent_field_domain(assemble_fe_ptr, UDO::OPROW, "P",
1551 pip.push_back(new OpDomainAssembleScalar("P", p_ptr));
1552 CHKERR add_parent_field_domain(assemble_fe_ptr, UDO::OPROW, "H",
1554 pip.push_back(new OpDomainAssembleScalar("H", h_ptr));
1555 CHKERR add_parent_field_domain(assemble_fe_ptr, UDO::OPROW, "G",
1557 pip.push_back(new OpDomainAssembleScalar("G", g_ptr));
1558 }
1559 auto parent_assemble_fe_ptr =
1560 get_parents_vel_fe_ptr(assemble_fe_ptr, bit(get_current_bit()));
1561 pip.push_back(create_run_parent_op(
1562 parent_assemble_fe_ptr, assemble_fe_ptr, bit(get_current_bit())));
1563
1565 };
1566
1567 auto set_domain_lhs = [&](auto fe) {
1569
1570 auto &pip = fe->getOpPtrVector();
1571
1573
1575 fe, "", UDO::OPSPACE, bit(get_skin_parent_bit()),
1576
1577 [&]() {
1578 boost::shared_ptr<ForcesAndSourcesCore> fe_parent(
1579 new DomainParentEle(mField));
1580 return fe_parent;
1581 },
1582
1583 QUIET, Sev::noisy);
1584
1585 // That can be done much smarter, by block, field by field. For simplicity
1586 // is like that.
1587 CHKERR add_parent_field_domain(fe, UDO::OPROW, "U",
1589 CHKERR add_parent_field_domain(fe, UDO::OPCOL, "U",
1591 pip.push_back(new OpDomainMassU("U", "U"));
1592 CHKERR add_parent_field_domain(fe, UDO::OPROW, "P",
1594 CHKERR add_parent_field_domain(fe, UDO::OPCOL, "P",
1596 pip.push_back(new OpDomainMassP("P", "P"));
1597 CHKERR add_parent_field_domain(fe, UDO::OPROW, "H",
1599 CHKERR add_parent_field_domain(fe, UDO::OPCOL, "H",
1601 pip.push_back(new OpDomainMassH("H", "H"));
1602 CHKERR add_parent_field_domain(fe, UDO::OPROW, "G",
1604 CHKERR add_parent_field_domain(fe, UDO::OPCOL, "G",
1606 pip.push_back(new OpDomainMassG("G", "G"));
1607
1609 };
1610
1611 auto set_bdy_rhs = [&](auto fe) {
1613 auto &pip = fe->getOpPtrVector();
1614
1615 auto l_ptr = boost::make_shared<VectorDouble>();
1616
1617 auto eval_fe_ptr = boost::make_shared<BoundaryParentEle>(mField);
1618 {
1619 auto &pip = eval_fe_ptr->getOpPtrVector();
1621 eval_fe_ptr, "", UDO::OPSPACE, bit(get_skin_projection_bit()),
1622
1623 [&]() {
1624 boost::shared_ptr<ForcesAndSourcesCore> fe_parent(
1626 return fe_parent;
1627 },
1628
1629 QUIET, Sev::noisy);
1630 // That can be done much smarter, by block, field by field. For
1631 // simplicity is like that.
1632 CHKERR add_parent_field_bdy(eval_fe_ptr, UDO::OPCOL, "L",
1634 pip.push_back(new OpCalculateScalarFieldValues("L", l_ptr));
1635 }
1636 auto parent_eval_fe_ptr =
1637 get_parents_vel_fe_ptr(eval_fe_ptr, bit(get_projection_bit()));
1638 pip.push_back(create_run_parent_op(parent_eval_fe_ptr, eval_fe_ptr,
1640
1641 auto assemble_fe_ptr = boost::make_shared<BoundaryParentEle>(mField);
1642 {
1643 auto &pip = assemble_fe_ptr->getOpPtrVector();
1645 assemble_fe_ptr, "", UDO::OPSPACE, bit(get_skin_parent_bit()),
1646
1647 [&]() {
1648 boost::shared_ptr<ForcesAndSourcesCore> fe_parent(
1650 return fe_parent;
1651 },
1652
1653 QUIET, Sev::noisy);
1654
1655 struct OpLSize : public BoundaryEleOp {
1656 OpLSize(boost::shared_ptr<VectorDouble> l_ptr)
1657 : BoundaryEleOp(NOSPACE, DomainEleOp::OPSPACE), lPtr(l_ptr) {}
1658 MoFEMErrorCode doWork(int, EntityType, EntData &) {
1660 if (lPtr->size() != getGaussPts().size2()) {
1661 lPtr->resize(getGaussPts().size2());
1662 lPtr->clear();
1663 }
1665 }
1666
1667 private:
1668 boost::shared_ptr<VectorDouble> lPtr;
1669 };
1670
1671 pip.push_back(new OpLSize(l_ptr));
1672
1673 CHKERR add_parent_field_bdy(assemble_fe_ptr, UDO::OPROW, "L",
1675 pip.push_back(new OpBoundaryAssembleScalar("L", l_ptr));
1676 }
1677 auto parent_assemble_fe_ptr =
1678 get_parents_vel_fe_ptr(assemble_fe_ptr, bit(get_current_bit()));
1679 pip.push_back(create_run_parent_op(
1680 parent_assemble_fe_ptr, assemble_fe_ptr, bit(get_current_bit())));
1681
1683 };
1684
1685 auto set_bdy_lhs = [&](auto fe) {
1687
1688 auto &pip = fe->getOpPtrVector();
1689
1691 fe, "", UDO::OPSPACE, bit(get_skin_parent_bit()),
1692
1693 [&]() {
1694 boost::shared_ptr<ForcesAndSourcesCore> fe_parent(
1696 return fe_parent;
1697 },
1698
1699 QUIET, Sev::noisy);
1700
1701 // That can be done much smarter, by block, field by field. For simplicity
1702 // is like that.
1703 CHKERR add_parent_field_bdy(fe, UDO::OPROW, "L",
1705 CHKERR add_parent_field_bdy(fe, UDO::OPCOL, "L",
1707 pip.push_back(new OpBoundaryMassL("L", "L"));
1708
1710 };
1711
1712 auto create_subdm = [&]() -> SmartPetscObj<DM> {
1713 auto level_ents_ptr = boost::make_shared<Range>();
1714 CHKERR mField.getInterface<BitRefManager>()->getEntitiesByRefLevel(
1715 bit(get_current_bit()), BitRefLevel().set(), *level_ents_ptr);
1716
1717 auto get_prj_ents = [&]() {
1718 Range prj_mesh;
1719 CHKERR bit_mng->getEntitiesByDimAndRefLevel(bit(get_projection_bit()),
1720 BitRefLevel().set(),
1721 SPACE_DIM, prj_mesh);
1722 auto common_ents = intersect(prj_mesh, *level_ents_ptr);
1723 prj_mesh = subtract(unite(*level_ents_ptr, prj_mesh), common_ents)
1724 .subset_by_dimension(SPACE_DIM);
1725
1726 return prj_mesh;
1727 };
1728
1729 auto prj_ents = get_prj_ents();
1730
1731 if (get_global_size(prj_ents.size())) {
1732
1733 auto rebuild = [&]() {
1734 auto prb_mng = mField.getInterface<ProblemsManager>();
1736
1737 std::vector<std::string> fields{"U", "P", "H", "G", "L"};
1738 std::map<std::string, boost::shared_ptr<Range>> range_maps{
1739
1740 {"U", level_ents_ptr},
1741 {"P", level_ents_ptr},
1742 {"H", level_ents_ptr},
1743 {"G", level_ents_ptr},
1744 {"L", level_ents_ptr}
1745
1746 };
1747
1748 CHKERR prb_mng->buildSubProblem("SUB_SOLVER", fields, fields,
1749 simple->getProblemName(), PETSC_TRUE,
1750 &range_maps, &range_maps);
1751
1752 // partition problem
1753 CHKERR prb_mng->partitionFiniteElements("SUB_SOLVER", true, 0,
1755 // set ghost nodes
1756 CHKERR prb_mng->partitionGhostDofsOnDistributedMesh("SUB_SOLVER");
1757
1759 };
1760
1761 MOFEM_LOG("FS", Sev::verbose) << "Create projection problem";
1762
1763 CHKERR rebuild();
1764
1765 auto dm = simple->getDM();
1766 DM subdm;
1767 CHKERR DMCreate(mField.get_comm(), &subdm);
1768 CHKERR DMSetType(subdm, "DMMOFEM");
1769 CHKERR DMMoFEMCreateSubDM(subdm, dm, "SUB_SOLVER");
1770 return SmartPetscObj<DM>(subdm);
1771 }
1772
1773 MOFEM_LOG("FS", Sev::inform) << "Nothing to project";
1774
1775 return SmartPetscObj<DM>();
1776 };
1777
1778 auto create_dummy_dm = [&]() {
1779 auto dummy_dm = createDM(mField.get_comm(), "DMMOFEM");
1781 simple->getProblemName().c_str(),
1783 "create dummy dm");
1784 return dummy_dm;
1785 };
1786
1787 auto subdm = create_subdm();
1788 if (subdm) {
1789
1790 pip_mng->getDomainRhsFE().reset();
1791 pip_mng->getDomainLhsFE().reset();
1792 pip_mng->getBoundaryRhsFE().reset();
1793 pip_mng->getBoundaryLhsFE().reset();
1794 CHKERR pip_mng->setDomainRhsIntegrationRule(integration_rule);
1795 CHKERR pip_mng->setDomainLhsIntegrationRule(integration_rule);
1796 CHKERR pip_mng->setBoundaryRhsIntegrationRule(integration_rule);
1797 CHKERR pip_mng->setBoundaryLhsIntegrationRule(integration_rule);
1798 pip_mng->getDomainLhsFE()->exeTestHook = exe_test;
1799 pip_mng->getDomainRhsFE()->exeTestHook = [](FEMethod *fe_ptr) {
1800 return get_fe_bit(fe_ptr).test(nb_levels - 1);
1801 };
1802 pip_mng->getBoundaryLhsFE()->exeTestHook = exe_test;
1803 pip_mng->getBoundaryRhsFE()->exeTestHook = [](FEMethod *fe_ptr) {
1804 return get_fe_bit(fe_ptr).test(nb_levels - 1);
1805 };
1806
1807 CHKERR set_domain_rhs(pip_mng->getCastDomainRhsFE());
1808 CHKERR set_domain_lhs(pip_mng->getCastDomainLhsFE());
1809 CHKERR set_bdy_rhs(pip_mng->getCastBoundaryRhsFE());
1810 CHKERR set_bdy_lhs(pip_mng->getCastBoundaryLhsFE());
1811
1812 auto D = createDMVector(subdm);
1813 auto F = vectorDuplicate(D);
1814
1815 auto ksp = pip_mng->createKSP(subdm);
1816 CHKERR KSPSetFromOptions(ksp);
1817 CHKERR KSPSetUp(ksp);
1818
1819 // get vector norm
1820 auto get_norm = [&](auto x) {
1821 double nrm;
1822 CHKERR VecNorm(x, NORM_2, &nrm);
1823 return nrm;
1824 };
1825
1826 /**
1827 * @brief Zero DOFs, used by FieldBlas
1828 */
1829 auto zero_dofs = [](boost::shared_ptr<FieldEntity> ent_ptr) {
1831 for (auto &v : ent_ptr->getEntFieldData()) {
1832 v = 0;
1833 }
1835 };
1836
1837 auto solve = [&](auto S) {
1839 CHKERR VecZeroEntries(S);
1840 CHKERR VecZeroEntries(F);
1841 CHKERR VecGhostUpdateBegin(F, INSERT_VALUES, SCATTER_FORWARD);
1842 CHKERR VecGhostUpdateEnd(F, INSERT_VALUES, SCATTER_FORWARD);
1843 CHKERR KSPSolve(ksp, F, S);
1844 CHKERR VecGhostUpdateBegin(S, INSERT_VALUES, SCATTER_FORWARD);
1845 CHKERR VecGhostUpdateEnd(S, INSERT_VALUES, SCATTER_FORWARD);
1846
1847
1848
1850 };
1851
1852 MOFEM_LOG("FS", Sev::inform) << "Solve projection";
1853 CHKERR solve(D);
1854
1855 auto glob_x = createDMVector(simple->getDM());
1856 auto sub_x = createDMVector(subdm);
1857 auto dummy_dm = create_dummy_dm();
1858
1859 /**
1860 * @brief get TSTheta data operators
1861 */
1862 auto apply_restrict = [&]() {
1863 auto get_is = [](auto v) {
1864 IS iy;
1865 auto create = [&]() {
1867 int n, ystart;
1868 CHKERR VecGetLocalSize(v, &n);
1869 CHKERR VecGetOwnershipRange(v, &ystart, NULL);
1870 CHKERR ISCreateStride(PETSC_COMM_SELF, n, ystart, 1, &iy);
1872 };
1873 CHK_THROW_MESSAGE(create(), "create is");
1874 return SmartPetscObj<IS>(iy);
1875 };
1876
1877 auto iy = get_is(glob_x);
1878 auto s = createVecScatter(glob_x, PETSC_NULLPTR, glob_x, iy);
1879
1881 DMSubDomainRestrict(simple->getDM(), s, PETSC_NULLPTR, dummy_dm),
1882 "restrict");
1883 Vec X0, Xdot;
1884 CHK_THROW_MESSAGE(DMGetNamedGlobalVector(dummy_dm, "TSTheta_X0", &X0),
1885 "get X0");
1887 DMGetNamedGlobalVector(dummy_dm, "TSTheta_Xdot", &Xdot),
1888 "get Xdot");
1889
1890 auto forward_ghost = [](auto g) {
1892 CHKERR VecGhostUpdateBegin(g, INSERT_VALUES, SCATTER_FORWARD);
1893 CHKERR VecGhostUpdateEnd(g, INSERT_VALUES, SCATTER_FORWARD);
1895 };
1896
1897 CHK_THROW_MESSAGE(forward_ghost(X0), "");
1898 CHK_THROW_MESSAGE(forward_ghost(Xdot), "");
1899
1900 if constexpr (debug) {
1901 MOFEM_LOG("FS", Sev::inform)
1902 << "Reverse restrict: X0 " << get_norm(X0) << " Xdot "
1903 << get_norm(Xdot);
1904 }
1905
1906 return std::vector<Vec>{X0, Xdot};
1907 };
1908
1909 auto ts_solver_vecs = apply_restrict();
1910
1911 if (ts_solver_vecs.size()) {
1912
1913 for (auto v : ts_solver_vecs) {
1914 MOFEM_LOG("FS", Sev::inform) << "Solve projection vector";
1915
1916 CHKERR DMoFEMMeshToLocalVector(simple->getDM(), v, INSERT_VALUES,
1917 SCATTER_REVERSE);
1918 CHKERR solve(sub_x);
1919
1920 for (auto f : {"U", "P", "H", "G", "L"}) {
1921 MOFEM_LOG("WORLD", Sev::verbose) << "Zero field " << f;
1922 CHKERR field_blas->fieldLambdaOnEntities(zero_dofs, f);
1923 }
1924
1925 CHKERR DMoFEMMeshToLocalVector(subdm, sub_x, INSERT_VALUES,
1926 SCATTER_REVERSE);
1927 CHKERR DMoFEMMeshToLocalVector(simple->getDM(), v, INSERT_VALUES,
1928 SCATTER_FORWARD);
1929
1930 MOFEM_LOG("FS", Sev::inform) << "Norm V " << get_norm(v);
1931 }
1932
1933 CHKERR DMRestoreNamedGlobalVector(dummy_dm, "TSTheta_X0",
1934 &ts_solver_vecs[0]);
1935 CHKERR DMRestoreNamedGlobalVector(dummy_dm, "TSTheta_Xdot",
1936 &ts_solver_vecs[1]);
1937 }
1938
1939 for (auto f : {"U", "P", "H", "G", "L"}) {
1940 MOFEM_LOG("WORLD", Sev::verbose) << "Zero field " << f;
1941 CHKERR field_blas->fieldLambdaOnEntities(zero_dofs, f);
1942 }
1943 CHKERR DMoFEMMeshToLocalVector(subdm, D, INSERT_VALUES, SCATTER_REVERSE);
1944 }
1945
1947 };
1948
1949 // postprocessing (only for debugging purposes)
1950 auto post_proc = [&](auto exe_test) {
1952 auto post_proc_fe = boost::make_shared<PostProcEleDomain>(mField);
1953 auto &pip = post_proc_fe->getOpPtrVector();
1954 post_proc_fe->exeTestHook = exe_test;
1955
1957
1959 post_proc_fe, "", UDO::OPSPACE, bit(get_skin_parent_bit()),
1960
1961 [&]() {
1962 boost::shared_ptr<ForcesAndSourcesCore> fe_parent(
1963 new DomainParentEle(mField));
1965 fe_parent->getOpPtrVector(), {H1});
1966 return fe_parent;
1967 },
1968
1969 QUIET, Sev::noisy);
1970
1972
1973 auto u_ptr = boost::make_shared<MatrixDouble>();
1974 auto p_ptr = boost::make_shared<VectorDouble>();
1975 auto h_ptr = boost::make_shared<VectorDouble>();
1976 auto g_ptr = boost::make_shared<VectorDouble>();
1977
1978 CHKERR add_parent_field_domain(post_proc_fe, UDO::OPCOL, "U",
1980 pip.push_back(new OpCalculateVectorFieldValues<SPACE_DIM>("U", u_ptr));
1981 CHKERR add_parent_field_domain(post_proc_fe, UDO::OPCOL, "P",
1983 pip.push_back(new OpCalculateScalarFieldValues("P", p_ptr));
1984 CHKERR add_parent_field_domain(post_proc_fe, UDO::OPCOL, "H",
1986 pip.push_back(new OpCalculateScalarFieldValues("H", h_ptr));
1987 CHKERR add_parent_field_domain(post_proc_fe, UDO::OPCOL, "G",
1989 pip.push_back(new OpCalculateScalarFieldValues("G", g_ptr));
1990
1991 post_proc_fe->getOpPtrVector().push_back(
1992
1993 new OpPPMap(post_proc_fe->getPostProcMesh(),
1994 post_proc_fe->getMapGaussPts(),
1995
1996 {{"P", p_ptr}, {"H", h_ptr}, {"G", g_ptr}},
1997
1998 {{"U", u_ptr}},
1999
2000 {},
2001
2002 {}
2003
2004 )
2005
2006 );
2007
2008 auto dm = simple->getDM();
2009 CHKERR DMoFEMLoopFiniteElements(dm, "dFE", post_proc_fe);
2010 CHKERR post_proc_fe->writeFile("out_projection.h5m");
2011
2013 };
2014
2015 CHKERR solve_projection([](FEMethod *fe_ptr) {
2016 return get_fe_bit(fe_ptr).test(get_current_bit());
2017 });
2018
2019 if constexpr (debug) {
2020 CHKERR post_proc([](FEMethod *fe_ptr) {
2021 return get_fe_bit(fe_ptr).test(get_current_bit());
2022 });
2023 }
2024
2025 fe_domain_lhs.swap(pip_mng->getDomainLhsFE());
2026 fe_domain_rhs.swap(pip_mng->getDomainRhsFE());
2027 fe_bdy_lhs.swap(pip_mng->getBoundaryLhsFE());
2028 fe_bdy_rhs.swap(pip_mng->getBoundaryRhsFE());
2029
2031}
2032//! [Data projection]
2033
2034//! [Push operators to pip]
2037 auto simple = mField.getInterface<Simple>();
2038
2040
2041 auto add_parent_field_domain = [&](auto fe, auto op, auto field) {
2042 return setParentDofs(
2043 fe, field, op, bit(get_skin_parent_bit()),
2044
2045 [&]() {
2046 boost::shared_ptr<ForcesAndSourcesCore> fe_parent(
2047 new DomainParentEle(mField));
2048 return fe_parent;
2049 },
2050
2051 QUIET, Sev::noisy);
2052 };
2053
2054 auto add_parent_field_bdy = [&](auto fe, auto op, auto field) {
2055 return setParentDofs(
2056 fe, field, op, bit(get_skin_parent_bit()),
2057
2058 [&]() {
2059 boost::shared_ptr<ForcesAndSourcesCore> fe_parent(
2061 return fe_parent;
2062 },
2063
2064 QUIET, Sev::noisy);
2065 };
2066
2067 auto test_bit_child = [](FEMethod *fe_ptr) {
2068 return fe_ptr->numeredEntFiniteElementPtr->getBitRefLevel().test(
2069 get_start_bit() + nb_levels - 1);
2070 };
2071
2072 auto dot_u_ptr = boost::make_shared<MatrixDouble>(); // time derivative of velocity u ie du/dt
2073 auto u_ptr = boost::make_shared<MatrixDouble>(); // velocity u
2074 auto grad_u_ptr = boost::make_shared<MatrixDouble>(); // velocity gradient tensor ie grad(u)
2075 auto dot_h_ptr = boost::make_shared<VectorDouble>(); // time derivative of phase field ie dh/dt
2076 auto h_ptr = boost::make_shared<VectorDouble>(); // phase field variable h
2077 auto grad_h_ptr = boost::make_shared<MatrixDouble>(); // phase field gradient ie grad(h)
2078 auto g_ptr = boost::make_shared<VectorDouble>(); // chemical potential g
2079 auto grad_g_ptr = boost::make_shared<MatrixDouble>(); // chemical potential gradient ie grad(g)
2080 auto lambda_ptr = boost::make_shared<VectorDouble>(); // Lagrange multiplier lambda
2081 auto p_ptr = boost::make_shared<VectorDouble>(); // pressure p
2082 auto div_u_ptr = boost::make_shared<VectorDouble>(); // divergence of velocity ie div(u)
2083
2084 // Push element from reference configuration to current configuration in 3d
2085 // space
2086 auto set_domain_general = [&](auto fe) {
2088 auto &pip = fe->getOpPtrVector();
2089
2091
2093 fe, "", UDO::OPSPACE, bit(get_skin_parent_bit()),
2094
2095 [&]() {
2096 boost::shared_ptr<ForcesAndSourcesCore> fe_parent(
2097 new DomainParentEle(mField));
2099 fe_parent->getOpPtrVector(), {H1});
2100 return fe_parent;
2101 },
2102
2103 QUIET, Sev::noisy);
2104
2105 CHKERR add_parent_field_domain(fe, UDO::OPROW, "U");
2106 CHKERR add_parent_field_domain(fe, UDO::OPCOL, "U");
2107 pip.push_back(
2109 pip.push_back(new OpCalculateVectorFieldValues<U_FIELD_DIM>("U", u_ptr));
2111 "U", grad_u_ptr));
2112
2113 switch (coord_type) {
2114 case CARTESIAN:
2115 pip.push_back(
2117 "U", div_u_ptr));
2118 break;
2119 case CYLINDRICAL:
2120 pip.push_back(
2122 "U", div_u_ptr));
2123 break;
2124 default:
2125 SETERRQ(PETSC_COMM_WORLD, MOFEM_NOT_IMPLEMENTED,
2126 "Coordinate system not implemented");
2127 }
2128
2129 CHKERR add_parent_field_domain(fe, UDO::OPCOL, "H");
2130 pip.push_back(new OpCalculateScalarFieldValuesDot("H", dot_h_ptr));
2131 pip.push_back(new OpCalculateScalarFieldValues("H", h_ptr));
2132 pip.push_back(
2133 new OpCalculateScalarFieldGradient<SPACE_DIM>("H", grad_h_ptr));
2134
2135 CHKERR add_parent_field_domain(fe, UDO::OPCOL, "G");
2136 pip.push_back(new OpCalculateScalarFieldValues("G", g_ptr));
2137 pip.push_back(
2138 new OpCalculateScalarFieldGradient<SPACE_DIM>("G", grad_g_ptr));
2139
2140 CHKERR add_parent_field_domain(fe, UDO::OPCOL, "P");
2141 pip.push_back(new OpCalculateScalarFieldValues("P", p_ptr));
2143 };
2144
2145 auto set_domain_rhs = [&](auto fe) {
2147 auto &pip = fe->getOpPtrVector();
2148
2149 CHKERR set_domain_general(fe);
2150
2151 CHKERR add_parent_field_domain(fe, UDO::OPROW, "U");
2152 pip.push_back(new OpRhsU("U", dot_u_ptr, u_ptr, grad_u_ptr, h_ptr,
2153 grad_h_ptr, g_ptr, p_ptr));
2154
2155 CHKERR add_parent_field_domain(fe, UDO::OPROW, "H");
2156 pip.push_back(new OpRhsH<false>("H", u_ptr, dot_h_ptr, h_ptr, grad_h_ptr,
2157 grad_g_ptr));
2158
2159 CHKERR add_parent_field_domain(fe, UDO::OPROW, "G");
2160 pip.push_back(new OpRhsG<false>("G", h_ptr, grad_h_ptr, g_ptr));
2161
2162 CHKERR add_parent_field_domain(fe, UDO::OPROW, "P");
2163 pip.push_back(new OpDomainAssembleScalar(
2164 "P", div_u_ptr, [](const double r, const double, const double) {
2165 return cylindrical(r);
2166 }));
2167 pip.push_back(new OpDomainAssembleScalar(
2168 "P", p_ptr, [](const double r, const double, const double) {
2169 return eps * cylindrical(r);
2170 }));
2172 };
2173
2174 auto set_domain_lhs = [&](auto fe) {
2176 auto &pip = fe->getOpPtrVector();
2177
2178 CHKERR set_domain_general(fe);
2179
2180 CHKERR add_parent_field_domain(fe, UDO::OPROW, "U");
2181 {
2182 CHKERR add_parent_field_domain(fe, UDO::OPCOL, "U");
2183 pip.push_back(new OpLhsU_dU("U", u_ptr, grad_u_ptr, h_ptr));
2184 CHKERR add_parent_field_domain(fe, UDO::OPCOL, "H");
2185 pip.push_back(
2186 new OpLhsU_dH("U", "H", dot_u_ptr, u_ptr, grad_u_ptr, h_ptr, g_ptr));
2187
2188 CHKERR add_parent_field_domain(fe, UDO::OPCOL, "G");
2189 pip.push_back(new OpLhsU_dG("U", "G", grad_h_ptr));
2190 }
2191
2192 CHKERR add_parent_field_domain(fe, UDO::OPROW, "H");
2193 {
2194 CHKERR add_parent_field_domain(fe, UDO::OPCOL, "U");
2195 pip.push_back(new OpLhsH_dU("H", "U", grad_h_ptr));
2196 CHKERR add_parent_field_domain(fe, UDO::OPCOL, "H");
2197 pip.push_back(new OpLhsH_dH<false>("H", u_ptr, h_ptr, grad_g_ptr));
2198 CHKERR add_parent_field_domain(fe, UDO::OPCOL, "G");
2199 pip.push_back(new OpLhsH_dG<false>("H", "G", h_ptr));
2200 }
2201
2202 CHKERR add_parent_field_domain(fe, UDO::OPROW, "G");
2203 {
2204 CHKERR add_parent_field_domain(fe, UDO::OPCOL, "H");
2205 pip.push_back(new OpLhsG_dH<false>("G", "H", h_ptr));
2206 CHKERR add_parent_field_domain(fe, UDO::OPCOL, "G");
2207 pip.push_back(new OpLhsG_dG("G"));
2208 }
2209
2210 CHKERR add_parent_field_domain(fe, UDO::OPROW, "P");
2211 {
2212 CHKERR add_parent_field_domain(fe, UDO::OPCOL, "U");
2213
2214 switch (coord_type) {
2215 case CARTESIAN:
2216 pip.push_back(new OpMixScalarTimesDiv<CARTESIAN>(
2217 "P", "U",
2218 [](const double r, const double, const double) {
2219 return cylindrical(r);
2220 },
2221 true, false));
2222 break;
2223 case CYLINDRICAL:
2224 pip.push_back(new OpMixScalarTimesDiv<CYLINDRICAL>(
2225 "P", "U",
2226 [](const double r, const double, const double) {
2227 return cylindrical(r);
2228 },
2229 true, false));
2230 break;
2231 default:
2232 SETERRQ(PETSC_COMM_WORLD, MOFEM_NOT_IMPLEMENTED,
2233 "Coordinate system not implemented");
2234 }
2235
2236 CHKERR add_parent_field_domain(fe, UDO::OPCOL, "P");
2237 pip.push_back(new OpDomainMassP("P", "P", [](double r, double, double) {
2238 return eps * cylindrical(r);
2239 }));
2240 }
2241
2243 };
2244
2245 auto get_block_name = [](auto name) {
2246 return boost::format("%s(.*)") % "WETTING_ANGLE";
2247 };
2248
2249 auto get_blocks = [&](auto &&name) {
2250 return mField.getInterface<MeshsetsManager>()->getCubitMeshsetPtr(
2251 std::regex(name.str()));
2252 };
2253
2254 auto set_boundary_rhs = [&](auto fe) {
2256 auto &pip = fe->getOpPtrVector();
2257
2259 fe, "", UDO::OPSPACE, bit(get_skin_parent_bit()),
2260
2261 [&]() {
2262 boost::shared_ptr<ForcesAndSourcesCore> fe_parent(
2264 return fe_parent;
2265 },
2266
2267 QUIET, Sev::noisy);
2268
2269 CHKERR add_parent_field_bdy(fe, UDO::OPCOL, "U");
2270 pip.push_back(new OpCalculateVectorFieldValues<U_FIELD_DIM>("U", u_ptr));
2271
2272 CHKERR add_parent_field_bdy(fe, UDO::OPCOL, "L");
2273 pip.push_back(new OpCalculateScalarFieldValues("L", lambda_ptr));
2274 pip.push_back(new OpNormalConstrainRhs("L", u_ptr));
2275
2277 pip, mField, "L", {}, "INFLUX",
2278 [](double r, double, double) { return cylindrical(r); }, Sev::inform);
2279
2280 CHKERR add_parent_field_bdy(fe, UDO::OPROW, "U");
2281 pip.push_back(new OpNormalForceRhs("U", lambda_ptr));
2282
2283 auto wetting_block = get_blocks(get_block_name("WETTING_ANGLE"));
2284 if (wetting_block.size()) {
2285 // push operators to the side element which is called from op_bdy_side
2286 auto op_bdy_side =
2287 new OpLoopSide<SideEle>(mField, simple->getDomainFEName(), SPACE_DIM);
2288 op_bdy_side->getSideFEPtr()->exeTestHook = test_bit_child;
2289
2291 op_bdy_side->getOpPtrVector(), {H1});
2292
2294 op_bdy_side->getSideFEPtr(), "", UDO::OPSPACE,
2296
2297 [&]() {
2298 boost::shared_ptr<ForcesAndSourcesCore> fe_parent(
2299 new DomainParentEle(mField));
2301 fe_parent->getOpPtrVector(), {H1});
2302 return fe_parent;
2303 },
2304
2305 QUIET, Sev::noisy);
2306
2307 CHKERR add_parent_field_domain(op_bdy_side->getSideFEPtr(), UDO::OPCOL,
2308 "H");
2309 op_bdy_side->getOpPtrVector().push_back(
2310 new OpCalculateScalarFieldGradient<SPACE_DIM>("H", grad_h_ptr));
2311 // push bdy side op
2312 pip.push_back(op_bdy_side);
2313
2314 // push operators for rhs wetting angle
2315 for (auto &b : wetting_block) {
2316 Range force_edges;
2317 std::vector<double> attr_vec;
2318 CHKERR b->getMeshsetIdEntitiesByDimension(
2319 mField.get_moab(), SPACE_DIM - 1, force_edges, true);
2320 b->getAttributes(attr_vec);
2321 if (attr_vec.size() != 1)
2322 SETERRQ(PETSC_COMM_SELF, MOFEM_INVALID_DATA,
2323 "Should be one attribute");
2324 MOFEM_LOG("FS", Sev::inform) << "Wetting angle: " << attr_vec.front();
2325 // need to find the attributes and pass to operator
2326 CHKERR add_parent_field_bdy(fe, UDO::OPROW, "G");
2327 CHKERR add_parent_field_bdy(fe, UDO::OPCOL, "G");
2328 pip.push_back(new OpWettingAngleRhs(
2329 "G", grad_h_ptr, boost::make_shared<Range>(force_edges),
2330 attr_vec.front()));
2331 }
2332 }
2333
2335 };
2336
2337 auto set_boundary_lhs = [&](auto fe) {
2339 auto &pip = fe->getOpPtrVector();
2340
2342 fe, "", UDO::OPSPACE, bit(get_skin_parent_bit()),
2343
2344 [&]() {
2345 boost::shared_ptr<ForcesAndSourcesCore> fe_parent(
2347 return fe_parent;
2348 },
2349
2350 QUIET, Sev::noisy);
2351
2352 CHKERR add_parent_field_bdy(fe, UDO::OPROW, "L");
2353 CHKERR add_parent_field_bdy(fe, UDO::OPCOL, "U");
2354 pip.push_back(new OpNormalConstrainLhs("L", "U"));
2355
2356 auto wetting_block = get_blocks(get_block_name("WETTING_ANGLE"));
2357 if (wetting_block.size()) {
2358 auto col_ind_ptr = boost::make_shared<std::vector<VectorInt>>();
2359 auto col_diff_base_ptr = boost::make_shared<std::vector<MatrixDouble>>();
2360
2361 // push operators to the side element which is called from op_bdy_side
2362 auto op_bdy_side =
2363 new OpLoopSide<SideEle>(mField, simple->getDomainFEName(), SPACE_DIM);
2364 op_bdy_side->getSideFEPtr()->exeTestHook = test_bit_child;
2365
2367 op_bdy_side->getOpPtrVector(), {H1});
2368
2370 op_bdy_side->getSideFEPtr(), "", UDO::OPSPACE,
2372
2373 [&]() {
2374 boost::shared_ptr<ForcesAndSourcesCore> fe_parent(
2375 new DomainParentEle(mField));
2377 fe_parent->getOpPtrVector(), {H1});
2378 return fe_parent;
2379 },
2380
2381 QUIET, Sev::noisy);
2382
2383 CHKERR add_parent_field_domain(op_bdy_side->getSideFEPtr(), UDO::OPROW,
2384 "H");
2385 CHKERR add_parent_field_domain(op_bdy_side->getSideFEPtr(), UDO::OPCOL,
2386 "H");
2387 op_bdy_side->getOpPtrVector().push_back(
2388 new OpCalculateScalarFieldGradient<SPACE_DIM>("H", grad_h_ptr));
2389 op_bdy_side->getOpPtrVector().push_back(
2390 new OpLoopSideGetDataForSideEle("H", col_ind_ptr, col_diff_base_ptr));
2391
2392 // push bdy side op
2393 pip.push_back(op_bdy_side);
2394
2395 // push operators for lhs wetting angle
2396 for (auto &b : wetting_block) {
2397 Range force_edges;
2398 std::vector<double> attr_vec;
2399 CHKERR b->getMeshsetIdEntitiesByDimension(
2400 mField.get_moab(), SPACE_DIM - 1, force_edges, true);
2401 b->getAttributes(attr_vec);
2402 if (attr_vec.size() != 1)
2403 SETERRQ(PETSC_COMM_SELF, MOFEM_INVALID_DATA,
2404 "Should be one attribute");
2405 MOFEM_LOG("FS", Sev::inform)
2406 << "wetting angle edges size " << force_edges.size();
2407
2408 CHKERR add_parent_field_bdy(fe, UDO::OPROW, "G");
2409 CHKERR add_parent_field_bdy(fe, UDO::OPCOL, "G");
2410 pip.push_back(new OpWettingAngleLhs(
2411 "G", grad_h_ptr, col_ind_ptr, col_diff_base_ptr,
2412 boost::make_shared<Range>(force_edges), attr_vec.front()));
2413 }
2414 }
2415
2417 };
2418
2419 auto *pip_mng = mField.getInterface<PipelineManager>();
2420
2421 CHKERR set_domain_rhs(pip_mng->getCastDomainRhsFE());
2422 CHKERR set_domain_lhs(pip_mng->getCastDomainLhsFE());
2423 CHKERR set_boundary_rhs(pip_mng->getCastBoundaryRhsFE());
2424 CHKERR set_boundary_lhs(pip_mng->getCastBoundaryLhsFE());
2425
2426 CHKERR pip_mng->setDomainRhsIntegrationRule(integration_rule);
2427 CHKERR pip_mng->setDomainLhsIntegrationRule(integration_rule);
2428 CHKERR pip_mng->setBoundaryRhsIntegrationRule(integration_rule);
2429 CHKERR pip_mng->setBoundaryLhsIntegrationRule(integration_rule);
2430
2431 pip_mng->getDomainLhsFE()->exeTestHook = test_bit_child;
2432 pip_mng->getDomainRhsFE()->exeTestHook = test_bit_child;
2433 pip_mng->getBoundaryLhsFE()->exeTestHook = test_bit_child;
2434 pip_mng->getBoundaryRhsFE()->exeTestHook = test_bit_child;
2435
2437}
2438//! [Push operators to pip]
2439
2440/**
2441 * @brief Monitor solution
2442 *
2443 * This function is called by TS solver at the end of each step. It is used
2444 */
2445struct Monitor : public FEMethod {
2447 SmartPetscObj<DM> dm, boost::shared_ptr<moab::Core> post_proc_mesh,
2448 boost::shared_ptr<PostProcEleDomainCont> post_proc,
2449 boost::shared_ptr<PostProcEleBdyCont> post_proc_edge,
2450 std::pair<boost::shared_ptr<BoundaryEle>, boost::shared_ptr<VectorDouble>>
2451 p)
2452 : dM(dm), postProcMesh(post_proc_mesh), postProc(post_proc),
2453 postProcEdge(post_proc_edge), liftFE(p.first), liftVec(p.second) {}
2456
2457 MOFEM_LOG("FS", Sev::verbose) << "Monitor";
2458 int save_every_nth_step = 1;
2459 CHKERR PetscOptionsGetInt(NULL, NULL, "-save_every_nth_step",
2460 &save_every_nth_step, NULL);
2461 if (ts_step % save_every_nth_step == 0) {
2462 MOFEM_LOG("FS", Sev::verbose) << "Mesh pre proc";
2463 MoFEM::Interface *m_field_ptr;
2464 CHKERR DMoFEMGetInterfacePtr(dM, &m_field_ptr);
2465 auto post_proc_begin =
2466 boost::make_shared<PostProcBrokenMeshInMoabBaseBegin>(*m_field_ptr,
2467 postProcMesh);
2468 auto post_proc_end = boost::make_shared<PostProcBrokenMeshInMoabBaseEnd>(
2469 *m_field_ptr, postProcMesh);
2470 CHKERR DMoFEMPreProcessFiniteElements(dM, post_proc_begin->getFEMethod());
2472 this->getCacheWeakPtr());
2474 this->getCacheWeakPtr());
2475 CHKERR DMoFEMPostProcessFiniteElements(dM, post_proc_end->getFEMethod());
2476 CHKERR post_proc_end->writeFile(
2477 "out_step_" + boost::lexical_cast<std::string>(ts_step) + ".h5m");
2478 MOFEM_LOG("FS", Sev::verbose) << "Mesh pre proc done";
2479 }
2480
2481 liftVec->resize(SPACE_DIM, false);
2482 liftVec->clear();
2484 MPI_Allreduce(MPI_IN_PLACE, &(*liftVec)[0], SPACE_DIM, MPI_DOUBLE, MPI_SUM,
2485 MPI_COMM_WORLD);
2486 MOFEM_LOG("FS", Sev::inform)
2487 << "Step " << ts_step << " time " << ts_t
2488 << " lift vec x: " << (*liftVec)[0] << " y: " << (*liftVec)[1];
2489
2491 }
2492
2493private:
2495 boost::shared_ptr<moab::Core> postProcMesh;
2496 boost::shared_ptr<PostProcEleDomainCont> postProc;
2497 boost::shared_ptr<PostProcEleBdyCont> postProcEdge;
2498 boost::shared_ptr<BoundaryEle> liftFE;
2499 boost::shared_ptr<VectorDouble> liftVec;
2500};
2501
2502//! [Solve]
2505
2507
2508 auto simple = mField.getInterface<Simple>();
2509 auto pip_mng = mField.getInterface<PipelineManager>();
2510
2511 auto create_solver_dm = [&](auto dm) -> SmartPetscObj<DM> {
2512 DM subdm;
2513
2514 auto setup_subdm = [&](auto dm) {
2516 auto simple = mField.getInterface<Simple>();
2517 auto bit_mng = mField.getInterface<BitRefManager>();
2518 auto dm = simple->getDM();
2519 auto level_ents_ptr = boost::make_shared<Range>();
2520 CHKERR bit_mng->getEntitiesByRefLevel(
2521 bit(get_current_bit()), BitRefLevel().set(), *level_ents_ptr);
2522 CHKERR DMCreate(mField.get_comm(), &subdm);
2523 CHKERR DMSetType(subdm, "DMMOFEM");
2524 CHKERR DMMoFEMCreateSubDM(subdm, dm, "SUB_SOLVER");
2525 CHKERR DMMoFEMAddElement(subdm, simple->getDomainFEName());
2526 CHKERR DMMoFEMAddElement(subdm, simple->getBoundaryFEName());
2527 CHKERR DMMoFEMSetSquareProblem(subdm, PETSC_TRUE);
2528 CHKERR DMMoFEMSetDestroyProblem(subdm, PETSC_FALSE);
2529 for (auto f : {"U", "P", "H", "G", "L"}) {
2530 CHKERR DMMoFEMAddSubFieldRow(subdm, f, level_ents_ptr);
2531 CHKERR DMMoFEMAddSubFieldCol(subdm, f, level_ents_ptr);
2532 }
2533 CHKERR DMSetUp(subdm);
2535 };
2536
2537 CHK_THROW_MESSAGE(setup_subdm(dm), "create subdm");
2538
2539 return SmartPetscObj<DM>(subdm);
2540 };
2541
2542 auto get_fe_post_proc = [&](auto post_proc_mesh) {
2543 auto add_parent_field_domain = [&](auto fe, auto op, auto field) {
2544 return setParentDofs(
2545 fe, field, op, bit(get_skin_parent_bit()),
2546
2547 [&]() {
2548 boost::shared_ptr<ForcesAndSourcesCore> fe_parent(
2549 new DomainParentEle(mField));
2550 return fe_parent;
2551 },
2552
2553 QUIET, Sev::noisy);
2554 };
2555
2556 auto post_proc_fe =
2557 boost::make_shared<PostProcEleDomainCont>(mField, post_proc_mesh);
2558 post_proc_fe->exeTestHook = [](FEMethod *fe_ptr) {
2559 return fe_ptr->numeredEntFiniteElementPtr->getBitRefLevel().test(
2560 get_start_bit() + nb_levels - 1);
2561 };
2562
2563 auto u_ptr = boost::make_shared<MatrixDouble>();
2564 auto grad_u_ptr = boost::make_shared<MatrixDouble>();
2565 auto h_ptr = boost::make_shared<VectorDouble>();
2566 auto grad_h_ptr = boost::make_shared<MatrixDouble>();
2567 auto p_ptr = boost::make_shared<VectorDouble>();
2568 auto g_ptr = boost::make_shared<VectorDouble>();
2569 auto grad_g_ptr = boost::make_shared<MatrixDouble>();
2570
2572 post_proc_fe->getOpPtrVector(), {H1});
2573
2575 post_proc_fe, "", UDO::OPSPACE, bit(get_skin_parent_bit()),
2576
2577 [&]() {
2578 boost::shared_ptr<ForcesAndSourcesCore> fe_parent(
2579 new DomainParentEle(mField));
2581 fe_parent->getOpPtrVector(), {H1});
2582 return fe_parent;
2583 },
2584
2585 QUIET, Sev::noisy);
2586
2587 CHKERR add_parent_field_domain(post_proc_fe, UDO::OPCOL, "U");
2588 post_proc_fe->getOpPtrVector().push_back(
2590 post_proc_fe->getOpPtrVector().push_back(
2592 grad_u_ptr));
2593
2594 CHKERR add_parent_field_domain(post_proc_fe, UDO::OPCOL, "H");
2595 post_proc_fe->getOpPtrVector().push_back(
2596 new OpCalculateScalarFieldValues("H", h_ptr));
2597 post_proc_fe->getOpPtrVector().push_back(
2598 new OpCalculateScalarFieldGradient<SPACE_DIM>("H", grad_h_ptr));
2599
2600 CHKERR add_parent_field_domain(post_proc_fe, UDO::OPCOL, "P");
2601 post_proc_fe->getOpPtrVector().push_back(
2602 new OpCalculateScalarFieldValues("P", p_ptr));
2603
2604 CHKERR add_parent_field_domain(post_proc_fe, UDO::OPCOL, "G");
2605 post_proc_fe->getOpPtrVector().push_back(
2606 new OpCalculateScalarFieldValues("G", g_ptr));
2607 post_proc_fe->getOpPtrVector().push_back(
2608 new OpCalculateScalarFieldGradient<SPACE_DIM>("G", grad_g_ptr));
2609
2611
2612 post_proc_fe->getOpPtrVector().push_back(
2613
2614 new OpPPMap(
2615 post_proc_fe->getPostProcMesh(), post_proc_fe->getMapGaussPts(),
2616
2617 {{"H", h_ptr}, {"P", p_ptr}, {"G", g_ptr}},
2618
2619 {{"U", u_ptr}, {"H_GRAD", grad_h_ptr}, {"G_GRAD", grad_g_ptr}},
2620
2621 {{"GRAD_U", grad_u_ptr}},
2622
2623 {}
2624
2625 )
2626
2627 );
2628
2629 return post_proc_fe;
2630 };
2631
2632 auto get_bdy_post_proc_fe = [&](auto post_proc_mesh) {
2633 auto add_parent_field_bdy = [&](auto fe, auto op, auto field) {
2634 return setParentDofs(
2635 fe, field, op, bit(get_skin_parent_bit()),
2636
2637 [&]() {
2638 boost::shared_ptr<ForcesAndSourcesCore> fe_parent(
2639 new BoundaryParentEle(mField));
2640 return fe_parent;
2641 },
2642
2643 QUIET, Sev::noisy);
2644 };
2645
2646 auto post_proc_fe =
2647 boost::make_shared<PostProcEleBdyCont>(mField, post_proc_mesh);
2648 post_proc_fe->exeTestHook = [](FEMethod *fe_ptr) {
2649 return fe_ptr->numeredEntFiniteElementPtr->getBitRefLevel().test(
2650 get_start_bit() + nb_levels - 1);
2651 };
2652
2653 CHKERR setParentDofs(
2654 post_proc_fe, "", UDO::OPSPACE, bit(get_skin_parent_bit()),
2655
2656 [&]() {
2657 boost::shared_ptr<ForcesAndSourcesCore> fe_parent(
2658 new BoundaryParentEle(mField));
2659 return fe_parent;
2660 },
2661
2662 QUIET, Sev::noisy);
2663
2664 struct OpGetNormal : public BoundaryEleOp {
2665
2666 OpGetNormal(boost::shared_ptr<VectorDouble> l_ptr,
2667 boost::shared_ptr<MatrixDouble> n_ptr)
2668 : BoundaryEleOp(NOSPACE, BoundaryEleOp::OPSPACE), ptrL(l_ptr),
2669 ptrNormal(n_ptr) {}
2670
2671 MoFEMErrorCode doWork(int side, EntityType type,
2674 auto t_l = getFTensor0FromVec(*ptrL);
2675 auto t_n_fe = getFTensor1NormalsAtGaussPts();
2676 ptrNormal->resize(getGaussPts().size2(), SPACE_DIM, false);
2677 auto t_n = getFTensor1FromMat<SPACE_DIM>(*ptrNormal);
2678 for (auto gg = 0; gg != getGaussPts().size2(); ++gg) {
2679 t_n(i) = t_n_fe(i) * t_l / std::sqrt(t_n_fe(i) * t_n_fe(i));
2680 ++t_n_fe;
2681 ++t_l;
2682 ++t_n;
2683 }
2685 };
2686
2687 protected:
2688 boost::shared_ptr<VectorDouble> ptrL;
2689 boost::shared_ptr<MatrixDouble> ptrNormal;
2690 };
2691
2692 auto u_ptr = boost::make_shared<MatrixDouble>();
2693 auto p_ptr = boost::make_shared<VectorDouble>();
2694 auto lambda_ptr = boost::make_shared<VectorDouble>();
2695 auto normal_l_ptr = boost::make_shared<MatrixDouble>();
2696
2697 CHKERR add_parent_field_bdy(post_proc_fe, UDO::OPCOL, "U");
2698 post_proc_fe->getOpPtrVector().push_back(
2700
2701 CHKERR add_parent_field_bdy(post_proc_fe, UDO::OPCOL, "L");
2702 post_proc_fe->getOpPtrVector().push_back(
2703 new OpCalculateScalarFieldValues("L", lambda_ptr));
2704 post_proc_fe->getOpPtrVector().push_back(
2705 new OpGetNormal(lambda_ptr, normal_l_ptr));
2706
2707 CHKERR add_parent_field_bdy(post_proc_fe, UDO::OPCOL, "P");
2708 post_proc_fe->getOpPtrVector().push_back(
2709 new OpCalculateScalarFieldValues("P", p_ptr));
2710
2711 auto op_ptr = new BoundaryEleOp(NOSPACE, BoundaryEleOp::OPSPACE);
2712 op_ptr->doWorkRhsHook = [&](DataOperator *base_op_ptr, int side,
2713 EntityType type,
2717 };
2718
2720
2721 post_proc_fe->getOpPtrVector().push_back(
2722
2723 new OpPPMap(post_proc_fe->getPostProcMesh(),
2724 post_proc_fe->getMapGaussPts(),
2725
2726 OpPPMap::DataMapVec{{"P", p_ptr}},
2727
2728 OpPPMap::DataMapMat{{"U", u_ptr}, {"L", normal_l_ptr}},
2729
2731
2733
2734 )
2735
2736 );
2737
2738 return post_proc_fe;
2739 };
2740
2741 auto get_lift_fe = [&]() {
2742 auto add_parent_field_bdy = [&](auto fe, auto op, auto field) {
2743 return setParentDofs(
2744 fe, field, op, bit(get_skin_parent_bit()),
2745
2746 [&]() {
2747 boost::shared_ptr<ForcesAndSourcesCore> fe_parent(
2748 new BoundaryParentEle(mField));
2749 return fe_parent;
2750 },
2751
2752 QUIET, Sev::noisy);
2753 };
2754
2755 auto fe = boost::make_shared<BoundaryEle>(mField);
2756 fe->exeTestHook = [](FEMethod *fe_ptr) {
2757 return fe_ptr->numeredEntFiniteElementPtr->getBitRefLevel().test(
2758 get_start_bit() + nb_levels - 1);
2759 };
2760
2761 auto lift_ptr = boost::make_shared<VectorDouble>();
2762 auto p_ptr = boost::make_shared<VectorDouble>();
2763 auto ents_ptr = boost::make_shared<Range>();
2764
2765 CHKERR setParentDofs(
2766 fe, "", UDO::OPSPACE, bit(get_skin_parent_bit()),
2767
2768 [&]() {
2769 boost::shared_ptr<ForcesAndSourcesCore> fe_parent(
2770 new BoundaryParentEle(mField));
2771 return fe_parent;
2772 },
2773
2774 QUIET, Sev::noisy);
2775
2776 std::vector<const CubitMeshSets *> vec_ptr;
2777 CHKERR mField.getInterface<MeshsetsManager>()->getCubitMeshsetPtr(
2778 std::regex("LIFT"), vec_ptr);
2779 for (auto m_ptr : vec_ptr) {
2780 auto meshset = m_ptr->getMeshset();
2781 Range ents;
2782 CHKERR mField.get_moab().get_entities_by_dimension(meshset, SPACE_DIM - 1,
2783 ents, true);
2784 ents_ptr->merge(ents);
2785 }
2786
2787 MOFEM_LOG("FS", Sev::noisy) << "Lift ents " << (*ents_ptr);
2788 CHKERR add_parent_field_bdy(fe, UDO::OPROW, "L");
2789 CHKERR add_parent_field_bdy(fe, UDO::OPCOL, "L");
2790 fe->getOpPtrVector().push_back(
2791 new OpCalculateScalarFieldValues("L", p_ptr));
2792 fe->getOpPtrVector().push_back(
2793 new OpCalculateLift("L", p_ptr, lift_ptr, ents_ptr));
2794
2795 return std::make_pair(fe, lift_ptr);
2796 };
2797
2798 auto set_post_proc_monitor = [&](auto ts) {
2800 DM dm;
2801 CHKERR TSGetDM(ts, &dm);
2802 boost::shared_ptr<FEMethod> null_fe;
2803 auto post_proc_mesh = boost::make_shared<moab::Core>();
2804 auto monitor_ptr = boost::make_shared<Monitor>(
2805 SmartPetscObj<DM>(dm, true), post_proc_mesh,
2806 get_fe_post_proc(post_proc_mesh), get_bdy_post_proc_fe(post_proc_mesh),
2807 get_lift_fe());
2808 CHKERR DMMoFEMTSSetMonitor(dm, ts, simple->getDomainFEName(), null_fe,
2809 null_fe, monitor_ptr);
2811 };
2812
2813 auto dm = simple->getDM();
2814 auto ts = createTS(mField.get_comm());
2815 CHKERR TSSetDM(ts, dm);
2816
2817 auto ts_pre_post_proc = boost::make_shared<TSPrePostProc>();
2818 tsPrePostProc = ts_pre_post_proc;
2819
2820 if (auto ptr = tsPrePostProc.lock()) {
2821
2822 ptr->fsRawPtr = this;
2823 ptr->solverSubDM = create_solver_dm(simple->getDM());
2824 ptr->globSol = createDMVector(dm);
2825 CHKERR DMoFEMMeshToLocalVector(dm, ptr->globSol, INSERT_VALUES,
2826 SCATTER_FORWARD);
2827 CHKERR VecAssemblyBegin(ptr->globSol);
2828 CHKERR VecAssemblyEnd(ptr->globSol);
2829
2830 auto sub_ts = pip_mng->createTSIM(ptr->solverSubDM);
2831
2832 CHKERR set_post_proc_monitor(sub_ts);
2833
2834 // Add monitor to time solver
2835 CHKERR TSSetFromOptions(ts);
2836 CHKERR ptr->tsSetUp(ts);
2837 CHKERR TSSetUp(ts);
2838
2839 auto print_fields_in_section = [&]() {
2841 auto section = mField.getInterface<ISManager>()->sectionCreate(
2842 simple->getProblemName());
2843 PetscInt num_fields;
2844 CHKERR PetscSectionGetNumFields(section, &num_fields);
2845 for (int f = 0; f < num_fields; ++f) {
2846 const char *field_name;
2847 CHKERR PetscSectionGetFieldName(section, f, &field_name);
2848 MOFEM_LOG("FS", Sev::inform)
2849 << "Field " << f << " " << std::string(field_name);
2850 }
2852 };
2853
2854 CHKERR print_fields_in_section();
2855
2856 CHKERR TSSolve(ts, ptr->globSol);
2857 }
2858
2860}
2861
2862/**
2863 * @brief Main function for free surface simulation
2864 * @param argc Number of command line arguments
2865 * @param argv Array of command line argument strings
2866 * @return Exit code (0 for success)
2867 *
2868 * Main driver function that:
2869 * 1. Initializes MoFEM/PETSc and MOAB data structures
2870 * 2. Sets up logging channels for debugging output
2871 * 3. Registers MoFEM discrete manager with PETSc
2872 * 4. Creates mesh database (MOAB) and finite element database (MoFEM)
2873 * 5. Runs the complete free surface simulation
2874 * 6. Handles cleanup and finalization
2875 *
2876 * Command line options are read from param_file.petsc and command line.
2877 * Python initialization is optional (controlled by PYTHON_INIT_SURFACE).
2878 */
2879int main(int argc, char *argv[]) {
2880
2881#ifdef PYTHON_INIT_SURFACE
2882 Py_Initialize();
2883#endif
2884
2885 // Initialisation of MoFEM/PETSc and MOAB data structures
2886 const char param_file[] = "param_file.petsc";
2887 MoFEM::Core::Initialize(&argc, &argv, param_file, help);
2888
2889 // Add logging channel for example
2890 auto core_log = logging::core::get();
2891 core_log->add_sink(LogManager::createSink(LogManager::getStrmWorld(), "FS"));
2892 LogManager::setLog("FS");
2893 MOFEM_LOG_TAG("FS", "free surface");
2894
2895 try {
2896
2897 //! [Register MoFEM discrete manager in PETSc]
2898 DMType dm_name = "DMMOFEM";
2899 CHKERR DMRegister_MoFEM(dm_name);
2900 //! [Register MoFEM discrete manager in PETSc
2901
2902 //! [Create MoAB]
2903 moab::Core mb_instance; ///< mesh database
2904 moab::Interface &moab = mb_instance; ///< mesh database interface
2905 //! [Create MoAB]
2906
2907 //! [Create MoFEM]
2908 MoFEM::Core core(moab); ///< finite element database
2909 MoFEM::Interface &m_field = core; ///< finite element database interface
2910 //! [Create MoFEM]
2911
2912 //! [FreeSurface]
2913 FreeSurface ex(m_field);
2914 CHKERR ex.runProblem();
2915 //! [FreeSurface]
2916 }
2918
2920
2921#ifdef PYTHON_INIT_SURFACE
2922 if (Py_FinalizeEx() < 0) {
2923 exit(120);
2924 }
2925#endif
2926}
2927
2928std::vector<Range>
2930
2931 auto &moab = mField.get_moab();
2932 auto bit_mng = mField.getInterface<BitRefManager>();
2933 auto comm_mng = mField.getInterface<CommInterface>();
2934
2935 Range vertices;
2936 CHK_THROW_MESSAGE(bit_mng->getEntitiesByTypeAndRefLevel(
2937 bit(0), BitRefLevel().set(), MBVERTEX, vertices),
2938 "can not get vertices on bit 0");
2939
2940 auto &dofs_mi = mField.get_dofs()->get<Unique_mi_tag>();
2941 auto field_bit_number = mField.get_field_bit_number("H");
2942
2943 Range plus_range, minus_range;
2944 std::vector<EntityHandle> plus, minus;
2945
2946 // get vertices on level 0 on plus and minus side
2947 for (auto p = vertices.pair_begin(); p != vertices.pair_end(); ++p) {
2948
2949 const auto f = p->first;
2950 const auto s = p->second;
2951
2952 // Lowest Dof UId for given field (field bit number) on entity f
2953 const auto lo_uid = DofEntity::getLoFieldEntityUId(field_bit_number, f);
2954 const auto hi_uid = DofEntity::getHiFieldEntityUId(field_bit_number, s);
2955 auto it = dofs_mi.lower_bound(lo_uid);
2956 const auto hi_it = dofs_mi.upper_bound(hi_uid);
2957
2958 plus.clear();
2959 minus.clear();
2960 plus.reserve(std::distance(it, hi_it));
2961 minus.reserve(std::distance(it, hi_it));
2962
2963 for (; it != hi_it; ++it) {
2964 const auto v = (*it)->getFieldData();
2965 if (v > 0)
2966 plus.push_back((*it)->getEnt());
2967 else
2968 minus.push_back((*it)->getEnt());
2969 }
2970
2971 plus_range.insert_list(plus.begin(), plus.end());
2972 minus_range.insert_list(minus.begin(), minus.end());
2973 }
2974
2975 MOFEM_LOG_CHANNEL("SYNC");
2976 MOFEM_TAG_AND_LOG("SYNC", Sev::noisy, "FS")
2977 << "Plus range " << plus_range << endl;
2978 MOFEM_TAG_AND_LOG("SYNC", Sev::noisy, "FS")
2979 << "Minus range " << minus_range << endl;
2981
2982 auto get_elems = [&](auto &ents, auto bit, auto mask) {
2983 Range adj;
2984 CHK_MOAB_THROW(moab.get_adjacencies(ents, SPACE_DIM, false, adj,
2985 moab::Interface::UNION),
2986 "can not get adjacencies");
2987 CHK_THROW_MESSAGE(bit_mng->filterEntitiesByRefLevel(bit, mask, adj),
2988 "can not filter elements with bit 0");
2989 return adj;
2990 };
2991
2992 CHKERR comm_mng->synchroniseEntities(plus_range);
2993 CHKERR comm_mng->synchroniseEntities(minus_range);
2994
2995 std::vector<Range> ele_plus(nb_levels), ele_minus(nb_levels);
2996 ele_plus[0] = get_elems(plus_range, bit(0), BitRefLevel().set());
2997 ele_minus[0] = get_elems(minus_range, bit(0), BitRefLevel().set());
2998 auto common = intersect(ele_plus[0], ele_minus[0]);
2999 ele_plus[0] = subtract(ele_plus[0], common);
3000 ele_minus[0] = subtract(ele_minus[0], common);
3001
3002 auto get_children = [&](auto &p, auto &c) {
3004 CHKERR bit_mng->updateRangeByChildren(p, c);
3005 c = c.subset_by_dimension(SPACE_DIM);
3007 };
3008
3009 for (auto l = 1; l != nb_levels; ++l) {
3010 CHK_THROW_MESSAGE(get_children(ele_plus[l - 1], ele_plus[l]),
3011 "get children");
3012 CHK_THROW_MESSAGE(get_children(ele_minus[l - 1], ele_minus[l]),
3013 "get children");
3014 }
3015
3016 auto get_level = [&](auto &p, auto &m, auto z, auto bit, auto mask) {
3017 Range l;
3019 bit_mng->getEntitiesByDimAndRefLevel(bit, mask, SPACE_DIM, l),
3020 "can not get vertices on bit");
3021 l = subtract(l, p);
3022 l = subtract(l, m);
3023 for (auto f = 0; f != z; ++f) {
3024 Range conn;
3025 CHK_MOAB_THROW(moab.get_connectivity(l, conn, true), "");
3026 CHKERR mField.getInterface<CommInterface>()->synchroniseEntities(conn);
3027 l = get_elems(conn, bit, mask);
3028 }
3029 return l;
3030 };
3031
3032 std::vector<Range> vec_levels(nb_levels);
3033 for (auto l = nb_levels - 1; l >= 0; --l) {
3034 vec_levels[l] = get_level(ele_plus[l], ele_minus[l], 2 * overlap, bit(l),
3035 BitRefLevel().set());
3036 }
3037
3038 if constexpr (debug) {
3039 if (mField.get_comm_size() == 1) {
3040 for (auto l = 0; l != nb_levels; ++l) {
3041 std::string name = (boost::format("out_r%d.h5m") % l).str();
3042 CHK_THROW_MESSAGE(save_range(mField.get_moab(), name, vec_levels[l]),
3043 "save mesh");
3044 }
3045 }
3046 }
3047
3048 return vec_levels;
3049}
3050
3053
3054 auto bit_mng = mField.getInterface<BitRefManager>();
3055
3056 BitRefLevel start_mask;
3057 for (auto s = 0; s != get_start_bit(); ++s)
3058 start_mask[s] = true;
3059
3060 // store prev_level
3061 Range prev_level;
3062 CHKERR bit_mng->getEntitiesByRefLevel(bit(get_current_bit()),
3063 BitRefLevel().set(), prev_level);
3064 Range prev_level_skin;
3065 CHKERR bit_mng->getEntitiesByRefLevel(bit(get_skin_parent_bit()),
3066 BitRefLevel().set(), prev_level_skin);
3067 // reset bit ref levels
3068 CHKERR bit_mng->lambdaBitRefLevel(
3069 [&](EntityHandle ent, BitRefLevel &bit) { bit &= start_mask; });
3070 CHKERR bit_mng->setNthBitRefLevel(prev_level, get_projection_bit(), true);
3071 CHKERR bit_mng->setNthBitRefLevel(prev_level_skin, get_skin_projection_bit(),
3072 true);
3073
3074 // set refinement levels
3075 auto set_levels = [&](auto &&
3076 vec_levels /*entities are refined on each level*/) {
3078
3079 // start with zero level, which is the coarsest mesh
3080 Range level0;
3081 CHKERR bit_mng->getEntitiesByRefLevel(bit(0), BitRefLevel().set(), level0);
3082 CHKERR bit_mng->setNthBitRefLevel(level0, get_start_bit(), true);
3083
3084 // get lower dimension entities
3085 auto get_adj = [&](auto ents) {
3086 Range conn;
3087 CHK_MOAB_THROW(mField.get_moab().get_connectivity(ents, conn, true),
3088 "get conn");
3089 for (auto d = 1; d != SPACE_DIM; ++d) {
3090 CHK_MOAB_THROW(mField.get_moab().get_adjacencies(
3091 ents.subset_by_dimension(SPACE_DIM), d, false, conn,
3092 moab::Interface::UNION),
3093 "get adj");
3094 }
3095 ents.merge(conn);
3096 return ents;
3097 };
3098
3099 // set bit levels
3100 for (auto l = 1; l != nb_levels; ++l) {
3101 Range level_prev;
3102 CHKERR bit_mng->getEntitiesByDimAndRefLevel(bit(get_start_bit() + l - 1),
3103 BitRefLevel().set(),
3104 SPACE_DIM, level_prev);
3105 Range parents;
3106 CHKERR bit_mng->updateRangeByParent(vec_levels[l], parents);
3107 // subtract entities from previous level, which are refined, so should be
3108 // not there
3109 level_prev = subtract(level_prev, parents);
3110 // and instead add their children
3111 level_prev.merge(vec_levels[l]);
3112 // set bit to each level
3113 CHKERR bit_mng->setNthBitRefLevel(level_prev, get_start_bit() + l, true);
3114 }
3115
3116 // set bit levels to lower dimension entities
3117 for (auto l = 1; l != nb_levels; ++l) {
3118 Range level;
3119 CHKERR bit_mng->getEntitiesByDimAndRefLevel(
3120 bit(get_start_bit() + l), BitRefLevel().set(), SPACE_DIM, level);
3121 level = get_adj(level);
3122 CHKERR mField.getInterface<CommInterface>()->synchroniseEntities(level);
3123 CHKERR bit_mng->setNthBitRefLevel(level, get_start_bit() + l, true);
3124 }
3125
3127 };
3128
3129 // resolve skin between refined levels
3130 auto set_skins = [&]() {
3132
3133 moab::Skinner skinner(&mField.get_moab());
3134 ParallelComm *pcomm =
3135 ParallelComm::get_pcomm(&mField.get_moab(), MYPCOMM_INDEX);
3136
3137 // get skin of bit level
3138 auto get_bit_skin = [&](BitRefLevel bit, BitRefLevel mask) {
3139 Range bit_ents;
3142 bit, mask, SPACE_DIM, bit_ents),
3143 "can't get bit level");
3144 Range bit_skin;
3145 CHK_MOAB_THROW(skinner.find_skin(0, bit_ents, false, bit_skin),
3146 "can't get skin");
3147 return bit_skin;
3148 };
3149
3150 auto get_level_skin = [&]() {
3151 Range skin;
3152 BitRefLevel bit_prev;
3153 for (auto l = 1; l != nb_levels; ++l) {
3154 auto skin_level_mesh = get_bit_skin(bit(l), BitRefLevel().set());
3155 // filter (remove) all entities which are on partition borders
3156 CHKERR pcomm->filter_pstatus(skin_level_mesh,
3157 PSTATUS_SHARED | PSTATUS_MULTISHARED,
3158 PSTATUS_NOT, -1, nullptr);
3159 auto skin_level =
3160 get_bit_skin(bit(get_start_bit() + l), BitRefLevel().set());
3161 skin_level = subtract(skin_level,
3162 skin_level_mesh); // get only internal skins, not
3163 // on the body boundary
3164 // get lower dimension adjacencies (FIXME: add edges if 3D)
3165 Range skin_level_verts;
3166 CHKERR mField.get_moab().get_connectivity(skin_level, skin_level_verts,
3167 true);
3168 skin_level.merge(skin_level_verts);
3169
3170 // remove previous level
3171 bit_prev.set(l - 1);
3172 Range level_prev;
3173 CHKERR bit_mng->getEntitiesByRefLevel(bit_prev, BitRefLevel().set(),
3174 level_prev);
3175 skin.merge(subtract(skin_level, level_prev));
3176 }
3177
3178 return skin;
3179 };
3180
3181 auto resolve_shared = [&](auto &&skin) {
3182 Range tmp_skin = skin;
3183
3184 map<int, Range> map_procs;
3185 CHKERR mField.getInterface<CommInterface>()->synchroniseEntities(
3186 tmp_skin, &map_procs);
3187
3188 Range from_other_procs; // entities which also exist on other processors
3189 for (auto &m : map_procs) {
3190 if (m.first != mField.get_comm_rank()) {
3191 from_other_procs.merge(m.second);
3192 }
3193 }
3194
3195 auto common = intersect(
3196 skin, from_other_procs); // entities which are on internal skin
3197 skin.merge(from_other_procs);
3198
3199 // entities which are on processor borders, and several processors are not
3200 // true skin.
3201 if (!common.empty()) {
3202 // skin is internal exist on other procs
3203 skin = subtract(skin, common);
3204 }
3205
3206 return skin;
3207 };
3208
3209 // get parents of entities
3210 auto get_parent_level_skin = [&](auto skin) {
3211 Range skin_parents;
3212 CHKERR bit_mng->updateRangeByParent(
3213 skin.subset_by_dimension(SPACE_DIM - 1), skin_parents);
3214 Range skin_parent_verts;
3215 CHKERR mField.get_moab().get_connectivity(skin_parents, skin_parent_verts,
3216 true);
3217 skin_parents.merge(skin_parent_verts);
3218 CHKERR mField.getInterface<CommInterface>()->synchroniseEntities(
3219 skin_parents);
3220 return skin_parents;
3221 };
3222
3223 auto child_skin = resolve_shared(get_level_skin());
3224 auto parent_skin = get_parent_level_skin(child_skin);
3225
3226 child_skin = subtract(child_skin, parent_skin);
3227 CHKERR bit_mng->setNthBitRefLevel(child_skin, get_skin_child_bit(), true);
3228 CHKERR bit_mng->setNthBitRefLevel(parent_skin, get_skin_parent_bit(), true);
3229
3231 };
3232
3233 // take last level, remove childs on boarder, and set bit
3234 auto set_current = [&]() {
3236 Range last_level;
3237 CHKERR bit_mng->getEntitiesByRefLevel(bit(get_start_bit() + nb_levels - 1),
3238 BitRefLevel().set(), last_level);
3239 Range skin_child;
3240 CHKERR bit_mng->getEntitiesByRefLevel(bit(get_skin_child_bit()),
3241 BitRefLevel().set(), skin_child);
3242
3243 last_level = subtract(last_level, skin_child);
3244 CHKERR bit_mng->setNthBitRefLevel(last_level, get_current_bit(), true);
3246 };
3247
3248 // set bits to levels
3249 CHKERR set_levels(findEntitiesCrossedByPhaseInterface(overlap));
3250 // set bits to skin
3251 CHKERR set_skins();
3252 // set current level bit
3253 CHKERR set_current();
3254
3255 if constexpr (debug) {
3256 if (mField.get_comm_size() == 1) {
3257 for (auto l = 0; l != nb_levels; ++l) {
3258 std::string name = (boost::format("out_level%d.h5m") % l).str();
3259 CHKERR bit_mng->writeBitLevel(BitRefLevel().set(get_start_bit() + l),
3260 BitRefLevel().set(), name.c_str(), "MOAB",
3261 "PARALLEL=WRITE_PART");
3262 }
3263 CHKERR bit_mng->writeBitLevel(BitRefLevel().set(get_current_bit()),
3264 BitRefLevel().set(), "current_bit.h5m",
3265 "MOAB", "PARALLEL=WRITE_PART");
3266 CHKERR bit_mng->writeBitLevel(BitRefLevel().set(get_projection_bit()),
3267 BitRefLevel().set(), "projection_bit.h5m",
3268 "MOAB", "PARALLEL=WRITE_PART");
3269
3270 CHKERR bit_mng->writeBitLevel(BitRefLevel().set(get_skin_child_bit()),
3271 BitRefLevel().set(), "skin_child_bit.h5m",
3272 "MOAB", "PARALLEL=WRITE_PART");
3273 CHKERR bit_mng->writeBitLevel(BitRefLevel().set(get_skin_parent_bit()),
3274 BitRefLevel().set(), "skin_parent_bit.h5m",
3275 "MOAB", "PARALLEL=WRITE_PART");
3276 }
3277 }
3278
3280};
3281
3283 boost::shared_ptr<FEMethod> fe_top, std::string field_name,
3285 BitRefLevel child_ent_bit,
3286 boost::function<boost::shared_ptr<ForcesAndSourcesCore>()> get_elem,
3287 int verbosity, LogManager::SeverityLevel sev) {
3289
3290 /**
3291 * @brief Collect data from parent elements to child
3292 */
3293 boost::function<void(boost::shared_ptr<ForcesAndSourcesCore>, int)>
3294 add_parent_level =
3295 [&](boost::shared_ptr<ForcesAndSourcesCore> parent_fe_pt, int level) {
3296 // Evaluate if not last parent element
3297 if (level > 0) {
3298
3299 // Create domain parent FE
3300 auto fe_ptr_current = get_elem();
3301
3302 // Call next level
3303 add_parent_level(
3304 boost::dynamic_pointer_cast<ForcesAndSourcesCore>(
3305 fe_ptr_current),
3306 level - 1);
3307
3308 // Add data to curent fe level
3310
3311 // Only base
3312 parent_fe_pt->getOpPtrVector().push_back(
3313
3315
3316 H1, op, fe_ptr_current,
3317
3318 BitRefLevel().set(), BitRefLevel().set(),
3319
3320 child_ent_bit, BitRefLevel().set(),
3321
3322 verbosity, sev));
3323
3324 } else {
3325
3326 // Filed data
3327 parent_fe_pt->getOpPtrVector().push_back(
3328
3330
3331 field_name, op, fe_ptr_current,
3332
3333 BitRefLevel().set(), BitRefLevel().set(),
3334
3335 child_ent_bit, BitRefLevel().set(),
3336
3337 verbosity, sev));
3338 }
3339 }
3340 };
3341
3342 add_parent_level(boost::dynamic_pointer_cast<ForcesAndSourcesCore>(fe_top),
3343 nb_levels);
3344
3346}
3347
3350
3351 if (auto ptr = tsPrePostProc.lock()) {
3352
3353 /**
3354 * @brief cut-off values at nodes, i.e. abs("H") <= 1
3355 *
3356 */
3357 auto cut_off_dofs = [&]() {
3359
3360 auto &m_field = ptr->fsRawPtr->mField;
3361
3362 Range current_verts;
3363 auto bit_mng = m_field.getInterface<BitRefManager>();
3365 bit(get_current_bit()), BitRefLevel().set(), MBVERTEX, current_verts);
3366
3367 auto cut_off_verts = [&](boost::shared_ptr<FieldEntity> ent_ptr) {
3369 for (auto &h : ent_ptr->getEntFieldData()) {
3370 h = cut_off(h);
3371 }
3373 };
3374
3375 auto field_blas = m_field.getInterface<FieldBlas>();
3376 CHKERR field_blas->fieldLambdaOnEntities(cut_off_verts, "H",
3377 &current_verts);
3379 };
3380
3381 CHKERR cut_off_dofs();
3382 }
3383
3384 if (auto ptr = tsPrePostProc.lock()) {
3385 MOFEM_LOG("FS", Sev::inform) << "Run step pre proc";
3386
3387 auto &m_field = ptr->fsRawPtr->mField;
3388 auto simple = m_field.getInterface<Simple>();
3389
3390 // get vector norm
3391 auto get_norm = [&](auto x) {
3392 double nrm;
3393 CHKERR VecNorm(x, NORM_2, &nrm);
3394 return nrm;
3395 };
3396
3397 // refine problem and project data, including theta data
3398 auto refine_problem = [&]() {
3400 MOFEM_LOG("FS", Sev::inform) << "Refine problem";
3401 CHKERR ptr->fsRawPtr->refineMesh(refine_overlap);
3402 CHKERR ptr->fsRawPtr->projectData();
3404 };
3405
3406 // set new jacobin operator, since problem and thus tangent matrix size has
3407 // changed
3408 auto set_jacobian_operators = [&]() {
3410 ptr->subB = createDMMatrix(ptr->solverSubDM);
3411 CHKERR KSPReset(ptr->subKSP);
3413 };
3414
3415 // set new solution
3416 auto set_solution = [&]() {
3418 MOFEM_LOG("FS", Sev::inform) << "Set solution";
3419
3420 PetscObjectState state;
3421
3422 // Record the state, and set it again. This is to fool PETSc that solution
3423 // vector is not updated. Otherwise PETSc will treat every step as a first
3424 // step.
3425
3426 // globSol is updated as result mesh refinement - this is not really set
3427 // a new solution.
3428
3429 CHKERR PetscObjectStateGet(getPetscObject(ptr->globSol.get()), &state);
3430 CHKERR DMoFEMMeshToLocalVector(simple->getDM(), ptr->globSol,
3431 INSERT_VALUES, SCATTER_FORWARD);
3432 CHKERR PetscObjectStateSet(getPetscObject(ptr->globSol.get()), state);
3433 MOFEM_LOG("FS", Sev::verbose)
3434 << "Set solution, vector norm " << get_norm(ptr->globSol);
3436 };
3437
3438 PetscBool is_theta;
3439 PetscObjectTypeCompare((PetscObject)ts, TSTHETA, &is_theta);
3440 if (is_theta) {
3441
3442 CHKERR refine_problem(); // refine problem
3443 CHKERR set_jacobian_operators(); // set new jacobian
3444 CHKERR set_solution(); // set solution
3445
3446 } else {
3447 SETERRQ(PETSC_COMM_WORLD, MOFEM_NOT_IMPLEMENTED,
3448 "Sorry, only TSTheta handling is implemented");
3449 }
3450
3451 // Need barriers, some functions in TS solver need are called collectively
3452 // and require the same state of variables
3453 PetscBarrier((PetscObject)ts);
3454
3455 MOFEM_LOG_CHANNEL("SYNC");
3456 MOFEM_TAG_AND_LOG("SYNC", Sev::verbose, "FS") << "PreProc done";
3457 MOFEM_LOG_SEVERITY_SYNC(m_field.get_comm(), Sev::verbose);
3458 }
3459
3461}
3462
3464 if (auto ptr = tsPrePostProc.lock()) {
3465 auto &m_field = ptr->fsRawPtr->mField;
3466 MOFEM_LOG_CHANNEL("SYNC");
3467 MOFEM_TAG_AND_LOG("SYNC", Sev::verbose, "FS") << "PostProc done";
3468 MOFEM_LOG_SEVERITY_SYNC(m_field.get_comm(), Sev::verbose);
3469 }
3470 return 0;
3471}
3472
3473MoFEMErrorCode TSPrePostProc::tsSetIFunction(TS ts, PetscReal t, Vec u, Vec u_t,
3474 Vec f, void *ctx) {
3476 if (auto ptr = tsPrePostProc.lock()) {
3477 auto sub_u = ptr->getSubVector();
3478 auto sub_u_t = vectorDuplicate(sub_u);
3479 auto sub_f = vectorDuplicate(sub_u);
3480 auto scatter = ptr->getScatter(sub_u, u, R);
3481
3482 auto apply_scatter_and_update = [&](auto x, auto sub_x) {
3484 CHKERR VecScatterBegin(scatter, x, sub_x, INSERT_VALUES, SCATTER_REVERSE);
3485 CHKERR VecScatterEnd(scatter, x, sub_x, INSERT_VALUES, SCATTER_REVERSE);
3486 CHKERR VecGhostUpdateBegin(sub_x, INSERT_VALUES, SCATTER_FORWARD);
3487 CHKERR VecGhostUpdateEnd(sub_x, INSERT_VALUES, SCATTER_FORWARD);
3489 };
3490
3491 CHKERR apply_scatter_and_update(u, sub_u);
3492 CHKERR apply_scatter_and_update(u_t, sub_u_t);
3493
3494 CHKERR TsSetIFunction(ts, t, sub_u, sub_u_t, sub_f, ptr->tsCtxPtr.get());
3495 CHKERR VecScatterBegin(scatter, sub_f, f, INSERT_VALUES, SCATTER_FORWARD);
3496 CHKERR VecScatterEnd(scatter, sub_f, f, INSERT_VALUES, SCATTER_FORWARD);
3497 }
3499}
3500
3501MoFEMErrorCode TSPrePostProc::tsSetIJacobian(TS ts, PetscReal t, Vec u, Vec u_t,
3502 PetscReal a, Mat A, Mat B,
3503 void *ctx) {
3505 if (auto ptr = tsPrePostProc.lock()) {
3506 auto sub_u = ptr->getSubVector();
3507 auto sub_u_t = vectorDuplicate(sub_u);
3508 auto scatter = ptr->getScatter(sub_u, u, R);
3509
3510 auto apply_scatter_and_update = [&](auto x, auto sub_x) {
3512 CHKERR VecScatterBegin(scatter, x, sub_x, INSERT_VALUES, SCATTER_REVERSE);
3513 CHKERR VecScatterEnd(scatter, x, sub_x, INSERT_VALUES, SCATTER_REVERSE);
3514 CHKERR VecGhostUpdateBegin(sub_x, INSERT_VALUES, SCATTER_FORWARD);
3515 CHKERR VecGhostUpdateEnd(sub_x, INSERT_VALUES, SCATTER_FORWARD);
3517 };
3518
3519 CHKERR apply_scatter_and_update(u, sub_u);
3520 CHKERR apply_scatter_and_update(u_t, sub_u_t);
3521
3522 CHKERR TsSetIJacobian(ts, t, sub_u, sub_u_t, a, ptr->subB, ptr->subB,
3523 ptr->tsCtxPtr.get());
3524 }
3526}
3527
3528MoFEMErrorCode TSPrePostProc::tsMonitor(TS ts, PetscInt step, PetscReal t,
3529 Vec u, void *ctx) {
3531 if (auto ptr = tsPrePostProc.lock()) {
3532 auto get_norm = [&](auto x) {
3533 double nrm;
3534 CHKERR VecNorm(x, NORM_2, &nrm);
3535 return nrm;
3536 };
3537
3538 auto sub_u = ptr->getSubVector();
3539 auto scatter = ptr->getScatter(sub_u, u, R);
3540 CHKERR VecScatterBegin(scatter, u, sub_u, INSERT_VALUES, SCATTER_REVERSE);
3541 CHKERR VecScatterEnd(scatter, u, sub_u, INSERT_VALUES, SCATTER_REVERSE);
3542 CHKERR VecGhostUpdateBegin(sub_u, INSERT_VALUES, SCATTER_FORWARD);
3543 CHKERR VecGhostUpdateEnd(sub_u, INSERT_VALUES, SCATTER_FORWARD);
3544
3545 MOFEM_LOG("FS", Sev::verbose)
3546 << "u norm " << get_norm(u) << " u sub nom " << get_norm(sub_u);
3547
3548 CHKERR TsMonitorSet(ts, step, t, sub_u, ptr->tsCtxPtr.get());
3549 }
3551}
3552
3555 if (auto ptr = tsPrePostProc.lock()) {
3556 MOFEM_LOG("FS", Sev::verbose) << "SetUP sub PC";
3557 ptr->subKSP = createKSP(ptr->fsRawPtr->mField.get_comm());
3558 CHKERR KSPSetFromOptions(ptr->subKSP);
3559 }
3561};
3562
3563MoFEMErrorCode TSPrePostProc::pcApply(PC pc, Vec pc_f, Vec pc_x) {
3565 if (auto ptr = tsPrePostProc.lock()) {
3566 auto sub_x = ptr->getSubVector();
3567 auto sub_f = vectorDuplicate(sub_x);
3568 auto scatter = ptr->getScatter(sub_x, pc_x, R);
3569 CHKERR VecScatterBegin(scatter, pc_f, sub_f, INSERT_VALUES,
3570 SCATTER_REVERSE);
3571 CHKERR VecScatterEnd(scatter, pc_f, sub_f, INSERT_VALUES, SCATTER_REVERSE);
3572 CHKERR KSPSetOperators(ptr->subKSP, ptr->subB, ptr->subB);
3573 MOFEM_LOG("FS", Sev::verbose) << "PCShell solve";
3574 CHKERR KSPSolve(ptr->subKSP, sub_f, sub_x);
3575 MOFEM_LOG("FS", Sev::verbose) << "PCShell solve <- done";
3576 CHKERR VecScatterBegin(scatter, sub_x, pc_x, INSERT_VALUES,
3577 SCATTER_FORWARD);
3578 CHKERR VecScatterEnd(scatter, sub_x, pc_x, INSERT_VALUES, SCATTER_FORWARD);
3579 }
3581};
3582
3584 if (auto ptr = tsPrePostProc.lock()) {
3585 auto prb_ptr = ptr->fsRawPtr->mField.get_problem("SUB_SOLVER");
3586 if (auto sub_data = prb_ptr->getSubData()) {
3587 auto is = sub_data->getSmartColIs();
3588 VecScatter s;
3589 if (fr == R) {
3590 CHK_THROW_MESSAGE(VecScatterCreate(x, PETSC_NULLPTR, y, is, &s),
3591 "crate scatter");
3592 } else {
3593 CHK_THROW_MESSAGE(VecScatterCreate(x, is, y, PETSC_NULLPTR, &s),
3594 "crate scatter");
3595 }
3596 return SmartPetscObj<VecScatter>(s);
3597 }
3598 }
3601}
3602
3606
3608
3609 auto &m_field = fsRawPtr->mField;
3610 auto simple = m_field.getInterface<Simple>();
3611
3613
3614 auto dm = simple->getDM();
3615
3617 CHKERR TSSetIFunction(ts, globRes, tsSetIFunction, nullptr);
3618 CHKERR TSSetIJacobian(ts, PETSC_NULLPTR, PETSC_NULLPTR, tsSetIJacobian, nullptr);
3619 CHKERR TSMonitorSet(ts, tsMonitor, fsRawPtr, PETSC_NULLPTR);
3620
3621 SNES snes;
3622 CHKERR TSGetSNES(ts, &snes);
3623 auto snes_ctx_ptr = getDMSnesCtx(dm);
3624
3625 auto set_section_monitor = [&](auto snes) {
3627 CHKERR SNESMonitorSet(snes,
3628 (MoFEMErrorCode(*)(SNES, PetscInt, PetscReal,
3629 void *))MoFEMSNESMonitorFields,
3630 (void *)(snes_ctx_ptr.get()), nullptr);
3632 };
3633
3634 CHKERR set_section_monitor(snes);
3635
3636 auto ksp = createKSP(m_field.get_comm());
3637 CHKERR KSPSetType(ksp, KSPPREONLY); // Run KSP internally in ShellPC
3638 auto sub_pc = createPC(fsRawPtr->mField.get_comm());
3639 CHKERR PCSetType(sub_pc, PCSHELL);
3640 CHKERR PCShellSetSetUp(sub_pc, pcSetup);
3641 CHKERR PCShellSetApply(sub_pc, pcApply);
3642 CHKERR KSPSetPC(ksp, sub_pc);
3643 CHKERR SNESSetKSP(snes, ksp);
3644
3647
3648 CHKERR TSSetPreStep(ts, tsPreProc);
3649 CHKERR TSSetPostStep(ts, tsPostProc);
3650
3652}
3653
3654//! [Check]
3657 PetscInt test_nb = 0;
3658 PetscBool test_flg = PETSC_FALSE;
3659 double regression_value = 0.0;
3660 CHKERR PetscOptionsGetInt(PETSC_NULLPTR, "", "-test", &test_nb, &test_flg);
3661 CHKERR PetscOptionsGetScalar(PETSC_NULLPTR, "", "-regression_value", &regression_value, PETSC_NULLPTR);
3662
3663 if (test_flg) {
3664 auto simple = mField.getInterface<Simple>();
3665 auto T = createDMVector(simple->getDM());
3666 CHKERR DMoFEMMeshToLocalVector(simple->getDM(), T, INSERT_VALUES,
3667 SCATTER_FORWARD);
3668 double nrm2;
3669 CHKERR VecNorm(T, NORM_2, &nrm2);
3670 MOFEM_LOG("FS", Sev::verbose) << "Regression norm " << nrm2;
3671 switch (test_nb) {
3672 case 1:
3673 break;
3674 default:
3675 SETERRQ(PETSC_COMM_WORLD, MOFEM_ATOM_TEST_INVALID, "Wrong test number.");
3676 break;
3677 }
3678 if (fabs(nrm2 - regression_value) > 1e-2)
3679 SETERRQ(PETSC_COMM_WORLD, MOFEM_ATOM_TEST_INVALID,
3680 "Regression test field; wrong norm value. %6.4e != %6.4e", nrm2,
3681 regression_value);
3682 }
3684}
3685//! [Check]
Implements operators for assembling residuals and Jacobians for the Navier–Stokes–Cahn–Hilliard free-...
std::string type
#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
OpDomainMassH OpDomainMassP
FormsIntegrators< DomainEleOp >::Assembly< A >::LinearForm< I >::OpBaseTimesVector< BASE_DIM, SPACE_DIM, SPACE_DIM > OpDomainAssembleVector
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)
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.
static auto getFTensor0FromVec(V &data)
Get tensor rank 0 (scalar) form data vector.
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:227
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:83
static MoFEMErrorCode Initialize(int *argc, char ***args, const char file[], const char help[])
Initializes the MoFEM database PETSc, MOAB and MPI.
Definition Core.cpp:68
static MoFEMErrorCode Finalize()
Checks for options to be called at the conclusion of the program.
Definition Core.cpp:123
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 MatrixDouble 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, ScalarDataPtr > DataMapVec
std::map< std::string, boost::shared_ptr< MatrixDouble > > DataMapMat
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:62
constexpr int SPACE_DIM