Skip to content

Feature/mixed mimetic discretization - #4139

Open
OmarDuran wants to merge 64 commits into
developfrom
feature/mixed_mimetic_discretization
Open

OmarDuran wants to merge 64 commits into
developfrom
feature/mixed_mimetic_discretization

Conversation

@OmarDuran

@OmarDuran OmarDuran commented Sep 4, 2026 •

Copy link
Copy Markdown
Contributor

This PR adds a mixed mimetic finite difference (MFD) discretization of single-phase flow, solved as a saddle-point problem. It adapts the consistency, each cell uses the TPFA inner product where TPFA is consistent and a consistent
MFD product elsewhere; fluxes between two TPFA cells are condensed. The associated theory can be found here: https://arxiv.org/abs/2607.23568.

It adds the mixedMimetic module, the SinglePhaseMixedMFD solver, a mixed-form RT inner product that requires no stabilization on simplexes, as it is the exact lower-order RT element, the ConsistencyAdaptation cell classification, a MGR strategy, and unit tests.

This is a work in collaboration with @chauj96 , @castelletto1 , @victorapm and myself.

The discretization block will look like this:

<MixedMimeticDiscretization
  name="mfdDiscretization"
  innerProductType="RT"
  adaptiveConsistency="1"
  consistencyTolerance="0.01"
  degeneracyTolerance="0.1"
  nominalGradient="{ 1.0, 1.0, 1.0 }"/>
  • innerProductType: the consistent product used in the MFD cells.
  • adaptiveConsistency: 1 selects TPFA or MFD per cell, 0 uses MFD everywhere.
  • consistencyTolerance: a cell becomes MFD when the relative two-point flux error, probed with nominalGradient, exceeds it.
  • degeneracyTolerance: a cell is degenerate, and stays TPFA, when its volume is smaller than this percentage of
    the combined volume of all cells sharing a vertex with it. This controls the conditioning of the system on meshes with cells with extreme aspect ratio.

Alternatively, the choice can be prescribed per cell from the VTK mesh with the integer cell array prescribedMfdFlag: 0 = TPFA, 1 = MFD, −1 = let consistencyTolerance and degeneracyTolerance decide. Prescribed cells with 0 = TPFA, 1 = MFD are never altered. The result of the classification is written as mfdFlag.

The following is an toy example of consistency adaptation on the full SPE10, where the green regions (MFD subregion) represent a simplex region connected with a large portion of hexahedral cells (TPFA subregion).

spe10_adapted

The following figure shows the MFD sub-region automatically identified employing a consistencyTolerance="0.01"
mfd_region_pressure_3d_view

The following figures show the pressure colormaps for adaptive-MFD (left panel), TPFA on a hexahedral mesh (middle panel), and TPFA on the hybrid mesh (right panel). The TPFA approximation on the hybrid mesh exhibits a visible effect due to the consistency error localized in the simplex cells. The adaptive-MFD approximation is quite similar to the TPFA approximation on the hexahedral mesh, where it is exact. The small differences are due to the different types of meshes.
comparative_plot_pressure_adaptive_mfd_vs_pure_tpfa

The following table was constructed using 8 ranks to show the following features:

  1. Optimal convergence of the pressure in l2-norm on smooth solutions;
  2. Robustness of the MGR solution strategy w.r.t refinement and material constrast;
  3. Reduction in the number of iterations with an increasing share of TPFA cells.

relative $L^2$ pressure error / GMRES iterations / linear solve time [s] (setup excluded); $\text{krylovTol} = 1.0\times 10^{-6}$
isotropic permeability in series along the flow ($x$): $k = 10^{-14}\text{ m}^2$ on $x &lt; 0.5$, $k = 10^{-14} / \text{contrast}$ on $x &gt; 0.5$; smooth exact pressure solution
$\text{unknowns} = \text{cells} + \text{faces}$ of the assembled matrix; $\text{reduced} = \text{cells} + \text{non-condensed flux dofs}$, the coupled system after the condensation (a condensed flux is a one-way closure row)

mesh mfd% n cells unknowns reduced contrast 1 contrast 10000 contrast 1e+07
hex 0 2 16 84 16 3.33e-01 / 4 / 0.000 1.00e+00 / 5 / 0.000 1.00e+00 / 5 / 0.000
hex 0 4 128 592 128 8.54e-02 / 7 / 0.001 3.33e-01 / 7 / 0.001 3.33e-01 / 7 / 0.001
hex 0 8 1024 4416 1024 2.14e-02 / 8 / 0.003 8.54e-02 / 9 / 0.003 8.54e-02 / 9 / 0.003
hex 0 16 8192 34048 8192 5.35e-03 / 10 / 0.027 2.14e-02 / 10 / 0.028 2.14e-02 / 10 / 0.027
hex 0 32 65536 267264 65536 1.34e-03 / 11 / 0.278 5.35e-03 / 11 / 0.277 5.35e-03 / 11 / 0.279
hex 0 64 524288 2117632 524288 3.34e-04 / 12 / 2.649 1.34e-03 / 12 / 2.677 1.34e-03 / 12 / 2.683
hex 25 2 16 84 36 2.74e-01 / 6 / 0.000 8.34e-01 / 6 / 0.000 8.35e-01 / 6 / 0.000
hex 25 4 128 592 256 7.11e-02 / 9 / 0.001 2.82e-01 / 9 / 0.001 2.82e-01 / 11 / 0.001
hex 25 8 1024 4416 1920 1.79e-02 / 11 / 0.004 7.26e-02 / 13 / 0.005 7.27e-02 / 15 / 0.006
hex 25 16 8192 34048 14848 4.47e-03 / 13 / 0.037 1.82e-02 / 14 / 0.041 1.82e-02 / 18 / 0.056
hex 25 32 65536 267264 116736 1.12e-03 / 14 / 0.365 4.56e-03 / 16 / 0.433 4.56e-03 / 20 / 0.581
hex 25 64 524288 2117632 925696 2.80e-04 / 16 / 3.759 1.14e-03 / 19 / 4.689 1.14e-03 / 21 / 5.343
hex 75 2 16 84 68 1.50e-01 / 6 / 0.000 4.69e-01 / 8 / 0.000 4.69e-01 / 8 / 0.000
hex 75 4 128 592 480 3.95e-02 / 9 / 0.001 1.62e-01 / 9 / 0.001 1.62e-01 / 11 / 0.001
hex 75 8 1024 4416 3584 9.99e-03 / 12 / 0.005 4.21e-02 / 13 / 0.005 4.22e-02 / 14 / 0.006
hex 75 16 8192 34048 27648 2.51e-03 / 13 / 0.037 1.06e-02 / 15 / 0.045 1.06e-02 / 18 / 0.058
hex 75 32 65536 267264 217088 6.27e-04 / 15 / 0.392 2.66e-03 / 17 / 0.465 2.66e-03 / 21 / 0.614
hex 75 64 524288 2117632 1720320 1.57e-04 / 16 / 3.710 6.65e-04 / 19 / 4.668 6.65e-04 / 21 / 5.295
hex 100 2 16 84 84 6.67e-02 / 10 / 0.000 2.00e-01 / 11 / 0.000 2.00e-01 / 11 / 0.000
hex 100 4 128 592 592 1.71e-02 / 13 / 0.001 6.66e-02 / 13 / 0.001 6.67e-02 / 14 / 0.001
hex 100 8 1024 4416 4416 4.28e-03 / 17 / 0.007 1.71e-02 / 18 / 0.007 1.71e-02 / 19 / 0.008
hex 100 16 8192 34048 34048 1.07e-03 / 18 / 0.057 4.28e-03 / 20 / 0.065 4.28e-03 / 24 / 0.084
hex 100 32 65536 267264 267264 2.67e-04 / 20 / 0.568 1.07e-03 / 23 / 0.685 1.07e-03 / 27 / 0.851
hex 100 64 524288 2117632 2117632 6.69e-05 / 23 / 5.944 2.67e-04 / 26 / 7.096 2.67e-04 / 28 / 7.840
tet 0 2 96 328 96 6.04e-01 / 9 / 0.000 1.04e+00 / 9 / 0.001 1.04e+00 / 9 / 0.000
tet 0 4 768 2464 768 4.82e-01 / 11 / 0.002 6.04e-01 / 11 / 0.002 6.04e-01 / 11 / 0.002
tet 0 8 6144 19072 6144 4.54e-01 / 13 / 0.020 4.82e-01 / 13 / 0.020 4.82e-01 / 13 / 0.020
tet 0 16 49152 150016 49152 4.47e-01 / 13 / 0.171 4.54e-01 / 14 / 0.189 4.54e-01 / 14 / 0.188
tet 0 32 393216 1189888 393216 4.45e-01 / 14 / 1.704 4.47e-01 / 14 / 1.699 4.47e-01 / 14 / 1.701
tet 0 64 3145728 9478144 3145728 4.45e-01 / 15 / 14.798 4.45e-01 / 15 / 15.138 4.45e-01 / 15 / 15.088
tet 25 2 96 328 160 5.16e-01 / 11 / 0.001 9.23e-01 / 13 / 0.001 9.23e-01 / 15 / 0.001
tet 25 4 768 2464 1216 3.83e-01 / 15 / 0.003 5.07e-01 / 17 / 0.004 5.07e-01 / 18 / 0.004
tet 25 8 6144 19072 9472 3.56e-01 / 16 / 0.027 4.00e-01 / 19 / 0.034 4.00e-01 / 21 / 0.038
tet 25 16 49152 150016 74752 3.50e-01 / 17 / 0.243 3.74e-01 / 19 / 0.279 3.74e-01 / 23 / 0.369
tet 25 32 393216 1189888 593920 3.48e-01 / 18 / 2.365 3.68e-01 / 21 / 2.880 3.68e-01 / 22 / 3.076
tet 25 64 3145728 9478144 4734976 3.48e-01 / 20 / 21.675 3.66e-01 / 21 / 23.160 3.66e-01 / 25 / 29.386
tet 75 2 96 328 272 3.08e-01 / 11 / 0.001 6.65e-01 / 13 / 0.001 6.65e-01 / 16 / 0.001
tet 75 4 768 2464 2048 1.79e-01 / 15 / 0.003 2.93e-01 / 17 / 0.004 2.93e-01 / 19 / 0.005
tet 75 8 6144 19072 15872 1.59e-01 / 17 / 0.029 2.10e-01 / 20 / 0.035 2.10e-01 / 22 / 0.041
tet 75 16 49152 150016 124928 1.54e-01 / 17 / 0.246 1.92e-01 / 19 / 0.290 1.92e-01 / 23 / 0.370
tet 75 32 393216 1189888 991232 1.53e-01 / 18 / 2.347 1.88e-01 / 21 / 2.873 1.88e-01 / 23 / 3.235
tet 75 64 3145728 9478144 7897088 1.53e-01 / 19 / 20.239 1.87e-01 / 21 / 23.089 1.87e-01 / 25 / 29.282
tet 100 2 96 328 328 1.46e-01 / 15 / 0.001 5.12e-01 / 17 / 0.001 5.12e-01 / 20 / 0.001
tet 100 4 768 2464 2464 3.69e-02 / 19 / 0.004 1.46e-01 / 20 / 0.005 1.46e-01 / 22 / 0.005
tet 100 8 6144 19072 19072 9.22e-03 / 21 / 0.038 3.68e-02 / 23 / 0.043 3.69e-02 / 25 / 0.049
tet 100 16 49152 150016 150016 2.30e-03 / 20 / 0.309 9.21e-03 / 23 / 0.368 9.22e-03 / 27 / 0.460
tet 100 32 393216 1189888 1189888 5.76e-04 / 22 / 3.052 2.30e-03 / 24 / 3.435 2.30e-03 / 26 / 3.841
tet 100 64 3145728 9478144 9478144 1.44e-04 / 22 / 24.415 5.76e-04 / 25 / 29.192 5.76e-04 / 52 / 87.726

chauj96 and others added 30 commits March 12, 2026 13:20
@OmarDuran OmarDuran added the flag: no rebaseline Does not require rebaseline label Sep 17, 2026
@OmarDuran OmarDuran added flag: requires rebaseline Requires rebaseline branch in integratedTests and removed flag: no rebaseline Does not require rebaseline labels Sep 17, 2026

@herve-gross herve-gross left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Excellent, thank you Omar. A Sphinx documentation page would be great. I recommend using your very clear GitHub PR description for this documentation.

@victorapm

Copy link
Copy Markdown
Contributor

Will be reviewed by @jafranc, @joshua-white

FaceManager & faceManager = mesh.getFaceManager();
arrayView1d< integer > const isPresBcFace = faceManager.getField< flow::isBoundaryFace >();

fsManager.apply< FaceManager >( 0.0,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This initializes the pressure-boundary mask at time 0, but I'm wondering if field specifications can activate and expire later. Then, should we clear and rebuild the mask together with the pressure values at the current assembly time? Might not be an issue, but just wanted to bring up

} );

// evaluate the boundary face pressure values used in the constitutive rows
applyFacePressureBCValues( time_n + dt, domain );

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like boundary values are evaluated during step setup, which is not repeated when the nonlinear retry loop reduces dt. A retry can therefore use the original endpoint's pressure. Could we refresh boundary data before flux assembly using the actual attempt dt, without overwriting beginning-of-step snapshots?

Comment on lines +205 to +208
bool const valid0 = ( m_elemRegionList[kf][0] >= 0 && m_elemSubRegionList[kf][0] >= 0 && m_elemList[kf][0] >= 0 );
bool const valid1 = ( m_elemRegionList[kf][1] >= 0 && m_elemSubRegionList[kf][1] >= 0 && m_elemList[kf][1] >= 0 );
bool const onBoundary = !( valid0 && valid1 );
stack.isNoFlowFace[i] = ( onBoundary && m_isPresBcFace[kf] == 0 ) ? 1 : 0;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like this counts both physical neighbors, while the condensed kernel counts only target cells at lines 564–583. A target/non-target interface is therefore interior here but a boundary there; the condensed path then returns assuming an identity closure was assembled. Should both paths use target-relative adjacency, with matching orientation and a partial-region regression?

vtkArray->GetName(), src, cellIdx ),
InputError );
}
val = static_cast< DstType >( src );

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm wondering if we should check that this casting makes sense before actually doing it. For example, a value such as 1e20 can reach an out-of-range floating-to-integer cast


// the same one-sided conductance and floor as the diagonal entries of TPFAInnerProduct::computeM
real64 const areaTolerance = m_lengthTolerance * m_lengthTolerance;
real64 const weightTolerance = 1e-30 * m_lengthTolerance;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm wondering if 1e-30 should be a variable instead


FieldIdentifiers fieldsToBeSync;
fieldsToBeSync.addElementFields( { mixedMimetic::mfdFlag::key(), mixedMimetic::consistencyIndicator::key() }, regionNames );
CommunicationTools::getInstance().synchronizeFields( fieldsToBeSync, mesh, neighbors, false );

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like the marking kernel writes these fields on the device, but synchronization selects host packing. Should we make memory-space transitions explicit using coherent host views or device packing?

subRegion.faceList().toViewConst(),
subRegion.getElementCenter(),
subRegion.getElementVolume(),
permeability[er][esr],

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like host indexing of permeability[er][esr] follows a device projection that captures the whole nested accessor

@victorapm victorapm left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the great work @chauj96 and @OmarDuran ! I left a few comments and I'm also working on a follow-up PR with some additional improvements built on top of this branch

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci: run code coverage enables running of the code coverage CI jobs ci: run device builds Allows to run the device (CUDA/HIP) builds ci: run integrated tests Allows to run the integrated tests in GEOS CI flag: ready for review flag: requires rebaseline Requires rebaseline branch in integratedTests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants