v0.16.3
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 CHKERR mField.getInterface<TestingInterface>()->checkSolver(
1360 snes, "free surface initial condition");
1361
1362 CHKERR VecGhostUpdateBegin(D, INSERT_VALUES, SCATTER_FORWARD);
1363 CHKERR VecGhostUpdateEnd(D, INSERT_VALUES, SCATTER_FORWARD);
1364 CHKERR DMoFEMMeshToLocalVector(subdm, D, INSERT_VALUES, SCATTER_REVERSE);
1365
1367 };
1368
1369 CHKERR reset_bits();
1370 CHKERR solve_init(
1371 [](FEMethod *fe_ptr) { return get_fe_bit(fe_ptr).test(0); });
1372 CHKERR refineMesh(refine_overlap);
1373
1374 for (auto f : {"U", "P", "H", "G", "L"}) {
1375 CHKERR mField.getInterface<FieldBlas>()->setField(0, f);
1376 }
1377 CHKERR solve_init([](FEMethod *fe_ptr) {
1378 return get_fe_bit(fe_ptr).test(get_start_bit() + nb_levels - 1);
1379 });
1380
1381 CHKERR post_proc([](FEMethod *fe_ptr) {
1382 return get_fe_bit(fe_ptr).test(get_start_bit() + nb_levels - 1);
1383 });
1384
1385 pip_mng->getOpDomainRhsPipeline().clear();
1386 pip_mng->getOpDomainLhsPipeline().clear();
1387
1388 // Remove DOFs where boundary conditions are set
1389 CHKERR bc_mng->removeBlockDOFsOnEntities(simple->getProblemName(), "SYM_X",
1390 "U", 0, 0);
1391 CHKERR bc_mng->removeBlockDOFsOnEntities(simple->getProblemName(), "SYM_X",
1392 "L", 0, 0);
1393 CHKERR bc_mng->removeBlockDOFsOnEntities(simple->getProblemName(), "SYM_Y",
1394 "U", 1, 1);
1395 CHKERR bc_mng->removeBlockDOFsOnEntities(simple->getProblemName(), "SYM_Y",
1396 "L", 1, 1);
1397 CHKERR bc_mng->removeBlockDOFsOnEntities(simple->getProblemName(), "FIX", "U",
1398 0, SPACE_DIM);
1399 CHKERR bc_mng->removeBlockDOFsOnEntities(simple->getProblemName(), "FIX", "L",
1400 0, 0);
1401 CHKERR bc_mng->removeBlockDOFsOnEntities(simple->getProblemName(), "ZERO",
1402 "L", 0, 0);
1403
1405}
1406//! [Boundary condition]
1407
1408//! [Data projection]
1411
1412 // FIXME: Functionality in this method should be improved (projection should
1413 // be done field by field), generalized, and move to become core
1414 // functionality.
1415
1416 auto simple = mField.getInterface<Simple>();
1417 auto pip_mng = mField.getInterface<PipelineManager>();
1418 auto bit_mng = mField.getInterface<BitRefManager>();
1419 auto field_blas = mField.getInterface<FieldBlas>();
1420
1421 // Store all existing elements pipelines, replace them by data projection
1422 // pipelines, to put them back when projection is done.
1423 auto fe_domain_lhs = pip_mng->getDomainLhsFE();
1424 auto fe_domain_rhs = pip_mng->getDomainRhsFE();
1425 auto fe_bdy_lhs = pip_mng->getBoundaryLhsFE();
1426 auto fe_bdy_rhs = pip_mng->getBoundaryRhsFE();
1427
1428 pip_mng->getDomainLhsFE().reset();
1429 pip_mng->getDomainRhsFE().reset();
1430 pip_mng->getBoundaryLhsFE().reset();
1431 pip_mng->getBoundaryRhsFE().reset();
1432
1434
1435 // extract field data for domain parent element
1436 auto add_parent_field_domain = [&](auto fe, auto op, auto field, auto bit) {
1437 return setParentDofs(
1438 fe, field, op, bit,
1439
1440 [&]() {
1441 boost::shared_ptr<ForcesAndSourcesCore> fe_parent(
1442 new DomainParentEle(mField));
1443 return fe_parent;
1444 },
1445
1446 QUIET, Sev::noisy);
1447 };
1448
1449 // extract field data for boundary parent element
1450 auto add_parent_field_bdy = [&](auto fe, auto op, auto field, auto bit) {
1451 return setParentDofs(
1452 fe, field, op, bit,
1453
1454 [&]() {
1455 boost::shared_ptr<ForcesAndSourcesCore> fe_parent(
1457 return fe_parent;
1458 },
1459
1460 QUIET, Sev::noisy);
1461 };
1462
1463 // run this element on element with given bit, otherwise run other nested
1464 // element
1465 auto create_run_parent_op = [&](auto parent_fe_ptr, auto this_fe_ptr,
1466 auto fe_bit) {
1467 auto parent_mask = fe_bit;
1468 parent_mask.flip();
1469 return new OpRunParent(parent_fe_ptr, BitRefLevel().set(), parent_mask,
1470 this_fe_ptr, fe_bit, BitRefLevel().set(), QUIET,
1471 Sev::inform);
1472 };
1473
1474 // create hierarchy of nested elements
1475 auto get_parents_vel_fe_ptr = [&](auto this_fe_ptr, auto fe_bit) {
1476 std::vector<boost::shared_ptr<DomainParentEle>> parents_elems_ptr_vec;
1477 for (int l = 0; l < nb_levels; ++l)
1478 parents_elems_ptr_vec.emplace_back(
1479 boost::make_shared<DomainParentEle>(mField));
1480 for (auto l = 1; l < nb_levels; ++l) {
1481 parents_elems_ptr_vec[l - 1]->getOpPtrVector().push_back(
1482 create_run_parent_op(parents_elems_ptr_vec[l], this_fe_ptr, fe_bit));
1483 }
1484 return parents_elems_ptr_vec[0];
1485 };
1486
1487 // solve projection problem
1488 auto solve_projection = [&](auto exe_test) {
1490
1491 auto set_domain_rhs = [&](auto fe) {
1493 auto &pip = fe->getOpPtrVector();
1494
1495 auto u_ptr = boost::make_shared<MatrixDouble>();
1496 auto p_ptr = boost::make_shared<VectorDouble>();
1497 auto h_ptr = boost::make_shared<VectorDouble>();
1498 auto g_ptr = boost::make_shared<VectorDouble>();
1499
1500 auto eval_fe_ptr = boost::make_shared<DomainParentEle>(mField);
1501 {
1502 auto &pip = eval_fe_ptr->getOpPtrVector();
1505 eval_fe_ptr, "", UDO::OPSPACE, bit(get_skin_projection_bit()),
1506
1507 [&]() {
1508 boost::shared_ptr<ForcesAndSourcesCore> fe_parent(
1509 new DomainParentEle(mField));
1510 return fe_parent;
1511 },
1512
1513 QUIET, Sev::noisy);
1514 // That can be done much smarter, by block, field by field. For
1515 // simplicity is like that.
1516 CHKERR add_parent_field_domain(eval_fe_ptr, UDO::OPCOL, "U",
1518 pip.push_back(new OpCalculateVectorFieldValues<SPACE_DIM>("U", u_ptr));
1519 CHKERR add_parent_field_domain(eval_fe_ptr, UDO::OPCOL, "P",
1521 pip.push_back(new OpCalculateScalarFieldValues("P", p_ptr));
1522 CHKERR add_parent_field_domain(eval_fe_ptr, UDO::OPCOL, "H",
1524 pip.push_back(new OpCalculateScalarFieldValues("H", h_ptr));
1525 CHKERR add_parent_field_domain(eval_fe_ptr, UDO::OPCOL, "G",
1527 pip.push_back(new OpCalculateScalarFieldValues("G", g_ptr));
1528 }
1529 auto parent_eval_fe_ptr =
1530 get_parents_vel_fe_ptr(eval_fe_ptr, bit(get_projection_bit()));
1531 pip.push_back(create_run_parent_op(parent_eval_fe_ptr, eval_fe_ptr,
1533
1534 auto assemble_fe_ptr = boost::make_shared<DomainParentEle>(mField);
1535 {
1536 auto &pip = assemble_fe_ptr->getOpPtrVector();
1539 assemble_fe_ptr, "", UDO::OPSPACE, bit(get_skin_parent_bit()),
1540
1541 [&]() {
1542 boost::shared_ptr<ForcesAndSourcesCore> fe_parent(
1543 new DomainParentEle(mField));
1544 return fe_parent;
1545 },
1546
1547 QUIET, Sev::noisy);
1548 CHKERR add_parent_field_domain(assemble_fe_ptr, UDO::OPROW, "U",
1550 pip.push_back(new OpDomainAssembleVector("U", u_ptr));
1551 CHKERR add_parent_field_domain(assemble_fe_ptr, UDO::OPROW, "P",
1553 pip.push_back(new OpDomainAssembleScalar("P", p_ptr));
1554 CHKERR add_parent_field_domain(assemble_fe_ptr, UDO::OPROW, "H",
1556 pip.push_back(new OpDomainAssembleScalar("H", h_ptr));
1557 CHKERR add_parent_field_domain(assemble_fe_ptr, UDO::OPROW, "G",
1559 pip.push_back(new OpDomainAssembleScalar("G", g_ptr));
1560 }
1561 auto parent_assemble_fe_ptr =
1562 get_parents_vel_fe_ptr(assemble_fe_ptr, bit(get_current_bit()));
1563 pip.push_back(create_run_parent_op(
1564 parent_assemble_fe_ptr, assemble_fe_ptr, bit(get_current_bit())));
1565
1567 };
1568
1569 auto set_domain_lhs = [&](auto fe) {
1571
1572 auto &pip = fe->getOpPtrVector();
1573
1575
1577 fe, "", UDO::OPSPACE, bit(get_skin_parent_bit()),
1578
1579 [&]() {
1580 boost::shared_ptr<ForcesAndSourcesCore> fe_parent(
1581 new DomainParentEle(mField));
1582 return fe_parent;
1583 },
1584
1585 QUIET, Sev::noisy);
1586
1587 // That can be done much smarter, by block, field by field. For simplicity
1588 // is like that.
1589 CHKERR add_parent_field_domain(fe, UDO::OPROW, "U",
1591 CHKERR add_parent_field_domain(fe, UDO::OPCOL, "U",
1593 pip.push_back(new OpDomainMassU("U", "U"));
1594 CHKERR add_parent_field_domain(fe, UDO::OPROW, "P",
1596 CHKERR add_parent_field_domain(fe, UDO::OPCOL, "P",
1598 pip.push_back(new OpDomainMassP("P", "P"));
1599 CHKERR add_parent_field_domain(fe, UDO::OPROW, "H",
1601 CHKERR add_parent_field_domain(fe, UDO::OPCOL, "H",
1603 pip.push_back(new OpDomainMassH("H", "H"));
1604 CHKERR add_parent_field_domain(fe, UDO::OPROW, "G",
1606 CHKERR add_parent_field_domain(fe, UDO::OPCOL, "G",
1608 pip.push_back(new OpDomainMassG("G", "G"));
1609
1611 };
1612
1613 auto set_bdy_rhs = [&](auto fe) {
1615 auto &pip = fe->getOpPtrVector();
1616
1617 auto l_ptr = boost::make_shared<VectorDouble>();
1618
1619 auto eval_fe_ptr = boost::make_shared<BoundaryParentEle>(mField);
1620 {
1621 auto &pip = eval_fe_ptr->getOpPtrVector();
1623 eval_fe_ptr, "", UDO::OPSPACE, bit(get_skin_projection_bit()),
1624
1625 [&]() {
1626 boost::shared_ptr<ForcesAndSourcesCore> fe_parent(
1628 return fe_parent;
1629 },
1630
1631 QUIET, Sev::noisy);
1632 // That can be done much smarter, by block, field by field. For
1633 // simplicity is like that.
1634 CHKERR add_parent_field_bdy(eval_fe_ptr, UDO::OPCOL, "L",
1636 pip.push_back(new OpCalculateScalarFieldValues("L", l_ptr));
1637 }
1638 auto parent_eval_fe_ptr =
1639 get_parents_vel_fe_ptr(eval_fe_ptr, bit(get_projection_bit()));
1640 pip.push_back(create_run_parent_op(parent_eval_fe_ptr, eval_fe_ptr,
1642
1643 auto assemble_fe_ptr = boost::make_shared<BoundaryParentEle>(mField);
1644 {
1645 auto &pip = assemble_fe_ptr->getOpPtrVector();
1647 assemble_fe_ptr, "", UDO::OPSPACE, bit(get_skin_parent_bit()),
1648
1649 [&]() {
1650 boost::shared_ptr<ForcesAndSourcesCore> fe_parent(
1652 return fe_parent;
1653 },
1654
1655 QUIET, Sev::noisy);
1656
1657 struct OpLSize : public BoundaryEleOp {
1658 OpLSize(boost::shared_ptr<VectorDouble> l_ptr)
1659 : BoundaryEleOp(NOSPACE, DomainEleOp::OPSPACE), lPtr(l_ptr) {}
1660 MoFEMErrorCode doWork(int, EntityType, EntData &) {
1662 if (lPtr->size() != getGaussPts().size2()) {
1663 lPtr->resize(getGaussPts().size2());
1664 lPtr->clear();
1665 }
1667 }
1668
1669 private:
1670 boost::shared_ptr<VectorDouble> lPtr;
1671 };
1672
1673 pip.push_back(new OpLSize(l_ptr));
1674
1675 CHKERR add_parent_field_bdy(assemble_fe_ptr, UDO::OPROW, "L",
1677 pip.push_back(new OpBoundaryAssembleScalar("L", l_ptr));
1678 }
1679 auto parent_assemble_fe_ptr =
1680 get_parents_vel_fe_ptr(assemble_fe_ptr, bit(get_current_bit()));
1681 pip.push_back(create_run_parent_op(
1682 parent_assemble_fe_ptr, assemble_fe_ptr, bit(get_current_bit())));
1683
1685 };
1686
1687 auto set_bdy_lhs = [&](auto fe) {
1689
1690 auto &pip = fe->getOpPtrVector();
1691
1693 fe, "", UDO::OPSPACE, bit(get_skin_parent_bit()),
1694
1695 [&]() {
1696 boost::shared_ptr<ForcesAndSourcesCore> fe_parent(
1698 return fe_parent;
1699 },
1700
1701 QUIET, Sev::noisy);
1702
1703 // That can be done much smarter, by block, field by field. For simplicity
1704 // is like that.
1705 CHKERR add_parent_field_bdy(fe, UDO::OPROW, "L",
1707 CHKERR add_parent_field_bdy(fe, UDO::OPCOL, "L",
1709 pip.push_back(new OpBoundaryMassL("L", "L"));
1710
1712 };
1713
1714 auto create_subdm = [&]() -> SmartPetscObj<DM> {
1715 auto level_ents_ptr = boost::make_shared<Range>();
1716 CHKERR mField.getInterface<BitRefManager>()->getEntitiesByRefLevel(
1717 bit(get_current_bit()), BitRefLevel().set(), *level_ents_ptr);
1718
1719 auto get_prj_ents = [&]() {
1720 Range prj_mesh;
1721 CHKERR bit_mng->getEntitiesByDimAndRefLevel(bit(get_projection_bit()),
1722 BitRefLevel().set(),
1723 SPACE_DIM, prj_mesh);
1724 auto common_ents = intersect(prj_mesh, *level_ents_ptr);
1725 prj_mesh = subtract(unite(*level_ents_ptr, prj_mesh), common_ents)
1726 .subset_by_dimension(SPACE_DIM);
1727
1728 return prj_mesh;
1729 };
1730
1731 auto prj_ents = get_prj_ents();
1732
1733 if (get_global_size(prj_ents.size())) {
1734
1735 auto rebuild = [&]() {
1736 auto prb_mng = mField.getInterface<ProblemsManager>();
1738
1739 std::vector<std::string> fields{"U", "P", "H", "G", "L"};
1740 std::map<std::string, boost::shared_ptr<Range>> range_maps{
1741
1742 {"U", level_ents_ptr},
1743 {"P", level_ents_ptr},
1744 {"H", level_ents_ptr},
1745 {"G", level_ents_ptr},
1746 {"L", level_ents_ptr}
1747
1748 };
1749
1750 CHKERR prb_mng->buildSubProblem("SUB_SOLVER", fields, fields,
1751 simple->getProblemName(), PETSC_TRUE,
1752 &range_maps, &range_maps);
1753
1754 // partition problem
1755 CHKERR prb_mng->partitionFiniteElements("SUB_SOLVER", true, 0,
1757 // set ghost nodes
1758 CHKERR prb_mng->partitionGhostDofsOnDistributedMesh("SUB_SOLVER");
1759
1761 };
1762
1763 MOFEM_LOG("FS", Sev::verbose) << "Create projection problem";
1764
1765 CHKERR rebuild();
1766
1767 auto dm = simple->getDM();
1768 DM subdm;
1769 CHKERR DMCreate(mField.get_comm(), &subdm);
1770 CHKERR DMSetType(subdm, "DMMOFEM");
1771 CHKERR DMMoFEMCreateSubDM(subdm, dm, "SUB_SOLVER");
1772 return SmartPetscObj<DM>(subdm);
1773 }
1774
1775 MOFEM_LOG("FS", Sev::inform) << "Nothing to project";
1776
1777 return SmartPetscObj<DM>();
1778 };
1779
1780 auto create_dummy_dm = [&]() {
1781 auto dummy_dm = createDM(mField.get_comm(), "DMMOFEM");
1783 simple->getProblemName().c_str(),
1785 "create dummy dm");
1786 return dummy_dm;
1787 };
1788
1789 auto subdm = create_subdm();
1790 if (subdm) {
1791
1792 pip_mng->getDomainRhsFE().reset();
1793 pip_mng->getDomainLhsFE().reset();
1794 pip_mng->getBoundaryRhsFE().reset();
1795 pip_mng->getBoundaryLhsFE().reset();
1796 CHKERR pip_mng->setDomainRhsIntegrationRule(integration_rule);
1797 CHKERR pip_mng->setDomainLhsIntegrationRule(integration_rule);
1798 CHKERR pip_mng->setBoundaryRhsIntegrationRule(integration_rule);
1799 CHKERR pip_mng->setBoundaryLhsIntegrationRule(integration_rule);
1800 pip_mng->getDomainLhsFE()->exeTestHook = exe_test;
1801 pip_mng->getDomainRhsFE()->exeTestHook = [](FEMethod *fe_ptr) {
1802 return get_fe_bit(fe_ptr).test(nb_levels - 1);
1803 };
1804 pip_mng->getBoundaryLhsFE()->exeTestHook = exe_test;
1805 pip_mng->getBoundaryRhsFE()->exeTestHook = [](FEMethod *fe_ptr) {
1806 return get_fe_bit(fe_ptr).test(nb_levels - 1);
1807 };
1808
1809 CHKERR set_domain_rhs(pip_mng->getCastDomainRhsFE());
1810 CHKERR set_domain_lhs(pip_mng->getCastDomainLhsFE());
1811 CHKERR set_bdy_rhs(pip_mng->getCastBoundaryRhsFE());
1812 CHKERR set_bdy_lhs(pip_mng->getCastBoundaryLhsFE());
1813
1814 auto D = createDMVector(subdm);
1815 auto F = vectorDuplicate(D);
1816
1817 auto ksp = pip_mng->createKSP(subdm);
1818 CHKERR KSPSetFromOptions(ksp);
1819 CHKERR KSPSetUp(ksp);
1820
1821 // get vector norm
1822 auto get_norm = [&](auto x) {
1823 double nrm;
1824 CHKERR VecNorm(x, NORM_2, &nrm);
1825 return nrm;
1826 };
1827
1828 /**
1829 * @brief Zero DOFs, used by FieldBlas
1830 */
1831 auto zero_dofs = [](boost::shared_ptr<FieldEntity> ent_ptr) {
1833 for (auto &v : ent_ptr->getEntFieldData()) {
1834 v = 0;
1835 }
1837 };
1838
1839 auto solve = [&](auto S) {
1841 CHKERR VecZeroEntries(S);
1842 CHKERR VecZeroEntries(F);
1843 CHKERR VecGhostUpdateBegin(F, INSERT_VALUES, SCATTER_FORWARD);
1844 CHKERR VecGhostUpdateEnd(F, INSERT_VALUES, SCATTER_FORWARD);
1845 CHKERR KSPSolve(ksp, F, S);
1847 ksp, "free surface projection");
1848 CHKERR VecGhostUpdateBegin(S, INSERT_VALUES, SCATTER_FORWARD);
1849 CHKERR VecGhostUpdateEnd(S, INSERT_VALUES, SCATTER_FORWARD);
1850
1851
1852
1854 };
1855
1856 MOFEM_LOG("FS", Sev::inform) << "Solve projection";
1857 CHKERR solve(D);
1858
1859 auto glob_x = createDMVector(simple->getDM());
1860 auto sub_x = createDMVector(subdm);
1861 auto dummy_dm = create_dummy_dm();
1862
1863 /**
1864 * @brief get TSTheta data operators
1865 */
1866 auto apply_restrict = [&]() {
1867 auto get_is = [](auto v) {
1868 IS iy;
1869 auto create = [&]() {
1871 int n, ystart;
1872 CHKERR VecGetLocalSize(v, &n);
1873 CHKERR VecGetOwnershipRange(v, &ystart, NULL);
1874 CHKERR ISCreateStride(PETSC_COMM_SELF, n, ystart, 1, &iy);
1876 };
1877 CHK_THROW_MESSAGE(create(), "create is");
1878 return SmartPetscObj<IS>(iy);
1879 };
1880
1881 auto iy = get_is(glob_x);
1882 auto s = createVecScatter(glob_x, PETSC_NULLPTR, glob_x, iy);
1883
1885 DMSubDomainRestrict(simple->getDM(), s, PETSC_NULLPTR, dummy_dm),
1886 "restrict");
1887 Vec X0, Xdot;
1888 CHK_THROW_MESSAGE(DMGetNamedGlobalVector(dummy_dm, "TSTheta_X0", &X0),
1889 "get X0");
1891 DMGetNamedGlobalVector(dummy_dm, "TSTheta_Xdot", &Xdot),
1892 "get Xdot");
1893
1894 auto forward_ghost = [](auto g) {
1896 CHKERR VecGhostUpdateBegin(g, INSERT_VALUES, SCATTER_FORWARD);
1897 CHKERR VecGhostUpdateEnd(g, INSERT_VALUES, SCATTER_FORWARD);
1899 };
1900
1901 CHK_THROW_MESSAGE(forward_ghost(X0), "");
1902 CHK_THROW_MESSAGE(forward_ghost(Xdot), "");
1903
1904 if constexpr (debug) {
1905 MOFEM_LOG("FS", Sev::inform)
1906 << "Reverse restrict: X0 " << get_norm(X0) << " Xdot "
1907 << get_norm(Xdot);
1908 }
1909
1910 return std::vector<Vec>{X0, Xdot};
1911 };
1912
1913 auto ts_solver_vecs = apply_restrict();
1914
1915 if (ts_solver_vecs.size()) {
1916
1917 for (auto v : ts_solver_vecs) {
1918 MOFEM_LOG("FS", Sev::inform) << "Solve projection vector";
1919
1920 CHKERR DMoFEMMeshToLocalVector(simple->getDM(), v, INSERT_VALUES,
1921 SCATTER_REVERSE);
1922 CHKERR solve(sub_x);
1923
1924 for (auto f : {"U", "P", "H", "G", "L"}) {
1925 MOFEM_LOG("WORLD", Sev::verbose) << "Zero field " << f;
1926 CHKERR field_blas->fieldLambdaOnEntities(zero_dofs, f);
1927 }
1928
1929 CHKERR DMoFEMMeshToLocalVector(subdm, sub_x, INSERT_VALUES,
1930 SCATTER_REVERSE);
1931 CHKERR DMoFEMMeshToLocalVector(simple->getDM(), v, INSERT_VALUES,
1932 SCATTER_FORWARD);
1933
1934 MOFEM_LOG("FS", Sev::inform) << "Norm V " << get_norm(v);
1935 }
1936
1937 CHKERR DMRestoreNamedGlobalVector(dummy_dm, "TSTheta_X0",
1938 &ts_solver_vecs[0]);
1939 CHKERR DMRestoreNamedGlobalVector(dummy_dm, "TSTheta_Xdot",
1940 &ts_solver_vecs[1]);
1941 }
1942
1943 for (auto f : {"U", "P", "H", "G", "L"}) {
1944 MOFEM_LOG("WORLD", Sev::verbose) << "Zero field " << f;
1945 CHKERR field_blas->fieldLambdaOnEntities(zero_dofs, f);
1946 }
1947 CHKERR DMoFEMMeshToLocalVector(subdm, D, INSERT_VALUES, SCATTER_REVERSE);
1948 }
1949
1951 };
1952
1953 // postprocessing (only for debugging purposes)
1954 auto post_proc = [&](auto exe_test) {
1956 auto post_proc_fe = boost::make_shared<PostProcEleDomain>(mField);
1957 auto &pip = post_proc_fe->getOpPtrVector();
1958 post_proc_fe->exeTestHook = exe_test;
1959
1961
1963 post_proc_fe, "", UDO::OPSPACE, bit(get_skin_parent_bit()),
1964
1965 [&]() {
1966 boost::shared_ptr<ForcesAndSourcesCore> fe_parent(
1967 new DomainParentEle(mField));
1969 fe_parent->getOpPtrVector(), {H1});
1970 return fe_parent;
1971 },
1972
1973 QUIET, Sev::noisy);
1974
1976
1977 auto u_ptr = boost::make_shared<MatrixDouble>();
1978 auto p_ptr = boost::make_shared<VectorDouble>();
1979 auto h_ptr = boost::make_shared<VectorDouble>();
1980 auto g_ptr = boost::make_shared<VectorDouble>();
1981
1982 CHKERR add_parent_field_domain(post_proc_fe, UDO::OPCOL, "U",
1984 pip.push_back(new OpCalculateVectorFieldValues<SPACE_DIM>("U", u_ptr));
1985 CHKERR add_parent_field_domain(post_proc_fe, UDO::OPCOL, "P",
1987 pip.push_back(new OpCalculateScalarFieldValues("P", p_ptr));
1988 CHKERR add_parent_field_domain(post_proc_fe, UDO::OPCOL, "H",
1990 pip.push_back(new OpCalculateScalarFieldValues("H", h_ptr));
1991 CHKERR add_parent_field_domain(post_proc_fe, UDO::OPCOL, "G",
1993 pip.push_back(new OpCalculateScalarFieldValues("G", g_ptr));
1994
1995 post_proc_fe->getOpPtrVector().push_back(
1996
1997 new OpPPMap(post_proc_fe->getPostProcMesh(),
1998 post_proc_fe->getMapGaussPts(),
1999
2000 {{"P", p_ptr}, {"H", h_ptr}, {"G", g_ptr}},
2001
2002 {{"U", u_ptr}},
2003
2004 {},
2005
2006 {}
2007
2008 )
2009
2010 );
2011
2012 auto dm = simple->getDM();
2013 CHKERR DMoFEMLoopFiniteElements(dm, "dFE", post_proc_fe);
2014 CHKERR post_proc_fe->writeFile("out_projection.h5m");
2015
2017 };
2018
2019 CHKERR solve_projection([](FEMethod *fe_ptr) {
2020 return get_fe_bit(fe_ptr).test(get_current_bit());
2021 });
2022
2023 if constexpr (debug) {
2024 CHKERR post_proc([](FEMethod *fe_ptr) {
2025 return get_fe_bit(fe_ptr).test(get_current_bit());
2026 });
2027 }
2028
2029 fe_domain_lhs.swap(pip_mng->getDomainLhsFE());
2030 fe_domain_rhs.swap(pip_mng->getDomainRhsFE());
2031 fe_bdy_lhs.swap(pip_mng->getBoundaryLhsFE());
2032 fe_bdy_rhs.swap(pip_mng->getBoundaryRhsFE());
2033
2035}
2036//! [Data projection]
2037
2038//! [Push operators to pip]
2041 auto simple = mField.getInterface<Simple>();
2042
2044
2045 auto add_parent_field_domain = [&](auto fe, auto op, auto field) {
2046 return setParentDofs(
2047 fe, field, op, bit(get_skin_parent_bit()),
2048
2049 [&]() {
2050 boost::shared_ptr<ForcesAndSourcesCore> fe_parent(
2051 new DomainParentEle(mField));
2052 return fe_parent;
2053 },
2054
2055 QUIET, Sev::noisy);
2056 };
2057
2058 auto add_parent_field_bdy = [&](auto fe, auto op, auto field) {
2059 return setParentDofs(
2060 fe, field, op, bit(get_skin_parent_bit()),
2061
2062 [&]() {
2063 boost::shared_ptr<ForcesAndSourcesCore> fe_parent(
2065 return fe_parent;
2066 },
2067
2068 QUIET, Sev::noisy);
2069 };
2070
2071 auto test_bit_child = [](FEMethod *fe_ptr) {
2072 return fe_ptr->numeredEntFiniteElementPtr->getBitRefLevel().test(
2073 get_start_bit() + nb_levels - 1);
2074 };
2075
2076 auto dot_u_ptr = boost::make_shared<MatrixDouble>(); // time derivative of velocity u ie du/dt
2077 auto u_ptr = boost::make_shared<MatrixDouble>(); // velocity u
2078 auto grad_u_ptr = boost::make_shared<MatrixDouble>(); // velocity gradient tensor ie grad(u)
2079 auto dot_h_ptr = boost::make_shared<VectorDouble>(); // time derivative of phase field ie dh/dt
2080 auto h_ptr = boost::make_shared<VectorDouble>(); // phase field variable h
2081 auto grad_h_ptr = boost::make_shared<MatrixDouble>(); // phase field gradient ie grad(h)
2082 auto g_ptr = boost::make_shared<VectorDouble>(); // chemical potential g
2083 auto grad_g_ptr = boost::make_shared<MatrixDouble>(); // chemical potential gradient ie grad(g)
2084 auto lambda_ptr = boost::make_shared<VectorDouble>(); // Lagrange multiplier lambda
2085 auto p_ptr = boost::make_shared<VectorDouble>(); // pressure p
2086 auto div_u_ptr = boost::make_shared<VectorDouble>(); // divergence of velocity ie div(u)
2087
2088 // Push element from reference configuration to current configuration in 3d
2089 // space
2090 auto set_domain_general = [&](auto fe) {
2092 auto &pip = fe->getOpPtrVector();
2093
2095
2097 fe, "", UDO::OPSPACE, bit(get_skin_parent_bit()),
2098
2099 [&]() {
2100 boost::shared_ptr<ForcesAndSourcesCore> fe_parent(
2101 new DomainParentEle(mField));
2103 fe_parent->getOpPtrVector(), {H1});
2104 return fe_parent;
2105 },
2106
2107 QUIET, Sev::noisy);
2108
2109 CHKERR add_parent_field_domain(fe, UDO::OPROW, "U");
2110 CHKERR add_parent_field_domain(fe, UDO::OPCOL, "U");
2111 pip.push_back(
2113 pip.push_back(new OpCalculateVectorFieldValues<U_FIELD_DIM>("U", u_ptr));
2115 "U", grad_u_ptr));
2116
2117 switch (coord_type) {
2118 case CARTESIAN:
2119 pip.push_back(
2121 "U", div_u_ptr));
2122 break;
2123 case CYLINDRICAL:
2124 pip.push_back(
2126 "U", div_u_ptr));
2127 break;
2128 default:
2129 SETERRQ(PETSC_COMM_WORLD, MOFEM_NOT_IMPLEMENTED,
2130 "Coordinate system not implemented");
2131 }
2132
2133 CHKERR add_parent_field_domain(fe, UDO::OPCOL, "H");
2134 pip.push_back(new OpCalculateScalarFieldValuesDot("H", dot_h_ptr));
2135 pip.push_back(new OpCalculateScalarFieldValues("H", h_ptr));
2136 pip.push_back(
2137 new OpCalculateScalarFieldGradient<SPACE_DIM>("H", grad_h_ptr));
2138
2139 CHKERR add_parent_field_domain(fe, UDO::OPCOL, "G");
2140 pip.push_back(new OpCalculateScalarFieldValues("G", g_ptr));
2141 pip.push_back(
2142 new OpCalculateScalarFieldGradient<SPACE_DIM>("G", grad_g_ptr));
2143
2144 CHKERR add_parent_field_domain(fe, UDO::OPCOL, "P");
2145 pip.push_back(new OpCalculateScalarFieldValues("P", p_ptr));
2147 };
2148
2149 auto set_domain_rhs = [&](auto fe) {
2151 auto &pip = fe->getOpPtrVector();
2152
2153 CHKERR set_domain_general(fe);
2154
2155 CHKERR add_parent_field_domain(fe, UDO::OPROW, "U");
2156 pip.push_back(new OpRhsU("U", dot_u_ptr, u_ptr, grad_u_ptr, h_ptr,
2157 grad_h_ptr, g_ptr, p_ptr));
2158
2159 CHKERR add_parent_field_domain(fe, UDO::OPROW, "H");
2160 pip.push_back(new OpRhsH<false>("H", u_ptr, dot_h_ptr, h_ptr, grad_h_ptr,
2161 grad_g_ptr));
2162
2163 CHKERR add_parent_field_domain(fe, UDO::OPROW, "G");
2164 pip.push_back(new OpRhsG<false>("G", h_ptr, grad_h_ptr, g_ptr));
2165
2166 CHKERR add_parent_field_domain(fe, UDO::OPROW, "P");
2167 pip.push_back(new OpDomainAssembleScalar(
2168 "P", div_u_ptr, [](const double r, const double, const double) {
2169 return cylindrical(r);
2170 }));
2171 pip.push_back(new OpDomainAssembleScalar(
2172 "P", p_ptr, [](const double r, const double, const double) {
2173 return eps * cylindrical(r);
2174 }));
2176 };
2177
2178 auto set_domain_lhs = [&](auto fe) {
2180 auto &pip = fe->getOpPtrVector();
2181
2182 CHKERR set_domain_general(fe);
2183
2184 CHKERR add_parent_field_domain(fe, UDO::OPROW, "U");
2185 {
2186 CHKERR add_parent_field_domain(fe, UDO::OPCOL, "U");
2187 pip.push_back(new OpLhsU_dU("U", u_ptr, grad_u_ptr, h_ptr));
2188 CHKERR add_parent_field_domain(fe, UDO::OPCOL, "H");
2189 pip.push_back(
2190 new OpLhsU_dH("U", "H", dot_u_ptr, u_ptr, grad_u_ptr, h_ptr, g_ptr));
2191
2192 CHKERR add_parent_field_domain(fe, UDO::OPCOL, "G");
2193 pip.push_back(new OpLhsU_dG("U", "G", grad_h_ptr));
2194 }
2195
2196 CHKERR add_parent_field_domain(fe, UDO::OPROW, "H");
2197 {
2198 CHKERR add_parent_field_domain(fe, UDO::OPCOL, "U");
2199 pip.push_back(new OpLhsH_dU("H", "U", grad_h_ptr));
2200 CHKERR add_parent_field_domain(fe, UDO::OPCOL, "H");
2201 pip.push_back(new OpLhsH_dH<false>("H", u_ptr, h_ptr, grad_g_ptr));
2202 CHKERR add_parent_field_domain(fe, UDO::OPCOL, "G");
2203 pip.push_back(new OpLhsH_dG<false>("H", "G", h_ptr));
2204 }
2205
2206 CHKERR add_parent_field_domain(fe, UDO::OPROW, "G");
2207 {
2208 CHKERR add_parent_field_domain(fe, UDO::OPCOL, "H");
2209 pip.push_back(new OpLhsG_dH<false>("G", "H", h_ptr));
2210 CHKERR add_parent_field_domain(fe, UDO::OPCOL, "G");
2211 pip.push_back(new OpLhsG_dG("G"));
2212 }
2213
2214 CHKERR add_parent_field_domain(fe, UDO::OPROW, "P");
2215 {
2216 CHKERR add_parent_field_domain(fe, UDO::OPCOL, "U");
2217
2218 switch (coord_type) {
2219 case CARTESIAN:
2220 pip.push_back(new OpMixScalarTimesDiv<CARTESIAN>(
2221 "P", "U",
2222 [](const double r, const double, const double) {
2223 return cylindrical(r);
2224 },
2225 true, false));
2226 break;
2227 case CYLINDRICAL:
2228 pip.push_back(new OpMixScalarTimesDiv<CYLINDRICAL>(
2229 "P", "U",
2230 [](const double r, const double, const double) {
2231 return cylindrical(r);
2232 },
2233 true, false));
2234 break;
2235 default:
2236 SETERRQ(PETSC_COMM_WORLD, MOFEM_NOT_IMPLEMENTED,
2237 "Coordinate system not implemented");
2238 }
2239
2240 CHKERR add_parent_field_domain(fe, UDO::OPCOL, "P");
2241 pip.push_back(new OpDomainMassP("P", "P", [](double r, double, double) {
2242 return eps * cylindrical(r);
2243 }));
2244 }
2245
2247 };
2248
2249 auto get_block_name = [](auto name) {
2250 return boost::format("%s(.*)") % "WETTING_ANGLE";
2251 };
2252
2253 auto get_blocks = [&](auto &&name) {
2254 return mField.getInterface<MeshsetsManager>()->getCubitMeshsetPtr(
2255 std::regex(name.str()));
2256 };
2257
2258 auto set_boundary_rhs = [&](auto fe) {
2260 auto &pip = fe->getOpPtrVector();
2261
2263 fe, "", UDO::OPSPACE, bit(get_skin_parent_bit()),
2264
2265 [&]() {
2266 boost::shared_ptr<ForcesAndSourcesCore> fe_parent(
2268 return fe_parent;
2269 },
2270
2271 QUIET, Sev::noisy);
2272
2273 CHKERR add_parent_field_bdy(fe, UDO::OPCOL, "U");
2274 pip.push_back(new OpCalculateVectorFieldValues<U_FIELD_DIM>("U", u_ptr));
2275
2276 CHKERR add_parent_field_bdy(fe, UDO::OPCOL, "L");
2277 pip.push_back(new OpCalculateScalarFieldValues("L", lambda_ptr));
2278 pip.push_back(new OpNormalConstrainRhs("L", u_ptr));
2279
2281 pip, mField, "L", {}, "INFLUX",
2282 [](double r, double, double) { return cylindrical(r); }, Sev::inform);
2283
2284 CHKERR add_parent_field_bdy(fe, UDO::OPROW, "U");
2285 pip.push_back(new OpNormalForceRhs("U", lambda_ptr));
2286
2287 auto wetting_block = get_blocks(get_block_name("WETTING_ANGLE"));
2288 if (wetting_block.size()) {
2289 // push operators to the side element which is called from op_bdy_side
2290 auto op_bdy_side =
2291 new OpLoopSide<SideEle>(mField, simple->getDomainFEName(), SPACE_DIM);
2292 op_bdy_side->getSideFEPtr()->exeTestHook = test_bit_child;
2293
2295 op_bdy_side->getOpPtrVector(), {H1});
2296
2298 op_bdy_side->getSideFEPtr(), "", UDO::OPSPACE,
2300
2301 [&]() {
2302 boost::shared_ptr<ForcesAndSourcesCore> fe_parent(
2303 new DomainParentEle(mField));
2305 fe_parent->getOpPtrVector(), {H1});
2306 return fe_parent;
2307 },
2308
2309 QUIET, Sev::noisy);
2310
2311 CHKERR add_parent_field_domain(op_bdy_side->getSideFEPtr(), UDO::OPCOL,
2312 "H");
2313 op_bdy_side->getOpPtrVector().push_back(
2314 new OpCalculateScalarFieldGradient<SPACE_DIM>("H", grad_h_ptr));
2315 // push bdy side op
2316 pip.push_back(op_bdy_side);
2317
2318 // push operators for rhs wetting angle
2319 for (auto &b : wetting_block) {
2320 Range force_edges;
2321 std::vector<double> attr_vec;
2322 CHKERR b->getMeshsetIdEntitiesByDimension(
2323 mField.get_moab(), SPACE_DIM - 1, force_edges, true);
2324 b->getAttributes(attr_vec);
2325 if (attr_vec.size() != 1)
2326 SETERRQ(PETSC_COMM_SELF, MOFEM_INVALID_DATA,
2327 "Should be one attribute");
2328 MOFEM_LOG("FS", Sev::inform) << "Wetting angle: " << attr_vec.front();
2329 // need to find the attributes and pass to operator
2330 CHKERR add_parent_field_bdy(fe, UDO::OPROW, "G");
2331 CHKERR add_parent_field_bdy(fe, UDO::OPCOL, "G");
2332 pip.push_back(new OpWettingAngleRhs(
2333 "G", grad_h_ptr, boost::make_shared<Range>(force_edges),
2334 attr_vec.front()));
2335 }
2336 }
2337
2339 };
2340
2341 auto set_boundary_lhs = [&](auto fe) {
2343 auto &pip = fe->getOpPtrVector();
2344
2346 fe, "", UDO::OPSPACE, bit(get_skin_parent_bit()),
2347
2348 [&]() {
2349 boost::shared_ptr<ForcesAndSourcesCore> fe_parent(
2351 return fe_parent;
2352 },
2353
2354 QUIET, Sev::noisy);
2355
2356 CHKERR add_parent_field_bdy(fe, UDO::OPROW, "L");
2357 CHKERR add_parent_field_bdy(fe, UDO::OPCOL, "U");
2358 pip.push_back(new OpNormalConstrainLhs("L", "U"));
2359
2360 auto wetting_block = get_blocks(get_block_name("WETTING_ANGLE"));
2361 if (wetting_block.size()) {
2362 auto col_ind_ptr = boost::make_shared<std::vector<VectorInt>>();
2363 auto col_diff_base_ptr = boost::make_shared<std::vector<MatrixDouble>>();
2364
2365 // push operators to the side element which is called from op_bdy_side
2366 auto op_bdy_side =
2367 new OpLoopSide<SideEle>(mField, simple->getDomainFEName(), SPACE_DIM);
2368 op_bdy_side->getSideFEPtr()->exeTestHook = test_bit_child;
2369
2371 op_bdy_side->getOpPtrVector(), {H1});
2372
2374 op_bdy_side->getSideFEPtr(), "", UDO::OPSPACE,
2376
2377 [&]() {
2378 boost::shared_ptr<ForcesAndSourcesCore> fe_parent(
2379 new DomainParentEle(mField));
2381 fe_parent->getOpPtrVector(), {H1});
2382 return fe_parent;
2383 },
2384
2385 QUIET, Sev::noisy);
2386
2387 CHKERR add_parent_field_domain(op_bdy_side->getSideFEPtr(), UDO::OPROW,
2388 "H");
2389 CHKERR add_parent_field_domain(op_bdy_side->getSideFEPtr(), UDO::OPCOL,
2390 "H");
2391 op_bdy_side->getOpPtrVector().push_back(
2392 new OpCalculateScalarFieldGradient<SPACE_DIM>("H", grad_h_ptr));
2393 op_bdy_side->getOpPtrVector().push_back(
2394 new OpLoopSideGetDataForSideEle("H", col_ind_ptr, col_diff_base_ptr));
2395
2396 // push bdy side op
2397 pip.push_back(op_bdy_side);
2398
2399 // push operators for lhs wetting angle
2400 for (auto &b : wetting_block) {
2401 Range force_edges;
2402 std::vector<double> attr_vec;
2403 CHKERR b->getMeshsetIdEntitiesByDimension(
2404 mField.get_moab(), SPACE_DIM - 1, force_edges, true);
2405 b->getAttributes(attr_vec);
2406 if (attr_vec.size() != 1)
2407 SETERRQ(PETSC_COMM_SELF, MOFEM_INVALID_DATA,
2408 "Should be one attribute");
2409 MOFEM_LOG("FS", Sev::inform)
2410 << "wetting angle edges size " << force_edges.size();
2411
2412 CHKERR add_parent_field_bdy(fe, UDO::OPROW, "G");
2413 CHKERR add_parent_field_bdy(fe, UDO::OPCOL, "G");
2414 pip.push_back(new OpWettingAngleLhs(
2415 "G", grad_h_ptr, col_ind_ptr, col_diff_base_ptr,
2416 boost::make_shared<Range>(force_edges), attr_vec.front()));
2417 }
2418 }
2419
2421 };
2422
2423 auto *pip_mng = mField.getInterface<PipelineManager>();
2424
2425 CHKERR set_domain_rhs(pip_mng->getCastDomainRhsFE());
2426 CHKERR set_domain_lhs(pip_mng->getCastDomainLhsFE());
2427 CHKERR set_boundary_rhs(pip_mng->getCastBoundaryRhsFE());
2428 CHKERR set_boundary_lhs(pip_mng->getCastBoundaryLhsFE());
2429
2430 CHKERR pip_mng->setDomainRhsIntegrationRule(integration_rule);
2431 CHKERR pip_mng->setDomainLhsIntegrationRule(integration_rule);
2432 CHKERR pip_mng->setBoundaryRhsIntegrationRule(integration_rule);
2433 CHKERR pip_mng->setBoundaryLhsIntegrationRule(integration_rule);
2434
2435 pip_mng->getDomainLhsFE()->exeTestHook = test_bit_child;
2436 pip_mng->getDomainRhsFE()->exeTestHook = test_bit_child;
2437 pip_mng->getBoundaryLhsFE()->exeTestHook = test_bit_child;
2438 pip_mng->getBoundaryRhsFE()->exeTestHook = test_bit_child;
2439
2441}
2442//! [Push operators to pip]
2443
2444/**
2445 * @brief Monitor solution
2446 *
2447 * This function is called by TS solver at the end of each step. It is used
2448 */
2449struct Monitor : public FEMethod {
2451 SmartPetscObj<DM> dm, boost::shared_ptr<moab::Core> post_proc_mesh,
2452 boost::shared_ptr<PostProcEleDomainCont> post_proc,
2453 boost::shared_ptr<PostProcEleBdyCont> post_proc_edge,
2454 std::pair<boost::shared_ptr<BoundaryEle>, boost::shared_ptr<VectorDouble>>
2455 p)
2456 : dM(dm), postProcMesh(post_proc_mesh), postProc(post_proc),
2457 postProcEdge(post_proc_edge), liftFE(p.first), liftVec(p.second) {}
2460
2461 MOFEM_LOG("FS", Sev::verbose) << "Monitor";
2462 int save_every_nth_step = 1;
2463 CHKERR PetscOptionsGetInt(NULL, NULL, "-save_every_nth_step",
2464 &save_every_nth_step, NULL);
2465 if (ts_step % save_every_nth_step == 0) {
2466 MOFEM_LOG("FS", Sev::verbose) << "Mesh pre proc";
2467 MoFEM::Interface *m_field_ptr;
2468 CHKERR DMoFEMGetInterfacePtr(dM, &m_field_ptr);
2469 auto post_proc_begin =
2470 boost::make_shared<PostProcBrokenMeshInMoabBaseBegin>(*m_field_ptr,
2471 postProcMesh);
2472 auto post_proc_end = boost::make_shared<PostProcBrokenMeshInMoabBaseEnd>(
2473 *m_field_ptr, postProcMesh);
2474 CHKERR DMoFEMPreProcessFiniteElements(dM, post_proc_begin->getFEMethod());
2476 this->getCacheWeakPtr());
2478 this->getCacheWeakPtr());
2479 CHKERR DMoFEMPostProcessFiniteElements(dM, post_proc_end->getFEMethod());
2480 CHKERR post_proc_end->writeFile(
2481 "out_step_" + boost::lexical_cast<std::string>(ts_step) + ".h5m");
2482 MOFEM_LOG("FS", Sev::verbose) << "Mesh pre proc done";
2483 }
2484
2485 liftVec->resize(SPACE_DIM, false);
2486 liftVec->clear();
2488 MPI_Allreduce(MPI_IN_PLACE, &(*liftVec)[0], SPACE_DIM, MPI_DOUBLE, MPI_SUM,
2489 MPI_COMM_WORLD);
2490 MOFEM_LOG("FS", Sev::inform)
2491 << "Step " << ts_step << " time " << ts_t
2492 << " lift vec x: " << (*liftVec)[0] << " y: " << (*liftVec)[1];
2493
2495 }
2496
2497private:
2499 boost::shared_ptr<moab::Core> postProcMesh;
2500 boost::shared_ptr<PostProcEleDomainCont> postProc;
2501 boost::shared_ptr<PostProcEleBdyCont> postProcEdge;
2502 boost::shared_ptr<BoundaryEle> liftFE;
2503 boost::shared_ptr<VectorDouble> liftVec;
2504};
2505
2506//! [Solve]
2509
2511
2512 auto simple = mField.getInterface<Simple>();
2513 auto pip_mng = mField.getInterface<PipelineManager>();
2514
2515 auto create_solver_dm = [&](auto dm) -> SmartPetscObj<DM> {
2516 DM subdm;
2517
2518 auto setup_subdm = [&](auto dm) {
2520 auto simple = mField.getInterface<Simple>();
2521 auto bit_mng = mField.getInterface<BitRefManager>();
2522 auto dm = simple->getDM();
2523 auto level_ents_ptr = boost::make_shared<Range>();
2524 CHKERR bit_mng->getEntitiesByRefLevel(
2525 bit(get_current_bit()), BitRefLevel().set(), *level_ents_ptr);
2526 CHKERR DMCreate(mField.get_comm(), &subdm);
2527 CHKERR DMSetType(subdm, "DMMOFEM");
2528 CHKERR DMMoFEMCreateSubDM(subdm, dm, "SUB_SOLVER");
2529 CHKERR DMMoFEMAddElement(subdm, simple->getDomainFEName());
2530 CHKERR DMMoFEMAddElement(subdm, simple->getBoundaryFEName());
2531 CHKERR DMMoFEMSetSquareProblem(subdm, PETSC_TRUE);
2532 CHKERR DMMoFEMSetDestroyProblem(subdm, PETSC_FALSE);
2533 for (auto f : {"U", "P", "H", "G", "L"}) {
2534 CHKERR DMMoFEMAddSubFieldRow(subdm, f, level_ents_ptr);
2535 CHKERR DMMoFEMAddSubFieldCol(subdm, f, level_ents_ptr);
2536 }
2537 CHKERR DMSetUp(subdm);
2539 };
2540
2541 CHK_THROW_MESSAGE(setup_subdm(dm), "create subdm");
2542
2543 return SmartPetscObj<DM>(subdm);
2544 };
2545
2546 auto get_fe_post_proc = [&](auto post_proc_mesh) {
2547 auto add_parent_field_domain = [&](auto fe, auto op, auto field) {
2548 return setParentDofs(
2549 fe, field, op, bit(get_skin_parent_bit()),
2550
2551 [&]() {
2552 boost::shared_ptr<ForcesAndSourcesCore> fe_parent(
2553 new DomainParentEle(mField));
2554 return fe_parent;
2555 },
2556
2557 QUIET, Sev::noisy);
2558 };
2559
2560 auto post_proc_fe =
2561 boost::make_shared<PostProcEleDomainCont>(mField, post_proc_mesh);
2562 post_proc_fe->exeTestHook = [](FEMethod *fe_ptr) {
2563 return fe_ptr->numeredEntFiniteElementPtr->getBitRefLevel().test(
2564 get_start_bit() + nb_levels - 1);
2565 };
2566
2567 auto u_ptr = boost::make_shared<MatrixDouble>();
2568 auto grad_u_ptr = boost::make_shared<MatrixDouble>();
2569 auto h_ptr = boost::make_shared<VectorDouble>();
2570 auto grad_h_ptr = boost::make_shared<MatrixDouble>();
2571 auto p_ptr = boost::make_shared<VectorDouble>();
2572 auto g_ptr = boost::make_shared<VectorDouble>();
2573 auto grad_g_ptr = boost::make_shared<MatrixDouble>();
2574
2576 post_proc_fe->getOpPtrVector(), {H1});
2577
2579 post_proc_fe, "", UDO::OPSPACE, bit(get_skin_parent_bit()),
2580
2581 [&]() {
2582 boost::shared_ptr<ForcesAndSourcesCore> fe_parent(
2583 new DomainParentEle(mField));
2585 fe_parent->getOpPtrVector(), {H1});
2586 return fe_parent;
2587 },
2588
2589 QUIET, Sev::noisy);
2590
2591 CHKERR add_parent_field_domain(post_proc_fe, UDO::OPCOL, "U");
2592 post_proc_fe->getOpPtrVector().push_back(
2594 post_proc_fe->getOpPtrVector().push_back(
2596 grad_u_ptr));
2597
2598 CHKERR add_parent_field_domain(post_proc_fe, UDO::OPCOL, "H");
2599 post_proc_fe->getOpPtrVector().push_back(
2600 new OpCalculateScalarFieldValues("H", h_ptr));
2601 post_proc_fe->getOpPtrVector().push_back(
2602 new OpCalculateScalarFieldGradient<SPACE_DIM>("H", grad_h_ptr));
2603
2604 CHKERR add_parent_field_domain(post_proc_fe, UDO::OPCOL, "P");
2605 post_proc_fe->getOpPtrVector().push_back(
2606 new OpCalculateScalarFieldValues("P", p_ptr));
2607
2608 CHKERR add_parent_field_domain(post_proc_fe, UDO::OPCOL, "G");
2609 post_proc_fe->getOpPtrVector().push_back(
2610 new OpCalculateScalarFieldValues("G", g_ptr));
2611 post_proc_fe->getOpPtrVector().push_back(
2612 new OpCalculateScalarFieldGradient<SPACE_DIM>("G", grad_g_ptr));
2613
2615
2616 post_proc_fe->getOpPtrVector().push_back(
2617
2618 new OpPPMap(
2619 post_proc_fe->getPostProcMesh(), post_proc_fe->getMapGaussPts(),
2620
2621 {{"H", h_ptr}, {"P", p_ptr}, {"G", g_ptr}},
2622
2623 {{"U", u_ptr}, {"H_GRAD", grad_h_ptr}, {"G_GRAD", grad_g_ptr}},
2624
2625 {{"GRAD_U", grad_u_ptr}},
2626
2627 {}
2628
2629 )
2630
2631 );
2632
2633 return post_proc_fe;
2634 };
2635
2636 auto get_bdy_post_proc_fe = [&](auto post_proc_mesh) {
2637 auto add_parent_field_bdy = [&](auto fe, auto op, auto field) {
2638 return setParentDofs(
2639 fe, field, op, bit(get_skin_parent_bit()),
2640
2641 [&]() {
2642 boost::shared_ptr<ForcesAndSourcesCore> fe_parent(
2643 new BoundaryParentEle(mField));
2644 return fe_parent;
2645 },
2646
2647 QUIET, Sev::noisy);
2648 };
2649
2650 auto post_proc_fe =
2651 boost::make_shared<PostProcEleBdyCont>(mField, post_proc_mesh);
2652 post_proc_fe->exeTestHook = [](FEMethod *fe_ptr) {
2653 return fe_ptr->numeredEntFiniteElementPtr->getBitRefLevel().test(
2654 get_start_bit() + nb_levels - 1);
2655 };
2656
2657 CHKERR setParentDofs(
2658 post_proc_fe, "", UDO::OPSPACE, bit(get_skin_parent_bit()),
2659
2660 [&]() {
2661 boost::shared_ptr<ForcesAndSourcesCore> fe_parent(
2662 new BoundaryParentEle(mField));
2663 return fe_parent;
2664 },
2665
2666 QUIET, Sev::noisy);
2667
2668 struct OpGetNormal : public BoundaryEleOp {
2669
2670 OpGetNormal(boost::shared_ptr<VectorDouble> l_ptr,
2671 boost::shared_ptr<MatrixDouble> n_ptr)
2672 : BoundaryEleOp(NOSPACE, BoundaryEleOp::OPSPACE), ptrL(l_ptr),
2673 ptrNormal(n_ptr) {}
2674
2675 MoFEMErrorCode doWork(int side, EntityType type,
2678 auto t_l = getFTensor0FromVec(*ptrL);
2679 auto t_n_fe = getFTensor1NormalsAtGaussPts();
2680 ptrNormal->resize(getGaussPts().size2(), SPACE_DIM, false);
2681 auto t_n = getFTensor1FromMat<SPACE_DIM>(*ptrNormal);
2682 for (auto gg = 0; gg != getGaussPts().size2(); ++gg) {
2683 t_n(i) = t_n_fe(i) * t_l / std::sqrt(t_n_fe(i) * t_n_fe(i));
2684 ++t_n_fe;
2685 ++t_l;
2686 ++t_n;
2687 }
2689 };
2690
2691 protected:
2692 boost::shared_ptr<VectorDouble> ptrL;
2693 boost::shared_ptr<MatrixDouble> ptrNormal;
2694 };
2695
2696 auto u_ptr = boost::make_shared<MatrixDouble>();
2697 auto p_ptr = boost::make_shared<VectorDouble>();
2698 auto lambda_ptr = boost::make_shared<VectorDouble>();
2699 auto normal_l_ptr = boost::make_shared<MatrixDouble>();
2700
2701 CHKERR add_parent_field_bdy(post_proc_fe, UDO::OPCOL, "U");
2702 post_proc_fe->getOpPtrVector().push_back(
2704
2705 CHKERR add_parent_field_bdy(post_proc_fe, UDO::OPCOL, "L");
2706 post_proc_fe->getOpPtrVector().push_back(
2707 new OpCalculateScalarFieldValues("L", lambda_ptr));
2708 post_proc_fe->getOpPtrVector().push_back(
2709 new OpGetNormal(lambda_ptr, normal_l_ptr));
2710
2711 CHKERR add_parent_field_bdy(post_proc_fe, UDO::OPCOL, "P");
2712 post_proc_fe->getOpPtrVector().push_back(
2713 new OpCalculateScalarFieldValues("P", p_ptr));
2714
2715 auto op_ptr = new BoundaryEleOp(NOSPACE, BoundaryEleOp::OPSPACE);
2716 op_ptr->doWorkRhsHook = [&](DataOperator *base_op_ptr, int side,
2717 EntityType type,
2721 };
2722
2724
2725 post_proc_fe->getOpPtrVector().push_back(
2726
2727 new OpPPMap(post_proc_fe->getPostProcMesh(),
2728 post_proc_fe->getMapGaussPts(),
2729
2730 OpPPMap::DataMapVec{{"P", p_ptr}},
2731
2732 OpPPMap::DataMapMat{{"U", u_ptr}, {"L", normal_l_ptr}},
2733
2735
2737
2738 )
2739
2740 );
2741
2742 return post_proc_fe;
2743 };
2744
2745 auto get_lift_fe = [&]() {
2746 auto add_parent_field_bdy = [&](auto fe, auto op, auto field) {
2747 return setParentDofs(
2748 fe, field, op, bit(get_skin_parent_bit()),
2749
2750 [&]() {
2751 boost::shared_ptr<ForcesAndSourcesCore> fe_parent(
2752 new BoundaryParentEle(mField));
2753 return fe_parent;
2754 },
2755
2756 QUIET, Sev::noisy);
2757 };
2758
2759 auto fe = boost::make_shared<BoundaryEle>(mField);
2760 fe->exeTestHook = [](FEMethod *fe_ptr) {
2761 return fe_ptr->numeredEntFiniteElementPtr->getBitRefLevel().test(
2762 get_start_bit() + nb_levels - 1);
2763 };
2764
2765 auto lift_ptr = boost::make_shared<VectorDouble>();
2766 auto p_ptr = boost::make_shared<VectorDouble>();
2767 auto ents_ptr = boost::make_shared<Range>();
2768
2769 CHKERR setParentDofs(
2770 fe, "", UDO::OPSPACE, bit(get_skin_parent_bit()),
2771
2772 [&]() {
2773 boost::shared_ptr<ForcesAndSourcesCore> fe_parent(
2774 new BoundaryParentEle(mField));
2775 return fe_parent;
2776 },
2777
2778 QUIET, Sev::noisy);
2779
2780 std::vector<const CubitMeshSets *> vec_ptr;
2781 CHKERR mField.getInterface<MeshsetsManager>()->getCubitMeshsetPtr(
2782 std::regex("LIFT"), vec_ptr);
2783 for (auto m_ptr : vec_ptr) {
2784 auto meshset = m_ptr->getMeshset();
2785 Range ents;
2786 CHKERR mField.get_moab().get_entities_by_dimension(meshset, SPACE_DIM - 1,
2787 ents, true);
2788 ents_ptr->merge(ents);
2789 }
2790
2791 MOFEM_LOG("FS", Sev::noisy) << "Lift ents " << (*ents_ptr);
2792 CHKERR add_parent_field_bdy(fe, UDO::OPROW, "L");
2793 CHKERR add_parent_field_bdy(fe, UDO::OPCOL, "L");
2794 fe->getOpPtrVector().push_back(
2795 new OpCalculateScalarFieldValues("L", p_ptr));
2796 fe->getOpPtrVector().push_back(
2797 new OpCalculateLift("L", p_ptr, lift_ptr, ents_ptr));
2798
2799 return std::make_pair(fe, lift_ptr);
2800 };
2801
2802 auto set_post_proc_monitor = [&](auto ts) {
2804 DM dm;
2805 CHKERR TSGetDM(ts, &dm);
2806 boost::shared_ptr<FEMethod> null_fe;
2807 auto post_proc_mesh = boost::make_shared<moab::Core>();
2808 auto monitor_ptr = boost::make_shared<Monitor>(
2809 SmartPetscObj<DM>(dm, true), post_proc_mesh,
2810 get_fe_post_proc(post_proc_mesh), get_bdy_post_proc_fe(post_proc_mesh),
2811 get_lift_fe());
2812 CHKERR DMMoFEMTSSetMonitor(dm, ts, simple->getDomainFEName(), null_fe,
2813 null_fe, monitor_ptr);
2815 };
2816
2817 auto dm = simple->getDM();
2818 auto ts = createTS(mField.get_comm());
2819 CHKERR TSSetDM(ts, dm);
2820
2821 auto ts_pre_post_proc = boost::make_shared<TSPrePostProc>();
2822 tsPrePostProc = ts_pre_post_proc;
2823
2824 if (auto ptr = tsPrePostProc.lock()) {
2825
2826 ptr->fsRawPtr = this;
2827 ptr->solverSubDM = create_solver_dm(simple->getDM());
2828 ptr->globSol = createDMVector(dm);
2829 CHKERR DMoFEMMeshToLocalVector(dm, ptr->globSol, INSERT_VALUES,
2830 SCATTER_FORWARD);
2831 CHKERR VecAssemblyBegin(ptr->globSol);
2832 CHKERR VecAssemblyEnd(ptr->globSol);
2833
2834 auto sub_ts = pip_mng->createTSIM(ptr->solverSubDM);
2835
2836 CHKERR set_post_proc_monitor(sub_ts);
2837
2838 // Add monitor to time solver
2839 CHKERR TSSetFromOptions(ts);
2840 CHKERR ptr->tsSetUp(ts);
2841 CHKERR TSSetUp(ts);
2842
2843 auto print_fields_in_section = [&]() {
2845 auto section = mField.getInterface<ISManager>()->sectionCreate(
2846 simple->getProblemName());
2847 PetscInt num_fields;
2848 CHKERR PetscSectionGetNumFields(section, &num_fields);
2849 for (int f = 0; f < num_fields; ++f) {
2850 const char *field_name;
2851 CHKERR PetscSectionGetFieldName(section, f, &field_name);
2852 MOFEM_LOG("FS", Sev::inform)
2853 << "Field " << f << " " << std::string(field_name);
2854 }
2856 };
2857
2858 CHKERR print_fields_in_section();
2859
2860 CHKERR TSSolve(ts, ptr->globSol);
2861 // Solver convergence is cheap to check and carries no magic numbers, so
2862 // it stands alone rather than waiting on a "-test_nb"/"-test_name"
2863 // selector.
2864 {
2865 PetscReal expected_time;
2866 CHKERR TSGetTime(ts, &expected_time);
2867 CHKERR mField.getInterface<TestingInterface>()->checkSolver(
2868 ts, expected_time, {1e-12, 1e-12}, "free surface");
2869 }
2870 }
2871
2873}
2874
2875/**
2876 * @brief Main function for free surface simulation
2877 * @param argc Number of command line arguments
2878 * @param argv Array of command line argument strings
2879 * @return Exit code (0 for success)
2880 *
2881 * Main driver function that:
2882 * 1. Initializes MoFEM/PETSc and MOAB data structures
2883 * 2. Sets up logging channels for debugging output
2884 * 3. Registers MoFEM discrete manager with PETSc
2885 * 4. Creates mesh database (MOAB) and finite element database (MoFEM)
2886 * 5. Runs the complete free surface simulation
2887 * 6. Handles cleanup and finalization
2888 *
2889 * Command line options are read from param_file.petsc and command line.
2890 * Python initialization is optional (controlled by PYTHON_INIT_SURFACE).
2891 */
2892int main(int argc, char *argv[]) {
2893
2894#ifdef PYTHON_INIT_SURFACE
2895 Py_Initialize();
2896#endif
2897
2898 // Initialisation of MoFEM/PETSc and MOAB data structures
2899 const char param_file[] = "param_file.petsc";
2900 MoFEM::Core::Initialize(&argc, &argv, param_file, help);
2901
2902 // Add logging channel for example
2903 auto core_log = logging::core::get();
2904 core_log->add_sink(LogManager::createSink(LogManager::getStrmWorld(), "FS"));
2905 LogManager::setLog("FS");
2906 MOFEM_LOG_TAG("FS", "free surface");
2907
2908 try {
2909
2910 //! [Register MoFEM discrete manager in PETSc]
2911 DMType dm_name = "DMMOFEM";
2912 CHKERR DMRegister_MoFEM(dm_name);
2913 //! [Register MoFEM discrete manager in PETSc
2914
2915 //! [Create MoAB]
2916 moab::Core mb_instance; ///< mesh database
2917 moab::Interface &moab = mb_instance; ///< mesh database interface
2918 //! [Create MoAB]
2919
2920 //! [Create MoFEM]
2921 MoFEM::Core core(moab); ///< finite element database
2922 MoFEM::Interface &m_field = core; ///< finite element database interface
2923 //! [Create MoFEM]
2924
2925 //! [FreeSurface]
2926 FreeSurface ex(m_field);
2927 CHKERR ex.runProblem();
2928 //! [FreeSurface]
2929 }
2931
2933
2934#ifdef PYTHON_INIT_SURFACE
2935 if (Py_FinalizeEx() < 0) {
2936 exit(120);
2937 }
2938#endif
2939}
2940
2941std::vector<Range>
2943
2944 auto &moab = mField.get_moab();
2945 auto bit_mng = mField.getInterface<BitRefManager>();
2946 auto comm_mng = mField.getInterface<CommInterface>();
2947
2948 Range vertices;
2949 CHK_THROW_MESSAGE(bit_mng->getEntitiesByTypeAndRefLevel(
2950 bit(0), BitRefLevel().set(), MBVERTEX, vertices),
2951 "can not get vertices on bit 0");
2952
2953 auto &dofs_mi = mField.get_dofs()->get<Unique_mi_tag>();
2954 auto field_bit_number = mField.get_field_bit_number("H");
2955
2956 Range plus_range, minus_range;
2957 std::vector<EntityHandle> plus, minus;
2958
2959 // get vertices on level 0 on plus and minus side
2960 for (auto p = vertices.pair_begin(); p != vertices.pair_end(); ++p) {
2961
2962 const auto f = p->first;
2963 const auto s = p->second;
2964
2965 // Lowest Dof UId for given field (field bit number) on entity f
2966 const auto lo_uid = DofEntity::getLoFieldEntityUId(field_bit_number, f);
2967 const auto hi_uid = DofEntity::getHiFieldEntityUId(field_bit_number, s);
2968 auto it = dofs_mi.lower_bound(lo_uid);
2969 const auto hi_it = dofs_mi.upper_bound(hi_uid);
2970
2971 plus.clear();
2972 minus.clear();
2973 plus.reserve(std::distance(it, hi_it));
2974 minus.reserve(std::distance(it, hi_it));
2975
2976 for (; it != hi_it; ++it) {
2977 const auto v = (*it)->getFieldData();
2978 if (v > 0)
2979 plus.push_back((*it)->getEnt());
2980 else
2981 minus.push_back((*it)->getEnt());
2982 }
2983
2984 plus_range.insert_list(plus.begin(), plus.end());
2985 minus_range.insert_list(minus.begin(), minus.end());
2986 }
2987
2988 MOFEM_LOG_CHANNEL("SYNC");
2989 MOFEM_TAG_AND_LOG("SYNC", Sev::noisy, "FS")
2990 << "Plus range " << plus_range << endl;
2991 MOFEM_TAG_AND_LOG("SYNC", Sev::noisy, "FS")
2992 << "Minus range " << minus_range << endl;
2994
2995 auto get_elems = [&](auto &ents, auto bit, auto mask) {
2996 Range adj;
2997 CHK_MOAB_THROW(moab.get_adjacencies(ents, SPACE_DIM, false, adj,
2998 moab::Interface::UNION),
2999 "can not get adjacencies");
3000 CHK_THROW_MESSAGE(bit_mng->filterEntitiesByRefLevel(bit, mask, adj),
3001 "can not filter elements with bit 0");
3002 return adj;
3003 };
3004
3005 CHKERR comm_mng->synchroniseEntities(plus_range);
3006 CHKERR comm_mng->synchroniseEntities(minus_range);
3007
3008 std::vector<Range> ele_plus(nb_levels), ele_minus(nb_levels);
3009 ele_plus[0] = get_elems(plus_range, bit(0), BitRefLevel().set());
3010 ele_minus[0] = get_elems(minus_range, bit(0), BitRefLevel().set());
3011 auto common = intersect(ele_plus[0], ele_minus[0]);
3012 ele_plus[0] = subtract(ele_plus[0], common);
3013 ele_minus[0] = subtract(ele_minus[0], common);
3014
3015 auto get_children = [&](auto &p, auto &c) {
3017 CHKERR bit_mng->updateRangeByChildren(p, c);
3018 c = c.subset_by_dimension(SPACE_DIM);
3020 };
3021
3022 for (auto l = 1; l != nb_levels; ++l) {
3023 CHK_THROW_MESSAGE(get_children(ele_plus[l - 1], ele_plus[l]),
3024 "get children");
3025 CHK_THROW_MESSAGE(get_children(ele_minus[l - 1], ele_minus[l]),
3026 "get children");
3027 }
3028
3029 auto get_level = [&](auto &p, auto &m, auto z, auto bit, auto mask) {
3030 Range l;
3032 bit_mng->getEntitiesByDimAndRefLevel(bit, mask, SPACE_DIM, l),
3033 "can not get vertices on bit");
3034 l = subtract(l, p);
3035 l = subtract(l, m);
3036 for (auto f = 0; f != z; ++f) {
3037 Range conn;
3038 CHK_MOAB_THROW(moab.get_connectivity(l, conn, true), "");
3039 CHKERR mField.getInterface<CommInterface>()->synchroniseEntities(conn);
3040 l = get_elems(conn, bit, mask);
3041 }
3042 return l;
3043 };
3044
3045 std::vector<Range> vec_levels(nb_levels);
3046 for (auto l = nb_levels - 1; l >= 0; --l) {
3047 vec_levels[l] = get_level(ele_plus[l], ele_minus[l], 2 * overlap, bit(l),
3048 BitRefLevel().set());
3049 }
3050
3051 if constexpr (debug) {
3052 if (mField.get_comm_size() == 1) {
3053 for (auto l = 0; l != nb_levels; ++l) {
3054 std::string name = (boost::format("out_r%d.h5m") % l).str();
3056 "save mesh");
3057 }
3058 }
3059 }
3060
3061 return vec_levels;
3062}
3063
3066
3067 auto bit_mng = mField.getInterface<BitRefManager>();
3068
3069 BitRefLevel start_mask;
3070 for (auto s = 0; s != get_start_bit(); ++s)
3071 start_mask[s] = true;
3072
3073 // store prev_level
3074 Range prev_level;
3075 CHKERR bit_mng->getEntitiesByRefLevel(bit(get_current_bit()),
3076 BitRefLevel().set(), prev_level);
3077 Range prev_level_skin;
3078 CHKERR bit_mng->getEntitiesByRefLevel(bit(get_skin_parent_bit()),
3079 BitRefLevel().set(), prev_level_skin);
3080 // reset bit ref levels
3081 CHKERR bit_mng->lambdaBitRefLevel(
3082 [&](EntityHandle ent, BitRefLevel &bit) { bit &= start_mask; });
3083 CHKERR bit_mng->setNthBitRefLevel(prev_level, get_projection_bit(), true);
3084 CHKERR bit_mng->setNthBitRefLevel(prev_level_skin, get_skin_projection_bit(),
3085 true);
3086
3087 // set refinement levels
3088 auto set_levels = [&](auto &&
3089 vec_levels /*entities are refined on each level*/) {
3091
3092 // start with zero level, which is the coarsest mesh
3093 Range level0;
3094 CHKERR bit_mng->getEntitiesByRefLevel(bit(0), BitRefLevel().set(), level0);
3095 CHKERR bit_mng->setNthBitRefLevel(level0, get_start_bit(), true);
3096
3097 // get lower dimension entities
3098 auto get_adj = [&](auto ents) {
3099 Range conn;
3100 CHK_MOAB_THROW(mField.get_moab().get_connectivity(ents, conn, true),
3101 "get conn");
3102 for (auto d = 1; d != SPACE_DIM; ++d) {
3103 CHK_MOAB_THROW(mField.get_moab().get_adjacencies(
3104 ents.subset_by_dimension(SPACE_DIM), d, false, conn,
3105 moab::Interface::UNION),
3106 "get adj");
3107 }
3108 ents.merge(conn);
3109 return ents;
3110 };
3111
3112 // set bit levels
3113 for (auto l = 1; l != nb_levels; ++l) {
3114 Range level_prev;
3115 CHKERR bit_mng->getEntitiesByDimAndRefLevel(bit(get_start_bit() + l - 1),
3116 BitRefLevel().set(),
3117 SPACE_DIM, level_prev);
3118 Range parents;
3119 CHKERR bit_mng->updateRangeByParent(vec_levels[l], parents);
3120 // subtract entities from previous level, which are refined, so should be
3121 // not there
3122 level_prev = subtract(level_prev, parents);
3123 // and instead add their children
3124 level_prev.merge(vec_levels[l]);
3125 // set bit to each level
3126 CHKERR bit_mng->setNthBitRefLevel(level_prev, get_start_bit() + l, true);
3127 }
3128
3129 // set bit levels to lower dimension entities
3130 for (auto l = 1; l != nb_levels; ++l) {
3131 Range level;
3132 CHKERR bit_mng->getEntitiesByDimAndRefLevel(
3133 bit(get_start_bit() + l), BitRefLevel().set(), SPACE_DIM, level);
3134 level = get_adj(level);
3135 CHKERR mField.getInterface<CommInterface>()->synchroniseEntities(level);
3136 CHKERR bit_mng->setNthBitRefLevel(level, get_start_bit() + l, true);
3137 }
3138
3140 };
3141
3142 // resolve skin between refined levels
3143 auto set_skins = [&]() {
3145
3146 moab::Skinner skinner(&mField.get_moab());
3147 ParallelComm *pcomm =
3148 ParallelComm::get_pcomm(&mField.get_moab(), MYPCOMM_INDEX);
3149
3150 // get skin of bit level
3151 auto get_bit_skin = [&](BitRefLevel bit, BitRefLevel mask) {
3152 Range bit_ents;
3155 bit, mask, SPACE_DIM, bit_ents),
3156 "can't get bit level");
3157 Range bit_skin;
3158 CHK_MOAB_THROW(skinner.find_skin(0, bit_ents, false, bit_skin),
3159 "can't get skin");
3160 return bit_skin;
3161 };
3162
3163 auto get_level_skin = [&]() {
3164 Range skin;
3165 BitRefLevel bit_prev;
3166 for (auto l = 1; l != nb_levels; ++l) {
3167 auto skin_level_mesh = get_bit_skin(bit(l), BitRefLevel().set());
3168 // filter (remove) all entities which are on partition borders
3169 CHKERR pcomm->filter_pstatus(skin_level_mesh,
3170 PSTATUS_SHARED | PSTATUS_MULTISHARED,
3171 PSTATUS_NOT, -1, nullptr);
3172 auto skin_level =
3173 get_bit_skin(bit(get_start_bit() + l), BitRefLevel().set());
3174 skin_level = subtract(skin_level,
3175 skin_level_mesh); // get only internal skins, not
3176 // on the body boundary
3177 // get lower dimension adjacencies (FIXME: add edges if 3D)
3178 Range skin_level_verts;
3179 CHKERR mField.get_moab().get_connectivity(skin_level, skin_level_verts,
3180 true);
3181 skin_level.merge(skin_level_verts);
3182
3183 // remove previous level
3184 bit_prev.set(l - 1);
3185 Range level_prev;
3186 CHKERR bit_mng->getEntitiesByRefLevel(bit_prev, BitRefLevel().set(),
3187 level_prev);
3188 skin.merge(subtract(skin_level, level_prev));
3189 }
3190
3191 return skin;
3192 };
3193
3194 auto resolve_shared = [&](auto &&skin) {
3195 Range tmp_skin = skin;
3196
3197 map<int, Range> map_procs;
3198 CHKERR mField.getInterface<CommInterface>()->synchroniseEntities(
3199 tmp_skin, &map_procs);
3200
3201 Range from_other_procs; // entities which also exist on other processors
3202 for (auto &m : map_procs) {
3203 if (m.first != mField.get_comm_rank()) {
3204 from_other_procs.merge(m.second);
3205 }
3206 }
3207
3208 auto common = intersect(
3209 skin, from_other_procs); // entities which are on internal skin
3210 skin.merge(from_other_procs);
3211
3212 // entities which are on processor borders, and several processors are not
3213 // true skin.
3214 if (!common.empty()) {
3215 // skin is internal exist on other procs
3216 skin = subtract(skin, common);
3217 }
3218
3219 return skin;
3220 };
3221
3222 // get parents of entities
3223 auto get_parent_level_skin = [&](auto skin) {
3224 Range skin_parents;
3225 CHKERR bit_mng->updateRangeByParent(
3226 skin.subset_by_dimension(SPACE_DIM - 1), skin_parents);
3227 Range skin_parent_verts;
3228 CHKERR mField.get_moab().get_connectivity(skin_parents, skin_parent_verts,
3229 true);
3230 skin_parents.merge(skin_parent_verts);
3231 CHKERR mField.getInterface<CommInterface>()->synchroniseEntities(
3232 skin_parents);
3233 return skin_parents;
3234 };
3235
3236 auto child_skin = resolve_shared(get_level_skin());
3237 auto parent_skin = get_parent_level_skin(child_skin);
3238
3239 child_skin = subtract(child_skin, parent_skin);
3240 CHKERR bit_mng->setNthBitRefLevel(child_skin, get_skin_child_bit(), true);
3241 CHKERR bit_mng->setNthBitRefLevel(parent_skin, get_skin_parent_bit(), true);
3242
3244 };
3245
3246 // take last level, remove childs on boarder, and set bit
3247 auto set_current = [&]() {
3249 Range last_level;
3250 CHKERR bit_mng->getEntitiesByRefLevel(bit(get_start_bit() + nb_levels - 1),
3251 BitRefLevel().set(), last_level);
3252 Range skin_child;
3253 CHKERR bit_mng->getEntitiesByRefLevel(bit(get_skin_child_bit()),
3254 BitRefLevel().set(), skin_child);
3255
3256 last_level = subtract(last_level, skin_child);
3257 CHKERR bit_mng->setNthBitRefLevel(last_level, get_current_bit(), true);
3259 };
3260
3261 // set bits to levels
3262 CHKERR set_levels(findEntitiesCrossedByPhaseInterface(overlap));
3263 // set bits to skin
3264 CHKERR set_skins();
3265 // set current level bit
3266 CHKERR set_current();
3267
3268 if constexpr (debug) {
3269 if (mField.get_comm_size() == 1) {
3270 for (auto l = 0; l != nb_levels; ++l) {
3271 std::string name = (boost::format("out_level%d.h5m") % l).str();
3272 CHKERR bit_mng->writeBitLevel(BitRefLevel().set(get_start_bit() + l),
3273 BitRefLevel().set(), name.c_str(), "MOAB",
3274 "PARALLEL=WRITE_PART");
3275 }
3276 CHKERR bit_mng->writeBitLevel(BitRefLevel().set(get_current_bit()),
3277 BitRefLevel().set(), "current_bit.h5m",
3278 "MOAB", "PARALLEL=WRITE_PART");
3279 CHKERR bit_mng->writeBitLevel(BitRefLevel().set(get_projection_bit()),
3280 BitRefLevel().set(), "projection_bit.h5m",
3281 "MOAB", "PARALLEL=WRITE_PART");
3282
3283 CHKERR bit_mng->writeBitLevel(BitRefLevel().set(get_skin_child_bit()),
3284 BitRefLevel().set(), "skin_child_bit.h5m",
3285 "MOAB", "PARALLEL=WRITE_PART");
3286 CHKERR bit_mng->writeBitLevel(BitRefLevel().set(get_skin_parent_bit()),
3287 BitRefLevel().set(), "skin_parent_bit.h5m",
3288 "MOAB", "PARALLEL=WRITE_PART");
3289 }
3290 }
3291
3293};
3294
3296 boost::shared_ptr<FEMethod> fe_top, std::string field_name,
3298 BitRefLevel child_ent_bit,
3299 boost::function<boost::shared_ptr<ForcesAndSourcesCore>()> get_elem,
3300 int verbosity, LogManager::SeverityLevel sev) {
3302
3303 /**
3304 * @brief Collect data from parent elements to child
3305 */
3306 boost::function<void(boost::shared_ptr<ForcesAndSourcesCore>, int)>
3307 add_parent_level =
3308 [&](boost::shared_ptr<ForcesAndSourcesCore> parent_fe_pt, int level) {
3309 // Evaluate if not last parent element
3310 if (level > 0) {
3311
3312 // Create domain parent FE
3313 auto fe_ptr_current = get_elem();
3314
3315 // Call next level
3316 add_parent_level(
3317 boost::dynamic_pointer_cast<ForcesAndSourcesCore>(
3318 fe_ptr_current),
3319 level - 1);
3320
3321 // Add data to curent fe level
3323
3324 // Only base
3325 parent_fe_pt->getOpPtrVector().push_back(
3326
3328
3329 H1, op, fe_ptr_current,
3330
3331 BitRefLevel().set(), BitRefLevel().set(),
3332
3333 child_ent_bit, BitRefLevel().set(),
3334
3335 verbosity, sev));
3336
3337 } else {
3338
3339 // Filed data
3340 parent_fe_pt->getOpPtrVector().push_back(
3341
3343
3344 field_name, op, fe_ptr_current,
3345
3346 BitRefLevel().set(), BitRefLevel().set(),
3347
3348 child_ent_bit, BitRefLevel().set(),
3349
3350 verbosity, sev));
3351 }
3352 }
3353 };
3354
3355 add_parent_level(boost::dynamic_pointer_cast<ForcesAndSourcesCore>(fe_top),
3356 nb_levels);
3357
3359}
3360
3363
3364 if (auto ptr = tsPrePostProc.lock()) {
3365
3366 /**
3367 * @brief cut-off values at nodes, i.e. abs("H") <= 1
3368 *
3369 */
3370 auto cut_off_dofs = [&]() {
3372
3373 auto &m_field = ptr->fsRawPtr->mField;
3374
3375 Range current_verts;
3376 auto bit_mng = m_field.getInterface<BitRefManager>();
3378 bit(get_current_bit()), BitRefLevel().set(), MBVERTEX, current_verts);
3379
3380 auto cut_off_verts = [&](boost::shared_ptr<FieldEntity> ent_ptr) {
3382 for (auto &h : ent_ptr->getEntFieldData()) {
3383 h = cut_off(h);
3384 }
3386 };
3387
3388 auto field_blas = m_field.getInterface<FieldBlas>();
3389 CHKERR field_blas->fieldLambdaOnEntities(cut_off_verts, "H",
3390 &current_verts);
3392 };
3393
3394 CHKERR cut_off_dofs();
3395 }
3396
3397 if (auto ptr = tsPrePostProc.lock()) {
3398 MOFEM_LOG("FS", Sev::inform) << "Run step pre proc";
3399
3400 auto &m_field = ptr->fsRawPtr->mField;
3401 auto simple = m_field.getInterface<Simple>();
3402
3403 // get vector norm
3404 auto get_norm = [&](auto x) {
3405 double nrm;
3406 CHKERR VecNorm(x, NORM_2, &nrm);
3407 return nrm;
3408 };
3409
3410 // refine problem and project data, including theta data
3411 auto refine_problem = [&]() {
3413 MOFEM_LOG("FS", Sev::inform) << "Refine problem";
3414 CHKERR ptr->fsRawPtr->refineMesh(refine_overlap);
3415 CHKERR ptr->fsRawPtr->projectData();
3417 };
3418
3419 // set new jacobin operator, since problem and thus tangent matrix size has
3420 // changed
3421 auto set_jacobian_operators = [&]() {
3423 ptr->subB = createDMMatrix(ptr->solverSubDM);
3424 CHKERR KSPReset(ptr->subKSP);
3426 };
3427
3428 // set new solution
3429 auto set_solution = [&]() {
3431 MOFEM_LOG("FS", Sev::inform) << "Set solution";
3432
3433 PetscObjectState state;
3434
3435 // Record the state, and set it again. This is to fool PETSc that solution
3436 // vector is not updated. Otherwise PETSc will treat every step as a first
3437 // step.
3438
3439 // globSol is updated as result mesh refinement - this is not really set
3440 // a new solution.
3441
3442 CHKERR PetscObjectStateGet(getPetscObject(ptr->globSol.get()), &state);
3443 CHKERR DMoFEMMeshToLocalVector(simple->getDM(), ptr->globSol,
3444 INSERT_VALUES, SCATTER_FORWARD);
3445 CHKERR PetscObjectStateSet(getPetscObject(ptr->globSol.get()), state);
3446 MOFEM_LOG("FS", Sev::verbose)
3447 << "Set solution, vector norm " << get_norm(ptr->globSol);
3449 };
3450
3451 PetscBool is_theta;
3452 PetscObjectTypeCompare((PetscObject)ts, TSTHETA, &is_theta);
3453 if (is_theta) {
3454
3455 CHKERR refine_problem(); // refine problem
3456 CHKERR set_jacobian_operators(); // set new jacobian
3457 CHKERR set_solution(); // set solution
3458
3459 } else {
3460 SETERRQ(PETSC_COMM_WORLD, MOFEM_NOT_IMPLEMENTED,
3461 "Sorry, only TSTheta handling is implemented");
3462 }
3463
3464 // Need barriers, some functions in TS solver need are called collectively
3465 // and require the same state of variables
3466 PetscBarrier((PetscObject)ts);
3467
3468 MOFEM_LOG_CHANNEL("SYNC");
3469 MOFEM_TAG_AND_LOG("SYNC", Sev::verbose, "FS") << "PreProc done";
3470 MOFEM_LOG_SEVERITY_SYNC(m_field.get_comm(), Sev::verbose);
3471 }
3472
3474}
3475
3477 if (auto ptr = tsPrePostProc.lock()) {
3478 auto &m_field = ptr->fsRawPtr->mField;
3479 MOFEM_LOG_CHANNEL("SYNC");
3480 MOFEM_TAG_AND_LOG("SYNC", Sev::verbose, "FS") << "PostProc done";
3481 MOFEM_LOG_SEVERITY_SYNC(m_field.get_comm(), Sev::verbose);
3482 }
3483 return 0;
3484}
3485
3486MoFEMErrorCode TSPrePostProc::tsSetIFunction(TS ts, PetscReal t, Vec u, Vec u_t,
3487 Vec f, void *ctx) {
3489 if (auto ptr = tsPrePostProc.lock()) {
3490 auto sub_u = ptr->getSubVector();
3491 auto sub_u_t = vectorDuplicate(sub_u);
3492 auto sub_f = vectorDuplicate(sub_u);
3493 auto scatter = ptr->getScatter(sub_u, u, R);
3494
3495 auto apply_scatter_and_update = [&](auto x, auto sub_x) {
3497 CHKERR VecScatterBegin(scatter, x, sub_x, INSERT_VALUES, SCATTER_REVERSE);
3498 CHKERR VecScatterEnd(scatter, x, sub_x, INSERT_VALUES, SCATTER_REVERSE);
3499 CHKERR VecGhostUpdateBegin(sub_x, INSERT_VALUES, SCATTER_FORWARD);
3500 CHKERR VecGhostUpdateEnd(sub_x, INSERT_VALUES, SCATTER_FORWARD);
3502 };
3503
3504 CHKERR apply_scatter_and_update(u, sub_u);
3505 CHKERR apply_scatter_and_update(u_t, sub_u_t);
3506
3507 CHKERR TsSetIFunction(ts, t, sub_u, sub_u_t, sub_f, ptr->tsCtxPtr.get());
3508 CHKERR VecScatterBegin(scatter, sub_f, f, INSERT_VALUES, SCATTER_FORWARD);
3509 CHKERR VecScatterEnd(scatter, sub_f, f, INSERT_VALUES, SCATTER_FORWARD);
3510 }
3512}
3513
3514MoFEMErrorCode TSPrePostProc::tsSetIJacobian(TS ts, PetscReal t, Vec u, Vec u_t,
3515 PetscReal a, Mat A, Mat B,
3516 void *ctx) {
3518 if (auto ptr = tsPrePostProc.lock()) {
3519 auto sub_u = ptr->getSubVector();
3520 auto sub_u_t = vectorDuplicate(sub_u);
3521 auto scatter = ptr->getScatter(sub_u, u, R);
3522
3523 auto apply_scatter_and_update = [&](auto x, auto sub_x) {
3525 CHKERR VecScatterBegin(scatter, x, sub_x, INSERT_VALUES, SCATTER_REVERSE);
3526 CHKERR VecScatterEnd(scatter, x, sub_x, INSERT_VALUES, SCATTER_REVERSE);
3527 CHKERR VecGhostUpdateBegin(sub_x, INSERT_VALUES, SCATTER_FORWARD);
3528 CHKERR VecGhostUpdateEnd(sub_x, INSERT_VALUES, SCATTER_FORWARD);
3530 };
3531
3532 CHKERR apply_scatter_and_update(u, sub_u);
3533 CHKERR apply_scatter_and_update(u_t, sub_u_t);
3534
3535 CHKERR TsSetIJacobian(ts, t, sub_u, sub_u_t, a, ptr->subB, ptr->subB,
3536 ptr->tsCtxPtr.get());
3537 }
3539}
3540
3541MoFEMErrorCode TSPrePostProc::tsMonitor(TS ts, PetscInt step, PetscReal t,
3542 Vec u, void *ctx) {
3544 if (auto ptr = tsPrePostProc.lock()) {
3545 auto get_norm = [&](auto x) {
3546 double nrm;
3547 CHKERR VecNorm(x, NORM_2, &nrm);
3548 return nrm;
3549 };
3550
3551 auto sub_u = ptr->getSubVector();
3552 auto scatter = ptr->getScatter(sub_u, u, R);
3553 CHKERR VecScatterBegin(scatter, u, sub_u, INSERT_VALUES, SCATTER_REVERSE);
3554 CHKERR VecScatterEnd(scatter, u, sub_u, INSERT_VALUES, SCATTER_REVERSE);
3555 CHKERR VecGhostUpdateBegin(sub_u, INSERT_VALUES, SCATTER_FORWARD);
3556 CHKERR VecGhostUpdateEnd(sub_u, INSERT_VALUES, SCATTER_FORWARD);
3557
3558 MOFEM_LOG("FS", Sev::verbose)
3559 << "u norm " << get_norm(u) << " u sub nom " << get_norm(sub_u);
3560
3561 CHKERR TsMonitorSet(ts, step, t, sub_u, ptr->tsCtxPtr.get());
3562 }
3564}
3565
3568 if (auto ptr = tsPrePostProc.lock()) {
3569 MOFEM_LOG("FS", Sev::verbose) << "SetUP sub PC";
3570 ptr->subKSP = createKSP(ptr->fsRawPtr->mField.get_comm());
3571 CHKERR KSPSetFromOptions(ptr->subKSP);
3572 }
3574};
3575
3576MoFEMErrorCode TSPrePostProc::pcApply(PC pc, Vec pc_f, Vec pc_x) {
3578 if (auto ptr = tsPrePostProc.lock()) {
3579 auto sub_x = ptr->getSubVector();
3580 auto sub_f = vectorDuplicate(sub_x);
3581 auto scatter = ptr->getScatter(sub_x, pc_x, R);
3582 CHKERR VecScatterBegin(scatter, pc_f, sub_f, INSERT_VALUES,
3583 SCATTER_REVERSE);
3584 CHKERR VecScatterEnd(scatter, pc_f, sub_f, INSERT_VALUES, SCATTER_REVERSE);
3585 CHKERR KSPSetOperators(ptr->subKSP, ptr->subB, ptr->subB);
3586 MOFEM_LOG("FS", Sev::verbose) << "PCShell solve";
3587 CHKERR KSPSolve(ptr->subKSP, sub_f, sub_x);
3588 MOFEM_LOG("FS", Sev::verbose) << "PCShell solve <- done";
3589 CHKERR VecScatterBegin(scatter, sub_x, pc_x, INSERT_VALUES,
3590 SCATTER_FORWARD);
3591 CHKERR VecScatterEnd(scatter, sub_x, pc_x, INSERT_VALUES, SCATTER_FORWARD);
3592 }
3594};
3595
3597 if (auto ptr = tsPrePostProc.lock()) {
3598 auto prb_ptr = ptr->fsRawPtr->mField.get_problem("SUB_SOLVER");
3599 if (auto sub_data = prb_ptr->getSubData()) {
3600 auto is = sub_data->getSmartColIs();
3601 VecScatter s;
3602 if (fr == R) {
3603 CHK_THROW_MESSAGE(VecScatterCreate(x, PETSC_NULLPTR, y, is, &s),
3604 "crate scatter");
3605 } else {
3606 CHK_THROW_MESSAGE(VecScatterCreate(x, is, y, PETSC_NULLPTR, &s),
3607 "crate scatter");
3608 }
3609 return SmartPetscObj<VecScatter>(s);
3610 }
3611 }
3614}
3615
3619
3621
3622 auto &m_field = fsRawPtr->mField;
3623 auto simple = m_field.getInterface<Simple>();
3624
3626
3627 auto dm = simple->getDM();
3628
3630 CHKERR TSSetIFunction(ts, globRes, tsSetIFunction, nullptr);
3631 CHKERR TSSetIJacobian(ts, PETSC_NULLPTR, PETSC_NULLPTR, tsSetIJacobian, nullptr);
3632 CHKERR TSMonitorSet(ts, tsMonitor, fsRawPtr, PETSC_NULLPTR);
3633
3634 SNES snes;
3635 CHKERR TSGetSNES(ts, &snes);
3636 auto snes_ctx_ptr = getDMSnesCtx(dm);
3637
3638 auto set_section_monitor = [&](auto snes) {
3640 CHKERR SNESMonitorSet(snes,
3641 (MoFEMErrorCode(*)(SNES, PetscInt, PetscReal,
3642 void *))MoFEMSNESMonitorFields,
3643 (void *)(snes_ctx_ptr.get()), nullptr);
3645 };
3646
3647 CHKERR set_section_monitor(snes);
3648
3649 auto ksp = createKSP(m_field.get_comm());
3650 CHKERR KSPSetType(ksp, KSPPREONLY); // Run KSP internally in ShellPC
3651 auto sub_pc = createPC(fsRawPtr->mField.get_comm());
3652 CHKERR PCSetType(sub_pc, PCSHELL);
3653 CHKERR PCShellSetSetUp(sub_pc, pcSetup);
3654 CHKERR PCShellSetApply(sub_pc, pcApply);
3655 CHKERR KSPSetPC(ksp, sub_pc);
3656 CHKERR SNESSetKSP(snes, ksp);
3657
3660
3661 CHKERR TSSetPreStep(ts, tsPreProc);
3662 CHKERR TSSetPostStep(ts, tsPostProc);
3663
3665}
3666
3667//! [Check]
3670 PetscInt test_nb = 0;
3671 PetscBool test_flg = PETSC_FALSE;
3672 double regression_value = 0.0;
3673 CHKERR PetscOptionsGetInt(PETSC_NULLPTR, "", "-test", &test_nb, &test_flg);
3674 CHKERR PetscOptionsGetScalar(PETSC_NULLPTR, "", "-regression_value", &regression_value, PETSC_NULLPTR);
3675
3676 if (test_flg) {
3677 auto simple = mField.getInterface<Simple>();
3678 auto T = createDMVector(simple->getDM());
3679 CHKERR DMoFEMMeshToLocalVector(simple->getDM(), T, INSERT_VALUES,
3680 SCATTER_FORWARD);
3681 double nrm2;
3682 CHKERR VecNorm(T, NORM_2, &nrm2);
3683 MOFEM_LOG("FS", Sev::verbose) << "Regression norm " << nrm2;
3684 switch (test_nb) {
3685 case 1:
3686 break;
3687 default:
3688 SETERRQ(PETSC_COMM_WORLD, MOFEM_ATOM_TEST_INVALID, "Wrong test number.");
3689 break;
3690 }
3691 if (fabs(nrm2 - regression_value) > 1e-2)
3692 SETERRQ(PETSC_COMM_WORLD, MOFEM_ATOM_TEST_INVALID,
3693 "Regression test field; wrong norm value. %6.4e != %6.4e", nrm2,
3694 regression_value);
3695 }
3697}
3698//! [Check]
SmartPetscObj< KSP > ksp
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.
std::string name
void simple(double P1[], double P2[], double P3[], double c[], const int N)
Definition acoustic.cpp:69
int main()
constexpr int SPACE_DIM
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₀
constexpr bool debug
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:244
PetscErrorCode DMMoFEMAddElement(DM dm, std::string fe_name)
add element to dm
Definition DMMoFEM.cpp:517
PetscErrorCode DMMoFEMSetSquareProblem(DM dm, PetscBool square_problem)
set squared problem
Definition DMMoFEM.cpp:479
PetscErrorCode DMMoFEMCreateMoFEM(DM dm, MoFEM::Interface *m_field_ptr, const char problem_name[], const MoFEM::BitRefLevel bit_level, const MoFEM::BitRefLevel bit_mask=MoFEM::BitRefLevel().set())
Must be called by user to set MoFEM data structures.
Definition DMMoFEM.cpp:143
PetscErrorCode DMoFEMPostProcessFiniteElements(DM dm, MoFEM::FEMethod *method)
execute finite element method for each element in dm (problem)
Definition DMMoFEM.cpp:575
PetscErrorCode DMMoFEMAddSubFieldRow(DM dm, const char field_name[])
Definition DMMoFEM.cpp:267
PetscErrorCode DMoFEMMeshToLocalVector(DM dm, Vec l, InsertMode mode, ScatterMode scatter_mode, RowColData rc=RowColData::COL)
set local (or ghosted) vector values on mesh for partition only
Definition DMMoFEM.cpp:543
PetscErrorCode DMRegister_MoFEM(const char sname[])
Register MoFEM problem.
Definition DMMoFEM.cpp:43
PetscErrorCode DMoFEMLoopFiniteElements(DM dm, const char fe_name[], MoFEM::FEMethod *method, CacheTupleWeakPtr cache_ptr=CacheTupleSharedPtr())
Executes FEMethod for finite elements in DM.
Definition DMMoFEM.cpp:605
auto createDMVector(DM dm, RowColData rc=RowColData::COL)
Get smart vector from DM.
Definition DMMoFEM.hpp:1250
PetscErrorCode DMoFEMGetInterfacePtr(DM dm, MoFEM::Interface **m_field_ptr)
Get pointer to MoFEM::Interface.
Definition DMMoFEM.cpp:439
PetscErrorCode DMMoFEMAddSubFieldCol(DM dm, const char field_name[])
Definition DMMoFEM.cpp:309
auto createDMMatrix(DM dm)
Get smart matrix from DM.
Definition DMMoFEM.hpp:1207
PetscErrorCode DMoFEMPreProcessFiniteElements(DM dm, MoFEM::FEMethod *method)
execute finite element method for each element in dm (problem)
Definition DMMoFEM.cpp:565
virtual 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.
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:1075
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:1292
PetscErrorCode DMMoFEMSetDestroyProblem(DM dm, PetscBool destroy_problem)
Definition DMMoFEM.cpp:463
PetscErrorCode PetscOptionsGetInt(PetscOptions *, const char pre[], const char name[], PetscInt *ivalue, PetscBool *set)
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:2951
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:1278
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:1195
int save_every_nth_step
OpPostProcMapInMoab< SPACE_DIM, SPACE_DIM > OpPPMap
ElementsAndOps< SPACE_DIM >::SideEle SideEle
Definition plastic.cpp:62
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
[Operators_definition]
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