diff --git a/inputFiles/immiscibleMultiphaseFlow/immiscibleTwoPhase_GravitySegregation_1d/immiscibleTwoPhase_GravitySegregation_1d.ats b/inputFiles/immiscibleMultiphaseFlow/immiscibleTwoPhase_GravitySegregation_1d/immiscibleTwoPhase_GravitySegregation_1d.ats new file mode 100644 index 00000000000..b7f470b932f --- /dev/null +++ b/inputFiles/immiscibleMultiphaseFlow/immiscibleTwoPhase_GravitySegregation_1d/immiscibleTwoPhase_GravitySegregation_1d.ats @@ -0,0 +1,18 @@ +from geos.ats.test_builder import TestDeck, RestartcheckParameters, generate_geos_tests + +restartcheck_params = {} +restartcheck_params['atol'] = 1.0E-8 +restartcheck_params['rtol'] = 1.0E-8 + +decks = [ + TestDeck( + name="immiscibleTwoPhase_GravitySegregation_1d", + description= + 'Test that 2 fluids can be seperated based on gravity.', + partitions=((1, 1, 1), ), + restart_step=0, + check_step=45, + restartcheck_params=RestartcheckParameters(**restartcheck_params)) +] + +generate_geos_tests(decks) diff --git a/inputFiles/immiscibleMultiphaseFlow/immiscibleTwoPhase_GravitySegregation_1d/immiscibleTwoPhase_GravitySegregation_1d.xml b/inputFiles/immiscibleMultiphaseFlow/immiscibleTwoPhase_GravitySegregation_1d/immiscibleTwoPhase_GravitySegregation_1d.xml new file mode 100644 index 00000000000..62d4f6cf3bb --- /dev/null +++ b/inputFiles/immiscibleMultiphaseFlow/immiscibleTwoPhase_GravitySegregation_1d/immiscibleTwoPhase_GravitySegregation_1d.xml @@ -0,0 +1,203 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inputFiles/immiscibleMultiphaseFlow/immiscibleTwoPhase_GravitySegregation_1d/initialPressure.txt b/inputFiles/immiscibleMultiphaseFlow/immiscibleTwoPhase_GravitySegregation_1d/initialPressure.txt new file mode 100644 index 00000000000..85a30e13876 --- /dev/null +++ b/inputFiles/immiscibleMultiphaseFlow/immiscibleTwoPhase_GravitySegregation_1d/initialPressure.txt @@ -0,0 +1,10 @@ +495.0 +485.0 +475.0 +465.0 +455.0 +400.0 +300.0 +200.0 +100.0 +0.0 diff --git a/inputFiles/immiscibleMultiphaseFlow/immiscibleTwoPhase_GravitySegregation_1d/initialSaturation1.txt b/inputFiles/immiscibleMultiphaseFlow/immiscibleTwoPhase_GravitySegregation_1d/initialSaturation1.txt new file mode 100644 index 00000000000..99f11f6b2e7 --- /dev/null +++ b/inputFiles/immiscibleMultiphaseFlow/immiscibleTwoPhase_GravitySegregation_1d/initialSaturation1.txt @@ -0,0 +1,10 @@ +0 +0 +0 +0 +0 +1 +1 +1 +1 +1 diff --git a/inputFiles/immiscibleMultiphaseFlow/immiscibleTwoPhase_GravitySegregation_1d/initialSaturation2.txt b/inputFiles/immiscibleMultiphaseFlow/immiscibleTwoPhase_GravitySegregation_1d/initialSaturation2.txt new file mode 100644 index 00000000000..1c03b9c1871 --- /dev/null +++ b/inputFiles/immiscibleMultiphaseFlow/immiscibleTwoPhase_GravitySegregation_1d/initialSaturation2.txt @@ -0,0 +1,10 @@ +1 +1 +1 +1 +1 +0 +0 +0 +0 +0 diff --git a/inputFiles/immiscibleMultiphaseFlow/immiscibleTwoPhase_GravitySegregation_1d/x.txt b/inputFiles/immiscibleMultiphaseFlow/immiscibleTwoPhase_GravitySegregation_1d/x.txt new file mode 100644 index 00000000000..2eb3c4fe4ee --- /dev/null +++ b/inputFiles/immiscibleMultiphaseFlow/immiscibleTwoPhase_GravitySegregation_1d/x.txt @@ -0,0 +1 @@ +0.5 diff --git a/inputFiles/immiscibleMultiphaseFlow/immiscibleTwoPhase_GravitySegregation_1d/y.txt b/inputFiles/immiscibleMultiphaseFlow/immiscibleTwoPhase_GravitySegregation_1d/y.txt new file mode 100644 index 00000000000..2eb3c4fe4ee --- /dev/null +++ b/inputFiles/immiscibleMultiphaseFlow/immiscibleTwoPhase_GravitySegregation_1d/y.txt @@ -0,0 +1 @@ +0.5 diff --git a/inputFiles/immiscibleMultiphaseFlow/immiscibleTwoPhase_GravitySegregation_1d/z.txt b/inputFiles/immiscibleMultiphaseFlow/immiscibleTwoPhase_GravitySegregation_1d/z.txt new file mode 100644 index 00000000000..f43a1f14d26 --- /dev/null +++ b/inputFiles/immiscibleMultiphaseFlow/immiscibleTwoPhase_GravitySegregation_1d/z.txt @@ -0,0 +1,10 @@ +0.5 +1.5 +2.5 +3.5 +4.5 +5.5 +6.5 +7.5 +8.5 +9.5 diff --git a/inputFiles/immiscibleMultiphaseFlow/immiscibleTwoPhase_interface_conditions/1D_case/initialPressure.txt b/inputFiles/immiscibleMultiphaseFlow/immiscibleTwoPhase_interface_conditions/1D_case/initialPressure.txt new file mode 100644 index 00000000000..63943bfa00d --- /dev/null +++ b/inputFiles/immiscibleMultiphaseFlow/immiscibleTwoPhase_interface_conditions/1D_case/initialPressure.txt @@ -0,0 +1,10 @@ +1e7 +1e7 +1e7 +1e7 +1e7 +1e7 +1e7 +1e7 +1e7 +1e7 diff --git a/inputFiles/immiscibleMultiphaseFlow/immiscibleTwoPhase_interface_conditions/1D_case/initialSaturation1.txt b/inputFiles/immiscibleMultiphaseFlow/immiscibleTwoPhase_interface_conditions/1D_case/initialSaturation1.txt new file mode 100644 index 00000000000..1859fabae78 --- /dev/null +++ b/inputFiles/immiscibleMultiphaseFlow/immiscibleTwoPhase_interface_conditions/1D_case/initialSaturation1.txt @@ -0,0 +1,10 @@ +0.95 +0.95 +0.95 +0.95 +0.95 +0.95 +0.95 +0.05 +0.05 +0.05 diff --git a/inputFiles/immiscibleMultiphaseFlow/immiscibleTwoPhase_interface_conditions/1D_case/initialSaturation2.txt b/inputFiles/immiscibleMultiphaseFlow/immiscibleTwoPhase_interface_conditions/1D_case/initialSaturation2.txt new file mode 100644 index 00000000000..d113536f2fd --- /dev/null +++ b/inputFiles/immiscibleMultiphaseFlow/immiscibleTwoPhase_interface_conditions/1D_case/initialSaturation2.txt @@ -0,0 +1,10 @@ +0.05 +0.05 +0.05 +0.05 +0.05 +0.05 +0.05 +0.95 +0.95 +0.95 diff --git a/inputFiles/immiscibleMultiphaseFlow/immiscibleTwoPhase_interface_conditions/1D_case/jFunction_linear.txt b/inputFiles/immiscibleMultiphaseFlow/immiscibleTwoPhase_interface_conditions/1D_case/jFunction_linear.txt new file mode 100644 index 00000000000..c3dfd50073f --- /dev/null +++ b/inputFiles/immiscibleMultiphaseFlow/immiscibleTwoPhase_interface_conditions/1D_case/jFunction_linear.txt @@ -0,0 +1,2 @@ +4.33172935918785 +1.66604975353379 diff --git a/inputFiles/immiscibleMultiphaseFlow/immiscibleTwoPhase_interface_conditions/1D_case/permx.geos b/inputFiles/immiscibleMultiphaseFlow/immiscibleTwoPhase_interface_conditions/1D_case/permx.geos new file mode 100644 index 00000000000..4d47d8fa568 --- /dev/null +++ b/inputFiles/immiscibleMultiphaseFlow/immiscibleTwoPhase_interface_conditions/1D_case/permx.geos @@ -0,0 +1,10 @@ +50 +50 +50 +50 +50 +200 +200 +200 +200 +200 diff --git a/inputFiles/immiscibleMultiphaseFlow/immiscibleTwoPhase_interface_conditions/1D_case/permy.geos b/inputFiles/immiscibleMultiphaseFlow/immiscibleTwoPhase_interface_conditions/1D_case/permy.geos new file mode 100644 index 00000000000..4d47d8fa568 --- /dev/null +++ b/inputFiles/immiscibleMultiphaseFlow/immiscibleTwoPhase_interface_conditions/1D_case/permy.geos @@ -0,0 +1,10 @@ +50 +50 +50 +50 +50 +200 +200 +200 +200 +200 diff --git a/inputFiles/immiscibleMultiphaseFlow/immiscibleTwoPhase_interface_conditions/1D_case/permz.geos b/inputFiles/immiscibleMultiphaseFlow/immiscibleTwoPhase_interface_conditions/1D_case/permz.geos new file mode 100644 index 00000000000..4d47d8fa568 --- /dev/null +++ b/inputFiles/immiscibleMultiphaseFlow/immiscibleTwoPhase_interface_conditions/1D_case/permz.geos @@ -0,0 +1,10 @@ +50 +50 +50 +50 +50 +200 +200 +200 +200 +200 diff --git a/inputFiles/immiscibleMultiphaseFlow/immiscibleTwoPhase_interface_conditions/1D_case/phaseVolumeFraction_water_linear.txt b/inputFiles/immiscibleMultiphaseFlow/immiscibleTwoPhase_interface_conditions/1D_case/phaseVolumeFraction_water_linear.txt new file mode 100644 index 00000000000..0d66ea1aee9 --- /dev/null +++ b/inputFiles/immiscibleMultiphaseFlow/immiscibleTwoPhase_interface_conditions/1D_case/phaseVolumeFraction_water_linear.txt @@ -0,0 +1,2 @@ +0 +1 diff --git a/inputFiles/immiscibleMultiphaseFlow/immiscibleTwoPhase_interface_conditions/1D_case/tables/jFunction b/inputFiles/immiscibleMultiphaseFlow/immiscibleTwoPhase_interface_conditions/1D_case/tables/jFunction new file mode 100644 index 00000000000..e69de29bb2d diff --git a/inputFiles/immiscibleMultiphaseFlow/immiscibleTwoPhase_interface_conditions/1D_case/tables/jFunction.txt b/inputFiles/immiscibleMultiphaseFlow/immiscibleTwoPhase_interface_conditions/1D_case/tables/jFunction.txt new file mode 100644 index 00000000000..72a78a75b85 --- /dev/null +++ b/inputFiles/immiscibleMultiphaseFlow/immiscibleTwoPhase_interface_conditions/1D_case/tables/jFunction.txt @@ -0,0 +1,76 @@ +99.9980934 +17.78445609 +11.20350909 +8.549878542 +7.057769576 +6.082201546 +5.386086427 +4.86006559 +4.446116454 +4.110353325 +3.831547061 +3.595663494 +3.393021946 +3.216710174 +3.061649542 +2.924017691 +2.800877928 +2.68993357 +2.589360389 +2.497689435 +2.413723433 +2.336475912 +2.265126101 +2.198985069 +2.137469915 +2.080083807 +2.02640045 +1.976051821 +1.928718377 +1.884121218 +1.842015737 +1.802186411 +1.764442543 +1.728614745 +1.694551981 +1.662119113 +1.631194842 +1.601669938 +1.573445757 +1.546432977 +1.520550493 +1.495724483 +1.471887601 +1.448978263 +1.426940034 +1.405721104 +1.385273798 +1.365554178 +1.346521677 +1.328138769 +1.310370692 +1.293185194 +1.276552299 +1.260444114 +1.244834648 +1.229699646 +1.215016445 +1.200763848 +1.186921998 +1.173472275 +1.160397205 +1.147680364 +1.135306301 +1.123260469 +1.111529155 +1.100099423 +1.088959056 +1.078096509 +1.067500858 +1.057161765 +1.047069433 +1.03721457 +1.027588362 +1.018182437 +1.008988837 +0.999999998 diff --git a/inputFiles/immiscibleMultiphaseFlow/immiscibleTwoPhase_interface_conditions/1D_case/tables/jFunction_linear.txt b/inputFiles/immiscibleMultiphaseFlow/immiscibleTwoPhase_interface_conditions/1D_case/tables/jFunction_linear.txt new file mode 100644 index 00000000000..13036f8fd03 --- /dev/null +++ b/inputFiles/immiscibleMultiphaseFlow/immiscibleTwoPhase_interface_conditions/1D_case/tables/jFunction_linear.txt @@ -0,0 +1,2 @@ +100.00 +1.00 diff --git a/inputFiles/immiscibleMultiphaseFlow/immiscibleTwoPhase_interface_conditions/1D_case/tables/phaseVolumeFraction_water.txt b/inputFiles/immiscibleMultiphaseFlow/immiscibleTwoPhase_interface_conditions/1D_case/tables/phaseVolumeFraction_water.txt new file mode 100644 index 00000000000..f38627d4e85 --- /dev/null +++ b/inputFiles/immiscibleMultiphaseFlow/immiscibleTwoPhase_interface_conditions/1D_case/tables/phaseVolumeFraction_water.txt @@ -0,0 +1,76 @@ +0.001 +0.013333333 +0.026666667 +0.04 +0.053333333 +0.066666667 +0.08 +0.093333333 +0.106666667 +0.12 +0.133333333 +0.146666667 +0.16 +0.173333333 +0.186666667 +0.2 +0.213333333 +0.226666667 +0.24 +0.253333333 +0.266666667 +0.28 +0.293333333 +0.306666667 +0.32 +0.333333333 +0.346666667 +0.36 +0.373333333 +0.386666667 +0.4 +0.413333333 +0.426666667 +0.44 +0.453333333 +0.466666667 +0.48 +0.493333333 +0.506666667 +0.52 +0.533333333 +0.546666667 +0.56 +0.573333333 +0.586666667 +0.6 +0.613333333 +0.626666667 +0.64 +0.653333333 +0.666666667 +0.68 +0.693333333 +0.706666667 +0.72 +0.733333333 +0.746666667 +0.76 +0.773333333 +0.786666667 +0.8 +0.813333333 +0.826666667 +0.84 +0.853333333 +0.866666667 +0.88 +0.893333333 +0.906666667 +0.92 +0.933333333 +0.946666667 +0.96 +0.973333333 +0.986666667 +1 diff --git a/inputFiles/immiscibleMultiphaseFlow/immiscibleTwoPhase_interface_conditions/1D_case/tables/phaseVolumeFraction_water_linear.txt b/inputFiles/immiscibleMultiphaseFlow/immiscibleTwoPhase_interface_conditions/1D_case/tables/phaseVolumeFraction_water_linear.txt new file mode 100644 index 00000000000..0a269ee3741 --- /dev/null +++ b/inputFiles/immiscibleMultiphaseFlow/immiscibleTwoPhase_interface_conditions/1D_case/tables/phaseVolumeFraction_water_linear.txt @@ -0,0 +1,2 @@ +0.0 +1.0 diff --git a/inputFiles/immiscibleMultiphaseFlow/immiscibleTwoPhase_interface_conditions/1D_case/uni_directional_flow_base.xml b/inputFiles/immiscibleMultiphaseFlow/immiscibleTwoPhase_interface_conditions/1D_case/uni_directional_flow_base.xml new file mode 100644 index 00000000000..efbd5b34421 --- /dev/null +++ b/inputFiles/immiscibleMultiphaseFlow/immiscibleTwoPhase_interface_conditions/1D_case/uni_directional_flow_base.xml @@ -0,0 +1,293 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inputFiles/immiscibleMultiphaseFlow/immiscibleTwoPhase_interface_conditions/1D_case/uni_directional_flow_interface_condition.xml b/inputFiles/immiscibleMultiphaseFlow/immiscibleTwoPhase_interface_conditions/1D_case/uni_directional_flow_interface_condition.xml new file mode 100644 index 00000000000..41ba4e3ed92 --- /dev/null +++ b/inputFiles/immiscibleMultiphaseFlow/immiscibleTwoPhase_interface_conditions/1D_case/uni_directional_flow_interface_condition.xml @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inputFiles/immiscibleMultiphaseFlow/immiscibleTwoPhase_interface_conditions/1D_case/x.txt b/inputFiles/immiscibleMultiphaseFlow/immiscibleTwoPhase_interface_conditions/1D_case/x.txt new file mode 100644 index 00000000000..2eb3c4fe4ee --- /dev/null +++ b/inputFiles/immiscibleMultiphaseFlow/immiscibleTwoPhase_interface_conditions/1D_case/x.txt @@ -0,0 +1 @@ +0.5 diff --git a/inputFiles/immiscibleMultiphaseFlow/immiscibleTwoPhase_interface_conditions/1D_case/y.txt b/inputFiles/immiscibleMultiphaseFlow/immiscibleTwoPhase_interface_conditions/1D_case/y.txt new file mode 100644 index 00000000000..2eb3c4fe4ee --- /dev/null +++ b/inputFiles/immiscibleMultiphaseFlow/immiscibleTwoPhase_interface_conditions/1D_case/y.txt @@ -0,0 +1 @@ +0.5 diff --git a/inputFiles/immiscibleMultiphaseFlow/immiscibleTwoPhase_interface_conditions/1D_case/z.txt b/inputFiles/immiscibleMultiphaseFlow/immiscibleTwoPhase_interface_conditions/1D_case/z.txt new file mode 100644 index 00000000000..f43a1f14d26 --- /dev/null +++ b/inputFiles/immiscibleMultiphaseFlow/immiscibleTwoPhase_interface_conditions/1D_case/z.txt @@ -0,0 +1,10 @@ +0.5 +1.5 +2.5 +3.5 +4.5 +5.5 +6.5 +7.5 +8.5 +9.5 diff --git a/inputFiles/immiscibleMultiphaseFlow/immiscibleTwoPhase_interface_conditions/3D_case/input/initialPressure.txt b/inputFiles/immiscibleMultiphaseFlow/immiscibleTwoPhase_interface_conditions/3D_case/input/initialPressure.txt new file mode 100644 index 00000000000..6963bb6148c --- /dev/null +++ b/inputFiles/immiscibleMultiphaseFlow/immiscibleTwoPhase_interface_conditions/3D_case/input/initialPressure.txt @@ -0,0 +1,1200 @@ +1.00022072e+07 +1.00022072e+07 +1.00022072e+07 +1.00022072e+07 +1.00022072e+07 +1.00022072e+07 +1.00022072e+07 +1.00022072e+07 +1.00022072e+07 +1.00022072e+07 +1.00022072e+07 +1.00022072e+07 +1.00022072e+07 +1.00022072e+07 +1.00022072e+07 +1.00022072e+07 +1.00022072e+07 +1.00022072e+07 +1.00022072e+07 +1.00022072e+07 +1.00022072e+07 +1.00022072e+07 +1.00022072e+07 +1.00022072e+07 +1.00022072e+07 +1.00022072e+07 +1.00022072e+07 +1.00022072e+07 +1.00022072e+07 +1.00022072e+07 +1.00022072e+07 +1.00022072e+07 +1.00022072e+07 +1.00022072e+07 +1.00022072e+07 +1.00022072e+07 +1.00022072e+07 +1.00022072e+07 +1.00022072e+07 +1.00022072e+07 +1.00022072e+07 +1.00022072e+07 +1.00022072e+07 +1.00022072e+07 +1.00022072e+07 +1.00022072e+07 +1.00022072e+07 +1.00022072e+07 +1.00022072e+07 +1.00022072e+07 +1.00022072e+07 +1.00022072e+07 +1.00022072e+07 +1.00022072e+07 +1.00022072e+07 +1.00022072e+07 +1.00022072e+07 +1.00022072e+07 +1.00022072e+07 +1.00022072e+07 +1.00066218e+07 +1.00066218e+07 +1.00066218e+07 +1.00066218e+07 +1.00066218e+07 +1.00066218e+07 +1.00066218e+07 +1.00066218e+07 +1.00066218e+07 +1.00066218e+07 +1.00066218e+07 +1.00066218e+07 +1.00066218e+07 +1.00066218e+07 +1.00066218e+07 +1.00066218e+07 +1.00066218e+07 +1.00066218e+07 +1.00066218e+07 +1.00066218e+07 +1.00066218e+07 +1.00066218e+07 +1.00066218e+07 +1.00066218e+07 +1.00066218e+07 +1.00066218e+07 +1.00066218e+07 +1.00066218e+07 +1.00066218e+07 +1.00066218e+07 +1.00066218e+07 +1.00066218e+07 +1.00066218e+07 +1.00066218e+07 +1.00066218e+07 +1.00066218e+07 +1.00066218e+07 +1.00066218e+07 +1.00066218e+07 +1.00066218e+07 +1.00066218e+07 +1.00066218e+07 +1.00066218e+07 +1.00066218e+07 +1.00066218e+07 +1.00066218e+07 +1.00066218e+07 +1.00066218e+07 +1.00066218e+07 +1.00066218e+07 +1.00066218e+07 +1.00066218e+07 +1.00066218e+07 +1.00066218e+07 +1.00066218e+07 +1.00066218e+07 +1.00066218e+07 +1.00066218e+07 +1.00066218e+07 +1.00066218e+07 +1.00110362e+07 +1.00110362e+07 +1.00110362e+07 +1.00110362e+07 +1.00110362e+07 +1.00110362e+07 +1.00110362e+07 +1.00110362e+07 +1.00110362e+07 +1.00110362e+07 +1.00110362e+07 +1.00110362e+07 +1.00110362e+07 +1.00110362e+07 +1.00110362e+07 +1.00110362e+07 +1.00110362e+07 +1.00110362e+07 +1.00110362e+07 +1.00110362e+07 +1.00110362e+07 +1.00110362e+07 +1.00110362e+07 +1.00110362e+07 +1.00110362e+07 +1.00110362e+07 +1.00110362e+07 +1.00110362e+07 +1.00110362e+07 +1.00110362e+07 +1.00110362e+07 +1.00110362e+07 +1.00110362e+07 +1.00110362e+07 +1.00110362e+07 +1.00110362e+07 +1.00110362e+07 +1.00110362e+07 +1.00110362e+07 +1.00110362e+07 +1.00110362e+07 +1.00110362e+07 +1.00110362e+07 +1.00110362e+07 +1.00110362e+07 +1.00110362e+07 +1.00110362e+07 +1.00110362e+07 +1.00110362e+07 +1.00110362e+07 +1.00110362e+07 +1.00110362e+07 +1.00110362e+07 +1.00110362e+07 +1.00110362e+07 +1.00110362e+07 +1.00110362e+07 +1.00110362e+07 +1.00110362e+07 +1.00110362e+07 +1.00154508e+07 +1.00154508e+07 +1.00154508e+07 +1.00154508e+07 +1.00154508e+07 +1.00154508e+07 +1.00154508e+07 +1.00154508e+07 +1.00154508e+07 +1.00154508e+07 +1.00154508e+07 +1.00154508e+07 +1.00154508e+07 +1.00154508e+07 +1.00154508e+07 +1.00154508e+07 +1.00154508e+07 +1.00154508e+07 +1.00154508e+07 +1.00154508e+07 +1.00154508e+07 +1.00154508e+07 +1.00154508e+07 +1.00154508e+07 +1.00154508e+07 +1.00154508e+07 +1.00154508e+07 +1.00154508e+07 +1.00154508e+07 +1.00154508e+07 +1.00154508e+07 +1.00154508e+07 +1.00154508e+07 +1.00154508e+07 +1.00154508e+07 +1.00154508e+07 +1.00154508e+07 +1.00154508e+07 +1.00154508e+07 +1.00154508e+07 +1.00154508e+07 +1.00154508e+07 +1.00154508e+07 +1.00154508e+07 +1.00154508e+07 +1.00154508e+07 +1.00154508e+07 +1.00154508e+07 +1.00154508e+07 +1.00154508e+07 +1.00154508e+07 +1.00154508e+07 +1.00154508e+07 +1.00154508e+07 +1.00154508e+07 +1.00154508e+07 +1.00154508e+07 +1.00154508e+07 +1.00154508e+07 +1.00154508e+07 +1.00198652e+07 +1.00198652e+07 +1.00198652e+07 +1.00198652e+07 +1.00198652e+07 +1.00198652e+07 +1.00198652e+07 +1.00198652e+07 +1.00198652e+07 +1.00198652e+07 +1.00198652e+07 +1.00198652e+07 +1.00198652e+07 +1.00198652e+07 +1.00198652e+07 +1.00198652e+07 +1.00198652e+07 +1.00198652e+07 +1.00198652e+07 +1.00198652e+07 +1.00198652e+07 +1.00198652e+07 +1.00198652e+07 +1.00198652e+07 +1.00198652e+07 +1.00198652e+07 +1.00198652e+07 +1.00198652e+07 +1.00198652e+07 +1.00198652e+07 +1.00198652e+07 +1.00198652e+07 +1.00198652e+07 +1.00198652e+07 +1.00198652e+07 +1.00198652e+07 +1.00198652e+07 +1.00198652e+07 +1.00198652e+07 +1.00198652e+07 +1.00198652e+07 +1.00198652e+07 +1.00198652e+07 +1.00198652e+07 +1.00198652e+07 +1.00198652e+07 +1.00198652e+07 +1.00198652e+07 +1.00198652e+07 +1.00198652e+07 +1.00198652e+07 +1.00198652e+07 +1.00198652e+07 +1.00198652e+07 +1.00198652e+07 +1.00198652e+07 +1.00198652e+07 +1.00198652e+07 +1.00198652e+07 +1.00198652e+07 +1.00242798e+07 +1.00242798e+07 +1.00242798e+07 +1.00242798e+07 +1.00242798e+07 +1.00242798e+07 +1.00242798e+07 +1.00242798e+07 +1.00242798e+07 +1.00242798e+07 +1.00242798e+07 +1.00242798e+07 +1.00242798e+07 +1.00242798e+07 +1.00242798e+07 +1.00242798e+07 +1.00242798e+07 +1.00242798e+07 +1.00242798e+07 +1.00242798e+07 +1.00242798e+07 +1.00242798e+07 +1.00242798e+07 +1.00242798e+07 +1.00242798e+07 +1.00242798e+07 +1.00242798e+07 +1.00242798e+07 +1.00242798e+07 +1.00242798e+07 +1.00242798e+07 +1.00242798e+07 +1.00242798e+07 +1.00242798e+07 +1.00242798e+07 +1.00242798e+07 +1.00242798e+07 +1.00242798e+07 +1.00242798e+07 +1.00242798e+07 +1.00242798e+07 +1.00242798e+07 +1.00242798e+07 +1.00242798e+07 +1.00242798e+07 +1.00242798e+07 +1.00242798e+07 +1.00242798e+07 +1.00242798e+07 +1.00242798e+07 +1.00242798e+07 +1.00242798e+07 +1.00242798e+07 +1.00242798e+07 +1.00242798e+07 +1.00242798e+07 +1.00242798e+07 +1.00242798e+07 +1.00242798e+07 +1.00242798e+07 +1.00286942e+07 +1.00286942e+07 +1.00286942e+07 +1.00286942e+07 +1.00286942e+07 +1.00286942e+07 +1.00286942e+07 +1.00286942e+07 +1.00286942e+07 +1.00286942e+07 +1.00286942e+07 +1.00286942e+07 +1.00286942e+07 +1.00286942e+07 +1.00286942e+07 +1.00286942e+07 +1.00286942e+07 +1.00286942e+07 +1.00286942e+07 +1.00286942e+07 +1.00286942e+07 +1.00286942e+07 +1.00286942e+07 +1.00286942e+07 +1.00286942e+07 +1.00286942e+07 +1.00286942e+07 +1.00286942e+07 +1.00286942e+07 +1.00286942e+07 +1.00286942e+07 +1.00286942e+07 +1.00286942e+07 +1.00286942e+07 +1.00286942e+07 +1.00286942e+07 +1.00286942e+07 +1.00286942e+07 +1.00286942e+07 +1.00286942e+07 +1.00286942e+07 +1.00286942e+07 +1.00286942e+07 +1.00286942e+07 +1.00286942e+07 +1.00286942e+07 +1.00286942e+07 +1.00286942e+07 +1.00286942e+07 +1.00286942e+07 +1.00286942e+07 +1.00286942e+07 +1.00286942e+07 +1.00286942e+07 +1.00286942e+07 +1.00286942e+07 +1.00286942e+07 +1.00286942e+07 +1.00286942e+07 +1.00286942e+07 +1.00331088e+07 +1.00331088e+07 +1.00331088e+07 +1.00331088e+07 +1.00331088e+07 +1.00331088e+07 +1.00331088e+07 +1.00331088e+07 +1.00331088e+07 +1.00331088e+07 +1.00331088e+07 +1.00331088e+07 +1.00331088e+07 +1.00331088e+07 +1.00331088e+07 +1.00331088e+07 +1.00331088e+07 +1.00331088e+07 +1.00331088e+07 +1.00331088e+07 +1.00331088e+07 +1.00331088e+07 +1.00331088e+07 +1.00331088e+07 +1.00331088e+07 +1.00331088e+07 +1.00331088e+07 +1.00331088e+07 +1.00331088e+07 +1.00331088e+07 +1.00331088e+07 +1.00331088e+07 +1.00331088e+07 +1.00331088e+07 +1.00331088e+07 +1.00331088e+07 +1.00331088e+07 +1.00331088e+07 +1.00331088e+07 +1.00331088e+07 +1.00331088e+07 +1.00331088e+07 +1.00331088e+07 +1.00331088e+07 +1.00331088e+07 +1.00331088e+07 +1.00331088e+07 +1.00331088e+07 +1.00331088e+07 +1.00331088e+07 +1.00331088e+07 +1.00331088e+07 +1.00331088e+07 +1.00331088e+07 +1.00331088e+07 +1.00331088e+07 +1.00331088e+07 +1.00331088e+07 +1.00331088e+07 +1.00331088e+07 +1.00375232e+07 +1.00375232e+07 +1.00375232e+07 +1.00375232e+07 +1.00375232e+07 +1.00375232e+07 +1.00375232e+07 +1.00375232e+07 +1.00375232e+07 +1.00375232e+07 +1.00375232e+07 +1.00375232e+07 +1.00375232e+07 +1.00375232e+07 +1.00375232e+07 +1.00375232e+07 +1.00375232e+07 +1.00375232e+07 +1.00375232e+07 +1.00375232e+07 +1.00375232e+07 +1.00375232e+07 +1.00375232e+07 +1.00375232e+07 +1.00375232e+07 +1.00375232e+07 +1.00375232e+07 +1.00375232e+07 +1.00375232e+07 +1.00375232e+07 +1.00375232e+07 +1.00375232e+07 +1.00375232e+07 +1.00375232e+07 +1.00375232e+07 +1.00375232e+07 +1.00375232e+07 +1.00375232e+07 +1.00375232e+07 +1.00375232e+07 +1.00375232e+07 +1.00375232e+07 +1.00375232e+07 +1.00375232e+07 +1.00375232e+07 +1.00375232e+07 +1.00375232e+07 +1.00375232e+07 +1.00375232e+07 +1.00375232e+07 +1.00375232e+07 +1.00375232e+07 +1.00375232e+07 +1.00375232e+07 +1.00375232e+07 +1.00375232e+07 +1.00375232e+07 +1.00375232e+07 +1.00375232e+07 +1.00375232e+07 +1.00419378e+07 +1.00419378e+07 +1.00419378e+07 +1.00419378e+07 +1.00419378e+07 +1.00419378e+07 +1.00419378e+07 +1.00419378e+07 +1.00419378e+07 +1.00419378e+07 +1.00419378e+07 +1.00419378e+07 +1.00419378e+07 +1.00419378e+07 +1.00419378e+07 +1.00419378e+07 +1.00419378e+07 +1.00419378e+07 +1.00419378e+07 +1.00419378e+07 +1.00419378e+07 +1.00419378e+07 +1.00419378e+07 +1.00419378e+07 +1.00419378e+07 +1.00419378e+07 +1.00419378e+07 +1.00419378e+07 +1.00419378e+07 +1.00419378e+07 +1.00419378e+07 +1.00419378e+07 +1.00419378e+07 +1.00419378e+07 +1.00419378e+07 +1.00419378e+07 +1.00419378e+07 +1.00419378e+07 +1.00419378e+07 +1.00419378e+07 +1.00419378e+07 +1.00419378e+07 +1.00419378e+07 +1.00419378e+07 +1.00419378e+07 +1.00419378e+07 +1.00419378e+07 +1.00419378e+07 +1.00419378e+07 +1.00419378e+07 +1.00419378e+07 +1.00419378e+07 +1.00419378e+07 +1.00419378e+07 +1.00419378e+07 +1.00419378e+07 +1.00419378e+07 +1.00419378e+07 +1.00419378e+07 +1.00419378e+07 +1.00463522e+07 +1.00463522e+07 +1.00463522e+07 +1.00463522e+07 +1.00463522e+07 +1.00463522e+07 +1.00463522e+07 +1.00463522e+07 +1.00463522e+07 +1.00463522e+07 +1.00463522e+07 +1.00463522e+07 +1.00463522e+07 +1.00463522e+07 +1.00463522e+07 +1.00463522e+07 +1.00463522e+07 +1.00463522e+07 +1.00463522e+07 +1.00463522e+07 +1.00463522e+07 +1.00463522e+07 +1.00463522e+07 +1.00463522e+07 +1.00463522e+07 +1.00463522e+07 +1.00463522e+07 +1.00463522e+07 +1.00463522e+07 +1.00463522e+07 +1.00463522e+07 +1.00463522e+07 +1.00463522e+07 +1.00463522e+07 +1.00463522e+07 +1.00463522e+07 +1.00463522e+07 +1.00463522e+07 +1.00463522e+07 +1.00463522e+07 +1.00463522e+07 +1.00463522e+07 +1.00463522e+07 +1.00463522e+07 +1.00463522e+07 +1.00463522e+07 +1.00463522e+07 +1.00463522e+07 +1.00463522e+07 +1.00463522e+07 +1.00463522e+07 +1.00463522e+07 +1.00463522e+07 +1.00463522e+07 +1.00463522e+07 +1.00463522e+07 +1.00463522e+07 +1.00463522e+07 +1.00463522e+07 +1.00463522e+07 +1.00507668e+07 +1.00507668e+07 +1.00507668e+07 +1.00507668e+07 +1.00507668e+07 +1.00507668e+07 +1.00507668e+07 +1.00507668e+07 +1.00507668e+07 +1.00507668e+07 +1.00507668e+07 +1.00507668e+07 +1.00507668e+07 +1.00507668e+07 +1.00507668e+07 +1.00507668e+07 +1.00507668e+07 +1.00507668e+07 +1.00507668e+07 +1.00507668e+07 +1.00507668e+07 +1.00507668e+07 +1.00507668e+07 +1.00507668e+07 +1.00507668e+07 +1.00507668e+07 +1.00507668e+07 +1.00507668e+07 +1.00507668e+07 +1.00507668e+07 +1.00507668e+07 +1.00507668e+07 +1.00507668e+07 +1.00507668e+07 +1.00507668e+07 +1.00507668e+07 +1.00507668e+07 +1.00507668e+07 +1.00507668e+07 +1.00507668e+07 +1.00507668e+07 +1.00507668e+07 +1.00507668e+07 +1.00507668e+07 +1.00507668e+07 +1.00507668e+07 +1.00507668e+07 +1.00507668e+07 +1.00507668e+07 +1.00507668e+07 +1.00507668e+07 +1.00507668e+07 +1.00507668e+07 +1.00507668e+07 +1.00507668e+07 +1.00507668e+07 +1.00507668e+07 +1.00507668e+07 +1.00507668e+07 +1.00507668e+07 +1.00551812e+07 +1.00551812e+07 +1.00551812e+07 +1.00551812e+07 +1.00551812e+07 +1.00551812e+07 +1.00551812e+07 +1.00551812e+07 +1.00551812e+07 +1.00551812e+07 +1.00551812e+07 +1.00551812e+07 +1.00551812e+07 +1.00551812e+07 +1.00551812e+07 +1.00551812e+07 +1.00551812e+07 +1.00551812e+07 +1.00551812e+07 +1.00551812e+07 +1.00551812e+07 +1.00551812e+07 +1.00551812e+07 +1.00551812e+07 +1.00551812e+07 +1.00551812e+07 +1.00551812e+07 +1.00551812e+07 +1.00551812e+07 +1.00551812e+07 +1.00551812e+07 +1.00551812e+07 +1.00551812e+07 +1.00551812e+07 +1.00551812e+07 +1.00551812e+07 +1.00551812e+07 +1.00551812e+07 +1.00551812e+07 +1.00551812e+07 +1.00551812e+07 +1.00551812e+07 +1.00551812e+07 +1.00551812e+07 +1.00551812e+07 +1.00551812e+07 +1.00551812e+07 +1.00551812e+07 +1.00551812e+07 +1.00551812e+07 +1.00551812e+07 +1.00551812e+07 +1.00551812e+07 +1.00551812e+07 +1.00551812e+07 +1.00551812e+07 +1.00551812e+07 +1.00551812e+07 +1.00551812e+07 +1.00551812e+07 +1.00595958e+07 +1.00595958e+07 +1.00595958e+07 +1.00595958e+07 +1.00595958e+07 +1.00595958e+07 +1.00595958e+07 +1.00595958e+07 +1.00595958e+07 +1.00595958e+07 +1.00595958e+07 +1.00595958e+07 +1.00595958e+07 +1.00595958e+07 +1.00595958e+07 +1.00595958e+07 +1.00595958e+07 +1.00595958e+07 +1.00595958e+07 +1.00595958e+07 +1.00595958e+07 +1.00595958e+07 +1.00595958e+07 +1.00595958e+07 +1.00595958e+07 +1.00595958e+07 +1.00595958e+07 +1.00595958e+07 +1.00595958e+07 +1.00595958e+07 +1.00595958e+07 +1.00595958e+07 +1.00595958e+07 +1.00595958e+07 +1.00595958e+07 +1.00595958e+07 +1.00595958e+07 +1.00595958e+07 +1.00595958e+07 +1.00595958e+07 +1.00595958e+07 +1.00595958e+07 +1.00595958e+07 +1.00595958e+07 +1.00595958e+07 +1.00595958e+07 +1.00595958e+07 +1.00595958e+07 +1.00595958e+07 +1.00595958e+07 +1.00595958e+07 +1.00595958e+07 +1.00595958e+07 +1.00595958e+07 +1.00595958e+07 +1.00595958e+07 +1.00595958e+07 +1.00595958e+07 +1.00595958e+07 +1.00595958e+07 +1.00640102e+07 +1.00640102e+07 +1.00640102e+07 +1.00640102e+07 +1.00640102e+07 +1.00640102e+07 +1.00640102e+07 +1.00640102e+07 +1.00640102e+07 +1.00640102e+07 +1.00640102e+07 +1.00640102e+07 +1.00640102e+07 +1.00640102e+07 +1.00640102e+07 +1.00640102e+07 +1.00640102e+07 +1.00640102e+07 +1.00640102e+07 +1.00640102e+07 +1.00640102e+07 +1.00640102e+07 +1.00640102e+07 +1.00640102e+07 +1.00640102e+07 +1.00640102e+07 +1.00640102e+07 +1.00640102e+07 +1.00640102e+07 +1.00640102e+07 +1.00640102e+07 +1.00640102e+07 +1.00640102e+07 +1.00640102e+07 +1.00640102e+07 +1.00640102e+07 +1.00640102e+07 +1.00640102e+07 +1.00640102e+07 +1.00640102e+07 +1.00640102e+07 +1.00640102e+07 +1.00640102e+07 +1.00640102e+07 +1.00640102e+07 +1.00640102e+07 +1.00640102e+07 +1.00640102e+07 +1.00640102e+07 +1.00640102e+07 +1.00640102e+07 +1.00640102e+07 +1.00640102e+07 +1.00640102e+07 +1.00640102e+07 +1.00640102e+07 +1.00640102e+07 +1.00640102e+07 +1.00640102e+07 +1.00640102e+07 +1.00684248e+07 +1.00684248e+07 +1.00684248e+07 +1.00684248e+07 +1.00684248e+07 +1.00684248e+07 +1.00684248e+07 +1.00684248e+07 +1.00684248e+07 +1.00684248e+07 +1.00684248e+07 +1.00684248e+07 +1.00684248e+07 +1.00684248e+07 +1.00684248e+07 +1.00684248e+07 +1.00684248e+07 +1.00684248e+07 +1.00684248e+07 +1.00684248e+07 +1.00684248e+07 +1.00684248e+07 +1.00684248e+07 +1.00684248e+07 +1.00684248e+07 +1.00684248e+07 +1.00684248e+07 +1.00684248e+07 +1.00684248e+07 +1.00684248e+07 +1.00684248e+07 +1.00684248e+07 +1.00684248e+07 +1.00684248e+07 +1.00684248e+07 +1.00684248e+07 +1.00684248e+07 +1.00684248e+07 +1.00684248e+07 +1.00684248e+07 +1.00684248e+07 +1.00684248e+07 +1.00684248e+07 +1.00684248e+07 +1.00684248e+07 +1.00684248e+07 +1.00684248e+07 +1.00684248e+07 +1.00684248e+07 +1.00684248e+07 +1.00684248e+07 +1.00684248e+07 +1.00684248e+07 +1.00684248e+07 +1.00684248e+07 +1.00684248e+07 +1.00684248e+07 +1.00684248e+07 +1.00684248e+07 +1.00684248e+07 +1.00728392e+07 +1.00728392e+07 +1.00728392e+07 +1.00728392e+07 +1.00728392e+07 +1.00728392e+07 +1.00728392e+07 +1.00728392e+07 +1.00728392e+07 +1.00728392e+07 +1.00728392e+07 +1.00728392e+07 +1.00728392e+07 +1.00728392e+07 +1.00728392e+07 +1.00728392e+07 +1.00728392e+07 +1.00728392e+07 +1.00728392e+07 +1.00728392e+07 +1.00728392e+07 +1.00728392e+07 +1.00728392e+07 +1.00728392e+07 +1.00728392e+07 +1.00728392e+07 +1.00728392e+07 +1.00728392e+07 +1.00728392e+07 +1.00728392e+07 +1.00728392e+07 +1.00728392e+07 +1.00728392e+07 +1.00728392e+07 +1.00728392e+07 +1.00728392e+07 +1.00728392e+07 +1.00728392e+07 +1.00728392e+07 +1.00728392e+07 +1.00728392e+07 +1.00728392e+07 +1.00728392e+07 +1.00728392e+07 +1.00728392e+07 +1.00728392e+07 +1.00728392e+07 +1.00728392e+07 +1.00728392e+07 +1.00728392e+07 +1.00728392e+07 +1.00728392e+07 +1.00728392e+07 +1.00728392e+07 +1.00728392e+07 +1.00728392e+07 +1.00728392e+07 +1.00728392e+07 +1.00728392e+07 +1.00728392e+07 +1.00772538e+07 +1.00772538e+07 +1.00772538e+07 +1.00772538e+07 +1.00772538e+07 +1.00772538e+07 +1.00772538e+07 +1.00772538e+07 +1.00772538e+07 +1.00772538e+07 +1.00772538e+07 +1.00772538e+07 +1.00772538e+07 +1.00772538e+07 +1.00772538e+07 +1.00772538e+07 +1.00772538e+07 +1.00772538e+07 +1.00772538e+07 +1.00772538e+07 +1.00772538e+07 +1.00772538e+07 +1.00772538e+07 +1.00772538e+07 +1.00772538e+07 +1.00772538e+07 +1.00772538e+07 +1.00772538e+07 +1.00772538e+07 +1.00772538e+07 +1.00772538e+07 +1.00772538e+07 +1.00772538e+07 +1.00772538e+07 +1.00772538e+07 +1.00772538e+07 +1.00772538e+07 +1.00772538e+07 +1.00772538e+07 +1.00772538e+07 +1.00772538e+07 +1.00772538e+07 +1.00772538e+07 +1.00772538e+07 +1.00772538e+07 +1.00772538e+07 +1.00772538e+07 +1.00772538e+07 +1.00772538e+07 +1.00772538e+07 +1.00772538e+07 +1.00772538e+07 +1.00772538e+07 +1.00772538e+07 +1.00772538e+07 +1.00772538e+07 +1.00772538e+07 +1.00772538e+07 +1.00772538e+07 +1.00772538e+07 +1.00816682e+07 +1.00816682e+07 +1.00816682e+07 +1.00816682e+07 +1.00816682e+07 +1.00816682e+07 +1.00816682e+07 +1.00816682e+07 +1.00816682e+07 +1.00816682e+07 +1.00816682e+07 +1.00816682e+07 +1.00816682e+07 +1.00816682e+07 +1.00816682e+07 +1.00816682e+07 +1.00816682e+07 +1.00816682e+07 +1.00816682e+07 +1.00816682e+07 +1.00816682e+07 +1.00816682e+07 +1.00816682e+07 +1.00816682e+07 +1.00816682e+07 +1.00816682e+07 +1.00816682e+07 +1.00816682e+07 +1.00816682e+07 +1.00816682e+07 +1.00816682e+07 +1.00816682e+07 +1.00816682e+07 +1.00816682e+07 +1.00816682e+07 +1.00816682e+07 +1.00816682e+07 +1.00816682e+07 +1.00816682e+07 +1.00816682e+07 +1.00816682e+07 +1.00816682e+07 +1.00816682e+07 +1.00816682e+07 +1.00816682e+07 +1.00816682e+07 +1.00816682e+07 +1.00816682e+07 +1.00816682e+07 +1.00816682e+07 +1.00816682e+07 +1.00816682e+07 +1.00816682e+07 +1.00816682e+07 +1.00816682e+07 +1.00816682e+07 +1.00816682e+07 +1.00816682e+07 +1.00816682e+07 +1.00816682e+07 +1.00860828e+07 +1.00860828e+07 +1.00860828e+07 +1.00860828e+07 +1.00860828e+07 +1.00860828e+07 +1.00860828e+07 +1.00860828e+07 +1.00860828e+07 +1.00860828e+07 +1.00860828e+07 +1.00860828e+07 +1.00860828e+07 +1.00860828e+07 +1.00860828e+07 +1.00860828e+07 +1.00860828e+07 +1.00860828e+07 +1.00860828e+07 +1.00860828e+07 +1.00860828e+07 +1.00860828e+07 +1.00860828e+07 +1.00860828e+07 +1.00860828e+07 +1.00860828e+07 +1.00860828e+07 +1.00860828e+07 +1.00860828e+07 +1.00860828e+07 +1.00860828e+07 +1.00860828e+07 +1.00860828e+07 +1.00860828e+07 +1.00860828e+07 +1.00860828e+07 +1.00860828e+07 +1.00860828e+07 +1.00860828e+07 +1.00860828e+07 +1.00860828e+07 +1.00860828e+07 +1.00860828e+07 +1.00860828e+07 +1.00860828e+07 +1.00860828e+07 +1.00860828e+07 +1.00860828e+07 +1.00860828e+07 +1.00860828e+07 +1.00860828e+07 +1.00860828e+07 +1.00860828e+07 +1.00860828e+07 +1.00860828e+07 +1.00860828e+07 +1.00860828e+07 +1.00860828e+07 +1.00860828e+07 +1.00860828e+07 diff --git a/inputFiles/immiscibleMultiphaseFlow/immiscibleTwoPhase_interface_conditions/3D_case/input/initialSaturation1.txt b/inputFiles/immiscibleMultiphaseFlow/immiscibleTwoPhase_interface_conditions/3D_case/input/initialSaturation1.txt new file mode 100644 index 00000000000..90ddc12151e --- /dev/null +++ b/inputFiles/immiscibleMultiphaseFlow/immiscibleTwoPhase_interface_conditions/3D_case/input/initialSaturation1.txt @@ -0,0 +1,1200 @@ +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e-02 +1.00000000e-02 +1.00000000e-02 +1.00000000e-02 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e-02 +1.00000000e-02 +1.00000000e-02 +1.00000000e-02 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e-02 +1.00000000e-02 +1.00000000e-02 +1.00000000e-02 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e-02 +1.00000000e-02 +1.00000000e-02 +1.00000000e-02 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e-02 +1.00000000e-02 +1.00000000e-02 +1.00000000e-02 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e-02 +1.00000000e-02 +1.00000000e-02 +1.00000000e-02 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e-02 +1.00000000e-02 +1.00000000e-02 +1.00000000e-02 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e-02 +1.00000000e-02 +1.00000000e-02 +1.00000000e-02 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e-02 +1.00000000e-02 +1.00000000e-02 +1.00000000e-02 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e-02 +1.00000000e-02 +1.00000000e-02 +1.00000000e-02 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e-02 +1.00000000e-02 +1.00000000e-02 +1.00000000e-02 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e-02 +1.00000000e-02 +1.00000000e-02 +1.00000000e-02 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e-02 +1.00000000e-02 +1.00000000e-02 +1.00000000e-02 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e-02 +1.00000000e-02 +1.00000000e-02 +1.00000000e-02 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e-02 +1.00000000e-02 +1.00000000e-02 +1.00000000e-02 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e-02 +1.00000000e-02 +1.00000000e-02 +1.00000000e-02 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e-02 +1.00000000e-02 +1.00000000e-02 +1.00000000e-02 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e-02 +1.00000000e-02 +1.00000000e-02 +1.00000000e-02 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e-02 +1.00000000e-02 +1.00000000e-02 +1.00000000e-02 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e-02 +1.00000000e-02 +1.00000000e-02 +1.00000000e-02 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e-02 +1.00000000e-02 +1.00000000e-02 +1.00000000e-02 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e-02 +1.00000000e-02 +1.00000000e-02 +1.00000000e-02 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e-02 +1.00000000e-02 +1.00000000e-02 +1.00000000e-02 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e-02 +1.00000000e-02 +1.00000000e-02 +1.00000000e-02 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e-02 +1.00000000e-02 +1.00000000e-02 +1.00000000e-02 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e-02 +1.00000000e-02 +1.00000000e-02 +1.00000000e-02 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e-02 +1.00000000e-02 +1.00000000e-02 +1.00000000e-02 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e-02 +1.00000000e-02 +1.00000000e-02 +1.00000000e-02 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e-02 +1.00000000e-02 +1.00000000e-02 +1.00000000e-02 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e-02 +1.00000000e-02 +1.00000000e-02 +1.00000000e-02 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e-02 +1.00000000e-02 +1.00000000e-02 +1.00000000e-02 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e-02 +1.00000000e-02 +1.00000000e-02 +1.00000000e-02 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e-02 +1.00000000e-02 +1.00000000e-02 +1.00000000e-02 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e-02 +1.00000000e-02 +1.00000000e-02 +1.00000000e-02 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e-02 +1.00000000e-02 +1.00000000e-02 +1.00000000e-02 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e-02 +1.00000000e-02 +1.00000000e-02 +1.00000000e-02 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e-02 +1.00000000e-02 +1.00000000e-02 +1.00000000e-02 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e-02 +1.00000000e-02 +1.00000000e-02 +1.00000000e-02 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e-02 +1.00000000e-02 +1.00000000e-02 +1.00000000e-02 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e-02 +1.00000000e-02 +1.00000000e-02 +1.00000000e-02 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e-02 +1.00000000e-02 +1.00000000e-02 +1.00000000e-02 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e-02 +1.00000000e-02 +1.00000000e-02 +1.00000000e-02 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e-02 +1.00000000e-02 +1.00000000e-02 +1.00000000e-02 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e-02 +1.00000000e-02 +1.00000000e-02 +1.00000000e-02 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e-02 +1.00000000e-02 +1.00000000e-02 +1.00000000e-02 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e-02 +1.00000000e-02 +1.00000000e-02 +1.00000000e-02 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e-02 +1.00000000e-02 +1.00000000e-02 +1.00000000e-02 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e+00 +1.00000000e-02 +1.00000000e-02 +1.00000000e-02 +1.00000000e-02 diff --git a/inputFiles/immiscibleMultiphaseFlow/immiscibleTwoPhase_interface_conditions/3D_case/input/initialSaturation2.txt b/inputFiles/immiscibleMultiphaseFlow/immiscibleTwoPhase_interface_conditions/3D_case/input/initialSaturation2.txt new file mode 100644 index 00000000000..d5ef52f254b --- /dev/null +++ b/inputFiles/immiscibleMultiphaseFlow/immiscibleTwoPhase_interface_conditions/3D_case/input/initialSaturation2.txt @@ -0,0 +1,1200 @@ +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +9.90000000e-01 +9.90000000e-01 +9.90000000e-01 +9.90000000e-01 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +9.90000000e-01 +9.90000000e-01 +9.90000000e-01 +9.90000000e-01 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +9.90000000e-01 +9.90000000e-01 +9.90000000e-01 +9.90000000e-01 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +9.90000000e-01 +9.90000000e-01 +9.90000000e-01 +9.90000000e-01 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +9.90000000e-01 +9.90000000e-01 +9.90000000e-01 +9.90000000e-01 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +9.90000000e-01 +9.90000000e-01 +9.90000000e-01 +9.90000000e-01 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +9.90000000e-01 +9.90000000e-01 +9.90000000e-01 +9.90000000e-01 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +9.90000000e-01 +9.90000000e-01 +9.90000000e-01 +9.90000000e-01 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +9.90000000e-01 +9.90000000e-01 +9.90000000e-01 +9.90000000e-01 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +9.90000000e-01 +9.90000000e-01 +9.90000000e-01 +9.90000000e-01 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +9.90000000e-01 +9.90000000e-01 +9.90000000e-01 +9.90000000e-01 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +9.90000000e-01 +9.90000000e-01 +9.90000000e-01 +9.90000000e-01 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +9.90000000e-01 +9.90000000e-01 +9.90000000e-01 +9.90000000e-01 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +9.90000000e-01 +9.90000000e-01 +9.90000000e-01 +9.90000000e-01 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +9.90000000e-01 +9.90000000e-01 +9.90000000e-01 +9.90000000e-01 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +9.90000000e-01 +9.90000000e-01 +9.90000000e-01 +9.90000000e-01 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +9.90000000e-01 +9.90000000e-01 +9.90000000e-01 +9.90000000e-01 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +9.90000000e-01 +9.90000000e-01 +9.90000000e-01 +9.90000000e-01 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +9.90000000e-01 +9.90000000e-01 +9.90000000e-01 +9.90000000e-01 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +9.90000000e-01 +9.90000000e-01 +9.90000000e-01 +9.90000000e-01 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +9.90000000e-01 +9.90000000e-01 +9.90000000e-01 +9.90000000e-01 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +9.90000000e-01 +9.90000000e-01 +9.90000000e-01 +9.90000000e-01 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +9.90000000e-01 +9.90000000e-01 +9.90000000e-01 +9.90000000e-01 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +9.90000000e-01 +9.90000000e-01 +9.90000000e-01 +9.90000000e-01 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +9.90000000e-01 +9.90000000e-01 +9.90000000e-01 +9.90000000e-01 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +9.90000000e-01 +9.90000000e-01 +9.90000000e-01 +9.90000000e-01 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +9.90000000e-01 +9.90000000e-01 +9.90000000e-01 +9.90000000e-01 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +9.90000000e-01 +9.90000000e-01 +9.90000000e-01 +9.90000000e-01 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +9.90000000e-01 +9.90000000e-01 +9.90000000e-01 +9.90000000e-01 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +9.90000000e-01 +9.90000000e-01 +9.90000000e-01 +9.90000000e-01 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +9.90000000e-01 +9.90000000e-01 +9.90000000e-01 +9.90000000e-01 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +9.90000000e-01 +9.90000000e-01 +9.90000000e-01 +9.90000000e-01 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +9.90000000e-01 +9.90000000e-01 +9.90000000e-01 +9.90000000e-01 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +9.90000000e-01 +9.90000000e-01 +9.90000000e-01 +9.90000000e-01 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +9.90000000e-01 +9.90000000e-01 +9.90000000e-01 +9.90000000e-01 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +9.90000000e-01 +9.90000000e-01 +9.90000000e-01 +9.90000000e-01 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +9.90000000e-01 +9.90000000e-01 +9.90000000e-01 +9.90000000e-01 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +9.90000000e-01 +9.90000000e-01 +9.90000000e-01 +9.90000000e-01 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +9.90000000e-01 +9.90000000e-01 +9.90000000e-01 +9.90000000e-01 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +9.90000000e-01 +9.90000000e-01 +9.90000000e-01 +9.90000000e-01 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +9.90000000e-01 +9.90000000e-01 +9.90000000e-01 +9.90000000e-01 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +9.90000000e-01 +9.90000000e-01 +9.90000000e-01 +9.90000000e-01 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +9.90000000e-01 +9.90000000e-01 +9.90000000e-01 +9.90000000e-01 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +9.90000000e-01 +9.90000000e-01 +9.90000000e-01 +9.90000000e-01 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +9.90000000e-01 +9.90000000e-01 +9.90000000e-01 +9.90000000e-01 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +9.90000000e-01 +9.90000000e-01 +9.90000000e-01 +9.90000000e-01 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +9.90000000e-01 +9.90000000e-01 +9.90000000e-01 +9.90000000e-01 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +0.00000000e+00 +9.90000000e-01 +9.90000000e-01 +9.90000000e-01 +9.90000000e-01 diff --git a/inputFiles/immiscibleMultiphaseFlow/immiscibleTwoPhase_interface_conditions/3D_case/input/permx.geos b/inputFiles/immiscibleMultiphaseFlow/immiscibleTwoPhase_interface_conditions/3D_case/input/permx.geos new file mode 100644 index 00000000000..439bd535a86 --- /dev/null +++ b/inputFiles/immiscibleMultiphaseFlow/immiscibleTwoPhase_interface_conditions/3D_case/input/permx.geos @@ -0,0 +1,1200 @@ +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 diff --git a/inputFiles/immiscibleMultiphaseFlow/immiscibleTwoPhase_interface_conditions/3D_case/input/permy.geos b/inputFiles/immiscibleMultiphaseFlow/immiscibleTwoPhase_interface_conditions/3D_case/input/permy.geos new file mode 100644 index 00000000000..439bd535a86 --- /dev/null +++ b/inputFiles/immiscibleMultiphaseFlow/immiscibleTwoPhase_interface_conditions/3D_case/input/permy.geos @@ -0,0 +1,1200 @@ +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 diff --git a/inputFiles/immiscibleMultiphaseFlow/immiscibleTwoPhase_interface_conditions/3D_case/input/permz.geos b/inputFiles/immiscibleMultiphaseFlow/immiscibleTwoPhase_interface_conditions/3D_case/input/permz.geos new file mode 100644 index 00000000000..439bd535a86 --- /dev/null +++ b/inputFiles/immiscibleMultiphaseFlow/immiscibleTwoPhase_interface_conditions/3D_case/input/permz.geos @@ -0,0 +1,1200 @@ +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +5.00000000e+01 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 +2.00000000e+02 diff --git a/inputFiles/immiscibleMultiphaseFlow/immiscibleTwoPhase_interface_conditions/3D_case/input/x.txt b/inputFiles/immiscibleMultiphaseFlow/immiscibleTwoPhase_interface_conditions/3D_case/input/x.txt new file mode 100644 index 00000000000..283fc50e7fb --- /dev/null +++ b/inputFiles/immiscibleMultiphaseFlow/immiscibleTwoPhase_interface_conditions/3D_case/input/x.txt @@ -0,0 +1,10 @@ +5.00000000e-01 +1.50000000e+00 +2.50000000e+00 +3.50000000e+00 +4.50000000e+00 +5.50000000e+00 +6.50000000e+00 +7.50000000e+00 +8.50000000e+00 +9.50000000e+00 diff --git a/inputFiles/immiscibleMultiphaseFlow/immiscibleTwoPhase_interface_conditions/3D_case/input/y.txt b/inputFiles/immiscibleMultiphaseFlow/immiscibleTwoPhase_interface_conditions/3D_case/input/y.txt new file mode 100644 index 00000000000..a01763bc5d1 --- /dev/null +++ b/inputFiles/immiscibleMultiphaseFlow/immiscibleTwoPhase_interface_conditions/3D_case/input/y.txt @@ -0,0 +1,6 @@ +5.00000000e-01 +1.50000000e+00 +2.50000000e+00 +3.50000000e+00 +4.50000000e+00 +5.50000000e+00 diff --git a/inputFiles/immiscibleMultiphaseFlow/immiscibleTwoPhase_interface_conditions/3D_case/input/z.txt b/inputFiles/immiscibleMultiphaseFlow/immiscibleTwoPhase_interface_conditions/3D_case/input/z.txt new file mode 100644 index 00000000000..d122a370abe --- /dev/null +++ b/inputFiles/immiscibleMultiphaseFlow/immiscibleTwoPhase_interface_conditions/3D_case/input/z.txt @@ -0,0 +1,20 @@ +2.50000000e-01 +7.50000000e-01 +1.25000000e+00 +1.75000000e+00 +2.25000000e+00 +2.75000000e+00 +3.25000000e+00 +3.75000000e+00 +4.25000000e+00 +4.75000000e+00 +5.25000000e+00 +5.75000000e+00 +6.25000000e+00 +6.75000000e+00 +7.25000000e+00 +7.75000000e+00 +8.25000000e+00 +8.75000000e+00 +9.25000000e+00 +9.75000000e+00 diff --git a/inputFiles/immiscibleMultiphaseFlow/immiscibleTwoPhase_interface_conditions/3D_case/uni_directional_flow_base.xml b/inputFiles/immiscibleMultiphaseFlow/immiscibleTwoPhase_interface_conditions/3D_case/uni_directional_flow_base.xml new file mode 100644 index 00000000000..3982a2b12c6 --- /dev/null +++ b/inputFiles/immiscibleMultiphaseFlow/immiscibleTwoPhase_interface_conditions/3D_case/uni_directional_flow_base.xml @@ -0,0 +1,299 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inputFiles/immiscibleMultiphaseFlow/immiscibleTwoPhase_interface_conditions/3D_case/uni_directional_flow_interface_condition.xml b/inputFiles/immiscibleMultiphaseFlow/immiscibleTwoPhase_interface_conditions/3D_case/uni_directional_flow_interface_condition.xml new file mode 100644 index 00000000000..9bac844ae7b --- /dev/null +++ b/inputFiles/immiscibleMultiphaseFlow/immiscibleTwoPhase_interface_conditions/3D_case/uni_directional_flow_interface_condition.xml @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/coreComponents/common/DataLayouts.hpp b/src/coreComponents/common/DataLayouts.hpp index cd09af18ef0..0f23039b745 100644 --- a/src/coreComponents/common/DataLayouts.hpp +++ b/src/coreComponents/common/DataLayouts.hpp @@ -21,6 +21,7 @@ #define GEOS_COMMON_DATALAYOUTS_HPP_ #include "common/GeosxConfig.hpp" +#include "common/DataTypes.hpp" #include "LvArray/src/Array.hpp" #include "RAJA/RAJA.hpp" diff --git a/src/coreComponents/common/logger/Logger.hpp b/src/coreComponents/common/logger/Logger.hpp index e59cfa05256..1224ec6ee66 100644 --- a/src/coreComponents/common/logger/Logger.hpp +++ b/src/coreComponents/common/logger/Logger.hpp @@ -978,6 +978,7 @@ struct InputError : public std::runtime_error InputError( std::exception const & subException, std::string const & msgToInsert ); }; + /** * @brief Exception class used to report errors in user input. */ diff --git a/src/coreComponents/constitutive/CMakeLists.txt b/src/coreComponents/constitutive/CMakeLists.txt index 6af439fe23f..bfb182dfe89 100644 --- a/src/coreComponents/constitutive/CMakeLists.txt +++ b/src/coreComponents/constitutive/CMakeLists.txt @@ -27,12 +27,14 @@ set( constitutive_headers ConstitutivePassThruHandler.hpp ExponentialRelation.hpp NullModel.hpp + KilloughHysteresis.hpp capillaryPressure/BrooksCoreyCapillaryPressure.hpp capillaryPressure/CapillaryPressureBase.hpp capillaryPressure/CapillaryPressureFields.hpp capillaryPressure/InverseCapillaryPressure.hpp capillaryPressure/JFunctionCapillaryPressure.hpp capillaryPressure/TableCapillaryPressure.hpp + capillaryPressure/TableCapillaryPressureHysteresis.hpp capillaryPressure/TableCapillaryPressureHelpers.hpp capillaryPressure/VanGenuchtenCapillaryPressure.hpp capillaryPressure/CapillaryPressureSelector.hpp @@ -226,11 +228,13 @@ set( constitutive_sources ConstitutiveBase.cpp ConstitutiveManager.cpp NullModel.cpp + KilloughHysteresis.cpp capillaryPressure/BrooksCoreyCapillaryPressure.cpp capillaryPressure/CapillaryPressureBase.cpp capillaryPressure/InverseCapillaryPressure.cpp capillaryPressure/JFunctionCapillaryPressure.cpp capillaryPressure/TableCapillaryPressure.cpp + capillaryPressure/TableCapillaryPressureHysteresis.cpp capillaryPressure/TableCapillaryPressureHelpers.cpp capillaryPressure/VanGenuchtenCapillaryPressure.cpp contact/BartonBandis.cpp @@ -369,6 +373,7 @@ if( ENABLE_PVTPackage ) endif() if (ENABLE_HPCREACT) + if( EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/HPCReact/CMakeLists.txt" ) set( constitutive_headers ${constitutive_headers} fluid/reactivefluid/ReactiveFluidSelector.hpp @@ -383,8 +388,9 @@ if (ENABLE_HPCREACT) ${constitutive_sources} fluid/reactivefluid/ReactiveSinglePhaseFluid.cpp ) - add_subdirectory( HPCReact ) - list( APPEND dependencyList hpcReact ) + add_subdirectory( HPCReact ) + list( APPEND dependencyList hpcReact ) + endif() endif() geos_decorate_link_dependencies( LIST decoratedDependencies diff --git a/src/coreComponents/constitutive/KilloughHysteresis.cpp b/src/coreComponents/constitutive/KilloughHysteresis.cpp new file mode 100644 index 00000000000..453a8b0585b --- /dev/null +++ b/src/coreComponents/constitutive/KilloughHysteresis.cpp @@ -0,0 +1,144 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * SPDX-License-Identifier: LGPL-2.1-only + * + * Copyright (c) 2018-2020 Lawrence Livermore National Security LLC + * Copyright (c) 2018-2020 The Board of Trustees of the Leland Stanford Junior University + * Copyright (c) 2018-2020 TotalEnergies + * Copyright (c) 2019- GEOSX Contributors + * All rights reserved + * + * See top level LICENSE, COPYRIGHT, CONTRIBUTORS, NOTICE, and ACKNOWLEDGEMENTS files for details. + * ------------------------------------------------------------------------------------------------------------ + */ + +/*** + * @file KilloughHysteresis.cpp + */ + +#include "KilloughHysteresis.hpp" + + +namespace geos +{ + +using namespace dataRepository; + +namespace constitutive +{ + + +void KilloughHysteresis::postProcessInput(real64 const &jerauldParam_a, real64 const &jerauldParam_b, + real64 const &killoughCurvatureParamRelPerm, + real64 const &killoughCurvatureParamPc) +{ + GEOS_THROW_IF( jerauldParam_a < 0, + GEOS_FMT( "{}: the parameter {} must be positive", + catalogName(), + viewKeyStruct::jerauldParameterAString() ), + InputError ); + + GEOS_THROW_IF( jerauldParam_b < 0, + GEOS_FMT( "{}: the paramater {} must be postitive", + catalogName(), + viewKeyStruct::jerauldParameterBString() ), + InputError ); + + GEOS_THROW_IF( killoughCurvatureParamRelPerm < 0, + GEOS_FMT( "{}: the paramater {} must be postitive", + catalogName(), + viewKeyStruct::killoughCurvatureParameterRelPermString() ), + InputError ); + + GEOS_THROW_IF( killoughCurvatureParamPc < 0, + GEOS_FMT( "{}: the paramater {} must be postitive", + catalogName(), + viewKeyStruct::killoughCurvatureParameterPcString() ), + InputError ); + +} + + + +//TODO +void KilloughHysteresis::computeLandCoefficient( KilloughHysteresis::HysteresisCurve const & hcurve, + real64 & landParam ) +{ + + // Note: for simplicity, the notations are taken from IX documentation (although this breaks our phaseVolFrac naming convention) + + // Step 1: Land parameter for the wetting phase + if( hcurve.isWetting() ) + { + real64 const Scrd = hcurve.oppositeBoundPhaseVolFraction; + real64 const Smxd = hcurve.drainageExtremaPhaseVolFraction; + real64 const Smxi = hcurve.imbibitionExtremaPhaseVolFraction; + real64 const Swc = Scrd; + GEOS_THROW_IF( (Smxi - Smxd) > 0, + GEOS_FMT( "{}: For wetting phase hysteresis, imbibition end-point saturation Smxi( {} ) must be smaller than the drainage saturation end-point Smxd( {} ).\n" + "Crossing relative permeability curves.\n", + catalogName(), + Smxi, + Smxd ), + InputError ); + + landParam = ( Smxd - Swc ) / LvArray::math::max( KilloughHysteresis::minScriMinusScrd, ( Smxd - Smxi ) ) - 1.0; + } + else + // Step 2: Land parameter for the non-wetting phase + + { + real64 const Smx = hcurve.oppositeBoundPhaseVolFraction; + real64 const Scrd = hcurve.drainageExtremaPhaseVolFraction; + real64 const Scri = hcurve.imbibitionExtremaPhaseVolFraction; + GEOS_THROW_IF( (Scrd - Scri) > 0, + GEOS_FMT( "{}: For non-wetting phase hysteresis, drainage trapped saturation Scrd( {} ) must be smaller than the imbibition saturation Scri( {} ).\n" + "Crossing relative permeability curves.\n", + catalogName(), + Scrd, + Scri ), + InputError ); + + landParam = ( Smx - Scrd ) / LvArray::math::max( KilloughHysteresis::minScriMinusScrd, ( Scri - Scrd ) ) - 1.0; + } +} + +GEOS_HOST_DEVICE +void +KilloughHysteresis:: + computeTrappedCriticalPhaseVolFraction( HysteresisCurve const & hcurve, + real64 const & Shy, + real64 const & landParam, + real64 const & jerauldParam_a, + real64 const & jerauldParam_b, + real64 & Scrt ) +{ + + if( hcurve.isWetting()) + { + //unpack values + real64 const Smxd = hcurve.drainageExtremaPhaseVolFraction; + real64 const Swc = hcurve.oppositeBoundPhaseVolFraction; + + real64 const A = 1 + jerauldParam_a * (Shy - Swc); + real64 const numerator = Shy - Smxd; + real64 const denom = A + landParam * pow((Smxd - Shy) / (Smxd - Swc), 1 + jerauldParam_b / landParam ); + Scrt = Smxd + numerator / denom; + } + else + { + //unpack values + real64 const Scrd = hcurve.drainageExtremaPhaseVolFraction; + real64 const Smx = hcurve.oppositeBoundPhaseVolFraction; + + real64 const A = 1 + jerauldParam_a * (Smx - Shy); + real64 const numerator = Shy - Scrd; + real64 const denom = A + landParam * pow((Shy - Scrd) / (Smx - Scrd), 1 + jerauldParam_b / landParam ); + Scrt = LvArray::math::max( 0.0, + Scrd + numerator / denom ); // trapped critical saturation from equation 2.162 + } + +} + +}//end namespace +}//end namespace diff --git a/src/coreComponents/constitutive/KilloughHysteresis.hpp b/src/coreComponents/constitutive/KilloughHysteresis.hpp new file mode 100644 index 00000000000..ce151f3aeae --- /dev/null +++ b/src/coreComponents/constitutive/KilloughHysteresis.hpp @@ -0,0 +1,179 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * SPDX-License-Identifier: LGPL-2.1-only + * + * Copyright (c) 2018-2020 Lawrence Livermore National Security LLC + * Copyright (c) 2018-2020 The Board of Trustees of the Leland Stanford Junior University + * Copyright (c) 2018-2020 TotalEnergies + * Copyright (c) 2019- GEOSX Contributors + * All rights reserved + * + * See top level LICENSE, COPYRIGHT, CONTRIBUTORS, NOTICE, and ACKNOWLEDGEMENTS files for details. + * ------------------------------------------------------------------------------------------------------------ + */ + +/** + * @file TableRelativePermeabilityHysteresis.hpp + */ + +#ifndef GEOS_KILLOUGHHYSTERESIS_HPP +#define GEOS_KILLOUGHHYSTERESIS_HPP + +#include "constitutive/ConstitutiveBase.hpp" +#include "functions/TableFunction.hpp" + +#include "relativePermeability/Layouts.hpp" +#include "capillaryPressure/Layouts.hpp" + +namespace geos +{ + +using namespace dataRepository; + +namespace constitutive +{ + +/*** + * @brief KilloughHysteresis is designed to hold Killough hystereis model parameters and + * be in charge of all compuration related to this model (trapped Saturation,Land Coefficient?) + */ + + + +//should be up to constitutiveBase or some new SCALConstitutiveBase but for now let's POC on relativePermeabilityBase +class KilloughHysteresis +{ +public: + + static constexpr real64 minScriMinusScrd = 1e-12; + /// To avoid frequent changes from drainage to imbibition and vice versa, we use this buffer + static constexpr real64 flowReversalBuffer = 1e-12; + + struct PhaseWettability + { + enum : integer + { + WETTING = 0, + NONWETTING = 1 + }; + }; + + /** + * @brief struct to represent hysteresis curves (relperm or capillary pressure) + * whether they are wetting or non wetting, storing key point as pairs of + * saturations and value, being either the relperm value (S,kr) or capillary pressure value (S,pc). + * this way we can tell wetting curve from non wetting by the ordering of drainage/imbibition key values. + * Indeed if imbibition comes at lower saturation than drainage then it is wetting curve, on the opposite + * if drainage comes before imbibition this is a non-wetting hysteresis. This is completed by an opposite + * point that is either the connate wetting saturation Swc or the maximum non wetting saturation Sgmax. + * @param oppositeBoundPhaseVolFraction represents either Swc or Sgmax depending if a wetting curve or nonwetting is described + * @param imbibitionExtremaPhaseVolFraction represents in wetting case the imibibition max and in non-wetting the imbibition residual + * @param drainageExtremaPhaseVolFraction represents in wetting case the drainage max and in non-wetting the drainage residual + * @param oppositeBoundSCALValue represents the associate relperm or capillary pressure value + * @param imbibitionExtremaSCALValue represents the associate relperm or capillary pressure value + * @param drainageExtremaSCALValue represents the associate relperm or capillary pressure value + */ + + struct HysteresisCurve + { + real64 oppositeBoundPhaseVolFraction = -1.; + real64 imbibitionExtremaPhaseVolFraction = -1.; + real64 drainageExtremaPhaseVolFraction = -1.; + + real64 oppositeBoundSCALValue = -1.; + real64 imbibitionExtremaSCALValue = -1.; + real64 drainageExtremaSCALValue = -1.; + + HysteresisCurve() = default; + + HysteresisCurve( std::pair< real64, real64 > const & opp, std::pair< real64, real64 > const & imbE, std::pair< real64, real64 > const & drainE ) + { + setPoints( opp, imbE, drainE ); + } + + void setPoints( std::pair< real64, real64 > const & opp, std::pair< real64, real64 > const & imbE, std::pair< real64, real64 > const & drainE ) + { + oppositeBoundPhaseVolFraction = opp.first; + imbibitionExtremaPhaseVolFraction = imbE.first; + drainageExtremaPhaseVolFraction = drainE.first; + + oppositeBoundSCALValue = opp.second; + imbibitionExtremaSCALValue = imbE.second; + drainageExtremaSCALValue = drainE.second; + } + + //tODO (jacques) check if relevant to invert relation with same SCAL value // might be misleading for kr + HysteresisCurve toWetting() const + { + if(!isWetting()) + return HysteresisCurve({1.-oppositeBoundPhaseVolFraction,oppositeBoundSCALValue}, + {1.- imbibitionExtremaPhaseVolFraction,imbibitionExtremaSCALValue}, + {1.-drainageExtremaPhaseVolFraction,drainageExtremaSCALValue}); + else + return *this; + } + + HysteresisCurve toNonWetting() const + { + if(isWetting()) + return HysteresisCurve({1.-oppositeBoundPhaseVolFraction,oppositeBoundSCALValue}, + {1.-imbibitionExtremaPhaseVolFraction,imbibitionExtremaSCALValue}, + {1.-drainageExtremaPhaseVolFraction,drainageExtremaSCALValue}); + else + return *this; + } + + bool isWetting() const + { + return ((drainageExtremaPhaseVolFraction > oppositeBoundPhaseVolFraction) ? PhaseWettability::WETTING : PhaseWettability::NONWETTING) == PhaseWettability::WETTING; + } + bool isZero() const + { + return (oppositeBoundPhaseVolFraction <= 0.0) && (imbibitionExtremaPhaseVolFraction <= 0.0) && (drainageExtremaPhaseVolFraction <= 0.0); + } + + }; + +// void setRelPermParameters( real64 const & jerauldA, real64 const & jerauldB, real64 const & relpermCurv ); + + static std::string catalogName() { return "KilloughHysteresis"; } + + static void postProcessInput(real64 const &jerauldParam_a, real64 const &jerauldParam_b, + real64 const &killoughCurvatureParamRelPerm, + real64 const &killoughCurvatureParamPc); + + GEOS_HOST_DEVICE + static void computeLandCoefficient( HysteresisCurve const & hcruve, real64 & landParam ); + /** + * @brief Function computing the trapped critical phase volume fraction + * @param[in] hcurve the hysteresis curve to be used and dispatched on + * @param[in] Shy the max historical phase volume fraction + * @param[in] landParam Land trapping parameter + * @param[in] jerauldParam_a jerauld expononent + * @param[in] jerauldParam_b jerauld expononent + * @param[out] Scrt the trapped critical phase volume fraction + */ + GEOS_HOST_DEVICE + static void computeTrappedCriticalPhaseVolFraction( HysteresisCurve const & hcurve, + real64 const & Shy, + real64 const & landParam, + real64 const & jerauldParam_a, + real64 const & jerauldParam_b, + real64 & Scrt ); + + struct viewKeyStruct + { + static constexpr char const * jerauldParameterAString() { return "jerauldParameterA"; } + static constexpr char const * jerauldParameterBString() { return "jerauldParameterB"; } + + static constexpr char const * killoughCurvatureParameterRelPermString() { return "killoughCurvatureParameterRelPerm"; } + static constexpr char const * killoughCurvatureParameterPcString() { return "killoughCurvatureParameterPc"; } + }; + +}; + +} + +} + +#endif //GEOS_KILLOUGHHYSTERESIS_HPP diff --git a/src/coreComponents/constitutive/capillaryPressure/BrooksCoreyCapillaryPressure.cpp b/src/coreComponents/constitutive/capillaryPressure/BrooksCoreyCapillaryPressure.cpp index f068365eb35..d7cc96a1dd1 100644 --- a/src/coreComponents/constitutive/capillaryPressure/BrooksCoreyCapillaryPressure.cpp +++ b/src/coreComponents/constitutive/capillaryPressure/BrooksCoreyCapillaryPressure.cpp @@ -114,6 +114,7 @@ BrooksCoreyCapillaryPressure::createKernelWrapper() m_volFracScale, m_phaseTypes, m_phaseOrder, + m_phaseTrappedVolFrac, m_phaseCapPressure, m_dPhaseCapPressure_dPhaseVolFrac ); } diff --git a/src/coreComponents/constitutive/capillaryPressure/BrooksCoreyCapillaryPressure.hpp b/src/coreComponents/constitutive/capillaryPressure/BrooksCoreyCapillaryPressure.hpp index 39d93e48858..e0c971492b2 100644 --- a/src/coreComponents/constitutive/capillaryPressure/BrooksCoreyCapillaryPressure.hpp +++ b/src/coreComponents/constitutive/capillaryPressure/BrooksCoreyCapillaryPressure.hpp @@ -39,10 +39,12 @@ class BrooksCoreyCapillaryPressureUpdate final : public CapillaryPressureBaseUpd real64 const volFracScale, arrayView1d< integer const > const & phaseTypes, arrayView1d< integer const > const & phaseOrder, + arrayView3d< real64, cappres::USD_CAPPRES > const & phaseTrapped, arrayView3d< real64, cappres::USD_CAPPRES > const & phaseCapPressure, arrayView4d< real64, cappres::USD_CAPPRES_DS > const & dPhaseCapPressure_dPhaseVolFrac ) : CapillaryPressureBaseUpdate( phaseTypes, phaseOrder, + phaseTrapped, phaseCapPressure, dPhaseCapPressure_dPhaseVolFrac ), m_phaseMinVolumeFraction( phaseMinVolumeFraction ), @@ -57,6 +59,11 @@ class BrooksCoreyCapillaryPressureUpdate final : public CapillaryPressureBaseUpd arraySlice1d< real64, cappres::USD_CAPPRES - 2 > const & phaseCapPres, arraySlice2d< real64, cappres::USD_CAPPRES_DS - 2 > const & dPhaseCapPres_dPhaseVolFrac ) const; + GEOS_HOST_DEVICE + void computeInv( arraySlice1d< real64, compflow::USD_PHASE - 1 > const & phaseVolFraction, + arraySlice1d< real64 const, cappres::USD_CAPPRES - 2 > const & phaseCapPres, + arraySlice2d< real64, cappres::USD_CAPPRES_DS - 2 > const & dPhaseCapPres_dPhaseVolFrac ) const; + GEOS_HOST_DEVICE virtual void update( localIndex const k, localIndex const q, @@ -80,14 +87,32 @@ class BrooksCoreyCapillaryPressureUpdate final : public CapillaryPressureBaseUpd real64 & phaseCapPressure, real64 & dPhaseCapPressure_dVolFrac ); + + GEOS_HOST_DEVICE + GEOS_FORCE_INLINE + static void + evaluateBrooksCoreyFunctionInv( real64 const phaseCapPressure, + int const ip, + real64 const volFracScaleInv, + real64 const exponentInv, + real64 const entryPressure, + real64 const maxCapPres_eps, + real64 const phaseMinVolumeFraction, + arrayView1d< integer const > const phaseOrder, + real64 & phaseVolFraction, + real64 & dPhaseCapPressure_dVolFrac ); + arrayView1d< real64 const > m_phaseMinVolumeFraction; arrayView1d< real64 const > m_phaseCapPressureExponentInv; arrayView1d< real64 const > m_phaseEntryPressure; real64 m_capPressureEpsilon; real64 m_volFracScale; + }; + + class BrooksCoreyCapillaryPressure : public CapillaryPressureBase { public: @@ -169,11 +194,14 @@ BrooksCoreyCapillaryPressureUpdate:: // compute first gas-oil capillary pressure as a function of gas-phase vol fraction integer const ip_gas = m_phaseOrder[CapillaryPressureBase::PhaseType::GAS]; - if( ip_gas >= 0 ) + integer const ip_oil = m_phaseOrder[CapillaryPressureBase::PhaseType::OIL]; + + GEOS_UNUSED_VAR( ip_gas ); + if( ip_oil >= 0 ) { - real64 const volFracScaled = (phaseVolFraction[ip_gas] - m_phaseMinVolumeFraction[ip_gas]) * volFracScaleInv; - real64 const exponentInv = m_phaseCapPressureExponentInv[ip_gas]; - real64 const entryPressure = -m_phaseEntryPressure[ip_gas]; // for gas capillary pressure, take the opposite of the + real64 const volFracScaled = (phaseVolFraction[ip_oil] - m_phaseMinVolumeFraction[ip_oil]) * volFracScaleInv; + real64 const exponentInv = m_phaseCapPressureExponentInv[ip_oil]; + real64 const entryPressure = -m_phaseEntryPressure[ip_oil]; // for gas capillary pressure, take the opposite of the // BC function real64 const wettingVolFracScaled = 1-volFracScaled; @@ -184,11 +212,103 @@ BrooksCoreyCapillaryPressureUpdate:: exponentInv, entryPressure, eps, - phaseCapPres[ip_gas], - dPhaseCapPres_dPhaseVolFrac[ip_gas][ip_gas] ); + phaseCapPres[ip_oil], + dPhaseCapPres_dPhaseVolFrac[ip_oil][ip_oil] ); } } +GEOS_HOST_DEVICE +inline void +BrooksCoreyCapillaryPressureUpdate:: + computeInv( arraySlice1d< real64, compflow::USD_PHASE - 1 > const & phaseVolFraction, + arraySlice1d< real64 const, cappres::USD_CAPPRES - 2 > const & phaseCapPres, + arraySlice2d< real64, cappres::USD_CAPPRES_DS - 2 > const & dPhaseCapPres_dPhaseVolFrac ) const +{ + LvArray::forValuesInSlice( dPhaseCapPres_dPhaseVolFrac, []( real64 & val ){ val = 0.0; } ); + + real64 const volFracScaleInv = 1.0 / m_volFracScale; + + // the Brooks-Corey model does not support volFracScaled = 0, + // hence we need an epsilon value to avoid a division by zero + // TODO: for S < epsilon, replace the original unbounded BC curve with a bounded power-law extension + real64 const eps = m_capPressureEpsilon; + + + // compute first water-oil capillary pressure as a function of water-phase vol fraction + integer const ip_water = m_phaseOrder[CapillaryPressureBase::PhaseType::WATER]; + integer const ip_gas = m_phaseOrder[CapillaryPressureBase::PhaseType::GAS]; + if( ip_water >= 0 ) + { + real64 const volFracScaled_eps = (eps - m_phaseMinVolumeFraction[ip_water]) * volFracScaleInv; + real64 const exponentInv = m_phaseCapPressureExponentInv[ip_water]; + real64 const entryPressure = m_phaseEntryPressure[ip_water]; + + real64 const wettingVolFracScaled_eps = volFracScaled_eps; + real64 const dWettingVolFracScaled_dVolFrac = volFracScaleInv; + + real64 maxCapPres_eps = 0.0; + real64 max_dpc_eps = 0.0; + + evaluateBrooksCoreyFunction( wettingVolFracScaled_eps, + dWettingVolFracScaled_dVolFrac, + exponentInv, + entryPressure, + eps, + maxCapPres_eps, + max_dpc_eps ); + + evaluateBrooksCoreyFunctionInv( phaseCapPres[ip_water], + ip_water, + volFracScaleInv, + exponentInv, + entryPressure, + maxCapPres_eps, + m_phaseMinVolumeFraction[ip_water], + m_phaseOrder, + phaseVolFraction[ip_water], + dPhaseCapPres_dPhaseVolFrac[ip_water][ip_water] ); + phaseVolFraction[ip_gas] = 1.0 - phaseVolFraction[ip_water]; + } + + + // compute first gas-oil capillary pressure as a function of gas-phase vol fraction + + + // if( ip_gas >= 0 ) + // { + // real64 const volFracScaled_eps = (eps - m_phaseMinVolumeFraction[ip_gas]) * volFracScaleInv; + // real64 const exponentInv = m_phaseCapPressureExponentInv[ip_gas]; + // real64 const entryPressure = -m_phaseEntryPressure[ip_gas]; // for gas capillary pressure, take the opposite of the + // // BC function + + // real64 const wettingVolFracScaled_eps = 1-volFracScaled_eps; + // real64 const dWettingVolFracScaled_dVolFrac = -volFracScaleInv; + + // real64 maxCapPres_eps = 0.0; + // real64 max_dpc_eps = 0.0; + + // evaluateBrooksCoreyFunction( wettingVolFracScaled_eps, + // dWettingVolFracScaled_dVolFrac, + // exponentInv, + // entryPressure, + // eps, + // maxCapPres_eps, + // max_dpc_eps ); + + // evaluateBrooksCoreyFunctionInv( phaseCapPres[ip_gas], + // ip_gas, + // volFracScaleInv, + // exponentInv, + // entryPressure, + // maxCapPres_eps, + // m_phaseMinVolumeFraction[ip_gas], + // m_phaseOrder, + // phaseVolFraction[ip_gas], + // dPhaseCapPres_dPhaseVolFrac[ip_gas][ip_gas] ); + // phaseVolFraction[ip_water] = 1.0 - phaseVolFraction[ip_gas]; + // } +} + GEOS_HOST_DEVICE inline void BrooksCoreyCapillaryPressureUpdate:: @@ -222,6 +342,49 @@ BrooksCoreyCapillaryPressureUpdate:: } +GEOS_HOST_DEVICE +inline void +BrooksCoreyCapillaryPressureUpdate:: + evaluateBrooksCoreyFunctionInv( real64 const phaseCapPressure, + int const ip, + real64 const volFracScaleInv, + real64 const exponentInv, + real64 const entryPressure, + real64 const maxCapPres_eps, + real64 const phaseMinVolumeFraction, + arrayView1d< integer const > const phaseOrder, + real64 & phaseVolFraction, + real64 & dPhaseCapPressure_dVolFrac ) +{ + + + phaseVolFraction = 0.0; + real64 value = 0.0; + dPhaseCapPressure_dVolFrac = 0.0; + integer const ip_oil = phaseOrder[CapillaryPressureBase::PhaseType::OIL]; + + real64 const dScaledWettingPhaseVolFrac_dVolFrac = (ip == ip_oil) + ? -volFracScaleInv : volFracScaleInv; + + if( phaseCapPressure <= maxCapPres_eps && phaseCapPressure >= entryPressure ) + { + // intermediate value + real64 const val = pow( entryPressure, exponentInv ) / pow( phaseCapPressure, exponentInv + 1 ); + + value = (phaseCapPressure * val) * volFracScaleInv + phaseMinVolumeFraction; // entryPressure * (S_w)^( - 1 / exponentInv ) + dPhaseCapPressure_dVolFrac = -dScaledWettingPhaseVolFrac_dVolFrac * val * exponentInv; + phaseVolFraction = (ip == ip_oil) ? 1.0 - value : value; + } + else // enforce a constant and bounded capillary pressure + { + real64 const val = (phaseCapPressure > maxCapPres_eps) + ? pow( entryPressure, exponentInv ) / pow( maxCapPres_eps, exponentInv ) : 1.0; + value = val * volFracScaleInv + phaseMinVolumeFraction; + phaseVolFraction = (ip == ip_oil) ? 1.0 - value : value; + } + +} + } // namespace constitutive diff --git a/src/coreComponents/constitutive/capillaryPressure/CapillaryPressureBase.cpp b/src/coreComponents/constitutive/capillaryPressure/CapillaryPressureBase.cpp index 9f1db0231ed..aea0662a591 100644 --- a/src/coreComponents/constitutive/capillaryPressure/CapillaryPressureBase.cpp +++ b/src/coreComponents/constitutive/capillaryPressure/CapillaryPressureBase.cpp @@ -52,6 +52,7 @@ CapillaryPressureBase::CapillaryPressureBase( string const & name, registerField< fields::cappres::phaseCapPressure >( &m_phaseCapPressure ); registerField< fields::cappres::dPhaseCapPressure_dPhaseVolFraction >( &m_dPhaseCapPressure_dPhaseVolFrac ); + registerField< fields::cappres::phaseTrappedVolFraction >( &m_phaseTrappedVolFrac ); } void CapillaryPressureBase::postInputInitialization() @@ -93,19 +94,31 @@ void CapillaryPressureBase::postInputInitialization() void CapillaryPressureBase::allocateConstitutiveData( Group & parent, localIndex const numPts ) { integer const NP = numFluidPhases(); - + //phase trapped for stats + m_phaseTrappedVolFrac.resize( 0, numPts, NP ); + m_phaseTrappedVolFrac.zero(); m_phaseCapPressure.resize( 0, numPts, NP ); m_dPhaseCapPressure_dPhaseVolFrac.resize( 0, numPts, NP, NP ); - + ConstitutiveBase::allocateConstitutiveData( parent, numPts ); } void CapillaryPressureBase::setLabels() { + getField< fields::cappres::phaseTrappedVolFraction >(). + setDimLabels( 2, m_phaseNames ); getField< fields::cappres::phaseCapPressure >(). setDimLabels( 2, m_phaseNames ); } +void CapillaryPressureBase::resizeFields( localIndex const size, localIndex const numPts ) +{ + integer const NP = numFluidPhases(); + m_phaseTrappedVolFrac.resize( size, numPts, NP ); + m_phaseCapPressure.resize( size, numPts, NP ); + m_dPhaseCapPressure_dPhaseVolFrac.resize( size, numPts, NP, NP ); +} + } // namespace constitutive } // namespace geos diff --git a/src/coreComponents/constitutive/capillaryPressure/CapillaryPressureBase.hpp b/src/coreComponents/constitutive/capillaryPressure/CapillaryPressureBase.hpp index b0617841488..34a4701b957 100644 --- a/src/coreComponents/constitutive/capillaryPressure/CapillaryPressureBase.hpp +++ b/src/coreComponents/constitutive/capillaryPressure/CapillaryPressureBase.hpp @@ -60,16 +60,19 @@ class CapillaryPressureBaseUpdate CapillaryPressureBaseUpdate( arrayView1d< integer const > const & phaseTypes, arrayView1d< integer const > const & phaseOrder, + arrayView3d< real64, cappres::USD_CAPPRES > const & phaseTrapped, arrayView3d< real64, cappres::USD_CAPPRES > const & phaseCapPressure, arrayView4d< real64, cappres::USD_CAPPRES_DS > const & dPhaseCapPressure_dPhaseVolFrac ) : m_phaseTypes( phaseTypes ), m_phaseOrder( phaseOrder ), + m_phaseTrappedVolFrac( phaseTrapped ), m_phaseCapPressure( phaseCapPressure ), m_dPhaseCapPressure_dPhaseVolFrac( dPhaseCapPressure_dPhaseVolFrac ) {} arrayView1d< integer const > m_phaseTypes; arrayView1d< integer const > m_phaseOrder; + arrayView3d< real64, cappres::USD_CAPPRES > m_phaseTrappedVolFrac; arrayView3d< real64, cappres::USD_CAPPRES > m_phaseCapPressure; arrayView4d< real64, cappres::USD_CAPPRES_DS > m_dPhaseCapPressure_dPhaseVolFrac; @@ -130,6 +133,14 @@ class CapillaryPressureBase : public ConstitutiveBase arrayView3d< real64 const > const & convergedPermeability ) const { GEOS_UNUSED_VAR( convergedPorosity, convergedPermeability ); } + + /** + * @brief Save converged phase volume fraction at the end of a time step (needed for hysteresis) + * @param[in] phaseVolFraction an array containing the phase volume fractions at the end of a converged time step + */ + virtual void saveConvergedPhaseVolFractionState( arrayView2d< real64 const, compflow::USD_PHASE > const & phaseVolFraction ) const + { GEOS_UNUSED_VAR( phaseVolFraction ); } + /* * @brief Getter for the number of fluid phases * @return the number of fluid phases @@ -182,9 +193,17 @@ class CapillaryPressureBase : public ConstitutiveBase void setLabels(); protected: +/** + * @brief Function called internally to resize member arrays + * @param size primary dimension (e.g. number of cells) + * @param numPts secondary dimension (e.g. number of gauss points per cell) + */ + virtual void resizeFields( localIndex const size, localIndex const numPts ); virtual void postInputInitialization() override; + std::tuple phaseIndex(const arrayView1d &phaseOrder); + // phase names read from input string_array m_phaseNames; @@ -198,8 +217,45 @@ class CapillaryPressureBase : public ConstitutiveBase // output quantities array3d< real64, cappres::LAYOUT_CAPPRES > m_phaseCapPressure; array4d< real64, cappres::LAYOUT_CAPPRES_DS > m_dPhaseCapPressure_dPhaseVolFrac; + + // trapped fraction + array3d< real64, cappres::LAYOUT_CAPPRES > m_phaseTrappedVolFrac; }; + inline std::tuple< integer, integer > CapillaryPressureBase::phaseIndex( arrayView1d< integer const > const & phaseOrder ) + { + using PT = PhaseType; + integer const ipWater = phaseOrder[PT::WATER]; + integer const ipOil = phaseOrder[PT::OIL]; + integer const ipGas = phaseOrder[PT::GAS]; + + integer ipWetting = -1, ipNonWetting = -1; + + if( ipWater >= 0 && ipOil >= 0 && ipGas >= 0 ) + { + ipWetting = ipWater; + ipNonWetting = ipGas; + } + else if( ipWater < 0 ) + { + ipWetting = ipOil; + ipNonWetting = ipGas; + } + else if( ipOil < 0 ) + { + ipWetting = ipWater; + ipNonWetting = ipGas; + } + else if( ipGas < 0 ) + { + ipWetting = ipWater; + ipNonWetting = ipOil; + } + + //maybe a bit too pythonic + return std::make_tuple( ipWetting, ipNonWetting ); + } + } // namespace constitutive } // namespace geos diff --git a/src/coreComponents/constitutive/capillaryPressure/CapillaryPressureFields.hpp b/src/coreComponents/constitutive/capillaryPressure/CapillaryPressureFields.hpp index 653b7faeda8..b3cdb79645e 100644 --- a/src/coreComponents/constitutive/capillaryPressure/CapillaryPressureFields.hpp +++ b/src/coreComponents/constitutive/capillaryPressure/CapillaryPressureFields.hpp @@ -23,45 +23,86 @@ #include "constitutive/capillaryPressure/Layouts.hpp" #include "mesh/MeshFields.hpp" -namespace geos -{ - -namespace fields -{ - -namespace cappres -{ - -using array3dLayoutCapPressure = array3d< real64, constitutive::cappres::LAYOUT_CAPPRES >; -using array4dLayoutCapPressure_dS = array4d< real64, constitutive::cappres::LAYOUT_CAPPRES_DS >; - -DECLARE_FIELD( phaseCapPressure, - "phaseCapPressure", - array3dLayoutCapPressure, - 0, - LEVEL_0, - WRITE_AND_READ, - "Phase capillary pressure" ); - -DECLARE_FIELD( dPhaseCapPressure_dPhaseVolFraction, - "dPhaseCapPressure_dPhaseVolFraction", - array4dLayoutCapPressure_dS, - 0, - NOPLOT, - WRITE_AND_READ, - "Derivative of phase capillary pressure with respect to phase volume fraction" ); - -DECLARE_FIELD( jFuncMultiplier, - "jFuncMultiplier", - array2d< real64 >, - 0, - NOPLOT, - WRITE_AND_READ, - "Multiplier for the Leverett J-function" ); +namespace geos { -} + namespace fields { -} + namespace cappres { + + using array2dLayoutPhase = array2d; + using array3dLayoutCapPressure = array3d; + using array4dLayoutCapPressure_dS = array4d; + + + + enum ModeIndexType : integer { + DRAINAGE = 0,//to be used in array of Kernels + IMBIBITION = 1, + DRAINAGE_TO_IMBIBITION = 2, + IMBIBITION_TO_DRAINAGE = 3 + }; + + DECLARE_FIELD(phaseCapPressure, + "phaseCapPressure", + array3dLayoutCapPressure, + 0, + LEVEL_0, + WRITE_AND_READ, + "Phase capillary pressure"); + + DECLARE_FIELD(dPhaseCapPressure_dPhaseVolFraction, + "dPhaseCapPressure_dPhaseVolFraction", + array4dLayoutCapPressure_dS, + 0, + NOPLOT, + WRITE_AND_READ, + "Derivative of phase capillary pressure with respect to phase volume fraction"); + + DECLARE_FIELD(jFuncMultiplier, + "jFuncMultiplier", + array2d, + 0, + NOPLOT, + WRITE_AND_READ, + "Multiplier for the Leverett J-function"); + + DECLARE_FIELD(phaseTrappedVolFraction, + "phaseTrappedVolumeFraction", + array3dLayoutCapPressure, + 0, + LEVEL_0, + WRITE_AND_READ, + "Phase Trapped Volume Fraction"); + + DECLARE_FIELD(mode, + "Hysteresis Mode", + array1d, + 0, + LEVEL_0, + WRITE_AND_READ, + "Hysteresis mode"); + + + DECLARE_FIELD(phaseMaxHistoricalVolFraction, + "phaseMaxHistoricalVolFraction", + array2dLayoutPhase, + 0, + LEVEL_0, + WRITE_AND_READ, + "Phase max historical phase volume fraction"); + + DECLARE_FIELD(phaseMinHistoricalVolFraction, + "phaseMinHistoricalVolFraction", + array2dLayoutPhase, + 0, + LEVEL_0, + WRITE_AND_READ, + "Phase min historical phase volume fraction"); + + + } + + } } diff --git a/src/coreComponents/constitutive/capillaryPressure/CapillaryPressureSelector.hpp b/src/coreComponents/constitutive/capillaryPressure/CapillaryPressureSelector.hpp index b164f55aa16..a8382183ba4 100644 --- a/src/coreComponents/constitutive/capillaryPressure/CapillaryPressureSelector.hpp +++ b/src/coreComponents/constitutive/capillaryPressure/CapillaryPressureSelector.hpp @@ -24,6 +24,7 @@ #include "constitutive/capillaryPressure/BrooksCoreyCapillaryPressure.hpp" #include "constitutive/capillaryPressure/JFunctionCapillaryPressure.hpp" #include "constitutive/capillaryPressure/TableCapillaryPressure.hpp" +#include "constitutive/capillaryPressure/TableCapillaryPressureHysteresis.hpp" #include "constitutive/capillaryPressure/VanGenuchtenCapillaryPressure.hpp" namespace geos @@ -39,6 +40,7 @@ void constitutiveUpdatePassThru( CapillaryPressureBase const & capPres, ConstitutivePassThruHandler< BrooksCoreyCapillaryPressure, JFunctionCapillaryPressure, TableCapillaryPressure, + TableCapillaryPressureHysteresis, VanGenuchtenCapillaryPressure >::execute( capPres, std::forward< LAMBDA >( lambda ) ); } @@ -49,6 +51,7 @@ void constitutiveUpdatePassThru( CapillaryPressureBase & capPres, ConstitutivePassThruHandler< BrooksCoreyCapillaryPressure, JFunctionCapillaryPressure, TableCapillaryPressure, + TableCapillaryPressureHysteresis, VanGenuchtenCapillaryPressure >::execute( capPres, std::forward< LAMBDA >( lambda ) ); } diff --git a/src/coreComponents/constitutive/capillaryPressure/InverseCapillaryPressure.cpp b/src/coreComponents/constitutive/capillaryPressure/InverseCapillaryPressure.cpp index 534037c55f4..0156d06ab67 100644 --- a/src/coreComponents/constitutive/capillaryPressure/InverseCapillaryPressure.cpp +++ b/src/coreComponents/constitutive/capillaryPressure/InverseCapillaryPressure.cpp @@ -20,6 +20,7 @@ #include "InverseCapillaryPressure.hpp" #include "constitutive/capillaryPressure/BrooksCoreyCapillaryPressure.hpp" #include "constitutive/capillaryPressure/TableCapillaryPressure.hpp" +#include "constitutive/capillaryPressure/TableCapillaryPressureHysteresis.hpp" #include "constitutive/capillaryPressure/VanGenuchtenCapillaryPressure.hpp" #include "constitutive/capillaryPressure/JFunctionCapillaryPressure.hpp" @@ -317,6 +318,7 @@ void InverseCapillaryPressure< CAP_PRESSURE >::calculateJFunctionIndex( integer template class InverseCapillaryPressure< BrooksCoreyCapillaryPressure >; template class InverseCapillaryPressure< TableCapillaryPressure >; +template class InverseCapillaryPressure< TableCapillaryPressureHysteresis >; template class InverseCapillaryPressure< JFunctionCapillaryPressure >; template class InverseCapillaryPressure< VanGenuchtenCapillaryPressure >; template class InverseCapillaryPressure< NoOpCapillaryPressure >; diff --git a/src/coreComponents/constitutive/capillaryPressure/JFunctionCapillaryPressure.cpp b/src/coreComponents/constitutive/capillaryPressure/JFunctionCapillaryPressure.cpp index e4eb76d02ba..fbb31266692 100644 --- a/src/coreComponents/constitutive/capillaryPressure/JFunctionCapillaryPressure.cpp +++ b/src/coreComponents/constitutive/capillaryPressure/JFunctionCapillaryPressure.cpp @@ -119,6 +119,14 @@ JFunctionCapillaryPressure::JFunctionCapillaryPressure( std::string const & name .setInputFlag( InputFlags::FALSE ); registerField< fields::cappres::jFuncMultiplier >( &m_jFuncMultiplier ); + + registerWrapper( viewKeyStruct::jFunctionWrappersString(), &m_jFuncKernelWrappers ). + setSizedFromParent( 0 ). + setRestartFlags( RestartFlags::NO_WRITE ); + + registerWrapper( viewKeyStruct::inverseJFunctionWrappersString(), &m_inverseJFuncKernelWrappers ). + setSizedFromParent( 0 ). + setRestartFlags( RestartFlags::NO_WRITE ); } void JFunctionCapillaryPressure::postInputInitialization() @@ -187,6 +195,7 @@ void JFunctionCapillaryPressure::initializePreSubGroups() ? true // pc on the gas phase, function must be increasing : false; // pc on the water phase, function must be decreasing TableCapillaryPressureHelpers::validateCapillaryPressureTable( jFuncTable, getFullName(), jFuncMustBeIncreasing ); + } else if( numPhases == 3 ) { @@ -205,6 +214,7 @@ void JFunctionCapillaryPressure::initializePreSubGroups() InputError, getDataContext() ); TableFunction const & jFuncTableNWI = functionManager.getGroup< TableFunction >( m_nonWettingIntermediateJFuncTableName ); TableCapillaryPressureHelpers::validateCapillaryPressureTable( jFuncTableNWI, getFullName(), true ); + } } @@ -300,11 +310,48 @@ void JFunctionCapillaryPressure::createAllTableKernelWrappers() // we want to make sure that the wrappers are always up-to-date, so we recreate them everytime m_jFuncKernelWrappers.clear(); + m_inverseJFuncKernelWrappers.clear(); + if( numPhases == 2 ) { + TableFunction const & jFuncTable = functionManager.getGroup< TableFunction >( m_wettingNonWettingJFuncTableName ); m_jFuncKernelWrappers.emplace_back( jFuncTable.createKernelWrapper() ); + auto const & satArrayView = jFuncTable.getCoordinates()[0]; + auto const & jArrayView = jFuncTable.getValues(); + + std::vector< real64 > satVec( satArrayView.size() ); + std::vector< real64 > jVec( jArrayView.size() ); + + std::copy( satArrayView.begin(), satArrayView.end(), satVec.begin() ); + std::copy( jArrayView.begin(), jArrayView.end(), jVec.begin() ); + + // Reverse both arrays (if original J is decreasing in S) + std::reverse( jVec.begin(), jVec.end() ); + std::reverse( satVec.begin(), satVec.end() ); + + + auto inverseTable = std::make_shared< TableFunction >( "inverseJFunc", this ); + + real64_array invJVec( jVec.size() ); + real64_array invSatVec( satVec.size() ); + std::copy( jVec.begin(), jVec.end(), invJVec.data() ); + std::copy( satVec.begin(), satVec.end(), invSatVec.data() ); + + array1d< real64_array > coordinates; + coordinates.emplace_back( std::move( invJVec ) ); + + + std::vector< units::Unit > dimUnits = { units::Unknown }; // or actual unit if available + + inverseTable->setTableCoordinates( coordinates, dimUnits ); + inverseTable->setTableValues( std::move( invSatVec ), units::Unknown ); + inverseTable->setInterpolationMethod( TableFunction::InterpolationType::Linear ); + + m_inverseJFuncKernelWrappers.emplace_back( inverseTable->createKernelWrapper() ); + m_inverseTables.emplace_back( std::move( inverseTable ) ); + // Populate the end-points from the tables TableCapillaryPressureHelpers::populateMinPhaseVolumeFraction( m_phaseOrder.toSliceConst(), jFuncTable, m_phaseMinVolumeFraction ); } @@ -313,8 +360,10 @@ void JFunctionCapillaryPressure::createAllTableKernelWrappers() // the assumption used everywhere in this class is that the WI information comes before the NWI information TableFunction const & jFuncTableWI = functionManager.getGroup< TableFunction >( m_wettingIntermediateJFuncTableName ); m_jFuncKernelWrappers.emplace_back( jFuncTableWI.createKernelWrapper() ); + m_inverseJFuncKernelWrappers.emplace_back( jFuncTableWI.createKernelWrapper() ); TableFunction const & jFuncTableNWI = functionManager.getGroup< TableFunction >( m_nonWettingIntermediateJFuncTableName ); m_jFuncKernelWrappers.emplace_back( jFuncTableNWI.createKernelWrapper() ); + m_inverseJFuncKernelWrappers.emplace_back( jFuncTableNWI.createKernelWrapper() ); // Populate the end-points from the tables TableCapillaryPressureHelpers::populateMinPhaseVolumeFraction( m_phaseOrder.toSliceConst(), jFuncTableWI, jFuncTableNWI, m_phaseMinVolumeFraction ); @@ -323,16 +372,20 @@ void JFunctionCapillaryPressure::createAllTableKernelWrappers() JFunctionCapillaryPressure::KernelWrapper:: KernelWrapper( arrayView1d< TableFunction::KernelWrapper const > const & jFuncKernelWrappers, + arrayView1d< TableFunction::KernelWrapper const > const & inverseJFuncKernelWrappers, arrayView2d< real64 const > const & jFuncMultiplier, arrayView1d< integer const > const & phaseTypes, arrayView1d< integer const > const & phaseOrder, + arrayView3d< real64, cappres::USD_CAPPRES > const & phaseTrapped, arrayView3d< real64, cappres::USD_CAPPRES > const & phaseCapPres, arrayView4d< real64, cappres::USD_CAPPRES_DS > const & dPhaseCapPres_dPhaseVolFrac ) : CapillaryPressureBaseUpdate( phaseTypes, phaseOrder, + phaseTrapped, phaseCapPres, dPhaseCapPres_dPhaseVolFrac ), m_jFuncKernelWrappers( jFuncKernelWrappers ), + m_inverseJFuncKernelWrappers( inverseJFuncKernelWrappers ), m_jFuncMultiplier( jFuncMultiplier ) {} @@ -341,9 +394,11 @@ JFunctionCapillaryPressure::createKernelWrapper() { createAllTableKernelWrappers(); return KernelWrapper( m_jFuncKernelWrappers, + m_inverseJFuncKernelWrappers, m_jFuncMultiplier, m_phaseTypes, m_phaseOrder, + m_phaseTrappedVolFrac, m_phaseCapPressure, m_dPhaseCapPressure_dPhaseVolFrac ); } diff --git a/src/coreComponents/constitutive/capillaryPressure/JFunctionCapillaryPressure.hpp b/src/coreComponents/constitutive/capillaryPressure/JFunctionCapillaryPressure.hpp index 4e42189d92e..29e7692bd95 100644 --- a/src/coreComponents/constitutive/capillaryPressure/JFunctionCapillaryPressure.hpp +++ b/src/coreComponents/constitutive/capillaryPressure/JFunctionCapillaryPressure.hpp @@ -65,9 +65,11 @@ class JFunctionCapillaryPressure : public CapillaryPressureBase public: KernelWrapper( arrayView1d< TableFunction::KernelWrapper const > const & jFuncKernelWrappers, + arrayView1d< TableFunction::KernelWrapper const > const & inverseJFuncKernelWrappers, arrayView2d< real64 const > const & jFuncMultiplier, arrayView1d< integer const > const & phaseTypes, arrayView1d< integer const > const & phaseOrder, + arrayView3d< real64, cappres::USD_CAPPRES > const & phaseTrapped, arrayView3d< real64, cappres::USD_CAPPRES > const & phaseCapPres, arrayView4d< real64, cappres::USD_CAPPRES_DS > const & dPhaseCapPres_dPhaseVolFrac ); @@ -77,6 +79,13 @@ class JFunctionCapillaryPressure : public CapillaryPressureBase arraySlice1d< real64, cappres::USD_CAPPRES - 2 > const & phaseCapPres, arraySlice2d< real64, cappres::USD_CAPPRES_DS - 2 > const & dPhaseCapPres_dPhaseVolFrac ) const; + GEOS_HOST_DEVICE + void computeInv( arraySlice1d< real64, compflow::USD_PHASE - 1 > const & phaseVolFraction, + arraySlice1d< real64 const > const & jFuncMultiplier, + arraySlice1d< real64 const, cappres::USD_CAPPRES - 2 > const & phaseCapPres, + arraySlice2d< real64, cappres::USD_CAPPRES_DS - 2 > const & dPhaseCapPres_dPhaseVolFrac ) const; + + GEOS_HOST_DEVICE virtual void update( localIndex const k, localIndex const q, @@ -87,6 +96,7 @@ class JFunctionCapillaryPressure : public CapillaryPressureBase /// Array of kernel wrappers for the J-function /// Is of size 1 for two-phase flow, and of size 2 for three-phase flow arrayView1d< TableFunction::KernelWrapper const > const m_jFuncKernelWrappers; + arrayView1d< TableFunction::KernelWrapper const > const m_inverseJFuncKernelWrappers; /// Array of cell-wise J-function multipliers /// The second dimension is of size 1 for two-phase flow, and of size 2 for three-phase flow @@ -112,6 +122,8 @@ class JFunctionCapillaryPressure : public CapillaryPressureBase static constexpr char const * porosityExponentString() { return "porosityExponent"; } static constexpr char const * permeabilityExponentString() { return "permeabilityExponent"; } static constexpr char const * permeabilityDirectionString() { return "permeabilityDirection"; } + static constexpr char const * jFunctionWrappersString() { return "jFunctionWrappers"; } + static constexpr char const * inverseJFunctionWrappersString() { return "inverseJFunctionWrappers"; } }; /** @@ -168,6 +180,9 @@ class JFunctionCapillaryPressure : public CapillaryPressureBase /// J-function kernel wrapper for the first pair (wetting-intermediate if NP=3, wetting-non-wetting otherwise) array1d< TableFunction::KernelWrapper > m_jFuncKernelWrappers; + array1d< TableFunction::KernelWrapper > m_inverseJFuncKernelWrappers; + + std::vector< std::shared_ptr< TableFunction > > m_inverseTables; }; @@ -242,6 +257,42 @@ JFunctionCapillaryPressure::KernelWrapper:: } } +GEOS_HOST_DEVICE +inline void +JFunctionCapillaryPressure::KernelWrapper:: + computeInv( arraySlice1d< real64, compflow::USD_PHASE - 1 > const & phaseVolFraction, + arraySlice1d< real64 const > const & jFuncMultiplier, + arraySlice1d< real64 const, cappres::USD_CAPPRES - 2 > const & phaseCapPres, + arraySlice2d< real64, cappres::USD_CAPPRES_DS - 2 > const & dPhaseCapPres_dPhaseVolFrac ) const +{ + LvArray::forValuesInSlice( dPhaseCapPres_dPhaseVolFrac, []( real64 & val ){ val = 0.0; } ); + + using PT = CapillaryPressureBase::PhaseType; + integer const ipWater = m_phaseOrder[PT::WATER]; + integer const ipOil = m_phaseOrder[PT::OIL]; + integer const ipGas = m_phaseOrder[PT::GAS]; + + GEOS_UNUSED_VAR( ipOil ); + // apply multiplier + real64 capPresWater_J = phaseCapPres[ipWater] / jFuncMultiplier[0]; + // std::cout << GEOS_FMT( " JM_2 = ( {:4.2e} )", jFuncMultiplier[0] ); + array1d< real64 > input( 1 ); + input[0] = capPresWater_J; + // std::cout << GEOS_FMT( " J_int2 = ( {:4.2e} )", input[0] ); + // std::cout << GEOS_FMT( " Pc_int2 = ( {:4.2e} )", phaseCapPres[ipWater] ); + auto inputSlice = input.toSliceConst(); + + + + phaseVolFraction[ipWater] = + m_inverseJFuncKernelWrappers[0].compute( inputSlice, + &(dPhaseCapPres_dPhaseVolFrac)[ipWater][ipWater] ); + dPhaseCapPres_dPhaseVolFrac[ipWater][ipWater] /= jFuncMultiplier[0]; + // std::cout << GEOS_FMT( " S_int2 = ( {:4.2e} )", phaseVolFraction[ipWater] ); + // std::cout << GEOS_FMT( " dS/dP = ( {:4.2e} )", dPhaseCapPres_dPhaseVolFrac[ipWater][ipWater] ); + phaseVolFraction[ipGas] = 1.0 - phaseVolFraction[ipWater]; +} + GEOS_HOST_DEVICE inline void JFunctionCapillaryPressure::KernelWrapper:: diff --git a/src/coreComponents/constitutive/capillaryPressure/TableCapillaryPressure.cpp b/src/coreComponents/constitutive/capillaryPressure/TableCapillaryPressure.cpp index 17b94f4730f..d8b71c829e3 100644 --- a/src/coreComponents/constitutive/capillaryPressure/TableCapillaryPressure.cpp +++ b/src/coreComponents/constitutive/capillaryPressure/TableCapillaryPressure.cpp @@ -69,6 +69,10 @@ TableCapillaryPressure::TableCapillaryPressure( std::string const & name, registerWrapper( viewKeyStruct::capPresWrappersString(), &m_capPresKernelWrappers ). setSizedFromParent( 0 ). setRestartFlags( RestartFlags::NO_WRITE ); + + registerWrapper( viewKeyStruct::inverseCapPresWrappersString(), &m_inverseCapPresWrappers ). + setSizedFromParent( 0 ). + setRestartFlags( RestartFlags::NO_WRITE ); } void TableCapillaryPressure::postInputInitialization() @@ -154,11 +158,47 @@ void TableCapillaryPressure::createAllTableKernelWrappers() // we want to make sure that the wrappers are always up-to-date, so we recreate them everytime m_capPresKernelWrappers.clear(); + m_inverseCapPresWrappers.clear(); + if( numPhases == 2 ) { TableFunction const & capPresTable = functionManager.getGroup< TableFunction >( m_wettingNonWettingCapPresTableName ); m_capPresKernelWrappers.emplace_back( capPresTable.createKernelWrapper() ); + auto const & satArrayView = capPresTable.getCoordinates()[0]; + auto const & capPresArrayView = capPresTable.getValues(); + + std::vector< real64 > satVec( satArrayView.size() ); + std::vector< real64 > pcVec( capPresArrayView.size() ); + + std::copy( satArrayView.begin(), satArrayView.end(), satVec.begin() ); + std::copy( capPresArrayView.begin(), capPresArrayView.end(), pcVec.begin() ); + + // Reverse both arrays (if original J is decreasing in S) + std::reverse( pcVec.begin(), pcVec.end() ); + std::reverse( satVec.begin(), satVec.end() ); + + + auto inverseTable = std::make_shared< TableFunction >( "inverseCapPres", this ); + + real64_array invPcVec( pcVec.size() ); + real64_array invSatVec( satVec.size() ); + std::copy( pcVec.begin(), pcVec.end(), invPcVec.data() ); + std::copy( satVec.begin(), satVec.end(), invSatVec.data() ); + + array1d< real64_array > coordinates; + coordinates.emplace_back( std::move( invPcVec ) ); + + + std::vector< units::Unit > dimUnits = { units::Unknown }; // or actual unit if available + + inverseTable->setTableCoordinates( coordinates, dimUnits ); + inverseTable->setTableValues( std::move( invSatVec ), units::Unknown ); + inverseTable->setInterpolationMethod( TableFunction::InterpolationType::Linear ); + + m_inverseCapPresWrappers.emplace_back( inverseTable->createKernelWrapper() ); + m_inverseTables.emplace_back( std::move( inverseTable ) ); + // Populate the end-points from the tables TableCapillaryPressureHelpers::populateMinPhaseVolumeFraction( m_phaseOrder.toSliceConst(), capPresTable, m_phaseMinVolumeFraction ); } @@ -166,8 +206,10 @@ void TableCapillaryPressure::createAllTableKernelWrappers() { TableFunction const & capPresTableWI = functionManager.getGroup< TableFunction >( m_wettingIntermediateCapPresTableName ); m_capPresKernelWrappers.emplace_back( capPresTableWI.createKernelWrapper() ); + m_inverseCapPresWrappers.emplace_back( capPresTableWI.createKernelWrapper() ); TableFunction const & capPresTableNWI = functionManager.getGroup< TableFunction >( m_nonWettingIntermediateCapPresTableName ); m_capPresKernelWrappers.emplace_back( capPresTableNWI.createKernelWrapper() ); + m_inverseCapPresWrappers.emplace_back( capPresTableNWI.createKernelWrapper() ); // Populate the end-points from the tables TableCapillaryPressureHelpers::populateMinPhaseVolumeFraction( m_phaseOrder.toSliceConst(), capPresTableWI, capPresTableNWI, m_phaseMinVolumeFraction ); @@ -177,15 +219,19 @@ void TableCapillaryPressure::createAllTableKernelWrappers() TableCapillaryPressure::KernelWrapper:: KernelWrapper( arrayView1d< TableFunction::KernelWrapper const > const & capPresKernelWrappers, + arrayView1d< TableFunction::KernelWrapper const > const & inverseCapPresWrappers, arrayView1d< integer const > const & phaseTypes, arrayView1d< integer const > const & phaseOrder, + arrayView3d< real64, cappres::USD_CAPPRES > const & phaseTrapped, arrayView3d< real64, cappres::USD_CAPPRES > const & phaseCapPres, arrayView4d< real64, cappres::USD_CAPPRES_DS > const & dPhaseCapPres_dPhaseVolFrac ) : CapillaryPressureBaseUpdate( phaseTypes, phaseOrder, + phaseTrapped, phaseCapPres, dPhaseCapPres_dPhaseVolFrac ), - m_capPresKernelWrappers( capPresKernelWrappers ) + m_capPresKernelWrappers( capPresKernelWrappers ), + m_inverseCapPresWrappers( inverseCapPresWrappers ) {} TableCapillaryPressure::KernelWrapper @@ -193,8 +239,10 @@ TableCapillaryPressure::createKernelWrapper() { createAllTableKernelWrappers(); return KernelWrapper( m_capPresKernelWrappers, + m_inverseCapPresWrappers, m_phaseTypes, m_phaseOrder, + m_phaseTrappedVolFrac, m_phaseCapPressure, m_dPhaseCapPressure_dPhaseVolFrac ); } diff --git a/src/coreComponents/constitutive/capillaryPressure/TableCapillaryPressure.hpp b/src/coreComponents/constitutive/capillaryPressure/TableCapillaryPressure.hpp index 739b9993083..8154cffbc8f 100644 --- a/src/coreComponents/constitutive/capillaryPressure/TableCapillaryPressure.hpp +++ b/src/coreComponents/constitutive/capillaryPressure/TableCapillaryPressure.hpp @@ -55,8 +55,10 @@ class TableCapillaryPressure : public CapillaryPressureBase public: KernelWrapper( arrayView1d< TableFunction::KernelWrapper const > const & capPresKernelWrappers, + arrayView1d< TableFunction::KernelWrapper const > const & inverseCapPresWrappers, arrayView1d< integer const > const & phaseTypes, arrayView1d< integer const > const & phaseOrder, + arrayView3d< real64, cappres::USD_CAPPRES > const & phaseTrapped, arrayView3d< real64, cappres::USD_CAPPRES > const & phaseCapPres, arrayView4d< real64, cappres::USD_CAPPRES_DS > const & dPhaseCapPres_dPhaseVolFrac ); @@ -65,6 +67,12 @@ class TableCapillaryPressure : public CapillaryPressureBase arraySlice1d< real64, cappres::USD_CAPPRES - 2 > const & phaseCapPres, arraySlice2d< real64, cappres::USD_CAPPRES_DS - 2 > const & dPhaseCapPres_dPhaseVolFrac ) const; + GEOS_HOST_DEVICE + void computeInv( arraySlice1d< real64, compflow::USD_PHASE - 1 > const & phaseVolFraction, + arraySlice1d< real64 const, cappres::USD_CAPPRES - 2 > const & phaseCapPres, + arraySlice2d< real64, cappres::USD_CAPPRES_DS - 2 > const & dPhaseCapPres_dPhaseVolFrac ) const; + + GEOS_HOST_DEVICE virtual void update( localIndex const k, localIndex const q, @@ -75,6 +83,7 @@ class TableCapillaryPressure : public CapillaryPressureBase /// Array of kernel wrappers for the capillary pressures /// Is of size 1 for two-phase flow, and of size 2 for three-phase flow arrayView1d< TableFunction::KernelWrapper const > const m_capPresKernelWrappers; + arrayView1d< TableFunction::KernelWrapper const > const m_inverseCapPresWrappers; }; @@ -91,6 +100,7 @@ class TableCapillaryPressure : public CapillaryPressureBase static constexpr char const * wettingIntermediateCapPresTableNameString() { return "wettingIntermediateCapPressureTableName"; } static constexpr char const * nonWettingIntermediateCapPresTableNameString() { return "nonWettingIntermediateCapPressureTableName"; } static constexpr char const * capPresWrappersString() { return "capPresWrappers"; } + static constexpr char const * inverseCapPresWrappersString() { return "inverseCapPresWrappers"; } }; @@ -116,6 +126,9 @@ class TableCapillaryPressure : public CapillaryPressureBase /// Capillary pressure kernel wrapper for the first pair (wetting-intermediate if NP=3, wetting-non-wetting otherwise) array1d< TableFunction::KernelWrapper > m_capPresKernelWrappers; + array1d< TableFunction::KernelWrapper > m_inverseCapPresWrappers; + + std::vector< std::shared_ptr< TableFunction > > m_inverseTables; }; @@ -173,6 +186,39 @@ TableCapillaryPressure::KernelWrapper:: } } +GEOS_HOST_DEVICE +inline void +TableCapillaryPressure::KernelWrapper:: + computeInv( arraySlice1d< real64, compflow::USD_PHASE - 1 > const & phaseVolFraction, + arraySlice1d< real64 const, cappres::USD_CAPPRES - 2 > const & phaseCapPres, + arraySlice2d< real64, cappres::USD_CAPPRES_DS - 2 > const & dPhaseCapPres_dPhaseVolFrac ) const +{ + LvArray::forValuesInSlice( dPhaseCapPres_dPhaseVolFrac, []( real64 & val ){ val = 0.0; } ); + + using PT = CapillaryPressureBase::PhaseType; + integer const ipWater = m_phaseOrder[PT::WATER]; + integer const ipOil = m_phaseOrder[PT::OIL]; + integer const ipGas = m_phaseOrder[PT::GAS]; + + GEOS_UNUSED_VAR( ipOil ); + + // put capillary pressure on the wetting phase + real64 capPresWater = phaseCapPres[ipWater]; + array1d< real64 > input( 1 ); + input[0] = capPresWater; + auto inputSlice = input.toSliceConst(); + + phaseVolFraction[ipWater] = + m_capPresKernelWrappers[0].compute( &(phaseCapPres)[ipWater], + &(dPhaseCapPres_dPhaseVolFrac)[ipWater][ipWater] ); + phaseVolFraction[ipWater] = + m_inverseCapPresWrappers[0].compute( inputSlice, + &(dPhaseCapPres_dPhaseVolFrac)[ipWater][ipWater] ); + phaseVolFraction[ipGas] = 1.0 - phaseVolFraction[ipWater]; + +} + + GEOS_HOST_DEVICE inline void TableCapillaryPressure::KernelWrapper:: @@ -183,6 +229,9 @@ TableCapillaryPressure::KernelWrapper:: compute( phaseVolFraction, m_phaseCapPressure[k][q], m_dPhaseCapPressure_dPhaseVolFrac[k][q] ); + + std::cerr << "cell :" << k << "\n phaseCap " << m_phaseCapPressure[k][q] << std::endl; + std::cerr << "dPhaseCap_dS" << m_phaseCapPressure[k][q] << std::endl; } } // namespace constitutive diff --git a/src/coreComponents/constitutive/capillaryPressure/TableCapillaryPressureHelpers.cpp b/src/coreComponents/constitutive/capillaryPressure/TableCapillaryPressureHelpers.cpp index 619e69a22db..d412260e7c5 100644 --- a/src/coreComponents/constitutive/capillaryPressure/TableCapillaryPressureHelpers.cpp +++ b/src/coreComponents/constitutive/capillaryPressure/TableCapillaryPressureHelpers.cpp @@ -130,18 +130,46 @@ void TableCapillaryPressureHelpers::populateMinPhaseVolumeFraction( } void -TableCapillaryPressureHelpers::validateCapillaryPressureTable( geos::TableFunction const & capPresTable, - geos::string const & fullConstitutiveName, - bool const capPresMustBeIncreasing, - geos::real64 & phaseMax, geos::real64 & phaseMin ) +TableCapillaryPressureHelpers::validateCapillaryPressureTable( const geos::TableFunction & capPresTable, + const geos::string & fullConstitutiveName, + const bool capPresMustBeIncreasing, + geos::real64 & phaseMax, + geos::real64 & phaseMin, + geos::real64 & phaseCapPresMinEndPoint, + geos::real64 & phaseCapPresMaxEndPoint ) { TableCapillaryPressureHelpers::validateCapillaryPressureTable( capPresTable, fullConstitutiveName, capPresMustBeIncreasing ); ArrayOfArraysView< real64 const > coords = capPresTable.getCoordinates(); arraySlice1d< real64 const > phaseVolFrac = coords[0]; + arrayView1d< real64 const > const capPres = capPresTable.getValues(); + phaseMin = phaseVolFrac[0]; + phaseCapPresMinEndPoint = capPres[0]; phaseMax = phaseVolFrac[phaseVolFrac.size()-1]; -} + phaseCapPresMaxEndPoint = capPres[phaseVolFrac.size()-1]; + + + if(capPresMustBeIncreasing) { + + for( localIndex i = 1; i < coords.sizeOfArray( 0 ); ++i ) { + if (isZero(capPres[i - 1]) && !isZero(capPres[i])) { + phaseMin = phaseVolFrac[i - 1]; + phaseCapPresMinEndPoint = capPres[i - 1]; + } + } + } + else + { + for( localIndex i = coords.sizeOfArray( 0 )-2; i>0; --i ) { + if (isZero(capPres[i + 1]) && !isZero(capPres[i])) { + phaseMax = phaseVolFrac[i + 1]; + phaseCapPresMaxEndPoint = capPres[i + 1]; + } + } + + } + } } // namespace constitutive diff --git a/src/coreComponents/constitutive/capillaryPressure/TableCapillaryPressureHelpers.hpp b/src/coreComponents/constitutive/capillaryPressure/TableCapillaryPressureHelpers.hpp index a691637002c..276d8c3eb92 100644 --- a/src/coreComponents/constitutive/capillaryPressure/TableCapillaryPressureHelpers.hpp +++ b/src/coreComponents/constitutive/capillaryPressure/TableCapillaryPressureHelpers.hpp @@ -47,7 +47,9 @@ struct TableCapillaryPressureHelpers string const & fullConstitutiveName, bool const capPresMustBeIncreasing, real64 & phaseMax, - real64 & phaseMin ); + real64 & phaseMin, + real64 & phaseCapPresMinEndPoint, + real64 & phaseCapPresMaxEndPoint ); /** * @brief Populates the minimum phase volume fraction for each phase from the ends of the provided tables diff --git a/src/coreComponents/constitutive/capillaryPressure/TableCapillaryPressureHysteresis.cpp b/src/coreComponents/constitutive/capillaryPressure/TableCapillaryPressureHysteresis.cpp new file mode 100644 index 00000000000..9a479d97b0c --- /dev/null +++ b/src/coreComponents/constitutive/capillaryPressure/TableCapillaryPressureHysteresis.cpp @@ -0,0 +1,2267 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * SPDX-License-Identifier: LGPL-2.1-only + * + * Copyright (c) 2018-2020 Lawrence Livermore National Security LLC + * Copyright (c) 2018-2020 The Board of Trustees of the Leland Stanford Junior University + * Copyright (c) 2018-2020 TotalEnergies + * Copyright (c) 2019- GEOSX Contributors + * All rights reserved + * + * See top level LICENSE, COPYRIGHT, CONTRIBUTORS, NOTICE, and ACKNOWLEDGEMENTS files for details. + * ------------------------------------------------------------------------------------------------------------ + */ + + +#include "TableCapillaryPressureHysteresis.hpp" + +#include "constitutive/capillaryPressure/TableCapillaryPressureHelpers.hpp" +#include "functions/FunctionManager.hpp" +#include "constitutive/ConstitutiveManager.hpp" + +namespace geos { + + using namespace dataRepository; + + namespace constitutive { + + TableCapillaryPressureHysteresis::TableCapillaryPressureHysteresis(const std::string &name, + dataRepository::Group *const parent) + : CapillaryPressureBase(name, parent) { + + registerWrapper(viewKeyStruct::phaseHasHysteresisString(), &m_phaseHasHysteresis). + setInputFlag(InputFlags::FALSE) + . // will be deduced from tables + setSizedFromParent(0); + + registerWrapper(viewKeyStruct::landParameterString(), &m_landParam). + setInputFlag(InputFlags::FALSE). // will be deduced from tables + setSizedFromParent(0); + + //2phase + registerWrapper(viewKeyStruct::drainageWettingNonWettingCapPresTableNameString(), + &m_drainageWettingNonWettingCapPresTableName). + setInputFlag(InputFlags::OPTIONAL). + setDescription("Name of the drainage two-phase table for capillary pressure curve. \n" + "If you want to use 3-phase flow please use instead " + + string(viewKeyStruct::drainageWettingIntermediateCapPresTableNameString()) + + " and " + + string(viewKeyStruct::drainageNonWettingIntermediateCapPresTableNameString()) + + "to specify the tables names"); + registerWrapper(viewKeyStruct::imbibitionWettingNonWettingCapPresTableNameString(), + &m_imbibitionWettingNonWettingCapPresTableName). + setInputFlag(InputFlags::OPTIONAL). + setDescription("Name of the drainage two-phase table for capillary pressure curve. \n" + "If you want to use 3-phase flow please use instead " + + string(viewKeyStruct::imbibitionWettingIntermediateCapPresTableNameString()) + + " and " + + string(viewKeyStruct::imbibitionNonWettingIntermediateCapPresTableNameString()) + + "to specify the tables names"); + //3phase + registerWrapper(viewKeyStruct::drainageWettingIntermediateCapPresTableNameString(), + &m_drainageWettingIntermediateCapPresTableName). + setInputFlag(InputFlags::OPTIONAL). + setDescription( + "Drainage wetting/intermediate (e.g. w/o) capillary pressure table name for the wetting phase.\n" + "To neglect hysteresis on this phase, just use the same table name for the drainage and imbibition curves"); + registerWrapper(viewKeyStruct::drainageNonWettingIntermediateCapPresTableNameString(), + &m_drainageNonWettingIntermediateCapPresTableName). + setInputFlag(InputFlags::OPTIONAL). + setDescription( + "Drainage non-wetting/intermediate (e.g. o/g) capillary pressure table name for the non-wetting phase.\n" + "To neglect hysteresis on this phase, just use the same table name for the drainage and imbibition curves"); + registerWrapper(viewKeyStruct::imbibitionWettingIntermediateCapPresTableNameString(), + &m_imbibitionWettingIntermediateCapPresTableName). + setInputFlag(InputFlags::OPTIONAL). + setDescription("Imbibition wetting/intermediate (e.g. w/o) table name for the wetting phase.\n" + "To neglect hysteresis on this phase, just use the same table name for the drainage and imbibition curves"); + registerWrapper(viewKeyStruct::imbibitionNonWettingIntermediateCapPresTableNameString(), + &m_imbibitionNonWettingIntermediateCapPresTableName). + setInputFlag(InputFlags::OPTIONAL). + setDescription("Imbibition non-wetting/intermediate (e.g. o/g) table name for the wetting phase.\n" + "To neglect hysteresis on this phase, just use the same table name for the drainage and imbibition curves"); + + // kernels + //2p + registerWrapper(viewKeyStruct::wettingNonWettingCapillaryPressureKernelWrappersString(), + &m_wettingNonWettingCapillaryPressureKernelWrappers) + .setSizedFromParent(0).setRestartFlags(RestartFlags::NO_WRITE); + //3p + registerWrapper(viewKeyStruct::wettingIntermediateCapillaryPressureKernelWrappersString(), + &m_wettingIntermediateCapillaryPressureKernelWrappers) + .setSizedFromParent(0).setRestartFlags(RestartFlags::NO_WRITE); + registerWrapper(viewKeyStruct::nonWettingIntermediateCapillaryPressureKernelWrappersString(), + &m_nonWettingIntermediateCapillaryPressureKernelWrappers) + .setSizedFromParent(0).setRestartFlags(RestartFlags::NO_WRITE); + + + registerWrapper(viewKeyStruct::wettingCurveString(), &m_wettingCurve). + setInputFlag( + InputFlags::FALSE). // will be deduced from tables + setSizedFromParent( + 0) + .setRestartFlags(RestartFlags::NO_WRITE); + + registerWrapper(viewKeyStruct::nonWettingCurveString(), &m_nonWettingCurve). + setInputFlag( + InputFlags::FALSE). // will be deduced from tables + setSizedFromParent( + 0) + .setRestartFlags(RestartFlags::NO_WRITE); + + //Forwarded to KilloughHysteresis + registerWrapper(KilloughHysteresis::viewKeyStruct::jerauldParameterAString(), &m_jerauldParam_a). + setInputFlag(InputFlags::OPTIONAL). + setApplyDefaultValue(0.1). + setDescription( + "First parameter (modification parameter) introduced by Jerauld in the Land trapping model (see RTD documentation)."); + + registerWrapper(KilloughHysteresis::viewKeyStruct::jerauldParameterBString(), &m_jerauldParam_b). + setInputFlag(InputFlags::OPTIONAL). + setApplyDefaultValue(0.0). + setDescription( + "Second parameter (modification parameter) introduced by Jerauld in the Land trapping model (see RTD documentation)."); + + + registerWrapper(KilloughHysteresis::viewKeyStruct::killoughCurvatureParameterPcString(), + &m_killoughCurvatureParamCapPres). + setInputFlag( + InputFlags::OPTIONAL). + setApplyDefaultValue( + .1). + setDescription( + "Curvature parameter introduced by Killough for wetting-phase hysteresis (see RTD documentation)."); + + //misc + registerWrapper(viewKeyStruct::phaseIntermediateMinVolFractionString(), &m_phaseIntermediateMinVolFraction). + setInputFlag(InputFlags::FALSE).setDescription("min vol fraction of intermediate if exist"). + // will be deduced from tables + setSizedFromParent(0); + + registerField< fields::cappres::mode >( &m_mode ); + + + registerField< fields::cappres::phaseMaxHistoricalVolFraction >( + &m_phaseMaxHistoricalVolFraction ); + registerField< fields::cappres::phaseMinHistoricalVolFraction >( + &m_phaseMinHistoricalVolFraction ); + + } + +/// usual utils + + void TableCapillaryPressureHysteresis::postProcessInput() { + + using TPP = ThreePhasePairPhaseType; + + integer const numPhases = m_phaseNames.size(); + GEOS_THROW_IF(numPhases != 2 && numPhases != 3, + GEOS_FMT("{}: the expected number of fluid phases is either two, or three", + getFullName()), + InputError); + + m_phaseHasHysteresis.resize(2); + + if (numPhases == 2) { + GEOS_THROW_IF(m_drainageWettingNonWettingCapPresTableName.empty(), + GEOS_FMT( + "{}: for a two-phase flow simulation, we must use {} to specify the capillary pressure table for the drainage pair (wetting phase, non-wetting phase)", + getFullName(), + viewKeyStruct::drainageWettingNonWettingCapPresTableNameString()), + InputError); + + + m_phaseHasHysteresis[TPP::INTERMEDIATE_WETTING] = (m_imbibitionWettingNonWettingCapPresTableName.empty() || + m_imbibitionWettingNonWettingCapPresTableName == + m_drainageWettingNonWettingCapPresTableName) + ? 0 : 1; + m_phaseHasHysteresis[TPP::INTERMEDIATE_NONWETTING] = m_phaseHasHysteresis[TPP::INTERMEDIATE_WETTING]; + + + } else if (numPhases == 3) { + + + GEOS_THROW_IF(m_drainageWettingIntermediateCapPresTableName.empty() || + m_drainageNonWettingIntermediateCapPresTableName.empty(), + GEOS_FMT( + "{}: for a three-phase flow simulation, we must use {} to specify the capillary pressure table " + "for the pair (wetting phase, intermediate phase), and {} to specify the capillary pressure table " + "for the pair (non-wetting phase, intermediate phase)", + getFullName(), + viewKeyStruct::drainageWettingIntermediateCapPresTableNameString(), + viewKeyStruct::drainageNonWettingIntermediateCapPresTableNameString()), + InputError); + + m_phaseHasHysteresis[TPP::INTERMEDIATE_WETTING] = (m_imbibitionWettingIntermediateCapPresTableName.empty() || + m_imbibitionWettingIntermediateCapPresTableName == + m_drainageWettingIntermediateCapPresTableName) + ? 0 : 1; + + m_phaseHasHysteresis[TPP::INTERMEDIATE_NONWETTING] = (m_imbibitionNonWettingIntermediateCapPresTableName.empty() || + m_imbibitionNonWettingIntermediateCapPresTableName == + m_drainageNonWettingIntermediateCapPresTableName) + ? 0 : 1; + } + //Killough section + //TODO improve hard coded default + KilloughHysteresis::postProcessInput(m_jerauldParam_a, m_jerauldParam_b, 0, + m_killoughCurvatureParamCapPres); + + GEOS_THROW_IF(m_phaseHasHysteresis[TPP::INTERMEDIATE_WETTING] == 0 && + m_phaseHasHysteresis[TPP::INTERMEDIATE_NONWETTING] == 0, + GEOS_FMT( + "{}: we must use {} (2-phase) / {} or {} (3-phase) to specify at least one imbibition relative permeability table", + getFullName(), + viewKeyStruct::imbibitionWettingNonWettingCapPresTableNameString(), + viewKeyStruct::imbibitionWettingIntermediateCapPresTableNameString(), + viewKeyStruct::imbibitionNonWettingIntermediateCapPresTableNameString()), + InputError); + + } + + void TableCapillaryPressureHysteresis::initializePreSubGroups() { + CapillaryPressureBase::initializePreSubGroups(); + + integer const numPhases = m_phaseNames.size(); + FunctionManager const &functionManager = FunctionManager::getInstance(); + + //equivalent to oil/gas - a.k.a two phase flow ordered by non wetting + bool const capPresMustBeIncreasing = (m_phaseOrder[PhaseType::WATER] < 0) + ? true // pc on the gas phase, function must be increasing + : false; // pc on the water phase, function must be decreasing + + + // Step 1: check sanity of drainage tables + if (numPhases == 2) { + + real64 drainageWettingPhaseMaxVolumeFraction, drainageWettingMinCapPres, + drainageNonWettingPhaseMinVolumeFraction, drainageNonWettingMinCapPres, + imbibitionWettingPhaseMaxVolumeFraction, imbibitionWettingMinCapPres, + imbibitionNonWettingPhaseMinVolumeFraction, imbibitionNonWettingMinCapPres, + wettingPhaseMinVolumeFraction, wettingMaxCapPres, + nonWettingPhaseMaxVolumeFraction, nonWettingMaxCapPres; + + { + + imbibitionNonWettingMinCapPres = 0.0; + + GEOS_THROW_IF(!functionManager.hasGroup(m_drainageWettingNonWettingCapPresTableName), + GEOS_FMT("{}: the table function named {} could not be found", + getFullName(), + m_drainageWettingNonWettingCapPresTableName), + InputError); + TableFunction const + &capPresTable = functionManager.getGroup( + m_drainageWettingNonWettingCapPresTableName); + + //w/o or w/g pair + if (!capPresMustBeIncreasing) { + TableCapillaryPressureHelpers::validateCapillaryPressureTable(capPresTable, getFullName(), + capPresMustBeIncreasing, + drainageWettingPhaseMaxVolumeFraction, + wettingPhaseMinVolumeFraction, + drainageWettingMinCapPres, + wettingMaxCapPres); + + drainageNonWettingPhaseMinVolumeFraction = 1. - drainageWettingPhaseMaxVolumeFraction; + nonWettingPhaseMaxVolumeFraction = 1. - wettingPhaseMinVolumeFraction; + + } else { // o/g pair + TableCapillaryPressureHelpers::validateCapillaryPressureTable(capPresTable, getFullName(), + capPresMustBeIncreasing, + nonWettingPhaseMaxVolumeFraction, + drainageNonWettingPhaseMinVolumeFraction, + nonWettingMaxCapPres, + drainageNonWettingMinCapPres ); + + drainageWettingPhaseMaxVolumeFraction = 1. - drainageNonWettingPhaseMinVolumeFraction; + wettingPhaseMinVolumeFraction = 1. - nonWettingPhaseMaxVolumeFraction; + } + + } + + { + GEOS_THROW_IF(!functionManager.hasGroup(m_imbibitionWettingNonWettingCapPresTableName), + GEOS_FMT("{}: the table function named {} could not be found", + getFullName(), + m_imbibitionWettingNonWettingCapPresTableName), + InputError); + TableFunction const + &capPresTable = functionManager.getGroup( + m_imbibitionWettingNonWettingCapPresTableName); + + //w/o or w/g pair + if (!capPresMustBeIncreasing) { + TableCapillaryPressureHelpers::validateCapillaryPressureTable(capPresTable, getFullName(), + capPresMustBeIncreasing, + imbibitionWettingPhaseMaxVolumeFraction, + wettingPhaseMinVolumeFraction, + imbibitionWettingMinCapPres, + wettingMaxCapPres); + + imbibitionNonWettingPhaseMinVolumeFraction = 1. - imbibitionWettingPhaseMaxVolumeFraction; + nonWettingPhaseMaxVolumeFraction = 1. - wettingPhaseMinVolumeFraction; + + } else { // o/g pair + TableCapillaryPressureHelpers::validateCapillaryPressureTable(capPresTable, getFullName(), + capPresMustBeIncreasing, + nonWettingPhaseMaxVolumeFraction, + imbibitionNonWettingPhaseMinVolumeFraction, + nonWettingMaxCapPres, + imbibitionWettingMinCapPres ); + + imbibitionWettingPhaseMaxVolumeFraction = 1. - imbibitionNonWettingPhaseMinVolumeFraction; + wettingPhaseMinVolumeFraction = 1. - nonWettingPhaseMaxVolumeFraction; + } + } + + //constructing wetting/nonwetting curves + + if(!capPresMustBeIncreasing) { + m_wettingCurve.setPoints( + {wettingPhaseMinVolumeFraction, wettingMaxCapPres}, // same as imbibition min + {imbibitionWettingPhaseMaxVolumeFraction, imbibitionWettingMinCapPres}, + {drainageWettingPhaseMaxVolumeFraction, drainageWettingMinCapPres}); + } + else { + m_nonWettingCurve.setPoints( + {nonWettingPhaseMaxVolumeFraction,nonWettingMaxCapPres}, + {imbibitionNonWettingPhaseMinVolumeFraction,imbibitionNonWettingMinCapPres}, + {drainageNonWettingPhaseMinVolumeFraction,drainageNonWettingMinCapPres} + ); + } + + } else if (numPhases == 3) { + + real64 drainageWettingPhaseMaxVolumeFraction, drainageWettingMinCapPres, + drainageNonWettingPhaseMinVolumeFraction, drainageNonWettingMinCapPres, + imbibitionWettingPhaseMaxVolumeFraction, imbibitionWettingMinCapPres, + imbibitionNonWettingPhaseMinVolumeFraction, imbibitionNonWettingMinCapPres, + wettingPhaseMinVolumeFraction, wettingMaxCapPres, + nonWettingPhaseMaxVolumeFraction, nonWettingMaxCapPres; + + GEOS_UNUSED_VAR( drainageWettingMinCapPres ); +//define scope to avoid differentiate temp var (lazy) + { + GEOS_THROW_IF(!functionManager.hasGroup(m_drainageWettingIntermediateCapPresTableName), + GEOS_FMT("{}: the table function named {} could not be found", + getFullName(), + m_drainageWettingIntermediateCapPresTableName), + InputError); + TableFunction const + &capPresTableWI = functionManager.getGroup( + m_drainageWettingIntermediateCapPresTableName); + TableCapillaryPressureHelpers::validateCapillaryPressureTable(capPresTableWI, getFullName(), false, + drainageWettingPhaseMaxVolumeFraction, + wettingPhaseMinVolumeFraction, + drainageNonWettingMinCapPres, + wettingMaxCapPres); + + GEOS_THROW_IF(!functionManager.hasGroup(m_drainageNonWettingIntermediateCapPresTableName), + GEOS_FMT("{}: the table function named {} could not be found", + getFullName(), + m_drainageNonWettingIntermediateCapPresTableName), + InputError); + TableFunction const &capPresTableNWI = + functionManager.getGroup(m_drainageNonWettingIntermediateCapPresTableName); + TableCapillaryPressureHelpers::validateCapillaryPressureTable(capPresTableNWI, getFullName(), true, + nonWettingPhaseMaxVolumeFraction, + drainageNonWettingPhaseMinVolumeFraction, + nonWettingMaxCapPres, + drainageWettingPhaseMaxVolumeFraction + ); + + m_phaseIntermediateMinVolFraction = + 1.0 - drainageWettingPhaseMaxVolumeFraction - drainageWettingPhaseMaxVolumeFraction; + } + + if (!m_imbibitionWettingIntermediateCapPresTableName.empty()) { + + GEOS_THROW_IF(!functionManager.hasGroup(m_imbibitionWettingIntermediateCapPresTableName), + GEOS_FMT("{}: the table function named {} could not be found", + getFullName(), + m_imbibitionWettingIntermediateCapPresTableName), + InputError); + TableFunction const + &capPresTableWI = functionManager.getGroup( + m_imbibitionWettingIntermediateCapPresTableName); + TableCapillaryPressureHelpers::validateCapillaryPressureTable(capPresTableWI, getFullName(), false, + imbibitionWettingPhaseMaxVolumeFraction, + wettingPhaseMinVolumeFraction, + imbibitionWettingMinCapPres, + wettingMaxCapPres + ); + + + } + + if (!m_imbibitionNonWettingIntermediateCapPresTableName.empty()) { + + GEOS_THROW_IF(!functionManager.hasGroup(m_imbibitionNonWettingIntermediateCapPresTableName), + GEOS_FMT("{}: the table function named {} could not be found", + getFullName(), + m_imbibitionNonWettingIntermediateCapPresTableName), + InputError); + TableFunction const &capPresTableNWI = + functionManager.getGroup(m_imbibitionNonWettingIntermediateCapPresTableName); + TableCapillaryPressureHelpers::validateCapillaryPressureTable(capPresTableNWI, getFullName(), true, + nonWettingPhaseMaxVolumeFraction, + imbibitionNonWettingPhaseMinVolumeFraction, + nonWettingMaxCapPres, + imbibitionNonWettingMinCapPres); + + + } + } + + // Step 2: check the sanity btw drainage and imbibition + auto const eps = 1e-15; + if (numPhases == 2) { + //TODO weak make stronger + GEOS_THROW_IF( + m_wettingCurve.isZero() && m_nonWettingCurve.isZero(), + GEOS_FMT( + "{}: Inconsistent data for capillary pressure hysteresis. No hysteresis curve is defined.", + getFullName()), + InputError); + + GEOS_THROW_IF( + !m_wettingCurve.isZero() && !m_nonWettingCurve.isZero(), + GEOS_FMT( + "{}: Inconsistent data for capillary pressure hysteresis. Both non wetting and wetting hysteresis curve are defined in two phase flow setting.", + getFullName()), + InputError); + + + } else if (numPhases == 3) { + + GEOS_THROW_IF(std::fabs(m_wettingCurve.oppositeBoundPhaseVolFraction - (1. - m_nonWettingCurve.oppositeBoundPhaseVolFraction - m_phaseIntermediateMinVolFraction)) > eps, + GEOS_FMT( + "{}: Inconsistent data for capillary pressure hysteresis. {}, {} and {} should sum up to 1.", + getFullName(), "Sw_min", "Snw_max", "Sinter_min"), + InputError); + GEOS_THROW_IF(std::fabs(m_wettingCurve.drainageExtremaPhaseVolFraction - (1. - m_nonWettingCurve.drainageExtremaPhaseVolFraction - m_phaseIntermediateMinVolFraction)) > eps, + GEOS_FMT( + "{}: Inconsistent data for capillary pressure hysteresis. {}, {} and {} should sum up to 1.", + getFullName(), "Sw_min", "Snw_max", "Sinter_min"), + InputError); + GEOS_THROW_IF(std::fabs(m_wettingCurve.imbibitionExtremaPhaseVolFraction - (1. - m_nonWettingCurve.imbibitionExtremaPhaseVolFraction - m_phaseIntermediateMinVolFraction)) > eps, + GEOS_FMT( + "{}: Inconsistent data for capillary pressure hysteresis. {}, {} and {} should sum up to 1.", + getFullName(), "Sw_min", "Snw_max", "Sinter_min"), + InputError); + + } + + + // Step 3: compute the Land coefficient + computeLandCoefficient(); + + // Step 4: Ensure arrays are properly resized if they weren't resized earlier + // This can happen if resizeFields() was called before phase names were set + if (m_phaseMaxHistoricalVolFraction.size(1) == 0 && numPhases > 0) { + localIndex const currentSize = m_phaseMaxHistoricalVolFraction.size(0); + if (currentSize > 0) { + m_phaseMaxHistoricalVolFraction.resize(currentSize, numPhases); + m_phaseMinHistoricalVolFraction.resize(currentSize, numPhases); + m_phaseMaxHistoricalVolFraction.setValues >(0.0); + m_phaseMinHistoricalVolFraction.setValues >(1.0); + } + } + } + +/// Land coeff (tb refactored out in KilloughHysteresis) and saved cvgd + + void TableCapillaryPressureHysteresis::computeLandCoefficient() { + // For now, we keep two separate Land parameters for the wetting and non-wetting phases + // For two-phase flow, we make sure that they are equal + m_landParam.resize(2); + + // Note: for simplicity, the notations are taken from IX documentation (although this breaks our phaseVolFrac naming convention) + + // Step 1: Land parameter for the wetting phase + + integer ipWetting, ipNonWetting; + std::tie(ipWetting, ipNonWetting) = phaseIndex(m_phaseOrder); + + KilloughHysteresis::computeLandCoefficient( m_wettingCurve, m_landParam[ipWetting] ); + KilloughHysteresis::computeLandCoefficient( m_nonWettingCurve, m_landParam[ipNonWetting] ); + + } + +/// common utils + void TableCapillaryPressureHysteresis::resizeFields(localIndex const size, localIndex const numPts) { + CapillaryPressureBase::resizeFields(size, numPts); + + integer const numPhases = numFluidPhases(); + + // If phase names haven't been set yet, we still need to resize m_mode + // The phase arrays will be resized properly once phase names are set + m_mode.resize(size); + + if (numPhases > 0) { + m_phaseMaxHistoricalVolFraction.resize(size, numPhases); + m_phaseMinHistoricalVolFraction.resize(size, numPhases); + m_phaseMaxHistoricalVolFraction.setValues >(0.0); + m_phaseMinHistoricalVolFraction.setValues >(1.0); + } + // If numPhases == 0, the arrays will remain uninitialized until resizeFields is called again + // after phase names are set. This should happen automatically during initialization. + } + + void TableCapillaryPressureHysteresis::saveConvergedPhaseVolFractionState( + arrayView2d const &phaseVolFraction) const { + CapillaryPressureBase::saveConvergedState(); + + arrayView2d phaseMaxHistoricalVolFraction = m_phaseMaxHistoricalVolFraction.toView(); + arrayView2d phaseMinHistoricalVolFraction = m_phaseMinHistoricalVolFraction.toView(); + + localIndex const numElems = phaseVolFraction.size(0); + integer const numPhases = numFluidPhases(); + + forAll >(numElems, [=] GEOS_HOST_DEVICE(localIndex const ei) { + for (integer ip = 0; ip < numPhases; ++ip) { + phaseMaxHistoricalVolFraction[ei][ip] = LvArray::math::max(phaseVolFraction[ei][ip], + phaseMaxHistoricalVolFraction[ei][ip]); + phaseMinHistoricalVolFraction[ei][ip] = LvArray::math::min(phaseVolFraction[ei][ip], + phaseMinHistoricalVolFraction[ei][ip]); + } + }); + + } + + void + TableCapillaryPressureHysteresis::KernelWrapper::computeImbibitionWettingCapillaryPressure( + const arrayView1d &wettingKernelWapper, + const KilloughHysteresis::HysteresisCurve &wettingCurve, + const KilloughHysteresis::HysteresisCurve &nonWettingCurve, //discard if not needed + const geos::real64 &landParam, + const geos::real64 &phaseVolFraction, + const geos::real64 &phaseMinHistoricalVolFraction, + geos::real64 &phaseTrappedVolFrac, + geos::real64 &phaseCapPressure, + geos::real64 &dPhaseCapPressure_dPhaseVolFrac, + const ModeIndexType &mode) const { + GEOS_ASSERT(wettingCurve.isWetting()); + real64 const S = phaseVolFraction; + real64 const Smxi = wettingCurve.imbibitionExtremaPhaseVolFraction; + real64 const Smxd = wettingCurve.drainageExtremaPhaseVolFraction; + real64 const Smin = wettingCurve.oppositeBoundPhaseVolFraction; + + GEOS_UNUSED_VAR( Smxi, Smxd, phaseTrappedVolFrac ); + +// if( S <= Smin ) +// { +// //below accessible range +// phaseCapPressure = CAP_INF; +// dPhaseCapPressure_dPhaseVolFrac = -CAP_INF_DERIV; +// } +// else if( S >= Smxd ) +// { +// //above accessible range +// phaseCapPressure = -CAP_INF; +// dPhaseCapPressure_dPhaseVolFrac = -CAP_INF_DERIV; +// } +// else + { + //drainage to imbibition + real64 dpci_dS, dpcd_dS; + real64 const pci = wettingKernelWapper[ModeIndexType::IMBIBITION].compute(&S, &dpci_dS); + real64 const pcd = wettingKernelWapper[ModeIndexType::DRAINAGE].compute(&S, &dpcd_dS); + real64 const Somin = m_phaseIntermediateMinVolFraction; + + // Step 1: get the trapped from wetting data + real64 const Shy = (phaseMinHistoricalVolFraction > Smin) ? phaseMinHistoricalVolFraction : Smin; + + real64 const E = m_killoughCurvatureParamCapPres; + + //Step 2. compute F as in (EQ 34.15) F = (1/(Sw-Shy+E)-1/E) / (1/(Swma-Shy+E)-1/E) + //drainage to imbibition branch + if (mode == ModeIndexType::DRAINAGE_TO_IMBIBITION) { + + real64 Scrt = 0.0; + KilloughHysteresis::computeTrappedCriticalPhaseVolFraction(nonWettingCurve, + Shy, + landParam, + m_jerauldParam_a, + m_jerauldParam_b, + Scrt); + real64 const Swma = 1 - Scrt - Somin; + real64 F = (1. / (S - Shy + E) - 1. / E) / (1. / (Swma - Shy + E) - 1. / E); + //force bound + F = LvArray::math::max(F, 0.0); + F = LvArray::math::min(F, 1.0); + + //Step 3. Eventually assemble everything following (EQ. 34.14) + phaseCapPressure = pcd + F * (pci - pcd); + dPhaseCapPressure_dPhaseVolFrac = dpcd_dS + F * (dpci_dS - dpcd_dS); + } + //imbibition to drainage + else if (mode == ModeIndexType::IMBIBITION_TO_DRAINAGE) { + real64 Scrt = 0.0; + KilloughHysteresis::computeTrappedCriticalPhaseVolFraction(wettingCurve, + Shy, + landParam, + m_jerauldParam_a, + m_jerauldParam_b, + Scrt); + + real64 F = (1. / (Shy - S + E) - 1. / E) / (1. / (Shy - Scrt + E) - 1. / E); + //force bound + F = LvArray::math::max(F, 0.0); + F = LvArray::math::min(F, 1.0); + + //Step 3. Eventually assemble everything following (EQ. 34.14) + phaseCapPressure = pci + F * (pcd - pci); + dPhaseCapPressure_dPhaseVolFrac = dpci_dS + F * (dpcd_dS - dpci_dS); + + + } else { + GEOS_THROW(GEOS_FMT("{}: State is {}.Shouldnt be used in pure DRAINAGE or IMBIBITION.", + "TableCapillaryPressureHysteresis", + (mode == ModeIndexType::DRAINAGE) ? "DRAINAGE" : ((mode == + ModeIndexType::IMBIBITION) + ? "IMBIBITION" + : "UNKNOWN")), + InputError); + } + + + } + + } + + void + TableCapillaryPressureHysteresis::KernelWrapper::computeImbibitionWettingCapillaryPressure( + const arrayView1d &wettingKernelWapper, + const KilloughHysteresis::HysteresisCurve &wettingCurve, + const geos::real64 &landParam, + const geos::real64 &phaseVolFraction, + const geos::real64 &phaseMinHistoricalVolFraction, + geos::real64 &phaseTrappedVolFrac, + geos::real64 &phaseCapPressure, + geos::real64 &dPhaseCapPressure_dPhaseVolFrac, + const ModeIndexType &mode) const { + GEOS_ASSERT(wettingCurve.isWetting()); + real64 const S = phaseVolFraction; + real64 const Smxi = wettingCurve.imbibitionExtremaPhaseVolFraction; + real64 const Smxd = wettingCurve.drainageExtremaPhaseVolFraction; + real64 const Smin = wettingCurve.oppositeBoundPhaseVolFraction; + + GEOS_UNUSED_VAR( Smxi, Smxd, phaseTrappedVolFrac ); + +// if( S <= Smin ) +// { +// //below accessible range +// phaseCapPressure = CAP_INF; +// dPhaseCapPressure_dPhaseVolFrac = -CAP_INF_DERIV; +// } +// else if( S >= Smxd ) +// { +// //above accessible range +// phaseCapPressure = -CAP_INF; +// dPhaseCapPressure_dPhaseVolFrac = -CAP_INF_DERIV; +// } +// else + { + //drainage to imbibition + real64 dpci_dS, dpcd_dS; + real64 const pci = wettingKernelWapper[ModeIndexType::IMBIBITION].compute(&S, &dpci_dS); + real64 const pcd = wettingKernelWapper[ModeIndexType::DRAINAGE].compute(&S, &dpcd_dS); + + // Step 1: get the trapped from wetting data + real64 const Shy = (phaseMinHistoricalVolFraction > Smin) ? phaseMinHistoricalVolFraction : Smin; + + real64 const E = m_killoughCurvatureParamCapPres; + + //Step 2. compute F as in (EQ 34.15) F = (1/(Sw-Shy+E)-1/E) / (1/(Swma-Shy+E)-1/E) + //drainage to imbibition branch + if (mode == ModeIndexType::DRAINAGE_TO_IMBIBITION) { + + real64 Scrt = 0.0; + KilloughHysteresis::computeTrappedCriticalPhaseVolFraction(wettingCurve, + Shy, + landParam, + m_jerauldParam_a, + m_jerauldParam_b, + Scrt); + + + + //should be the pore space accessible to the two wetting phase + real64 const Swma = 1 - (1 - Scrt); + real64 F = (1. / (S - Shy + E) - 1. / E) / (1. / (Swma - Shy + E) - 1. / E); + //force bound + F = LvArray::math::max(F, 0.0); + F = LvArray::math::min(F, 1.0); + + //Step 3. Eventually assemble everything following (EQ. 34.14) + phaseCapPressure = pcd + F * (pci - pcd); + dPhaseCapPressure_dPhaseVolFrac = dpcd_dS + F * (dpci_dS - dpcd_dS); + } + //imbibition to drainage + else if (mode == ModeIndexType::IMBIBITION_TO_DRAINAGE) { + real64 Scrt = 0.0; + KilloughHysteresis::computeTrappedCriticalPhaseVolFraction(wettingCurve, + Shy, + landParam, + m_jerauldParam_a, + m_jerauldParam_b, + Scrt); + + real64 F = (1. / (Shy - S + E) - 1. / E) / (1. / (Shy - Scrt + E) - 1. / E); + //force bound + F = LvArray::math::max(F, 0.0); + F = LvArray::math::min(F, 1.0); + + //Step 3. Eventually assemble everything following (EQ. 34.14) + phaseCapPressure = pci + F * (pcd - pci); + dPhaseCapPressure_dPhaseVolFrac = dpci_dS + F * (dpcd_dS - dpci_dS); + + + } else { + GEOS_THROW(GEOS_FMT("{}: State is {}.Shouldnt be used in pure DRAINAGE or IMBIBITION.", + "TableCapillaryPressureHysteresis", + (mode == ModeIndexType::DRAINAGE) ? "DRAINAGE" : ((mode == + ModeIndexType::IMBIBITION) + ? "IMBIBITION" + : "UNKNOWN")), + InputError); + } + + + } + + } + void TableCapillaryPressureHysteresis::KernelWrapper::computeTwoPhaseWetting(const geos::integer ipWetting, + const geos::integer GEOS_UNUSED_PARAM( ipNonWetting ), + const arraySlice1d &phaseVolFraction, + const arraySlice1d &phaseMaxHistoricalVolFraction, + const arraySlice1d &phaseMinHistoricalVolFraction, + const arraySlice1d &phaseTrappedVolFrac, + arraySlice1d const &phaseCapPressure, + arraySlice2d const &dPhaseCapPressure_dPhaseVolFrac, + ModeIndexType &mode) const { + using TTP = ThreePhasePairPhaseType; + + // Validate array sizes and indices before accessing + GEOS_ASSERT_MSG(ipWetting >= 0, "ipWetting must be non-negative"); + GEOS_ASSERT_MSG(static_cast(phaseVolFraction.size()) > ipWetting, + GEOS_FMT("phaseVolFraction array too small: size={}, ipWetting={}. " + "This usually means the arrays haven't been properly resized. " + "Ensure resizeFields() has been called before using the KernelWrapper.", + phaseVolFraction.size(), ipWetting)); + GEOS_ASSERT_MSG(static_cast(phaseMaxHistoricalVolFraction.size()) > ipWetting, + GEOS_FMT("phaseMaxHistoricalVolFraction array too small: size={}, ipWetting={}. " + "This usually means the arrays haven't been properly resized. " + "Ensure resizeFields() has been called before using the KernelWrapper.", + phaseMaxHistoricalVolFraction.size(), ipWetting)); + GEOS_ASSERT_MSG(static_cast(phaseMinHistoricalVolFraction.size()) > ipWetting, + GEOS_FMT("phaseMinHistoricalVolFraction array too small: size={}, ipWetting={}. " + "This usually means the arrays haven't been properly resized. " + "Ensure resizeFields() has been called before using the KernelWrapper.", + phaseMinHistoricalVolFraction.size(), ipWetting)); + GEOS_ASSERT_MSG(static_cast(m_wettingNonWettingCapillaryPressureKernelWrappers.size()) >= 2, + GEOS_FMT("m_wettingNonWettingCapillaryPressureKernelWrappers must have at least 2 elements, but got {}. " + "This usually means createAllTableKernelWrappers() failed to populate the arrays.", + m_wettingNonWettingCapillaryPressureKernelWrappers.size())); + + // Determine mode based on saturation condition (matching relative permeability logic) + // Use DRAINAGE when saturation is at or below minimum, use scanning curves when above minimum + // This matches the relative permeability logic: drainage when S <= S_min, imbibition (scanning) when S > S_min + bool const useDrainage = !m_phaseHasHysteresis[TTP::INTERMEDIATE_WETTING] || + phaseVolFraction[ipWetting] <= phaseMinHistoricalVolFraction[ipWetting] + flowReversalBuffer; + + //--- wetting cap pressure -- W/O or W/G two phase flow + // Use drainage curve when S <= S_min (matching relative permeability logic) + // Use scanning curves when S > S_min (matching relative permeability logic) + // DEBUG: Print mode for capillary pressure + // printf("CapPressure: mode=%d, S_w=%.6e, S_min=%.6e, S_max=%.6e, hasHyst=%d\n", + // static_cast(mode), + // phaseVolFraction[ipWetting], + // phaseMinHistoricalVolFraction[ipWetting], + // phaseMaxHistoricalVolFraction[ipWetting], + // static_cast(m_phaseHasHysteresis[TTP::INTERMEDIATE_WETTING])); + + if (useDrainage) { + // Use simple drainage curve (matching relative permeability) + mode = ModeIndexType::DRAINAGE; + phaseTrappedVolFrac[ipWetting] = LvArray::math::min(phaseVolFraction[ipWetting], + m_wettingCurve.oppositeBoundPhaseVolFraction); + // printf("CapPressure: Using DRAINAGE curve (arrayIndex=0)\n"); + GEOS_ASSERT_MSG(static_cast(m_wettingNonWettingCapillaryPressureKernelWrappers.size()) > ModeIndexType::DRAINAGE, + "Invalid array index for kernel wrapper access"); + computeBoundCapillaryPressure( + m_wettingNonWettingCapillaryPressureKernelWrappers[ModeIndexType::DRAINAGE], + phaseVolFraction[ipWetting], + phaseCapPressure[ipWetting], + dPhaseCapPressure_dPhaseVolFrac[ipWetting][ipWetting]); + } + else { + // Use scanning curves (matching relative permeability) + // When S > S_min, saturation is increasing (moving away from minimum), + // so we're transitioning from drainage to imbibition -> use DRAINAGE_TO_IMBIBITION mode + mode = ModeIndexType::DRAINAGE_TO_IMBIBITION; + // printf("CapPressure: Using IMBIBITION (scanning) curves (mode=%d)\n", static_cast(mode)); + computeImbibitionWettingCapillaryPressure(m_wettingNonWettingCapillaryPressureKernelWrappers, + m_wettingCurve, + m_landParam[ipWetting], + phaseVolFraction[ipWetting], + phaseMinHistoricalVolFraction[ipWetting], + phaseTrappedVolFrac[ipWetting], + phaseCapPressure[ipWetting], + dPhaseCapPressure_dPhaseVolFrac[ipWetting][ipWetting], + mode); + } + +// trapped vol fraction + if (mode == ModeIndexType::DRAINAGE || mode == ModeIndexType::DRAINAGE_TO_IMBIBITION) { + + + + real64 const Smin = m_wettingCurve.oppositeBoundPhaseVolFraction; + real64 const Shy = (phaseMinHistoricalVolFraction[ipWetting] < Smin) + ? phaseMinHistoricalVolFraction[ipWetting] + : Smin; + real64 Scrt = 0.0; + KilloughHysteresis::computeTrappedCriticalPhaseVolFraction(m_wettingCurve, Shy, + m_landParam[ipWetting], + m_jerauldParam_a, + m_jerauldParam_b, + Scrt); + phaseTrappedVolFrac[ipWetting] = LvArray::math::min(Scrt, phaseVolFraction[ipWetting]); + + + //keep the same Land coeff as two phase only + KilloughHysteresis::computeTrappedCriticalPhaseVolFraction(m_wettingCurve.toNonWetting(), Shy, + m_landParam[ipWetting], + m_jerauldParam_a, + m_jerauldParam_b, + Scrt); + phaseTrappedVolFrac[ipWetting] = LvArray::math::min(Scrt, phaseVolFraction[ipWetting]); + + + + + } + else if (mode == ModeIndexType::IMBIBITION || mode == ModeIndexType::IMBIBITION_TO_DRAINAGE) { + + real64 const Smax = m_wettingCurve.imbibitionExtremaPhaseVolFraction; + real64 const Shy = (phaseMaxHistoricalVolFraction[ipWetting] < Smax) + ? phaseMaxHistoricalVolFraction[ipWetting] + : Smax; + real64 Scrt = 0.0; + //TODO (jacques) check if still accurate + KilloughHysteresis::computeTrappedCriticalPhaseVolFraction(m_wettingCurve, + Shy, + m_landParam[ipWetting], + m_jerauldParam_a, + m_jerauldParam_b, + Scrt); + + phaseTrappedVolFrac[ipWetting] = LvArray::math::min(Scrt, phaseVolFraction[ipWetting]); + + //keep the same Land coeff as two phase only + KilloughHysteresis::computeTrappedCriticalPhaseVolFraction(m_wettingCurve.toNonWetting(), Shy, + m_landParam[ipWetting], + m_jerauldParam_a, + m_jerauldParam_b, + Scrt); + phaseTrappedVolFrac[ipWetting] = LvArray::math::min(Scrt, phaseVolFraction[ipWetting]); + + } + + } + + void TableCapillaryPressureHysteresis::KernelWrapper::computeTwoPhaseNonWetting(const geos::integer ipWetting, + const geos::integer ipNonWetting, + const arraySlice1d &phaseVolFraction, + const arraySlice1d &phaseMaxHistoricalVolFraction, + const arraySlice1d &phaseMinHistoricalVolFraction, + const arraySlice1d &phaseTrappedVolFrac, + arraySlice1d const &phaseCapPressure, + arraySlice2d const &dPhaseCapPressure_dPhaseVolFrac, + ModeIndexType &mode) const { + using TTP = ThreePhasePairPhaseType; + //update state + // TODO check if we can get rid of DRAINAGE_TO_IMBIBITION && IMBIBITION_TO_DRAINAGE + if (mode == ModeIndexType::DRAINAGE_TO_IMBIBITION && + phaseVolFraction[ipNonWetting] >= phaseMaxHistoricalVolFraction[ipNonWetting] + flowReversalBuffer) + mode = ModeIndexType::DRAINAGE; + if (mode == ModeIndexType::IMBIBITION_TO_DRAINAGE && + phaseVolFraction[ipWetting] <= phaseMinHistoricalVolFraction[ipNonWetting] + flowReversalBuffer) + mode = ModeIndexType::IMBIBITION; + + // Update mode based on flow direction if we're in a pure state + // For non-wetting phase: if saturation is increasing (imbibition), mode should be IMBIBITION + // If saturation is decreasing (drainage), mode should be DRAINAGE + if (mode == ModeIndexType::DRAINAGE || mode == ModeIndexType::IMBIBITION) { + // If current saturation is significantly above min historical, we're in imbibition + if (phaseVolFraction[ipNonWetting] > phaseMinHistoricalVolFraction[ipNonWetting] + flowReversalBuffer) { + if (mode == ModeIndexType::DRAINAGE) { + mode = ModeIndexType::DRAINAGE_TO_IMBIBITION; + } + } + // If current saturation is significantly below max historical, we're in drainage + else if (phaseVolFraction[ipNonWetting] < phaseMaxHistoricalVolFraction[ipNonWetting] - flowReversalBuffer) { + if (mode == ModeIndexType::IMBIBITION) { + mode = ModeIndexType::IMBIBITION_TO_DRAINAGE; + } + } + } + + // Use simple drainage/imbibition curves when: + // 1. No hysteresis enabled, OR + // 2. We're in pure DRAINAGE mode (use drainage curve), OR + // 3. We're in pure IMBIBITION mode (use imbibition curve) + // Use scanning curves only during transitions (DRAINAGE_TO_IMBIBITION or IMBIBITION_TO_DRAINAGE) + if (!m_phaseHasHysteresis[TTP::INTERMEDIATE_NONWETTING] || + mode == ModeIndexType::DRAINAGE || + mode == ModeIndexType::IMBIBITION) { + phaseTrappedVolFrac[ipNonWetting] = LvArray::math::min(phaseVolFraction[ipNonWetting], + (mode == ModeIndexType::DRAINAGE) + ? m_nonWettingCurve.drainageExtremaPhaseVolFraction + : m_nonWettingCurve.imbibitionExtremaPhaseVolFraction); + // Ensure mode is a valid array index (0 or 1) + integer const arrayIndex = (mode == ModeIndexType::DRAINAGE) ? ModeIndexType::DRAINAGE : ModeIndexType::IMBIBITION; + GEOS_ASSERT_MSG(arrayIndex >= 0 && arrayIndex < static_cast(m_wettingNonWettingCapillaryPressureKernelWrappers.size()), + "Invalid array index for kernel wrapper access"); + computeBoundCapillaryPressure( + m_wettingNonWettingCapillaryPressureKernelWrappers[arrayIndex], + phaseVolFraction[ipNonWetting], + phaseCapPressure[ipNonWetting], + dPhaseCapPressure_dPhaseVolFrac[ipNonWetting][ipNonWetting]); + // when pc is on the gas phase, we need to multiply user input by -1 + // because CompositionalMultiphaseFVM does: pres_gas = pres_oil - pc_og, so we need a negative pc_og + phaseCapPressure[ipNonWetting] *= -1; + dPhaseCapPressure_dPhaseVolFrac[ipNonWetting][ipNonWetting] *= -1; + + } else { + // We're in a transition state (DRAINAGE_TO_IMBIBITION or IMBIBITION_TO_DRAINAGE) + // Use scanning curves + + computeImbibitionNonWettingCapillaryPressure(m_wettingNonWettingCapillaryPressureKernelWrappers, + m_nonWettingCurve, + m_landParam[ipNonWetting], + phaseVolFraction[ipNonWetting], + phaseMaxHistoricalVolFraction[ipNonWetting], + phaseTrappedVolFrac[ipNonWetting], + phaseCapPressure[ipNonWetting], + dPhaseCapPressure_dPhaseVolFrac[ipNonWetting][ipNonWetting], + mode); + + // when pc is on the gas phase, we need to multiply user input by -1 + // because CompositionalMultiphaseFVM does: pres_gas = pres_oil - pc_og, so we need a negative pc_og + phaseCapPressure[ipNonWetting] *= -1; + dPhaseCapPressure_dPhaseVolFrac[ipNonWetting][ipNonWetting] *= -1; + } + +// trapped vol fraction + if (mode == ModeIndexType::DRAINAGE || mode == ModeIndexType::DRAINAGE_TO_IMBIBITION) { + + { + real64 const Smax = m_nonWettingCurve.oppositeBoundPhaseVolFraction; + real64 const Shy = (phaseMaxHistoricalVolFraction[ipNonWetting] > Smax) + ? phaseMaxHistoricalVolFraction[ipNonWetting] + : Smax; + real64 Scrt = 0.0; + KilloughHysteresis::computeTrappedCriticalPhaseVolFraction(m_nonWettingCurve, Shy, + m_landParam[ipNonWetting], + m_jerauldParam_a, m_jerauldParam_b, + Scrt); + phaseTrappedVolFrac[ipNonWetting] = LvArray::math::min(Scrt, phaseVolFraction[ipNonWetting]); + + //keep the same Land coeff as two phase only + KilloughHysteresis::computeTrappedCriticalPhaseVolFraction(m_nonWettingCurve.toWetting(), Shy, + m_landParam[ipNonWetting], + m_jerauldParam_a, m_jerauldParam_b, + Scrt); + phaseTrappedVolFrac[ipNonWetting] = LvArray::math::min(Scrt, phaseVolFraction[ipNonWetting]); + } + } else if (mode == ModeIndexType::IMBIBITION || mode == ModeIndexType::IMBIBITION_TO_DRAINAGE) { + + { + real64 const Smin = m_nonWettingCurve.imbibitionExtremaPhaseVolFraction;; + real64 const Shy = (phaseMinHistoricalVolFraction[ipNonWetting] > Smin) + ? phaseMinHistoricalVolFraction[ipNonWetting] + : Smin; + real64 Scrt = 0.0; + KilloughHysteresis::computeTrappedCriticalPhaseVolFraction(m_nonWettingCurve, Shy, + m_landParam[ipNonWetting], + m_jerauldParam_a, m_jerauldParam_b, + Scrt); + phaseTrappedVolFrac[ipNonWetting] = LvArray::math::min(Scrt, phaseVolFraction[ipNonWetting]); + + //keep the same Land coeff as two phase only + KilloughHysteresis::computeTrappedCriticalPhaseVolFraction(m_nonWettingCurve.toWetting(), Shy, + m_landParam[ipNonWetting], + m_jerauldParam_a, m_jerauldParam_b, + Scrt); + phaseTrappedVolFrac[ipNonWetting] = LvArray::math::min(Scrt, phaseVolFraction[ipNonWetting]); + } + } + + + } + + void TableCapillaryPressureHysteresis::KernelWrapper::computeThreePhase(const geos::integer ipWetting, + const geos::integer GEOS_UNUSED_PARAM( ipInter ), + const geos::integer ipNonWetting, + const arraySlice1d &phaseVolFraction, + const arraySlice1d &phaseMaxHistoricalVolFraction, + const arraySlice1d &phaseMinHistoricalVolFraction, + const arraySlice1d &phaseTrappedVolFrac, + const arraySlice1d &phaseCapPressure, + const arraySlice2d &dPhaseCapPressure_dPhaseVolFrac, + ModeIndexType &mode) const { + + + LvArray::forValuesInSlice(dPhaseCapPressure_dPhaseVolFrac, [](real64 &val) { val = 0.0; }); + using TTP = ThreePhasePairPhaseType; + + // -- wetting curve if drainage only + if (!m_phaseHasHysteresis[TTP::INTERMEDIATE_WETTING] || + (mode == ModeIndexType::DRAINAGE && + phaseVolFraction[ipWetting] <= phaseMinHistoricalVolFraction[ipWetting] + flowReversalBuffer) || + (mode == ModeIndexType::IMBIBITION && + phaseVolFraction[ipWetting] >= phaseMaxHistoricalVolFraction[ipWetting] + flowReversalBuffer)) { + // water-oil capillary pressure + phaseTrappedVolFrac[ipWetting] = LvArray::math::min(phaseVolFraction[ipWetting], + m_wettingCurve.oppositeBoundPhaseVolFraction); + phaseCapPressure[ipWetting] = + m_wettingIntermediateCapillaryPressureKernelWrappers[mode].compute( + &(phaseVolFraction)[ipWetting], + &(dPhaseCapPressure_dPhaseVolFrac)[ipWetting][ipWetting]); + } else { + mode = (mode == ModeIndexType::DRAINAGE) ? ModeIndexType::DRAINAGE_TO_IMBIBITION + : ModeIndexType::IMBIBITION_TO_DRAINAGE; + computeImbibitionWettingCapillaryPressure(m_wettingIntermediateCapillaryPressureKernelWrappers, + m_wettingCurve, + m_nonWettingCurve, + m_landParam[ipWetting], + phaseVolFraction[ipWetting], + phaseMinHistoricalVolFraction[ipWetting], + phaseTrappedVolFrac[ipWetting], + phaseCapPressure[ipWetting], + dPhaseCapPressure_dPhaseVolFrac[ipWetting][ipWetting], + mode); + + + } + + + // -- non-wetting cure if drainage only + // gas-oil capillary pressure + if (!m_phaseHasHysteresis[TTP::INTERMEDIATE_NONWETTING] || + (mode == ModeIndexType::DRAINAGE && + phaseVolFraction[ipNonWetting] >= phaseMaxHistoricalVolFraction[ipNonWetting] + flowReversalBuffer) || + (mode == ModeIndexType::IMBIBITION && + phaseVolFraction[ipNonWetting] <= phaseMinHistoricalVolFraction[ipNonWetting] + flowReversalBuffer)) { + phaseTrappedVolFrac[ipNonWetting] = LvArray::math::min(phaseVolFraction[ipNonWetting], + (mode == ModeIndexType::DRAINAGE) + ? m_nonWettingCurve.drainageExtremaPhaseVolFraction + : m_nonWettingCurve.imbibitionExtremaPhaseVolFraction); + phaseCapPressure[ipNonWetting] = + m_nonWettingIntermediateCapillaryPressureKernelWrappers[mode].compute( + &(phaseVolFraction)[ipNonWetting], + &(dPhaseCapPressure_dPhaseVolFrac)[ipNonWetting][ipNonWetting]); + + + // when pc is on the gas phase, we need to multiply user input by -1 + // because CompositionalMultiphaseFVM does: pres_gas = pres_oil - pc_og, so we need a negative pc_og + phaseCapPressure[ipNonWetting] *= -1; + dPhaseCapPressure_dPhaseVolFrac[ipNonWetting][ipNonWetting] *= -1; + } else { + + mode = (mode == ModeIndexType::DRAINAGE) ? ModeIndexType::DRAINAGE_TO_IMBIBITION + : ModeIndexType::IMBIBITION_TO_DRAINAGE; + + computeImbibitionNonWettingCapillaryPressure(m_nonWettingIntermediateCapillaryPressureKernelWrappers, + m_nonWettingCurve, + m_wettingCurve, + m_landParam[ipNonWetting], + phaseVolFraction[ipNonWetting], + phaseMinHistoricalVolFraction[ipNonWetting], + phaseTrappedVolFrac[ipNonWetting], + phaseCapPressure[ipNonWetting], + dPhaseCapPressure_dPhaseVolFrac[ipNonWetting][ipNonWetting], + mode); + // when pc is on the gas phase, we need to multiply user input by -1 + // because CompositionalMultiphaseFVM does: pres_gas = pres_oil - pc_og, so we need a negative pc_og + phaseCapPressure[ipNonWetting] *= -1; + dPhaseCapPressure_dPhaseVolFrac[ipNonWetting][ipNonWetting] *= -1; + + //update trapped fraction + if (mode == ModeIndexType::DRAINAGE || mode == ModeIndexType::DRAINAGE_TO_IMBIBITION) { + + + { + real64 const Smin = m_wettingCurve.oppositeBoundPhaseVolFraction; + real64 const Shy = (phaseMinHistoricalVolFraction[ipWetting] < Smin) + ? phaseMinHistoricalVolFraction[ipWetting] + : Smin; + real64 Scrt = 0.0; + KilloughHysteresis::computeTrappedCriticalPhaseVolFraction(m_wettingCurve, Shy, + m_landParam[ipWetting], + m_jerauldParam_a, m_jerauldParam_b, + Scrt); + phaseTrappedVolFrac[ipWetting] = LvArray::math::min(Scrt, phaseVolFraction[ipWetting]); + } + + { + real64 const Smax = m_nonWettingCurve.oppositeBoundPhaseVolFraction; + real64 const Shy = (phaseMaxHistoricalVolFraction[ipNonWetting] > Smax) + ? phaseMaxHistoricalVolFraction[ipNonWetting] + : Smax; + real64 Scrt = 0.0; + KilloughHysteresis::computeTrappedCriticalPhaseVolFraction(m_nonWettingCurve, Shy, + m_landParam[ipNonWetting], + m_jerauldParam_a, m_jerauldParam_b, + Scrt); + phaseTrappedVolFrac[ipNonWetting] = LvArray::math::min(Scrt, phaseVolFraction[ipNonWetting]); + } + } else if (mode == ModeIndexType::IMBIBITION || mode == ModeIndexType::IMBIBITION_TO_DRAINAGE) { + { + real64 const Smax = m_wettingCurve.imbibitionExtremaPhaseVolFraction; + real64 const Shy = (phaseMaxHistoricalVolFraction[ipWetting] < Smax) + ? phaseMaxHistoricalVolFraction[ipWetting] + : Smax; + real64 Scrt = 0.0; + KilloughHysteresis::computeTrappedCriticalPhaseVolFraction(m_wettingCurve, Shy, + m_landParam[ipWetting], + m_jerauldParam_a, m_jerauldParam_b, + Scrt); + phaseTrappedVolFrac[ipWetting] = LvArray::math::min(Scrt, phaseVolFraction[ipWetting]); + } + + { + real64 const Smin = m_nonWettingCurve.imbibitionExtremaPhaseVolFraction;; + real64 const Shy = (phaseMinHistoricalVolFraction[ipNonWetting] > Smin) + ? phaseMinHistoricalVolFraction[ipNonWetting] + : Smin; + real64 Scrt = 0.0; + KilloughHysteresis::computeTrappedCriticalPhaseVolFraction(m_nonWettingCurve, Shy, + m_landParam[ipNonWetting], + m_jerauldParam_a, m_jerauldParam_b, + Scrt); + phaseTrappedVolFrac[ipNonWetting] = LvArray::math::min(Scrt, phaseVolFraction[ipNonWetting]); + } + } + + + } + + + } + + + void + TableCapillaryPressureHysteresis::KernelWrapper::computeImbibitionNonWettingCapillaryPressure( + const arrayView1d &nonWettingKernelWrapper, + const KilloughHysteresis::HysteresisCurve &nonWettingCurve, + const KilloughHysteresis::HysteresisCurve &wettingCurve, + const geos::real64 &landParam, + const geos::real64 &phaseVolFraction, + const geos::real64 &phaseMaxHistoricalVolFraction, + geos::real64 &phaseTrappedVolFrac, + geos::real64 &phaseCapPressure, + geos::real64 &dPhaseCapPressure_dPhaseVolFrac, + const ModeIndexType &mode) const { + GEOS_ASSERT(!nonWettingCurve.isWetting()); + real64 const S = phaseVolFraction; + real64 const Smii = nonWettingCurve.imbibitionExtremaPhaseVolFraction; + real64 const Smid = nonWettingCurve.drainageExtremaPhaseVolFraction; + real64 const Smax = nonWettingCurve.oppositeBoundPhaseVolFraction; + + GEOS_UNUSED_VAR( Smii, Smid ); + +// if( S >= Smax ) +// { +// //above accessible range +// phaseCapPressure = CAP_INF; +// dPhaseCapPressure_dPhaseVolFrac = CAP_INF_DERIV; +// } +// else if( S <= Smid ) +// { +// //below accessible range +// phaseCapPressure = -CAP_INF; +// dPhaseCapPressure_dPhaseVolFrac = CAP_INF_DERIV; +// } +// else + { + //drainage to imbibition + real64 dpci_dS, dpcd_dS; + real64 const pci = nonWettingKernelWrapper[ModeIndexType::IMBIBITION].compute(&S, &dpci_dS); + real64 const pcd = nonWettingKernelWrapper[ModeIndexType::DRAINAGE].compute(&S, &dpcd_dS); + + // Step 1: get the trapped from wetting data + real64 const Shy = (phaseMaxHistoricalVolFraction < Smax) ? phaseMaxHistoricalVolFraction : Smax; + + //drainage to imbibition + if (mode == ModeIndexType::DRAINAGE_TO_IMBIBITION) { + real64 Scrt = 0.0; + KilloughHysteresis::computeTrappedCriticalPhaseVolFraction(nonWettingCurve, Shy, landParam, + m_jerauldParam_a, m_jerauldParam_b, + Scrt); + + real64 const E = m_killoughCurvatureParamCapPres; + + //Set 2. compute F as in (EQ 34.21) F = (1/(Shy-S+E)-1/E) / (1/(Shy - Sgcr +E)-1/E) + real64 F = (1. / (Shy - S + E) - 1. / E) / (1. / (Shy - Scrt + E) - 1. / E); + //force bound + F = LvArray::math::max(F, 0.0); + F = LvArray::math::min(F, 1.0); + + //Step 3. compute dF_dS + real64 dF_dS = (1. / (S * S)) / (1. / (Shy - Scrt + E) - 1. / E); + + //Step 4. Eventually assemble everything following (EQ. 34.20) + phaseCapPressure = pcd + F * (pci - pcd); + dPhaseCapPressure_dPhaseVolFrac = dpci_dS + F * (dpci_dS - dpcd_dS); + dPhaseCapPressure_dPhaseVolFrac += dF_dS * (pci - pcd); + + //update trapped fraction + phaseTrappedVolFrac = LvArray::math::min(Scrt, S); + + } + //imbibition to drainage + else if (mode == ModeIndexType::IMBIBITION_TO_DRAINAGE) { + real64 Scrt = 0.0; + KilloughHysteresis::computeTrappedCriticalPhaseVolFraction(wettingCurve, Shy, landParam, + m_jerauldParam_a, m_jerauldParam_b, + Scrt); + real64 Sgma = 1. - Scrt - m_phaseIntermediateMinVolFraction; + + real64 const E = m_killoughCurvatureParamCapPres; + + //Set 2. compute F as in (EQ 34.21) F = (1/(Shy-S+E)-1/E) / (1/(Shy - Sgcr +E)-1/E) + real64 F = (1. / (S - Shy + E) - 1. / E) / (1. / (Sgma - Shy + E) - 1. / E); + //force bound + F = LvArray::math::max(F, 0.0); + F = LvArray::math::min(F, 1.0); + + //Step 3. compute dF_dS + real64 dF_dS = (-1. / (S * S)) / (1. / (Shy - Scrt + E) - 1. / E); + + //Step 4. Eventually assemble everything following (EQ. 34.20) + phaseCapPressure = pci + F * (pcd - pci); + dPhaseCapPressure_dPhaseVolFrac = dpcd_dS + F * (dpcd_dS - dpci_dS); + dPhaseCapPressure_dPhaseVolFrac += dF_dS * (pcd - pci); + } else { + GEOS_THROW(GEOS_FMT("{}: State is {}.Shouldnt be used in pure DRAINAGE or IMBIBITION.", + "TableCapillaryPressureHysteresis", + (mode == ModeIndexType::DRAINAGE) ? "DRAINAGE" : ((mode == + ModeIndexType::IMBIBITION) + ? "IMBIBITION" + : "UNKNOWN")), + InputError); + } + + + } + } + + + void + TableCapillaryPressureHysteresis::KernelWrapper::computeImbibitionNonWettingCapillaryPressure( + const arrayView1d &nonWettingKernelWrapper, + const KilloughHysteresis::HysteresisCurve &nonWettingCurve, + const geos::real64 &landParam, + const geos::real64 &phaseVolFraction, + const geos::real64 &phaseMaxHistoricalVolFraction, + geos::real64 &phaseTrappedVolFrac, + geos::real64 &phaseCapPressure, + geos::real64 &dPhaseCapPressure_dPhaseVolFrac, + const ModeIndexType &mode) const { + + GEOS_ASSERT(!nonWettingCurve.isWetting()); + real64 const S = phaseVolFraction; + real64 const Smii = nonWettingCurve.imbibitionExtremaPhaseVolFraction; + real64 const Smid = nonWettingCurve.drainageExtremaPhaseVolFraction; + real64 const Smax = nonWettingCurve.oppositeBoundPhaseVolFraction; + + GEOS_UNUSED_VAR( Smii, Smid ); + +// if( S >= Smax ) +// { +// //above accessible range +// phaseCapPressure = CAP_INF; +// dPhaseCapPressure_dPhaseVolFrac = CAP_INF_DERIV; +// } +// else if( S <= Smid ) +// { +// //below accessible range +// phaseCapPressure = -CAP_INF; +// dPhaseCapPressure_dPhaseVolFrac = CAP_INF_DERIV; +// } +// else + { + //drainage to imbibition + real64 dpci_dS, dpcd_dS; + real64 const pci = nonWettingKernelWrapper[ModeIndexType::IMBIBITION].compute(&S, &dpci_dS); + real64 const pcd = nonWettingKernelWrapper[ModeIndexType::DRAINAGE].compute(&S, &dpcd_dS); + + // Step 1: get the trapped from wetting data + real64 const Shy = (phaseMaxHistoricalVolFraction < Smax) ? phaseMaxHistoricalVolFraction : Smax; + + //drainage to imbibition + if (mode == ModeIndexType::DRAINAGE_TO_IMBIBITION) { + real64 Scrt = 0.0; + KilloughHysteresis::computeTrappedCriticalPhaseVolFraction(nonWettingCurve, Shy, landParam, + m_jerauldParam_a, m_jerauldParam_b, + Scrt); + + real64 const E = m_killoughCurvatureParamCapPres; + + //Set 2. compute F as in (EQ 34.21) F = (1/(Shy-S+E)-1/E) / (1/(Shy - Sgcr +E)-1/E) + real64 F = (1. / (Shy - S + E) - 1. / E) / (1. / (Shy - Scrt + E) - 1. / E); + //force bound + F = LvArray::math::max(F, 0.0); + F = LvArray::math::min(F, 1.0); + + //Step 3. compute dF_dS + real64 dF_dS = (1. / (S * S)) / (1. / (Shy - Scrt + E) - 1. / E); + + //Step 4. Eventually assemble everything following (EQ. 34.20) + phaseCapPressure = pcd + F * (pci - pcd); + dPhaseCapPressure_dPhaseVolFrac = dpci_dS + F * (dpci_dS - dpcd_dS); + dPhaseCapPressure_dPhaseVolFrac += dF_dS * (pci - pcd); + + //update trapped fraction + phaseTrappedVolFrac = LvArray::math::min(Scrt, S); + + } + //imbibition to drainage + else if (mode == ModeIndexType::IMBIBITION_TO_DRAINAGE) { + real64 Scrt = 0.0; + KilloughHysteresis::computeTrappedCriticalPhaseVolFraction(nonWettingCurve, Shy, landParam, + m_jerauldParam_a, m_jerauldParam_b, + Scrt); + real64 Sgma = 1. - (1. - Scrt); + + real64 const E = m_killoughCurvatureParamCapPres; + + //Set 2. compute F as in (EQ 34.21) F = (1/(Shy-S+E)-1/E) / (1/(Shy - Sgcr +E)-1/E) + real64 F = (1. / (S - Shy + E) - 1. / E) / (1. / (Sgma - Shy + E) - 1. / E); + //force bound + F = LvArray::math::max(F, 0.0); + F = LvArray::math::min(F, 1.0); + + //Step 3. compute dF_dS + real64 dF_dS = (-1. / (S * S)) / (1. / (Shy - Scrt + E) - 1. / E); + + //Step 4. Eventually assemble everything following (EQ. 34.20) + phaseCapPressure = pci + F * (pcd - pci); + dPhaseCapPressure_dPhaseVolFrac = dpcd_dS + F * (dpcd_dS - dpci_dS); + dPhaseCapPressure_dPhaseVolFrac += dF_dS * (pcd - pci); + } else { + GEOS_THROW(GEOS_FMT("{}: State is {}.Shouldnt be used in pure DRAINAGE or IMBIBITION.", + "TableCapillaryPressureHysteresis", + (mode == ModeIndexType::DRAINAGE) ? "DRAINAGE" : ((mode == + ModeIndexType::IMBIBITION) + ? "IMBIBITION" + : "UNKNOWN")), + InputError); + } + + + } + } + + + void TableCapillaryPressureHysteresis::KernelWrapper::computeBoundCapillaryPressure( + const TableFunction::KernelWrapper &drainageRelpermWrapper, + const geos::real64 &phaseVolFraction, + geos::real64 &phaseCapPressure, + geos::real64 &dPhaseCapPressure_dPhaseVolFrac) const { + phaseCapPressure = drainageRelpermWrapper.compute(&phaseVolFraction, + &dPhaseCapPressure_dPhaseVolFrac); + + } + + /// Helper function to compute Pc(S) for given S, mode, and historical values + /// Used for Newton-Raphson inversion in computeInv + GEOS_HOST_DEVICE + inline real64 TableCapillaryPressureHysteresis::KernelWrapper::computeCapillaryPressureForSaturation( + real64 const S, + fields::cappres::ModeIndexType const &mode, + integer const ipPhase, + real64 const &phaseMinHistoricalVolFraction, + real64 const &phaseMaxHistoricalVolFraction, + arrayView1d const &capPresKernelWrappers, + KilloughHysteresis::HysteresisCurve const &wettingCurve, + KilloughHysteresis::HysteresisCurve const &nonWettingCurve, + real64 const &landParam, + real64 const &phaseIntermediateMinVolFraction, + real64 const &killoughCurvatureParam, + real64 const &jerauldParam_a, + real64 const &jerauldParam_b, + bool const isWettingPhase, + real64 const precomputedScrt, + real64 const precomputedDenomF, + real64 const precomputedShy) const + { + real64 pc = 0.0; + real64 dpc_dS = 0.0; + + // For pure drainage or imbibition modes, use the table directly + if (mode == fields::cappres::ModeIndexType::DRAINAGE || mode == fields::cappres::ModeIndexType::IMBIBITION) { + integer const arrayIndex = (mode == fields::cappres::ModeIndexType::DRAINAGE) ? fields::cappres::ModeIndexType::DRAINAGE : fields::cappres::ModeIndexType::IMBIBITION; + if (arrayIndex < static_cast(capPresKernelWrappers.size())) { + pc = capPresKernelWrappers[arrayIndex].compute(&S, &dpc_dS); + } + } + // For scanning curves (DRAINAGE_TO_IMBIBITION or IMBIBITION_TO_DRAINAGE) + else { + // Precomputed values should be provided by caller (from computeFlux before local_solver) + // If not provided, fall back to drainage curve to avoid calling computeTrappedCriticalPhaseVolFraction + if (precomputedScrt < 0.0) { + // Precomputed values not provided - return drainage curve value + real64 const arrayIndex = fields::cappres::ModeIndexType::DRAINAGE; + if (arrayIndex < static_cast(capPresKernelWrappers.size())) { + pc = capPresKernelWrappers[arrayIndex].compute(&S, &dpc_dS); + } + return pc; + } + + real64 dpci_dS, dpcd_dS; + real64 const pci = capPresKernelWrappers[fields::cappres::ModeIndexType::IMBIBITION].compute(&S, &dpci_dS); + real64 const pcd = capPresKernelWrappers[fields::cappres::ModeIndexType::DRAINAGE].compute(&S, &dpcd_dS); + + real64 const E = killoughCurvatureParam; + + if (isWettingPhase) { + real64 const Smin = wettingCurve.oppositeBoundPhaseVolFraction; + real64 const Shy = (precomputedShy >= 0.0) ? precomputedShy : + ((phaseMinHistoricalVolFraction > Smin) ? phaseMinHistoricalVolFraction : Smin); + + if (mode == fields::cappres::ModeIndexType::DRAINAGE_TO_IMBIBITION) { + // Use precomputed value - should have been computed before local_solver + real64 const Scrt = precomputedScrt; + real64 const Swma = 1 - Scrt - phaseIntermediateMinVolFraction; + real64 denomF = precomputedDenomF; + if (LvArray::math::abs(precomputedDenomF) < 1e-15) { + denomF = (1. / (Swma - Shy + E) - 1. / E); + } + // Guard against division by zero + real64 F = 0.0; + if (LvArray::math::abs(denomF) >= 1e-15) { + real64 const F_num = (1. / (S - Shy + E) - 1. / E); + F = F_num / denomF; + } + F = LvArray::math::max(F, 0.0); + F = LvArray::math::min(F, 1.0); + + pc = pcd + F * (pci - pcd); + dpc_dS = dpcd_dS + F * (dpci_dS - dpcd_dS); + } + else if (mode == fields::cappres::ModeIndexType::IMBIBITION_TO_DRAINAGE) { + // Use precomputed value - should have been computed before local_solver + real64 const Scrt = precomputedScrt; + + real64 denomF = precomputedDenomF; + if (LvArray::math::abs(precomputedDenomF) < 1e-15) { + denomF = (1. / (Shy - Scrt + E) - 1. / E); + } + // Guard against division by zero + real64 F = 0.0; + if (LvArray::math::abs(denomF) >= 1e-15) { + real64 const F_num = (1. / (Shy - S + E) - 1. / E); + F = F_num / denomF; + } + F = LvArray::math::max(F, 0.0); + F = LvArray::math::min(F, 1.0); + + pc = pci + F * (pcd - pci); + dpc_dS = dpci_dS + F * (dpcd_dS - dpci_dS); + } + } + else { + // Non-wetting phase + real64 const Smax = nonWettingCurve.oppositeBoundPhaseVolFraction; + real64 const Shy = (precomputedShy >= 0.0) ? precomputedShy : + ((phaseMaxHistoricalVolFraction < Smax) ? phaseMaxHistoricalVolFraction : Smax); + + if (mode == fields::cappres::ModeIndexType::DRAINAGE_TO_IMBIBITION) { + // Use precomputed value - should have been computed before local_solver + real64 const Scrt = precomputedScrt; + + real64 denomF = precomputedDenomF; + if (LvArray::math::abs(precomputedDenomF) < 1e-15) { + denomF = (1. / (Shy - Scrt + E) - 1. / E); + } + // Guard against division by zero + real64 F = 0.0; + if (LvArray::math::abs(denomF) >= 1e-15) { + real64 const F_num = (1. / (Shy - S + E) - 1. / E); + F = F_num / denomF; + } + F = LvArray::math::max(F, 0.0); + F = LvArray::math::min(F, 1.0); + + pc = pcd + F * (pci - pcd); + dpc_dS = dpci_dS + F * (dpci_dS - dpcd_dS); + // Note: there's also a dF/dS term in the non-wetting case, but for inversion we approximate + } + else if (mode == fields::cappres::ModeIndexType::IMBIBITION_TO_DRAINAGE) { + // Use precomputed value - should have been computed before local_solver + real64 const Scrt = precomputedScrt; + real64 const Sgma = 1. - Scrt - phaseIntermediateMinVolFraction; + + real64 denomF = precomputedDenomF; + if (LvArray::math::abs(precomputedDenomF) < 1e-15) { + denomF = (1. / (Sgma - Shy + E) - 1. / E); + } + // Guard against division by zero + real64 F = 0.0; + if (LvArray::math::abs(denomF) >= 1e-15) { + real64 const F_num = (1. / (S - Shy + E) - 1. / E); + F = F_num / denomF; + } + F = LvArray::math::max(F, 0.0); + F = LvArray::math::min(F, 1.0); + + pc = pci + F * (pcd - pci); + dpc_dS = dpcd_dS + F * (dpcd_dS - dpci_dS); + // Note: there's also a dF/dS term in the non-wetting case, but for inversion we approximate + } + } + } + + return pc; + } + + void + TableCapillaryPressureHysteresis::KernelWrapper::computeInv( + arraySlice1d const &phaseVolFraction, + arraySlice1d const &phaseMaxHistoricalVolFraction, + arraySlice1d const &phaseMinHistoricalVolFraction, + arraySlice1d const &phaseTrappedVolFrac, + arraySlice1d const &phaseCapPressure, + arraySlice2d const &dPhaseCapPressure_dPhaseVolFrac, + fields::cappres::ModeIndexType const &mode) const + { + LvArray::forValuesInSlice(dPhaseCapPressure_dPhaseVolFrac, [](real64 &val) { val = 0.0; }); + + using PT = CapillaryPressureBase::PhaseType; + integer const ipWater = (PT::WATER < m_phaseOrder.size()) ? m_phaseOrder[PT::WATER] : -1; + integer const ipOil = (PT::OIL < m_phaseOrder.size()) ? m_phaseOrder[PT::OIL] : -1; + integer const ipGas = (PT::GAS < m_phaseOrder.size()) ? m_phaseOrder[PT::GAS] : -1; + + // Newton-Raphson parameters + constexpr real64 tol = 1e-9; + constexpr integer maxIter = 20; + constexpr real64 minS = 0.0; + constexpr real64 maxS = 1.0; + + // Determine which phase pairs need inversion + if (ipWater >= 0 && ipOil >= 0 && ipGas >= 0) { + // Three-phase: invert wetting and non-wetting capillary pressures + + // 1. Invert wetting phase (water-oil capillary pressure) + constexpr real64 pcEpsilon = 1e-10; + if (ipWater >= 0 && LvArray::math::abs(phaseCapPressure[ipWater]) > pcEpsilon) { + real64 S_guess = phaseMaxHistoricalVolFraction[ipWater]; + if (S_guess <= phaseMinHistoricalVolFraction[ipWater] || S_guess < minS || S_guess > maxS) { + // Fall back to drainage/imbibition curve evaluation if available + real64 const Smin = m_wettingCurve.oppositeBoundPhaseVolFraction; + real64 const Smax = m_wettingCurve.drainageExtremaPhaseVolFraction; + S_guess = 0.5 * (Smin + Smax); + S_guess = LvArray::math::max(S_guess, minS); + S_guess = LvArray::math::min(S_guess, maxS); + } + + // Precomputed values should be provided by caller (from computeFlux before local_solver) + // If not provided (sentinel values), fall back to simple drainage/imbibition curves + // This avoids calling computeTrappedCriticalPhaseVolFraction inside local_solver + real64 precomputedScrt_water = -1.0; + real64 precomputedDenomF_water = 0.0; + real64 precomputedShy_water = -1.0; + + // For scanning curves without precomputed values, use drainage curve as fallback + // This avoids the floating-point exception that would occur in computeTrappedCriticalPhaseVolFraction + + real64 S = S_guess; + for (integer iter = 0; iter < maxIter; ++iter) { + // Compute Pc(S) using the helper function + real64 pc_computed = this->computeCapillaryPressureForSaturation( + S, mode, ipWater, + phaseMinHistoricalVolFraction[ipWater], + phaseMaxHistoricalVolFraction[ipWater], + m_wettingIntermediateCapillaryPressureKernelWrappers, + m_wettingCurve, + m_nonWettingCurve, + m_landParam[ipWater], + m_phaseIntermediateMinVolFraction, + m_killoughCurvatureParamCapPres, + m_jerauldParam_a, + m_jerauldParam_b, + true, // isWettingPhase + precomputedScrt_water, + precomputedDenomF_water, + precomputedShy_water); + + real64 residual = pc_computed - phaseCapPressure[ipWater]; + + if (LvArray::math::abs(residual) < tol) { + break; + } + + // Compute derivative dPc/dS (approximate using finite difference if needed) + // For simplicity, use the derivative from the table evaluation + // In a more sophisticated implementation, we could compute the analytical derivative + real64 const dS = 1e-6; + real64 const S_pert = LvArray::math::min(S + dS, maxS); + real64 const pc_pert = this->computeCapillaryPressureForSaturation( + S_pert, mode, ipWater, + phaseMinHistoricalVolFraction[ipWater], + phaseMaxHistoricalVolFraction[ipWater], + m_wettingIntermediateCapillaryPressureKernelWrappers, + m_wettingCurve, + m_nonWettingCurve, + m_landParam[ipWater], + m_phaseIntermediateMinVolFraction, + m_killoughCurvatureParamCapPres, + m_jerauldParam_a, + m_jerauldParam_b, + true, // isWettingPhase + precomputedScrt_water, + precomputedDenomF_water, + precomputedShy_water); + + real64 const dpc_dS = (pc_pert - pc_computed) / dS; + + // Avoid division by zero + if (LvArray::math::abs(dpc_dS) > 1e-12) { + S = S - residual / dpc_dS; + // Clamp to valid range + S = LvArray::math::max(S, minS); + S = LvArray::math::min(S, maxS); + } else { + // If derivative is zero, try bisection or use a small step + S = S - residual * 1e-6; // Small fixed step + S = LvArray::math::max(S, minS); + S = LvArray::math::min(S, maxS); + } + } + + phaseVolFraction[ipWater] = S; + phaseVolFraction[ipOil] = 1.0 - S - phaseVolFraction[ipGas]; + + // Compute derivative at final S + real64 dpc_dS_final = 0.0; + if (mode == fields::cappres::ModeIndexType::DRAINAGE || mode == fields::cappres::ModeIndexType::IMBIBITION) { + integer const arrayIndex = (mode == fields::cappres::ModeIndexType::DRAINAGE) ? fields::cappres::ModeIndexType::DRAINAGE : fields::cappres::ModeIndexType::IMBIBITION; + m_wettingIntermediateCapillaryPressureKernelWrappers[arrayIndex].compute(&S, &dpc_dS_final); + } + dPhaseCapPressure_dPhaseVolFrac[ipWater][ipWater] = dpc_dS_final; + } + + // 2. Invert non-wetting phase (gas-oil capillary pressure) + if (ipGas >= 0 && LvArray::math::abs(phaseCapPressure[ipGas]) > pcEpsilon) { + // Note: For non-wetting phase, the capillary pressure is typically negative + // and the relationship may be inverted (increasing Pc with decreasing S) + real64 S_guess = phaseMaxHistoricalVolFraction[ipGas]; + if (S_guess <= phaseMinHistoricalVolFraction[ipGas] || S_guess < minS || S_guess > maxS) { + real64 const Smin = m_nonWettingCurve.imbibitionExtremaPhaseVolFraction; + real64 const Smax = m_nonWettingCurve.drainageExtremaPhaseVolFraction; + S_guess = 0.5 * (Smin + Smax); + S_guess = LvArray::math::max(S_guess, minS); + S_guess = LvArray::math::min(S_guess, maxS); + } + + // Precompute fixed parameters for scanning curves (these don't change during Newton-Raphson) + real64 precomputedScrt_gas = -1.0; + real64 precomputedDenomF_gas = 0.0; + real64 precomputedShy_gas = -1.0; + + // Only precompute for scanning curves (DRAINAGE_TO_IMBIBITION or IMBIBITION_TO_DRAINAGE) + if (mode == fields::cappres::ModeIndexType::DRAINAGE_TO_IMBIBITION || + mode == fields::cappres::ModeIndexType::IMBIBITION_TO_DRAINAGE) { + real64 const Smax = m_nonWettingCurve.oppositeBoundPhaseVolFraction; + precomputedShy_gas = (phaseMaxHistoricalVolFraction[ipGas] < Smax) ? + phaseMaxHistoricalVolFraction[ipGas] : Smax; + real64 const E = m_killoughCurvatureParamCapPres; + + if (mode == fields::cappres::ModeIndexType::DRAINAGE_TO_IMBIBITION) { + // Precomputed values should be provided by caller - skip computation here + // to avoid calling computeTrappedCriticalPhaseVolFraction inside local_solver + precomputedScrt_gas = -1.0; // Indicates not precomputed + precomputedDenomF_gas = 0.0; + } + else if (mode == fields::cappres::ModeIndexType::IMBIBITION_TO_DRAINAGE) { + // Precomputed values should be provided by caller - skip computation here + // to avoid calling computeTrappedCriticalPhaseVolFraction inside local_solver + precomputedScrt_gas = -1.0; // Indicates not precomputed + precomputedDenomF_gas = 0.0; + } + } + + real64 S = S_guess; + for (integer iter = 0; iter < maxIter; ++iter) { + real64 pc_computed = this->computeCapillaryPressureForSaturation( + S, mode, ipGas, + phaseMinHistoricalVolFraction[ipGas], + phaseMaxHistoricalVolFraction[ipGas], + m_nonWettingIntermediateCapillaryPressureKernelWrappers, + m_wettingCurve, + m_nonWettingCurve, + m_landParam[ipGas], + m_phaseIntermediateMinVolFraction, + m_killoughCurvatureParamCapPres, + m_jerauldParam_a, + m_jerauldParam_b, + false, // isWettingPhase = false + precomputedScrt_gas, + precomputedDenomF_gas, + precomputedShy_gas); + + // For non-wetting phase, capillary pressure is typically multiplied by -1 + // Check which sign to use based on the input + real64 residual = pc_computed - phaseCapPressure[ipGas]; + + if (LvArray::math::abs(residual) < tol) { + break; + } + + real64 const dS = 1e-6; + real64 const S_pert = LvArray::math::min(S + dS, maxS); + real64 const pc_pert = this->computeCapillaryPressureForSaturation( + S_pert, mode, ipGas, + phaseMinHistoricalVolFraction[ipGas], + phaseMaxHistoricalVolFraction[ipGas], + m_nonWettingIntermediateCapillaryPressureKernelWrappers, + m_wettingCurve, + m_nonWettingCurve, + m_landParam[ipGas], + m_phaseIntermediateMinVolFraction, + m_killoughCurvatureParamCapPres, + m_jerauldParam_a, + m_jerauldParam_b, + false, // isWettingPhase = false + precomputedScrt_gas, + precomputedDenomF_gas, + precomputedShy_gas); + + real64 const dpc_dS = (pc_pert - pc_computed) / dS; + + if (LvArray::math::abs(dpc_dS) > 1e-12) { + S = S - residual / dpc_dS; + S = LvArray::math::max(S, minS); + S = LvArray::math::min(S, maxS); + } else { + S = S - residual * 1e-6; + S = LvArray::math::max(S, minS); + S = LvArray::math::min(S, maxS); + } + } + + phaseVolFraction[ipGas] = S; + + // Update oil phase (ensure sum = 1) + if (ipWater >= 0 && ipOil >= 0) { + phaseVolFraction[ipOil] = 1.0 - phaseVolFraction[ipWater] - phaseVolFraction[ipGas]; + phaseVolFraction[ipOil] = LvArray::math::max(phaseVolFraction[ipOil], 0.0); + phaseVolFraction[ipOil] = LvArray::math::min(phaseVolFraction[ipOil], 1.0); + } + + // Compute derivative at final S + real64 dpc_dS_final = 0.0; + if (mode == fields::cappres::ModeIndexType::DRAINAGE || mode == fields::cappres::ModeIndexType::IMBIBITION) { + integer const arrayIndex = (mode == fields::cappres::ModeIndexType::DRAINAGE) ? fields::cappres::ModeIndexType::DRAINAGE : fields::cappres::ModeIndexType::IMBIBITION; + m_nonWettingIntermediateCapillaryPressureKernelWrappers[arrayIndex].compute(&S, &dpc_dS_final); + } + dPhaseCapPressure_dPhaseVolFrac[ipGas][ipGas] = dpc_dS_final; + } + } + else if (ipWater < 0) { + // Two-phase: oil-gas (non-wetting phase) + // Similar to above but simpler + constexpr real64 pcEpsilon = 1e-10; + if (ipGas >= 0 && LvArray::math::abs(phaseCapPressure[ipGas]) > pcEpsilon) { + real64 S_guess = phaseMaxHistoricalVolFraction[ipGas]; + if (S_guess < minS || S_guess > maxS) { + S_guess = 0.5; + } + + // Precompute fixed parameters for scanning curves (these don't change during Newton-Raphson) + real64 precomputedScrt_gas = -1.0; + real64 precomputedDenomF_gas = 0.0; + real64 precomputedShy_gas = -1.0; + + // Only precompute for scanning curves (DRAINAGE_TO_IMBIBITION or IMBIBITION_TO_DRAINAGE) + if (mode == fields::cappres::ModeIndexType::DRAINAGE_TO_IMBIBITION || + mode == fields::cappres::ModeIndexType::IMBIBITION_TO_DRAINAGE) { + real64 const Smax = m_nonWettingCurve.oppositeBoundPhaseVolFraction; + precomputedShy_gas = (phaseMaxHistoricalVolFraction[ipGas] < Smax) ? + phaseMaxHistoricalVolFraction[ipGas] : Smax; + real64 const E = m_killoughCurvatureParamCapPres; + + if (mode == fields::cappres::ModeIndexType::DRAINAGE_TO_IMBIBITION) { + // Precomputed values should be provided by caller - skip computation here + // to avoid calling computeTrappedCriticalPhaseVolFraction inside local_solver + precomputedScrt_gas = -1.0; // Indicates not precomputed + precomputedDenomF_gas = 0.0; + } + else if (mode == fields::cappres::ModeIndexType::IMBIBITION_TO_DRAINAGE) { + // Precomputed values should be provided by caller - skip computation here + // to avoid calling computeTrappedCriticalPhaseVolFraction inside local_solver + precomputedScrt_gas = -1.0; // Indicates not precomputed + precomputedDenomF_gas = 0.0; + } + } + + real64 S = S_guess; + for (integer iter = 0; iter < maxIter; ++iter) { + real64 pc_computed = this->computeCapillaryPressureForSaturation( + S, mode, ipGas, + phaseMinHistoricalVolFraction[ipGas], + phaseMaxHistoricalVolFraction[ipGas], + m_wettingNonWettingCapillaryPressureKernelWrappers, + m_wettingCurve, + m_nonWettingCurve, + m_landParam[ipGas], + m_phaseIntermediateMinVolFraction, + m_killoughCurvatureParamCapPres, + m_jerauldParam_a, + m_jerauldParam_b, + false, // isWettingPhase = false + precomputedScrt_gas, + precomputedDenomF_gas, + precomputedShy_gas); + + real64 residual = pc_computed - phaseCapPressure[ipGas]; + + if (LvArray::math::abs(residual) < tol) { + break; + } + + real64 const dS = 1e-6; + real64 const S_pert = LvArray::math::min(S + dS, maxS); + real64 const pc_pert = this->computeCapillaryPressureForSaturation( + S_pert, mode, ipGas, + phaseMinHistoricalVolFraction[ipGas], + phaseMaxHistoricalVolFraction[ipGas], + m_wettingNonWettingCapillaryPressureKernelWrappers, + m_wettingCurve, + m_nonWettingCurve, + m_landParam[ipGas], + m_phaseIntermediateMinVolFraction, + m_killoughCurvatureParamCapPres, + m_jerauldParam_a, + m_jerauldParam_b, + false, // isWettingPhase = false + precomputedScrt_gas, + precomputedDenomF_gas, + precomputedShy_gas); + + real64 const dpc_dS = (pc_pert - pc_computed) / dS; + + if (LvArray::math::abs(dpc_dS) > 1e-12) { + S = S - residual / dpc_dS; + S = LvArray::math::max(S, minS); + S = LvArray::math::min(S, maxS); + } else { + S = S - residual * 1e-6; + S = LvArray::math::max(S, minS); + S = LvArray::math::min(S, maxS); + } + } + + phaseVolFraction[ipGas] = S; + if (ipOil >= 0) { + phaseVolFraction[ipOil] = 1.0 - S; + } + } + } + else { + // Two-phase: water-oil or water-gas (wetting phase) + constexpr real64 pcEpsilon = 1e-10; + if (ipWater >= 0 && LvArray::math::abs(phaseCapPressure[ipWater]) > pcEpsilon) { + real64 S_guess = phaseMaxHistoricalVolFraction[ipWater]; + if (S_guess < minS || S_guess > maxS) { + real64 const Smin = m_wettingCurve.oppositeBoundPhaseVolFraction; + real64 const Smax = m_wettingCurve.drainageExtremaPhaseVolFraction; + S_guess = 0.5 * (Smin + Smax); + S_guess = LvArray::math::max(S_guess, minS); + S_guess = LvArray::math::min(S_guess, maxS); + } + + // Precompute fixed parameters for scanning curves (these don't change during Newton-Raphson) + real64 precomputedScrt_water = -1.0; + real64 precomputedDenomF_water = 0.0; + real64 precomputedShy_water = -1.0; + + // Only precompute for scanning curves (DRAINAGE_TO_IMBIBITION or IMBIBITION_TO_DRAINAGE) + if (mode == fields::cappres::ModeIndexType::DRAINAGE_TO_IMBIBITION || + mode == fields::cappres::ModeIndexType::IMBIBITION_TO_DRAINAGE) { + real64 const Smin = m_wettingCurve.oppositeBoundPhaseVolFraction; + precomputedShy_water = (phaseMinHistoricalVolFraction[ipWater] > Smin) ? + phaseMinHistoricalVolFraction[ipWater] : Smin; + real64 const E = m_killoughCurvatureParamCapPres; + + if (mode == fields::cappres::ModeIndexType::DRAINAGE_TO_IMBIBITION) { + // Precomputed values should be provided by caller - skip computation here + // to avoid calling computeTrappedCriticalPhaseVolFraction inside local_solver + precomputedScrt_water = -1.0; // Indicates not precomputed + precomputedDenomF_water = 0.0; + } + else if (mode == fields::cappres::ModeIndexType::IMBIBITION_TO_DRAINAGE) { + // Precomputed values should be provided by caller - skip computation here + // to avoid calling computeTrappedCriticalPhaseVolFraction inside local_solver + precomputedScrt_water = -1.0; // Indicates not precomputed + precomputedDenomF_water = 0.0; + } + } + + real64 S = S_guess; + for (integer iter = 0; iter < maxIter; ++iter) { + real64 pc_computed = this->computeCapillaryPressureForSaturation( + S, mode, ipWater, + phaseMinHistoricalVolFraction[ipWater], + phaseMaxHistoricalVolFraction[ipWater], + m_wettingNonWettingCapillaryPressureKernelWrappers, + m_wettingCurve, + m_nonWettingCurve, + m_landParam[ipWater], + m_phaseIntermediateMinVolFraction, + m_killoughCurvatureParamCapPres, + m_jerauldParam_a, + m_jerauldParam_b, + true, // isWettingPhase + precomputedScrt_water, + precomputedDenomF_water, + precomputedShy_water); + + real64 residual = pc_computed - phaseCapPressure[ipWater]; + + if (LvArray::math::abs(residual) < tol) { + break; + } + + real64 const dS = 1e-6; + real64 const S_pert = LvArray::math::min(S + dS, maxS); + real64 const pc_pert = this->computeCapillaryPressureForSaturation( + S_pert, mode, ipWater, + phaseMinHistoricalVolFraction[ipWater], + phaseMaxHistoricalVolFraction[ipWater], + m_wettingNonWettingCapillaryPressureKernelWrappers, + m_wettingCurve, + m_nonWettingCurve, + m_landParam[ipWater], + m_phaseIntermediateMinVolFraction, + m_killoughCurvatureParamCapPres, + m_jerauldParam_a, + m_jerauldParam_b, + true, // isWettingPhase + precomputedScrt_water, + precomputedDenomF_water, + precomputedShy_water); + + real64 const dpc_dS = (pc_pert - pc_computed) / dS; + + if (LvArray::math::abs(dpc_dS) > 1e-12) { + S = S - residual / dpc_dS; + S = LvArray::math::max(S, minS); + S = LvArray::math::min(S, maxS); + } else { + S = S - residual * 1e-6; + S = LvArray::math::max(S, minS); + S = LvArray::math::min(S, maxS); + } + } + + phaseVolFraction[ipWater] = S; + // Set non-wetting phase + if (ipGas >= 0) { + phaseVolFraction[ipGas] = 1.0 - S; + } else if (ipOil >= 0) { + phaseVolFraction[ipOil] = 1.0 - S; + } + } + } + } + +/// kernel creation + + TableCapillaryPressureHysteresis::KernelWrapper + TableCapillaryPressureHysteresis::createKernelWrapper() { + + // we want to make sure that the wrappers are always up-to-date, so we recreate them everytime + createAllTableKernelWrappers(); + + // Validate that the arrays are properly populated + integer const numPhases = m_phaseNames.size(); + if (numPhases == 2) { + GEOS_THROW_IF(m_wettingNonWettingCapillaryPressureKernelWrappers.size() != 2, + GEOS_FMT("{}: Expected 2 kernel wrappers for two-phase flow, but got {}. " + "This usually means createAllTableKernelWrappers() failed to populate the arrays. " + "Check that table functions '{}' and '{}' exist and are properly defined.", + getFullName(), + m_wettingNonWettingCapillaryPressureKernelWrappers.size(), + m_drainageWettingNonWettingCapPresTableName, + m_imbibitionWettingNonWettingCapPresTableName.empty() ? m_drainageWettingNonWettingCapPresTableName : m_imbibitionWettingNonWettingCapPresTableName), + InputError); + } else if (numPhases == 3) { + GEOS_THROW_IF(m_wettingIntermediateCapillaryPressureKernelWrappers.size() != 2, + GEOS_FMT("{}: Expected 2 wetting-intermediate kernel wrappers for three-phase flow, but got {}", + getFullName(), + m_wettingIntermediateCapillaryPressureKernelWrappers.size()), + InputError); + GEOS_THROW_IF(m_nonWettingIntermediateCapillaryPressureKernelWrappers.size() != 2, + GEOS_FMT("{}: Expected 2 non-wetting-intermediate kernel wrappers for three-phase flow, but got {}", + getFullName(), + m_nonWettingIntermediateCapillaryPressureKernelWrappers.size()), + InputError); + } + + // Validate that m_phaseHasHysteresis is properly initialized + GEOS_THROW_IF(m_phaseHasHysteresis.size() != 2, + GEOS_FMT("{}: m_phaseHasHysteresis must have size 2, but got {}", + getFullName(), + m_phaseHasHysteresis.size()), + InputError); + + // Validate that the historical volume fraction arrays have been resized + // These arrays should be resized by resizeFields() before the KernelWrapper is used + // If they're empty, it means resizeFields() hasn't been called yet + GEOS_THROW_IF(m_phaseMaxHistoricalVolFraction.size(0) == 0 || m_phaseMaxHistoricalVolFraction.size(1) == 0, + GEOS_FMT("{}: phaseMaxHistoricalVolFraction array has not been resized (size=[{}, {}]). " + "This usually means resizeFields() has not been called yet. " + "The arrays must be resized before the KernelWrapper can be used.", + getFullName(), + m_phaseMaxHistoricalVolFraction.size(0), + m_phaseMaxHistoricalVolFraction.size(1)), + InputError); + GEOS_THROW_IF(m_phaseMinHistoricalVolFraction.size(0) == 0 || m_phaseMinHistoricalVolFraction.size(1) == 0, + GEOS_FMT("{}: phaseMinHistoricalVolFraction array has not been resized (size=[{}, {}]). " + "This usually means resizeFields() has not been called yet. " + "The arrays must be resized before the KernelWrapper can be used.", + getFullName(), + m_phaseMinHistoricalVolFraction.size(0), + m_phaseMinHistoricalVolFraction.size(1)), + InputError); + + // then we create the actual TableRelativePermeabilityHysteresis::KernelWrapper + return KernelWrapper(m_wettingNonWettingCapillaryPressureKernelWrappers, + m_wettingIntermediateCapillaryPressureKernelWrappers, + m_nonWettingIntermediateCapillaryPressureKernelWrappers, + m_phaseHasHysteresis, + m_landParam, + m_jerauldParam_a, + m_jerauldParam_b, + m_killoughCurvatureParamCapPres, + m_phaseIntermediateMinVolFraction, + m_wettingCurve, + m_nonWettingCurve, + m_phaseMinHistoricalVolFraction, + m_phaseMaxHistoricalVolFraction, + m_phaseTypes, + m_phaseOrder, + m_mode, + m_phaseTrappedVolFrac, + m_phaseCapPressure, + m_dPhaseCapPressure_dPhaseVolFrac); + } + + void TableCapillaryPressureHysteresis::createAllTableKernelWrappers() { + using TPP = ThreePhasePairPhaseType; + + FunctionManager const &functionManager = FunctionManager::getInstance(); + + integer const numPhases = m_phaseNames.size(); + + // Ensure m_phaseHasHysteresis is initialized before accessing it + // This can happen if createKernelWrapper is called before postProcessInput + if (m_phaseHasHysteresis.size() == 0) { + m_phaseHasHysteresis.resize(2); + if (numPhases == 2) { + m_phaseHasHysteresis[TPP::INTERMEDIATE_WETTING] = + (m_imbibitionWettingNonWettingCapPresTableName.empty() || + m_imbibitionWettingNonWettingCapPresTableName == m_drainageWettingNonWettingCapPresTableName) + ? 0 : 1; + m_phaseHasHysteresis[TPP::INTERMEDIATE_NONWETTING] = m_phaseHasHysteresis[TPP::INTERMEDIATE_WETTING]; + } else if (numPhases == 3) { + m_phaseHasHysteresis[TPP::INTERMEDIATE_WETTING] = + (m_imbibitionWettingIntermediateCapPresTableName.empty() || + m_imbibitionWettingIntermediateCapPresTableName == m_drainageWettingIntermediateCapPresTableName) + ? 0 : 1; + m_phaseHasHysteresis[TPP::INTERMEDIATE_NONWETTING] = + (m_imbibitionNonWettingIntermediateCapPresTableName.empty() || + m_imbibitionNonWettingIntermediateCapPresTableName == m_drainageNonWettingIntermediateCapPresTableName) + ? 0 : 1; + } + } + + // we want to make sure that the wrappers are always up-to-date, so we recreate them everytime + + m_wettingNonWettingCapillaryPressureKernelWrappers.clear(); + m_wettingIntermediateCapillaryPressureKernelWrappers.clear(); + m_nonWettingIntermediateCapillaryPressureKernelWrappers.clear(); + + if (numPhases == 2) { + GEOS_THROW_IF(m_drainageWettingNonWettingCapPresTableName.empty(), + GEOS_FMT("{}: drainageWettingNonWettingCapPressureTableName is empty for two-phase flow", + getFullName()), + InputError); + + GEOS_THROW_IF(!functionManager.hasGroup(m_drainageWettingNonWettingCapPresTableName), + GEOS_FMT("{}: the table function named '{}' could not be found", + getFullName(), + m_drainageWettingNonWettingCapPresTableName), + InputError); + TableFunction const &drainageCapPresTable = functionManager.getGroup( + m_drainageWettingNonWettingCapPresTableName); + m_wettingNonWettingCapillaryPressureKernelWrappers.emplace_back( + drainageCapPresTable.createKernelWrapper()); + + string const &imbibitionTableName = m_phaseHasHysteresis[TPP::INTERMEDIATE_WETTING] + ? m_imbibitionWettingNonWettingCapPresTableName + : m_drainageWettingNonWettingCapPresTableName; + GEOS_THROW_IF(imbibitionTableName.empty(), + GEOS_FMT("{}: imbibition table name is empty for two-phase flow", + getFullName()), + InputError); + GEOS_THROW_IF(!functionManager.hasGroup(imbibitionTableName), + GEOS_FMT("{}: the table function named '{}' could not be found", + getFullName(), + imbibitionTableName), + InputError); + TableFunction const &imbibitionWettingCapPresTable = functionManager.getGroup( + imbibitionTableName); + m_wettingNonWettingCapillaryPressureKernelWrappers.emplace_back( + imbibitionWettingCapPresTable.createKernelWrapper()); + + GEOS_THROW_IF(m_wettingNonWettingCapillaryPressureKernelWrappers.size() != 2, + GEOS_FMT("{}: Expected 2 kernel wrappers after creation, but got {}", + getFullName(), + m_wettingNonWettingCapillaryPressureKernelWrappers.size()), + InputError); + + } else if (numPhases == 3) { + TableFunction const &drainageWICapPres = functionManager.getGroup( + m_drainageWettingIntermediateCapPresTableName); + m_wettingIntermediateCapillaryPressureKernelWrappers.emplace_back( + drainageWICapPres.createKernelWrapper()); + + TableFunction const &drainageNWICapPres = functionManager.getGroup( + m_drainageNonWettingIntermediateCapPresTableName); + m_nonWettingIntermediateCapillaryPressureKernelWrappers.emplace_back( + drainageNWICapPres.createKernelWrapper()); + + TableFunction const &imbibitionWICapPres = m_phaseHasHysteresis[TPP::INTERMEDIATE_WETTING] + ? functionManager.getGroup( + m_imbibitionWettingIntermediateCapPresTableName) + : functionManager.getGroup( + m_drainageWettingIntermediateCapPresTableName); + m_wettingIntermediateCapillaryPressureKernelWrappers.emplace_back( + imbibitionWICapPres.createKernelWrapper()); + + TableFunction const &imbibitionNWICapPres = m_phaseHasHysteresis[TPP::INTERMEDIATE_NONWETTING] + ? functionManager.getGroup( + m_imbibitionNonWettingIntermediateCapPresTableName) + : functionManager.getGroup( + m_drainageNonWettingIntermediateCapPresTableName); + m_nonWettingIntermediateCapillaryPressureKernelWrappers.emplace_back( + imbibitionNWICapPres.createKernelWrapper()); + } + + } + +///kernel ctor + TableCapillaryPressureHysteresis::KernelWrapper::KernelWrapper( + arrayView1d const &wettingNonWettingCapillaryPressureKernelWrappers, + arrayView1d const &wettingIntermediateCapillaryPressureKernelWrappers, + arrayView1d const &nonWettingIntermediateCapillaryPressureKernelWrappers, + const arrayView1d &phaseHasHysteresis, + const arrayView1d &landParam, + const real64 & jerauldParam_a, + const real64 & jerauldParam_b, + const real64 & killoughCurvaturePcParam, + const geos::real64 &phaseIntermediateMinVolFraction, + const KilloughHysteresis::HysteresisCurve &wettingCurve, + const KilloughHysteresis::HysteresisCurve &nonWettingCurve, + const arrayView2d &phaseMinHistoricalVolFraction, + const arrayView2d &phaseMaxHistoricalVolFraction, + arrayView1d const &phaseTypes, + arrayView1d const &phaseOrder, + arrayView1d const &mode, + arrayView3d const &phaseTrapped, + const arrayView3d &phaseCapPressure, + const arrayView4d &dPhaseCapPressure_dPhaseVolFrac) + : + CapillaryPressureBaseUpdate(phaseTypes, + phaseOrder, + phaseTrapped, + phaseCapPressure, + dPhaseCapPressure_dPhaseVolFrac), + m_wettingNonWettingCapillaryPressureKernelWrappers(wettingNonWettingCapillaryPressureKernelWrappers), + m_wettingIntermediateCapillaryPressureKernelWrappers( + wettingIntermediateCapillaryPressureKernelWrappers), + m_nonWettingIntermediateCapillaryPressureKernelWrappers( + nonWettingIntermediateCapillaryPressureKernelWrappers), + m_phaseHasHysteresis(phaseHasHysteresis), + m_landParam(landParam), + m_jerauldParam_a(jerauldParam_a), + m_jerauldParam_b(jerauldParam_b), + m_killoughCurvatureParamCapPres(killoughCurvaturePcParam), + m_phaseIntermediateMinVolFraction(phaseIntermediateMinVolFraction), + m_wettingCurve(wettingCurve), + m_nonWettingCurve(nonWettingCurve), + m_phaseMinHistoricalVolFraction(phaseMinHistoricalVolFraction), + m_phaseMaxHistoricalVolFraction(phaseMaxHistoricalVolFraction), + m_mode(mode) {} + + + REGISTER_CATALOG_ENTRY(ConstitutiveBase, TableCapillaryPressureHysteresis, std::string const &, Group * const) + + } +} // geos diff --git a/src/coreComponents/constitutive/capillaryPressure/TableCapillaryPressureHysteresis.hpp b/src/coreComponents/constitutive/capillaryPressure/TableCapillaryPressureHysteresis.hpp new file mode 100644 index 00000000000..e0e1098245f --- /dev/null +++ b/src/coreComponents/constitutive/capillaryPressure/TableCapillaryPressureHysteresis.hpp @@ -0,0 +1,597 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * SPDX-License-Identifier: LGPL-2.1-only + * + * Copyright (c) 2018-2020 Lawrence Livermore National Security LLC + * Copyright (c) 2018-2020 The Board of Trustees of the Leland Stanford Junior University + * Copyright (c) 2018-2020 TotalEnergies + * Copyright (c) 2019- GEOSX Contributors + * All rights reserved + * + * See top level LICENSE, COPYRIGHT, CONTRIBUTORS, NOTICE, and ACKNOWLEDGEMENTS files for details. + * ------------------------------------------------------------------------------------------------------------ + */ + + +#ifndef GEOS_CONSTITUTIVE_TABLECAPILLARYPRESSUREHYSTERESIS_HPP +#define GEOS_CONSTITUTIVE_TABLECAPILLARYPRESSUREHYSTERESIS_HPP + +#include "constitutive/capillaryPressure/CapillaryPressureBase.hpp" +#include "functions/TableFunction.hpp" + +#include "constitutive/KilloughHysteresis.hpp" +#include "CapillaryPressureFields.hpp" + +namespace geos { + + + namespace constitutive { + + class TableCapillaryPressureHysteresis : public CapillaryPressureBase { +// /// useful constant +// static constexpr real64 CAP_INF = 1e9; +//// std::numeric_limits< real64 >::max(); +// static constexpr real64 CAP_INF_DERIV = 1e9; +//// std::numeric_limits< real64 >::max(); + + typedef fields::cappres::ModeIndexType ModeIndexType; + + public: + + /// order of the phase properties for three-phase flow + struct ThreePhasePairPhaseType { + enum : integer { + INTERMEDIATE_WETTING = 0, ///< index for intermediate-wetting + INTERMEDIATE_NONWETTING = 1 ///< index for intermediate-non-wetting + }; + }; + + + TableCapillaryPressureHysteresis(std::string const &name, + dataRepository::Group *const parent); + + static std::string catalogName() { return "TableCapillaryPressureHysteresis"; } + + virtual string getCatalogName() const override { return catalogName(); } + + ///Kernel + class KernelWrapper final : public CapillaryPressureBaseUpdate { + public: + + KernelWrapper( + arrayView1d const &wettingNonWettingCapillaryPressureKernelWrappers, + arrayView1d const &wettingIntermediateCapillaryPressureKernelWrappers, + arrayView1d const &nonWettingIntermediateCapillaryPressureKernelWrappers, + arrayView1d const &phaseHasHysteresis, + arrayView1d const &landParam, + real64 const &jerauldParam_a, + real64 const &jerauldParam_b, + real64 const &killoughCurvaturePcParameter, + real64 const &phaseIntermediateMinVolFraction, + KilloughHysteresis::HysteresisCurve const &wettingCurve, + KilloughHysteresis::HysteresisCurve const &nonWettingCurve, + arrayView2d const &phaseMinHistoricalVolFraction, + arrayView2d const &phaseMaxHistoricalVolFraction, + arrayView1d const &phaseTypes, + arrayView1d const &phaseOrder, + arrayView1d const &mode, + arrayView3d const &phaseTrappedVolFrac, + arrayView3d const &phaseCapPressure, + arrayView4d const &dPhaseCapPressure_dPhaseVolFrac); + + //actual workers + GEOS_HOST_DEVICE + void computeBoundCapillaryPressure(TableFunction::KernelWrapper const &drainageCapPressureWrapper, + real64 const &phaseVolFraction, + real64 &phaseCapPressure, + real64 &dPhaseCapPressure_dPhaseVolFrac) const; + + GEOS_HOST_DEVICE + void + computeImbibitionWettingCapillaryPressure( + const arrayView1d &wettingKernelWapper, + const KilloughHysteresis::HysteresisCurve &wettingCurve, + const KilloughHysteresis::HysteresisCurve &nonWettingCurve, + const geos::real64 &landParam, + const geos::real64 &phaseVolFraction, + const geos::real64 &phaseMinHistoricalVolFraction, + geos::real64 &phaseTrappedVolFrac, + geos::real64 &phaseCapPressure, + geos::real64 &dPhaseCapPressure_dPhaseVolFrac, + const ModeIndexType &mode) const; + + //two phase flow overload + GEOS_HOST_DEVICE + void + computeImbibitionWettingCapillaryPressure( + const arrayView1d &wettingKernelWapper, + const KilloughHysteresis::HysteresisCurve &wettingCurve, + const geos::real64 &landParam, + const geos::real64 &phaseVolFraction, + const geos::real64 &phaseMinHistoricalVolFraction, + geos::real64 &phaseTrappedVolFrac, + geos::real64 &phaseCapPressure, + geos::real64 &dPhaseCapPressure_dPhaseVolFrac, + const ModeIndexType &mode) const; + + GEOS_HOST_DEVICE + void + computeImbibitionNonWettingCapillaryPressure( + const arrayView1d &nonWettingKernelWrapper, + const KilloughHysteresis::HysteresisCurve &nonWettingCurve, + const KilloughHysteresis::HysteresisCurve &wettingCurve, + const geos::real64 &landParam, + const geos::real64 &phaseVolFraction, + const geos::real64 &phaseMaxHistoricalVolFraction, + geos::real64 &phaseTrappedVolFrac, + geos::real64 &phaseCapPressure, + geos::real64 &dPhaseCapPressure_dPhaseVolFrac, + const ModeIndexType &mode) const; + + //2phase flow overload + GEOS_HOST_DEVICE + void + computeImbibitionNonWettingCapillaryPressure( + const arrayView1d &nonWettingKernelWrapper, + const KilloughHysteresis::HysteresisCurve &nonWettingCurve, + const geos::real64 &landParam, + const geos::real64 &phaseVolFraction, + const geos::real64 &phaseMaxHistoricalVolFraction, + geos::real64 &phaseTrappedVolFrac, + geos::real64 &phaseCapPressure, + geos::real64 &dPhaseCapPressure_dPhaseVolFrac, + const ModeIndexType &mode) const; + + + + //wrapper call wrt number of phase + GEOS_HOST_DEVICE + void computeTwoPhaseWetting(integer const ipWetting, + integer const ipNonWetting, + arraySlice1d const &phaseVolFraction, + arraySlice1d const &phaseMaxHistoricalVolFraction, + arraySlice1d const &phaseMinHistoricalVolFraction, + arraySlice1d const &phaseTrappedVolFrac, + arraySlice1d const &phaseCapPressure, + arraySlice2d const &dPhaseCapPressure_dPhaseVolFrac, + ModeIndexType &mode) const; + + + GEOS_HOST_DEVICE + void computeTwoPhaseNonWetting(integer const ipWetting, + integer const ipNonWetting, + arraySlice1d const &phaseVolFraction, + arraySlice1d const &phaseMaxHistoricalVolFraction, + arraySlice1d const &phaseMinHistoricalVolFraction, + arraySlice1d const &phaseTrappedVolFrac, + arraySlice1d const &phaseCapPressure, + arraySlice2d const &dPhaseCapPressure_dPhaseVolFrac, + ModeIndexType &mode) const; + + GEOS_HOST_DEVICE + void computeThreePhase(integer const ipWetting, + integer const ipInter, + integer const ipNonWetting, + arraySlice1d const &phaseVolFraction, + arraySlice1d const &phaseMaxHistoricalVolFraction, + arraySlice1d const &phaseMinHistoricalVolFraction, + arraySlice1d const &phaseTrappedVolFrac, + arraySlice1d const &phaseCapPressure, + arraySlice2d const &dPhaseCapPressure_dPhaseVolFrac, + ModeIndexType &mode) const; + + //uppermost call-wrappers + // Standard 3-argument compute method for compatibility with InverseCapillaryPressure + GEOS_HOST_DEVICE + void compute(arraySlice1d const &phaseVolFraction, + arraySlice1d const &phaseCapPressure, + arraySlice2d const &dPhaseCapPressure_dPhaseVolFrac) const; + + GEOS_HOST_DEVICE + virtual void compute(arraySlice1d const &phaseVolFraction, + arraySlice1d const &phaseMaxHistoricalVolFraction, + arraySlice1d const &phaseMinHistoricalVolFraction, + arraySlice1d const &phaseTrappedVolFrac, + arraySlice1d const &phaseCapPressure, + arraySlice2d const &dPhaseCapPressure_dPhaseVolFrac, + ModeIndexType &mode) const; + + GEOS_HOST_DEVICE + virtual void update(localIndex const k, + localIndex const q, + arraySlice1d const &phaseVolFraction) const override; + + /** + * @brief Compute phase volume fraction from capillary pressure (inverse operation). + * @param phaseVolFraction [out] Computed phase volume fractions + * @param phaseMaxHistoricalVolFraction [in] Maximum historical phase volume fractions + * @param phaseMinHistoricalVolFraction [in] Minimum historical phase volume fractions + * @param phaseTrappedVolFrac [in] Trapped phase volume fractions + * @param phaseCapPressure [in] Target capillary pressures (input) + * @param dPhaseCapPressure_dPhaseVolFrac [out] Derivatives of capillary pressure w.r.t. phase volume fraction + * @param mode [in] Hysteresis mode (DRAINAGE, IMBIBITION, DRAINAGE_TO_IMBIBITION, IMBIBITION_TO_DRAINAGE) + * + * Uses Newton-Raphson iteration to invert the capillary pressure function. + */ + GEOS_HOST_DEVICE + void computeInv(arraySlice1d const &phaseVolFraction, + arraySlice1d const &phaseMaxHistoricalVolFraction, + arraySlice1d const &phaseMinHistoricalVolFraction, + arraySlice1d const &phaseTrappedVolFrac, + arraySlice1d const &phaseCapPressure, + arraySlice2d const &dPhaseCapPressure_dPhaseVolFrac, + fields::cappres::ModeIndexType const &mode) const; + + private: + + /** + * @brief Helper function to compute Pc(S) for given S, mode, and historical values. + * @param S Phase volume fraction + * @param mode Hysteresis mode + * @param ipPhase Phase index + * @param phaseMinHistoricalVolFraction Minimum historical volume fraction + * @param phaseMaxHistoricalVolFraction Maximum historical volume fraction + * @param capPresKernelWrappers Capillary pressure kernel wrappers + * @param wettingCurve Wetting curve data + * @param nonWettingCurve Non-wetting curve data + * @param landParam Land parameter + * @param phaseIntermediateMinVolFraction Intermediate phase minimum volume fraction + * @param killoughCurvatureParam Killough curvature parameter + * @param jerauldParam_a Jerauld parameter a + * @param jerauldParam_b Jerauld parameter b + * @param isWettingPhase Whether this is the wetting phase + * @param precomputedScrt Precomputed trapped critical saturation (optional, use -1.0 to compute) + * @param precomputedDenomF Precomputed denominator for F calculation (optional, use 0.0 to compute) + * @param precomputedShy Precomputed historical saturation Shy (optional, use -1.0 to compute) + * @return Computed capillary pressure + * + * Used for Newton-Raphson inversion in computeInv. + * If precomputed values are provided (precomputedScrt >= 0, precomputedDenomF != 0, precomputedShy >= 0), + * they will be used instead of recomputing them. + */ + GEOS_HOST_DEVICE + real64 computeCapillaryPressureForSaturation( + real64 const S, + fields::cappres::ModeIndexType const &mode, + integer const ipPhase, + real64 const &phaseMinHistoricalVolFraction, + real64 const &phaseMaxHistoricalVolFraction, + arrayView1d const &capPresKernelWrappers, + KilloughHysteresis::HysteresisCurve const &wettingCurve, + KilloughHysteresis::HysteresisCurve const &nonWettingCurve, + real64 const &landParam, + real64 const &phaseIntermediateMinVolFraction, + real64 const &killoughCurvatureParam, + real64 const &jerauldParam_a, + real64 const &jerauldParam_b, + bool const isWettingPhase, + real64 const precomputedScrt = -1.0, + real64 const precomputedDenomF = 0.0, + real64 const precomputedShy = -1.0) const; + + static constexpr real64 flowReversalBuffer = KilloughHysteresis::flowReversalBuffer; +// ModeIndexType& m_mode; + + //2p + arrayView1d const m_wettingNonWettingCapillaryPressureKernelWrappers; + //3p + arrayView1d const m_wettingIntermediateCapillaryPressureKernelWrappers; + arrayView1d const m_nonWettingIntermediateCapillaryPressureKernelWrappers; + + ///Land Coeff + arrayView1d m_phaseHasHysteresis; + arrayView1d m_landParam; + + /// Parameter a introduced by Jerauld in the Land model + const real64 m_jerauldParam_a; + + /// Parameter b introduced by Jerauld in the Land model + const real64 m_jerauldParam_b; + + /// Curvature parameter in Killough wetting phase hysteresis (enpoints curvatures) + const real64 m_killoughCurvatureParamCapPres; + + /// needed in 3p-wetting hysteresis as we need to get the max accessible pore space + real64 const m_phaseIntermediateMinVolFraction; + + KilloughHysteresis::HysteresisCurve const m_wettingCurve; + KilloughHysteresis::HysteresisCurve const m_nonWettingCurve; + + /// Minimum historical phase volume fraction for each phase + arrayView2d m_phaseMinHistoricalVolFraction; + + /// Maximum historical phase volume fraction for each phase + arrayView2d m_phaseMaxHistoricalVolFraction; + + // Drainage / Imbibition flags cellwise + arrayView1d m_mode; + + }; + + /** + * @brief Create an update kernel wrapper. + * @return the wrapper + */ + KernelWrapper createKernelWrapper(); + + //might need it to be virtual one level higher --> from Killough/Hysteresis common class + virtual void saveConvergedPhaseVolFractionState( + arrayView2d const &phaseVolFraction) const override; + + + struct viewKeyStruct : CapillaryPressureBase::viewKeyStruct { + + + ///Land Coeff + static constexpr char const *landParameterString() { return "landParameter"; } + + ///flag + static constexpr char const *phaseHasHysteresisString() { return "phaseHasHysteresis"; } + + ///and packed curves data struct + static constexpr char const *wettingCurveString() { return "wettingCurve"; }; + + static constexpr char const *nonWettingCurveString() { return "nonWettingCurve"; }; + + + ///tables and assoc. wrappers + //2phase + static constexpr char const * + drainageWettingNonWettingCapPresTableNameString() { return "drainageWettingNonWettingCapPressureTableName"; } + + static constexpr char const * + imbibitionWettingNonWettingCapPresTableNameString() { return "imbibitionWettingNonWettingCapPressureTableName"; } + + //3phase + static constexpr char const * + drainageWettingIntermediateCapPresTableNameString() { return "drainageWettingIntermediateCapPressureTableName"; } + + static constexpr char const * + drainageNonWettingIntermediateCapPresTableNameString() { return "drainageNonWettingIntermediateCapPressureTableName"; } + + static constexpr char const * + imbibitionWettingIntermediateCapPresTableNameString() { return "imbibitionWettingIntermediateCapPressureTableName"; } + + static constexpr char const * + imbibitionNonWettingIntermediateCapPresTableNameString() { return "imbibitionNonWettingIntermediateCapPressureTableName"; } + + static constexpr char const * + wettingNonWettingCapillaryPressureKernelWrappersString() { return "wettingNonWettingCapillaryPressureKernelWrappers"; } + + static constexpr char const * + wettingIntermediateCapillaryPressureKernelWrappersString() { return "wettingIntermediateCapillaryPressureKernelWrappers"; } + + static constexpr char const * + nonWettingIntermediateCapillaryPressureKernelWrappersString() { return "nonWettingIntermediateCapillaryPressureKernelWrappers"; } + + //misc + static constexpr char const * + phaseIntermediateMinVolFractionString() { return "phaseIntermediateMinVolFraction"; } + //to decide wheter drainage/drainage to imbibition or imbibition/imbibition to drainage + }; + + + private: + virtual void postProcessInput(); + + virtual void initializePreSubGroups() override; + + void resizeFields(localIndex const size, + localIndex const numPts) override; + + + /** + * @brief Create all the table kernel wrappers needed for the simulation (for all the phases present) + */ + void createAllTableKernelWrappers(); + + /** + * @brief Compute the Land coefficient for the wetting and non-wetting phases + */ + void computeLandCoefficient(); + + ///data members + + + //TODO impl +// array1d< integer > m_tCurveOption; + + KilloughHysteresis::HysteresisCurve m_wettingCurve; + KilloughHysteresis::HysteresisCurve m_nonWettingCurve; + + ///tables + //2p + string m_drainageWettingNonWettingCapPresTableName; + string m_imbibitionWettingNonWettingCapPresTableName; + //3p + string m_drainageWettingIntermediateCapPresTableName; + string m_drainageNonWettingIntermediateCapPresTableName; + string m_imbibitionWettingIntermediateCapPresTableName; + string m_imbibitionNonWettingIntermediateCapPresTableName; + // kernel wrappers + /// Imbibition kernel wrappers for relative permeabilities in the following order: + /// 0- drainage + /// 1- imbibition (cf. struct ModeIndexType) + //2p + array1d m_wettingNonWettingCapillaryPressureKernelWrappers; + //3p + array1d m_wettingIntermediateCapillaryPressureKernelWrappers; + array1d m_nonWettingIntermediateCapillaryPressureKernelWrappers; + + + /// Flag to specify whether the phase has hysteresis or not (deduced from table input) + array1d m_phaseHasHysteresis; + + /// Trapping parameter from the Land model (typically called C) + array1d m_landParam; + + /// Parameter a introduced by Jerauld in the Land model + real64 m_jerauldParam_a; + + /// Parameter b introduced by Jerauld in the Land model + real64 m_jerauldParam_b; + + /// Curvature parameter in Killough wetting phase hysteresis (Scanning curves curvatures) + real64 m_killoughCurvatureParamCapPres; + + /// Cell-wise status imbibition, imbibitioon_to_drainage, ... etc + array1d m_mode; + + // Max historical saturations + /// Minimum historical phase volume fraction for each phase + array2d m_phaseMinHistoricalVolFraction; + + /// Maximum historical phase volume fraction for each phase + array2d m_phaseMaxHistoricalVolFraction; + + //needed in hysteresis of wetting phase + real64 m_phaseIntermediateMinVolFraction; + + }; + + // Standard 3-argument compute method for compatibility with InverseCapillaryPressure + // Uses zero/default values for historical fractions (no hysteresis in inverse computation) + GEOS_HOST_DEVICE + inline void TableCapillaryPressureHysteresis::KernelWrapper::compute( + arraySlice1d const &phaseVolFraction, + arraySlice1d const &phaseCapPressure, + arraySlice2d const &dPhaseCapPressure_dPhaseVolFrac + ) const { + // Create temporary arrays for historical fractions and trapped fraction + // Initialize to zero/default values since inverse computation doesn't use hysteresis + constexpr integer MAX_NUM_PHASES = CapillaryPressureBase::MAX_NUM_PHASES; + real64 phaseMaxHistoricalVolFraction[MAX_NUM_PHASES]{}; + real64 phaseMinHistoricalVolFraction[MAX_NUM_PHASES]{}; + real64 phaseTrappedVolFrac[MAX_NUM_PHASES]{}; + ModeIndexType mode = ModeIndexType::DRAINAGE; // Default to drainage mode + + // Create ArrayView from stack arrays, then get slices + integer const numPhases = LvArray::integerConversion< integer >( phaseVolFraction.size() ); + localIndex dims[1] = { numPhases }; + localIndex strides[1] = { 1 }; + + arraySlice1d< real64 const, compflow::USD_PHASE - 1 > const phaseMaxHistSlice( + phaseMaxHistoricalVolFraction, dims, strides ); + arraySlice1d< real64 const, compflow::USD_PHASE - 1 > const phaseMinHistSlice( + phaseMinHistoricalVolFraction, dims, strides ); + arraySlice1d< real64, cappres::USD_CAPPRES - 2 > phaseTrappedSlice( + phaseTrappedVolFrac, dims, strides ); + + // Call the full compute method + compute( phaseVolFraction, + phaseMaxHistSlice, + phaseMinHistSlice, + phaseTrappedSlice, + phaseCapPressure, + dPhaseCapPressure_dPhaseVolFrac, + mode ); + } + + GEOS_HOST_DEVICE + inline void TableCapillaryPressureHysteresis::KernelWrapper::compute( + arraySlice1d const &phaseVolFraction, + arraySlice1d const &phaseMaxHistoricalVolFraction, + arraySlice1d const &phaseMinHistoricalVolFraction, + arraySlice1d const &phaseTrappedVolFrac, + arraySlice1d const &phaseCapPressure, + arraySlice2d const &dPhaseCapPressure_dPhaseVolFrac, + ModeIndexType &mode + ) const { + // Early return if m_phaseOrder is empty or input arrays are empty + if( m_phaseOrder.size() == 0 || + phaseVolFraction.size() == 0 || + phaseCapPressure.size() == 0 ) + { + return; + } + + LvArray::forValuesInSlice(dPhaseCapPressure_dPhaseVolFrac, [](real64 &val) { val = 0.0; }); + + using PT = CapillaryPressureBase::PhaseType; + // Check bounds before accessing m_phaseOrder + integer const ipWater = ( PT::WATER < m_phaseOrder.size() ) ? m_phaseOrder[PT::WATER] : -1; + integer const ipOil = ( PT::OIL < m_phaseOrder.size() ) ? m_phaseOrder[PT::OIL] : -1; + integer const ipGas = ( PT::GAS < m_phaseOrder.size() ) ? m_phaseOrder[PT::GAS] : -1; + + if (ipWater >= 0 && ipOil >= 0 && ipGas >= 0) { + computeThreePhase(ipWater, // wetting + ipOil, // intermediate + ipGas, // non-wetting + phaseVolFraction, + phaseMaxHistoricalVolFraction, + phaseMinHistoricalVolFraction, + phaseTrappedVolFrac, + phaseCapPressure, + dPhaseCapPressure_dPhaseVolFrac, + mode); + + } else if (ipWater < 0) { + computeTwoPhaseNonWetting(ipOil, // leading + ipGas, // deduced + phaseVolFraction, + phaseMaxHistoricalVolFraction, + phaseMinHistoricalVolFraction, + phaseTrappedVolFrac, + phaseCapPressure, + dPhaseCapPressure_dPhaseVolFrac, + mode); + } else if (ipOil < 0) { + computeTwoPhaseWetting(ipWater, // leading + ipGas, // deduced + phaseVolFraction, + phaseMaxHistoricalVolFraction, + phaseMinHistoricalVolFraction, + phaseTrappedVolFrac, + phaseCapPressure, + dPhaseCapPressure_dPhaseVolFrac, + mode); + } else if (ipGas < 0) { + computeTwoPhaseWetting(ipWater, //leading + ipOil, //deduced + phaseVolFraction, + phaseMaxHistoricalVolFraction, + phaseMinHistoricalVolFraction, + phaseTrappedVolFrac, + phaseCapPressure, + dPhaseCapPressure_dPhaseVolFrac, + mode); + } + + + } + + GEOS_HOST_DEVICE + inline void TableCapillaryPressureHysteresis::KernelWrapper::update(const geos::localIndex k, + const geos::localIndex q, + const arraySlice1d &phaseVolFraction) const { + compute(phaseVolFraction, + m_phaseMaxHistoricalVolFraction[k], + m_phaseMinHistoricalVolFraction[k], + m_phaseTrappedVolFrac[k][q], + m_phaseCapPressure[k][q], + m_dPhaseCapPressure_dPhaseVolFrac[k][q], + m_mode[k]); + } + + + } //constitutive +} // geos + +#endif //GEOS_CONSTITUTIVE_TABLECAPILLARYPRESSUREHYSTERESIS_HPP diff --git a/src/coreComponents/constitutive/capillaryPressure/VanGenuchtenCapillaryPressure.cpp b/src/coreComponents/constitutive/capillaryPressure/VanGenuchtenCapillaryPressure.cpp index b324366f4f2..17a0a74e426 100644 --- a/src/coreComponents/constitutive/capillaryPressure/VanGenuchtenCapillaryPressure.cpp +++ b/src/coreComponents/constitutive/capillaryPressure/VanGenuchtenCapillaryPressure.cpp @@ -122,6 +122,7 @@ VanGenuchtenCapillaryPressure::createKernelWrapper() m_volFracScale, m_phaseTypes, m_phaseOrder, + m_phaseTrappedVolFrac, m_phaseCapPressure, m_dPhaseCapPressure_dPhaseVolFrac ); } diff --git a/src/coreComponents/constitutive/capillaryPressure/VanGenuchtenCapillaryPressure.hpp b/src/coreComponents/constitutive/capillaryPressure/VanGenuchtenCapillaryPressure.hpp index f3af8198d60..2234b405037 100644 --- a/src/coreComponents/constitutive/capillaryPressure/VanGenuchtenCapillaryPressure.hpp +++ b/src/coreComponents/constitutive/capillaryPressure/VanGenuchtenCapillaryPressure.hpp @@ -39,10 +39,12 @@ class VanGenuchtenCapillaryPressureUpdate final : public CapillaryPressureBaseUp real64 const volFracScale, arrayView1d< integer const > const & phaseTypes, arrayView1d< integer const > const & phaseOrder, + arrayView3d< geos::real64, cappres::USD_CAPPRES > const & phaseTrapped, arrayView3d< real64, cappres::USD_CAPPRES > const & phaseCapPressure, arrayView4d< real64, cappres::USD_CAPPRES_DS > const & dPhaseCapPressure_dPhaseVolFrac ) : CapillaryPressureBaseUpdate( phaseTypes, phaseOrder, + phaseTrapped, phaseCapPressure, dPhaseCapPressure_dPhaseVolFrac ), m_phaseMinVolumeFraction( phaseMinVolumeFraction ), @@ -57,6 +59,11 @@ class VanGenuchtenCapillaryPressureUpdate final : public CapillaryPressureBaseUp arraySlice1d< real64, cappres::USD_CAPPRES - 2 > const & phaseCapPres, arraySlice2d< real64, cappres::USD_CAPPRES_DS - 2 > const & dPhaseCapPres_dPhaseVolFrac ) const; + GEOS_HOST_DEVICE + void computeInv( arraySlice1d< real64 const, compflow::USD_PHASE - 1 > const & phaseVolFraction, + arraySlice1d< real64, cappres::USD_CAPPRES - 2 > const & phaseCapPres, + arraySlice2d< real64, cappres::USD_CAPPRES_DS - 2 > const & dPhaseCapPres_dPhaseVolFrac ) const; + GEOS_HOST_DEVICE virtual void update( localIndex const k, localIndex const q, @@ -189,6 +196,69 @@ VanGenuchtenCapillaryPressureUpdate:: } } +GEOS_HOST_DEVICE +inline void +VanGenuchtenCapillaryPressureUpdate:: + computeInv( arraySlice1d< real64 const, compflow::USD_PHASE - 1 > const & phaseVolFraction, + arraySlice1d< real64, cappres::USD_CAPPRES - 2 > const & phaseCapPres, + arraySlice2d< real64, cappres::USD_CAPPRES_DS - 2 > const & dPhaseCapPres_dPhaseVolFrac ) const +{ + LvArray::forValuesInSlice( dPhaseCapPres_dPhaseVolFrac, []( real64 & val ){ val = 0.0; } ); + + // the VanGenuchten model does not support volFracScaled = 0 and = 1 + // hence we need an epsilon value to avoid a division by zero + // TODO: for S < epsilon and S > 1 - epsilon, replace the original unbounded VG curve with a bounded power-law + // extension + real64 const eps = m_capPressureEpsilon; + real64 const volFracScaleInv = 1.0 / m_volFracScale; + + // compute first water-oil capillary pressure as a function of water-phase vol fraction + integer const ip_water = m_phaseOrder[CapillaryPressureBase::PhaseType::WATER]; + if( ip_water >= 0 ) + { + + real64 const volFracScaled = (phaseVolFraction[ip_water] - m_phaseMinVolumeFraction[ip_water]) * volFracScaleInv; + real64 const exponentInv = m_phaseCapPressureExponentInv[ip_water]; // div by 0 taken care of by initialization + // check + real64 const multiplier = m_phaseCapPressureMultiplier[ip_water]; + + real64 const scaledWettingVolFrac = volFracScaled; + real64 const dScaledWettingPhaseVolFrac_dVolFrac = volFracScaleInv; + + evaluateVanGenuchtenFunction( scaledWettingVolFrac, + dScaledWettingPhaseVolFrac_dVolFrac, + exponentInv, + multiplier, + eps, + phaseCapPres[ip_water], + dPhaseCapPres_dPhaseVolFrac[ip_water][ip_water] ); + + } + + + // then compute the oil-gas capillary pressure as a function of gas-phase vol fraction + integer const ip_gas = m_phaseOrder[CapillaryPressureBase::PhaseType::GAS]; + if( ip_gas >= 0 ) + { + real64 const volFracScaled = (phaseVolFraction[ip_gas] - m_phaseMinVolumeFraction[ip_gas]) * volFracScaleInv; + real64 const exponentInv = m_phaseCapPressureExponentInv[ip_gas]; // div by 0 taken care of by initialization + // check + real64 const multiplier = -m_phaseCapPressureMultiplier[ip_gas]; // for gas capillary pressure, take the opposite + // of the VG function + + real64 const scaledWettingVolFrac = 1-volFracScaled; + real64 const dScaledWettingPhaseVolFrac_dVolFrac = -volFracScaleInv; + + evaluateVanGenuchtenFunction( scaledWettingVolFrac, + dScaledWettingPhaseVolFrac_dVolFrac, + exponentInv, + multiplier, + eps, + phaseCapPres[ip_gas], + dPhaseCapPres_dPhaseVolFrac[ip_gas][ip_gas] ); + } +} + GEOS_HOST_DEVICE GEOS_FORCE_INLINE void diff --git a/src/coreComponents/constitutive/docs/TwoPhaseFluid.rst b/src/coreComponents/constitutive/docs/TwoPhaseFluid.rst new file mode 100644 index 00000000000..39b35510c63 --- /dev/null +++ b/src/coreComponents/constitutive/docs/TwoPhaseFluid.rst @@ -0,0 +1,90 @@ +.. _TwoPhaseFluid: + +############################################ +Two-phase fluid model +############################################ + +Overview +========================= + +This model represents a two-phase fluid with pressure-dependent density and viscosity. + +For each phase, both density and viscosity are described as tabulated data, either in the form of ``TableFunction`` or text files. + +In the case of text files, one file is expected per phase and should consist of three columns: pressure, density and viscosity. + +Note that currently, there is no temperature dependence in the model. + + +Parameters +========================= + +The model is represented by ```` node in the input. + +The following attributes are supported: + +.. include:: /docs/sphinx/datastructure/TwoPhaseFluid.rst + + +Example using TableFunctions +============================ + +.. code-block:: xml + + + + + + + + + + + + + + + + +Example using text files +========================= + +.. code-block:: xml + + + + + + +with, for example, ``water.txt`` being set as: + +.. code-block:: text + + # P(Pa) Dens(kg/m3) Visc(Pa.s) + 2068000 980.683 0.0003 + 5516000 982.07 0.0003 + 30600000 992.233 0.0003 + 55160000 1002.265 0.0003 diff --git a/src/coreComponents/constitutive/fluid/twophasefluid/TwoPhaseFluid.cpp b/src/coreComponents/constitutive/fluid/twophasefluid/TwoPhaseFluid.cpp new file mode 100644 index 00000000000..ad6379ea129 --- /dev/null +++ b/src/coreComponents/constitutive/fluid/twophasefluid/TwoPhaseFluid.cpp @@ -0,0 +1,268 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * SPDX-License-Identifier: LGPL-2.1-only + * + * Copyright (c) 2016-2024 Lawrence Livermore National Security LLC + * Copyright (c) 2018-2024 Total, S.A + * Copyright (c) 2018-2024 The Board of Trustees of the Leland Stanford Junior University + * Copyright (c) 2023-2024 Chevron + * Copyright (c) 2019- GEOS/GEOSX Contributors + * All rights reserved + * + * See top level LICENSE, COPYRIGHT, CONTRIBUTORS, NOTICE, and ACKNOWLEDGEMENTS files for details. + * ------------------------------------------------------------------------------------------------------------ + */ + +/** + * @file TwoPhaseFluid.cpp + */ + +#include "constitutive/fluid/multifluid/CO2Brine/functions/PVTFunctionHelpers.hpp" // for readTable +#include "TwoPhaseFluid.hpp" +#include "TwoPhaseFluidFields.hpp" + +#include "functions/FunctionManager.hpp" + + +namespace geos +{ + +using namespace dataRepository; + +namespace constitutive +{ + + +TwoPhaseFluid::TwoPhaseFluid( string const & name, Group * const parent ) + : ConstitutiveBase( name, parent ) +{ + registerWrapper( viewKeyStruct::phaseNamesString(), &m_phaseNames ). + setRTTypeName( rtTypes::CustomTypes::groupNameRefArray ). + setInputFlag( InputFlags::OPTIONAL ). + setDescription( "List of fluid phases" ); + + // 1) First option: specify PVT tables from one file per phase, read the files line by line, and populate the internal TableFunctions + registerWrapper( viewKeyStruct::tableFilesString(), &m_tableFiles ). + setInputFlag( InputFlags::OPTIONAL ). + setRestartFlags( RestartFlags::NO_WRITE ). + setDescription( "List of filenames with input PVT tables (one per phase)" ); + + // 2) Second option: specify TableFunction names for each phase, + registerWrapper( viewKeyStruct::densityTableNamesString(), &m_densityTableNames ). + setRTTypeName( rtTypes::CustomTypes::groupNameRefArray ). + setInputFlag( InputFlags::OPTIONAL ). + setDescription( "List of density TableFuncion names from the Function block. \n" + "The user must provide one TableFunction per phase, respecting the order provided in \"phaseNames\"." ); + + registerWrapper( viewKeyStruct::viscosityTableNamesString(), &m_viscosityTableNames ). + setRTTypeName( rtTypes::CustomTypes::groupNameRefArray ). + setInputFlag( InputFlags::OPTIONAL ). + setDescription( "List of viscosity TableFuncion names from the Function block. \n" + "The user must provide one TableFunction per phase, respecting the order provided in \"phaseNames\"." ); + + registerField( fields::twophasefluid::phaseDensity{}, &m_phaseDensity.value ); + registerField( fields::twophasefluid::dPhaseDensity{}, &m_phaseDensity.derivs ); + registerField( fields::twophasefluid::phaseDensity_n{}, &m_phaseDensity_n ); + + registerField( fields::twophasefluid::phaseViscosity{}, &m_phaseViscosity.value ); + registerField( fields::twophasefluid::dPhaseViscosity{}, &m_phaseViscosity.derivs ); +} + + +std::unique_ptr< ConstitutiveBase > +TwoPhaseFluid::deliverClone( string const & name, Group * const parent ) const +{ + return ConstitutiveBase::deliverClone( name, parent ); +} + + +void TwoPhaseFluid::resizeFields( localIndex const size, localIndex const numPts ) +{ + // Assume sole dependency on pressure, i.e. one derivative + m_phaseDensity.value.resize( size, numPts, 2 ); + m_phaseDensity.derivs.resize( size, numPts, 2, 1 ); + + m_phaseDensity_n.resize( size, numPts, 2 ); + + m_phaseViscosity.value.resize( size, numPts, 2 ); + m_phaseViscosity.derivs.resize( size, numPts, 2, 1 ); +} + + +void TwoPhaseFluid::allocateConstitutiveData( dataRepository::Group & parent, + localIndex const numConstitutivePointsPerParentIndex ) +{ + ConstitutiveBase::allocateConstitutiveData( parent, numConstitutivePointsPerParentIndex ); + resizeFields( parent.size(), numConstitutivePointsPerParentIndex ); +} + + +void TwoPhaseFluid::postInputInitialization() +{ + ConstitutiveBase::postInputInitialization(); + + // Input relationships can be provided either as text files or TableFunctions. + m_tableFiles.empty() ? readInputDataFromTableFunctions() : readInputDataFromFileTableFunctions(); + + checkTableConsistency(); +} + + +void TwoPhaseFluid::fillData( integer const ip, + array1d< array1d< real64 > > const & tableValues ) +{ + array1d< array1d< real64 > > pressureCoords( 1 ); + pressureCoords[0].resize( tableValues.size() ); + array1d< real64 > density( tableValues.size() ); + array1d< real64 > viscosity( tableValues.size() ); + + for( localIndex i = 0; i < tableValues.size(); ++i ) + { + GEOS_THROW_IF_NE_MSG( tableValues[i].size(), 3, + GEOS_FMT( "{}: three columns (pressure, density, and viscosity) are expected", getFullName() ), + InputError ); + + pressureCoords[0][i] = tableValues[i][0]; + density[i] = tableValues[i][1]; + viscosity[i] = tableValues[i][2]; + } + + string const densityTableName = getName() + "DensityPhase" + GEOS_FMT( "{}", ip ); + string const viscosityTableName = getName() + "ViscosityPhase" + GEOS_FMT( "{}", ip ); + m_densityTableNames.emplace_back( densityTableName ); + m_viscosityTableNames.emplace_back( viscosityTableName ); + + FunctionManager & functionManager = FunctionManager::getInstance(); + + TableFunction & tableDensity = + dynamicCast< TableFunction & >( *functionManager.createChild( "TableFunction", densityTableName ) ); + tableDensity.setTableCoordinates( pressureCoords, { units::Pressure } ); + tableDensity.setTableValues( density, units::Density ); + tableDensity.setInterpolationMethod( TableFunction::InterpolationType::Linear ); + + TableFunction & tableViscosity = + dynamicCast< TableFunction & >( *functionManager.createChild( "TableFunction", viscosityTableName ) ); + tableViscosity.setTableCoordinates( pressureCoords, { units::Pressure } ); + tableViscosity.setTableValues( viscosity, units::Viscosity ); + tableViscosity.setInterpolationMethod( TableFunction::InterpolationType::Linear ); +} + + +void TwoPhaseFluid::readInputDataFromFileTableFunctions() +{ + // Check for ambiguous definition + GEOS_THROW_IF( !(m_densityTableNames.empty() && m_viscosityTableNames.empty()), + GEOS_FMT( "{}: input is redundant (both TableFunction names and text files)", getFullName() ), + InputError ); + + + // Check that we have exactly two table files (one per phase) + GEOS_THROW_IF_NE_MSG( m_tableFiles.size(), 2, + GEOS_FMT( "{}: expecting two table files (one per phase)", getFullName() ), + InputError ); + + array1d< array1d< real64 > > tableValues; + for( integer ip = 0; ip < 2; ++ip ) + { + tableValues.clear(); + geos::constitutive::PVTProps::BlackOilTables::readTable( m_tableFiles[ip], 3, tableValues ); + fillData( ip, tableValues ); + } +} + + +void TwoPhaseFluid::readInputDataFromTableFunctions() +{ + // Check for ambiguous definition + GEOS_THROW_IF( !m_tableFiles.empty(), + GEOS_FMT( "{}: input is redundant (both TableFunction names and text files)", getFullName() ), + InputError ); + + // Since we are considering a two phase fluid, we should have exactly 2 tables per property + GEOS_THROW_IF_NE_MSG( m_densityTableNames.size(), 2, + GEOS_FMT( "{}: one density table must be provided for each phase", getFullName() ), + InputError ); + + GEOS_THROW_IF_NE_MSG( m_viscosityTableNames.size(), 2, + GEOS_FMT( "{}: one viscosity table must be provided for each phase", getFullName() ), + InputError ); + + + FunctionManager const & functionManager = FunctionManager::getInstance(); + + for( integer iph = 0; iph < 2; ++iph ) + { + GEOS_THROW_IF( !functionManager.hasGroup( m_densityTableNames[iph] ), + GEOS_FMT( "{}: density table '{}' not found", getFullName(), m_densityTableNames[iph] ), + InputError ); + + GEOS_THROW_IF( !functionManager.hasGroup( m_viscosityTableNames[iph] ), + GEOS_FMT( "{}: viscosity table '{}' not found", getFullName(), m_viscosityTableNames[iph] ), + InputError ); + } +} + + +void TwoPhaseFluid::initializePostSubGroups() +{ + ConstitutiveBase::initializePostSubGroups(); + + FunctionManager const & functionManager = FunctionManager::getInstance(); + for( integer iph = 0; iph < 2; ++iph ) + { + // Grab the tables by name from the function manager, + // then add them in a list to create their table wrappers when needed + TableFunction const & densityTable = functionManager.getGroup< TableFunction const >( m_densityTableNames[iph] ); + m_densityTables.emplace_back( &densityTable ); + m_densityTableKernels.emplace_back( m_densityTables[iph]->createKernelWrapper() ); + + TableFunction const & viscosityTable = functionManager.getGroup< TableFunction const >( m_viscosityTableNames[iph] ); + m_viscosityTables.emplace_back( &viscosityTable ); + m_viscosityTableKernels.emplace_back( m_viscosityTables[iph]->createKernelWrapper() ); + } +} + + +void TwoPhaseFluid::checkTableConsistency() const +{ + FunctionManager const & functionManager = FunctionManager::getInstance(); + for( integer iph = 0; iph < 2; ++iph ) + { + TableFunction const & densityTable = functionManager.getGroup< TableFunction const >( m_densityTableNames[iph] ); + arrayView1d< real64 const > const density = densityTable.getValues(); + + for( localIndex i = 1; i < density.size(); ++i ) + { + GEOS_THROW_IF( density[i] - density[i-1] < 0, + GEOS_FMT( "{}: in table '{}' density values must be increasing", getFullName(), densityTable.getName() ), + InputError ); + } + } +} + + +TwoPhaseFluid::KernelWrapper +TwoPhaseFluid::createKernelWrapper() +{ + return KernelWrapper( m_densityTableKernels, + m_viscosityTableKernels, + m_phaseDensity.toView(), + m_phaseViscosity.toView()); +} + + +TwoPhaseFluid::KernelWrapper::KernelWrapper( + arrayView1d< TableFunction::KernelWrapper const > densityTables, + arrayView1d< TableFunction::KernelWrapper const > viscosityTables, + PhaseProp::ViewType phaseDensity, + PhaseProp::ViewType phaseViscosity ) + : m_densityTables( std::move( densityTables )), + m_viscosityTables( std::move( viscosityTables )), + m_phaseDensity( std::move( phaseDensity )), + m_phaseViscosity( std::move( phaseViscosity )) {} + + +REGISTER_CATALOG_ENTRY( ConstitutiveBase, TwoPhaseFluid, string const &, Group * const ) + +} // namespace constitutive +} // namespace geos diff --git a/src/coreComponents/constitutive/fluid/twophasefluid/TwoPhaseFluid.hpp b/src/coreComponents/constitutive/fluid/twophasefluid/TwoPhaseFluid.hpp new file mode 100644 index 00000000000..18cdc357bb0 --- /dev/null +++ b/src/coreComponents/constitutive/fluid/twophasefluid/TwoPhaseFluid.hpp @@ -0,0 +1,344 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * SPDX-License-Identifier: LGPL-2.1-only + * + * Copyright (c) 2016-2024 Lawrence Livermore National Security LLC + * Copyright (c) 2018-2024 Total, S.A + * Copyright (c) 2018-2024 The Board of Trustees of the Leland Stanford Junior University + * Copyright (c) 2023-2024 Chevron + * Copyright (c) 2019- GEOS/GEOSX Contributors + * All rights reserved + * + * See top level LICENSE, COPYRIGHT, CONTRIBUTORS, NOTICE, and ACKNOWLEDGEMENTS files for details. + * ------------------------------------------------------------------------------------------------------------ + */ + +/** + * @file TwoPhaseFluid.hpp + */ + +#ifndef GEOS_CONSTITUTIVE_FLUID_TWOPHASEFLUID_TWOPHASEFLUID_HPP_ +#define GEOS_CONSTITUTIVE_FLUID_TWOPHASEFLUID_TWOPHASEFLUID_HPP_ + +#include "common/DataLayouts.hpp" +#include "functions/TableFunction.hpp" +#include "constitutive/ConstitutiveBase.hpp" + +#include "constitutive/fluid/multifluid/Layouts.hpp" +#include "constitutive/fluid/multifluid/MultiFluidUtils.hpp" + +#include "constitutive/ConstitutivePassThruHandler.hpp" + + +namespace geos +{ +namespace constitutive +{ + +class TwoPhaseFluid : public ConstitutiveBase +{ +public: + + TwoPhaseFluid( string const & name, + Group * const parent ); + + virtual std::unique_ptr< ConstitutiveBase > + deliverClone( string const & name, + Group * const parent ) const override; + + virtual void allocateConstitutiveData( dataRepository::Group & parent, + localIndex const numConstitutivePointsPerParentIndex ) override; + + /** + * @name Static Factory Catalog members and functions + */ + ///@{ + + /** + * @brief Static catalog string + * @return A string that is used to register/lookup this class in the registry + */ + static std::string catalogName() { return "TwoPhaseFluid"; } + + /** + * @brief Get catalog name + * @return Name string + */ + virtual string getCatalogName() const override { return catalogName(); } + + ///@} + + + + /** + * @brief Getter for the fluid phase names + * @return an array storing the phase names + */ + string_array const & phaseNames() const { return m_phaseNames; } + + struct viewKeyStruct : ConstitutiveBase::viewKeyStruct + { + static constexpr char const * tableFilesString() { return "tableFiles"; } + static constexpr char const * phaseNamesString() { return "phaseNames"; } + static constexpr char const * densityTableNamesString() { return "densityTableNames"; } + static constexpr char const * viscosityTableNamesString() { return "viscosityTableNames"; } + }; + + arrayView3d< real64 const, multifluid::USD_PHASE > phaseDensity_n() const + { return m_phaseDensity_n; } + + arrayView3d< real64 const, multifluid::USD_PHASE > phaseDensity() const + { return m_phaseDensity.value; } + + arrayView4d< real64 const, multifluid::USD_PHASE_DC > dPhaseDensity() const + { return m_phaseDensity.derivs; } + + arrayView3d< real64 const, multifluid::USD_PHASE > phaseViscosity() const + { return m_phaseViscosity.value; } + + arrayView4d< real64 const, multifluid::USD_PHASE_DC > dPhaseViscosity() const + { return m_phaseViscosity.derivs; } + + using PhaseProp = MultiFluidVar< real64, 3, constitutive::multifluid::LAYOUT_PHASE, constitutive::multifluid::LAYOUT_PHASE_DC >; + + + class KernelWrapper + { +public: + + /// @cond DO_NOT_DOCUMENT + /// We need these SMFs to avoid host-device errors with CUDA. + KernelWrapper() = default; + KernelWrapper( KernelWrapper const & ) = default; + KernelWrapper & operator=( KernelWrapper const & ) = default; + KernelWrapper & operator=( KernelWrapper && ) = default; + /// @endcond + + /** + * @brief Get number of elements in this wrapper. + * @return number of elements + */ + GEOS_HOST_DEVICE + GEOS_FORCE_INLINE + localIndex numElems() const { return m_phaseDensity.value.size( 0 ); } + + /** + * @brief Get number of gauss points per element. + * @return number of gauss points per element + */ + GEOS_HOST_DEVICE + GEOS_FORCE_INLINE + localIndex numGauss() const { return m_phaseDensity.value.size( 1 ); } + + + GEOS_HOST_DEVICE + void compute( real64 const pressure, + PhaseProp::SliceType const phaseDensity, + PhaseProp::SliceType const phaseViscosity ) const; + + GEOS_HOST_DEVICE + void update( localIndex const k, + localIndex const q, + real64 const pressure ) const; + +private: + + friend class TwoPhaseFluid; + + /** + * @brief Constructor for the class doing in-kernel two-phase fluid updates + * @param[in] densityTables density tables + * @param[in] viscosityTables viscosity tables + * @param[in] phaseDensity phase densities (+ derivatives) in the cell + * @param[in] phaseViscosity phase viscosities (+ derivatives) in the cell + */ + KernelWrapper( + arrayView1d< TableFunction::KernelWrapper const > densityTables, + arrayView1d< TableFunction::KernelWrapper const > viscosityTables, + PhaseProp::ViewType phaseDensity, + PhaseProp::ViewType phaseViscosity ); + + +protected: + + KernelWrapper( + arrayView1d< TableFunction::KernelWrapper const > densityTables, + arrayView1d< TableFunction::KernelWrapper const > viscosityTables + ); + + /// Table kernel wrappers to interpolate in the two phase (\rho vs p) tables + arrayView1d< TableFunction::KernelWrapper const > m_densityTables; + + /// Table kernel wrappers to interpolate in the two phase (\mu vs p) tables + arrayView1d< TableFunction::KernelWrapper const > m_viscosityTables; + + /** + * @brief Utility function to compute densities as a function of pressure (keeping derivatives) + * @param[in] pressure pressure in the cell + * @param[out] phaseDensity the phase density in the cell (+ derivatives) + */ + GEOS_HOST_DEVICE + void computeDensities( real64 const pressure, + PhaseProp::SliceType const & phaseDensity ) const; + + /** + * @brief Utility function to compute viscosities as a function of pressure (keeping derivatives) + * @param[in] pressure pressure in the cell + * @param[out] phaseViscosity the phase viscosities in the cell (+ derivatives) + */ + GEOS_HOST_DEVICE + void computeViscosities( real64 const pressure, + PhaseProp::SliceType const & phaseViscosity ) const; + + /// Views on the phase properties + PhaseProp::ViewType m_phaseDensity; + PhaseProp::ViewType m_phaseViscosity; + + }; //class KernelWrapper + + + string_array m_phaseNames; + + + path_array m_tableFiles; + + /// Names of the density tables (one per phase) + string_array m_densityTableNames; + + /// Names of the viscosity tables (one per phase) + string_array m_viscosityTableNames; + + PhaseProp m_phaseDensity; + PhaseProp m_phaseViscosity; + + /// Backup data + array3d< real64, multifluid::LAYOUT_PHASE > m_phaseDensity_n; + + + virtual void resizeFields( localIndex const size, localIndex const numPts ); + + virtual void postInputInitialization() override; + + virtual void initializePostSubGroups() override; + + /// Table kernel wrappers to interpolate (\rho vs p) tables + array1d< TableFunction const * > m_densityTables; + /// Table kernel wrappers of m_densityTables + array1d< TableFunction::KernelWrapper > m_densityTableKernels; + + /// Table kernel wrappers to interpolate (\mu vs p) tables + array1d< TableFunction const * > m_viscosityTables; + /// Table kernel wrappers of m_viscosityTables + array1d< TableFunction::KernelWrapper > m_viscosityTableKernels; + + KernelWrapper createKernelWrapper(); + + +private: + void readInputDataFromTableFunctions(); + void readInputDataFromFileTableFunctions(); + + /** + * @brief Fill the fluid data (pressure, density, viscosity) + * @param[in] ip the index of the phase + * @param[in] tableValues the values in the fluid table + */ + void fillData( integer const ip, + array1d< array1d< real64 > > const & tableValues ); + + /** + * @brief Check the monotonicity of the PVT relationship + */ + void checkTableConsistency() const; +}; + + +GEOS_HOST_DEVICE +GEOS_FORCE_INLINE +void TwoPhaseFluid::KernelWrapper:: + computeDensities( real64 const pressure, + PhaseProp::SliceType const & phaseDensity ) const +{ + using Deriv = constitutive::multifluid::DerivativeOffset; + + LvArray::forValuesInSlice( phaseDensity.derivs, []( real64 & val ) { val = 0.0; } ); + + for( integer iph = 0; iph < 2; ++iph ) + { + // interpolate in the table to get the phase density and derivatives + real64 dPhaseDens_dPres = 0.0; + + phaseDensity.value[iph] = m_densityTables[iph].compute( &pressure, &dPhaseDens_dPres ); + phaseDensity.derivs[iph][Deriv::dP] = dPhaseDens_dPres; + } +} + + +GEOS_HOST_DEVICE +GEOS_FORCE_INLINE +void TwoPhaseFluid::KernelWrapper:: + computeViscosities( real64 const pressure, + PhaseProp::SliceType const & phaseViscosity ) const +{ + using Deriv = constitutive::multifluid::DerivativeOffset; + + LvArray::forValuesInSlice( phaseViscosity.derivs, []( real64 & val ) { val = 0.0; } ); + + for( integer iph = 0; iph < 2; ++iph ) + { + // interpolate in the table to get the phase viscosity and derivatives + real64 dPhaseVisc_dPres = 0.0; + phaseViscosity.value[iph] = m_viscosityTables[iph].compute( &pressure, &dPhaseVisc_dPres ); + phaseViscosity.derivs[iph][Deriv::dP] = dPhaseVisc_dPres; + } +} + + +GEOS_HOST_DEVICE +GEOS_FORCE_INLINE +void TwoPhaseFluid::KernelWrapper:: + compute( real64 const pressure, + PhaseProp::SliceType const phaseDensity, + PhaseProp::SliceType const phaseViscosity ) const +{ + computeDensities( pressure, + phaseDensity ); + + computeViscosities( pressure, + phaseViscosity ); +} + + +GEOS_HOST_DEVICE +GEOS_FORCE_INLINE +void TwoPhaseFluid::KernelWrapper:: + update( localIndex const k, + localIndex const q, + real64 const pressure + ) const +{ + compute( pressure, + m_phaseDensity( k, q ), + m_phaseViscosity( k, q ) ); +} + + +template< typename LAMBDA > +void constitutiveUpdatePassThru( TwoPhaseFluid const & fluid, + LAMBDA && lambda ) +{ + ConstitutivePassThruHandler< TwoPhaseFluid >::execute( fluid, std::forward< LAMBDA >( lambda ) ); +} + + +template< typename LAMBDA > +void constitutiveUpdatePassThru( TwoPhaseFluid & fluid, + LAMBDA && lambda ) +{ + ConstitutivePassThruHandler< TwoPhaseFluid >::execute( fluid, std::forward< LAMBDA >( lambda ) ); +} + +} // namespace constitutive +} // namespace geos + +#endif // GEOS_CONSTITUTIVE_FLUID_TWOPHASEFLUID_TWOPHASEFLUID_HPP_ diff --git a/src/coreComponents/constitutive/fluid/twophasefluid/TwoPhaseFluidFields.hpp b/src/coreComponents/constitutive/fluid/twophasefluid/TwoPhaseFluidFields.hpp new file mode 100644 index 00000000000..9ce0dc5c33d --- /dev/null +++ b/src/coreComponents/constitutive/fluid/twophasefluid/TwoPhaseFluidFields.hpp @@ -0,0 +1,84 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * SPDX-License-Identifier: LGPL-2.1-only + * + * Copyright (c) 2016-2024 Lawrence Livermore National Security LLC + * Copyright (c) 2018-2024 Total, S.A + * Copyright (c) 2018-2024 The Board of Trustees of the Leland Stanford Junior University + * Copyright (c) 2023-2024 Chevron + * Copyright (c) 2019- GEOS/GEOSX Contributors + * All rights reserved + * + * See top level LICENSE, COPYRIGHT, CONTRIBUTORS, NOTICE, and ACKNOWLEDGEMENTS files for details. + * ------------------------------------------------------------------------------------------------------------ + */ + +/** + * @file TwoPhaseFluidFields.hpp + */ + +#ifndef GEOS_CONSTITUTIVE_FLUID_TWOPHASEFLUIDFIELDS_HPP_ +#define GEOS_CONSTITUTIVE_FLUID_TWOPHASEFLUIDFIELDS_HPP_ + +#include "constitutive/fluid/multifluid/Layouts.hpp" +#include "mesh/MeshFields.hpp" + + +namespace geos +{ + +namespace fields +{ + +namespace twophasefluid +{ + +using array3dLayoutPhase = array3d< real64, constitutive::multifluid::LAYOUT_PHASE >; +using array4dLayoutPhase_d = array4d< real64, constitutive::multifluid::LAYOUT_PHASE_DC >; + +DECLARE_FIELD( phaseDensity, + "phaseDensity", + array3dLayoutPhase, + 0, + LEVEL_0, + WRITE_AND_READ, + "Phase density" ); + +DECLARE_FIELD( phaseDensity_n, + "phaseDensity_n", + array3dLayoutPhase, + 0, + NOPLOT, + WRITE_AND_READ, + "Phase density at the previous converged time step" ); + +DECLARE_FIELD( dPhaseDensity, + "dPhaseDensity", + array4dLayoutPhase_d, + 0, + NOPLOT, + NO_WRITE, + "Derivative of phase density with respect to pressure" ); + +DECLARE_FIELD( phaseViscosity, + "phaseViscosity", + array3dLayoutPhase, + 0, + LEVEL_0, + WRITE_AND_READ, + "Phase viscosity" ); + +DECLARE_FIELD( dPhaseViscosity, + "dPhaseViscosity", + array4dLayoutPhase_d, + 0, + NOPLOT, + NO_WRITE, + "Derivative of phase viscosity with respect to pressure" ); + +} // namespace twophasefluid + +} // namespace constitutive +} // namespace geos + +#endif // GEOS_CONSTITUTIVE_FLUID_TWOPHASEFLUIDFIELDS_HPP_ diff --git a/src/coreComponents/constitutive/relativePermeability/BrooksCoreyBakerRelativePermeability.hpp b/src/coreComponents/constitutive/relativePermeability/BrooksCoreyBakerRelativePermeability.hpp index 476def9f455..21fdbe4a8a6 100644 --- a/src/coreComponents/constitutive/relativePermeability/BrooksCoreyBakerRelativePermeability.hpp +++ b/src/coreComponents/constitutive/relativePermeability/BrooksCoreyBakerRelativePermeability.hpp @@ -139,19 +139,17 @@ class BrooksCoreyBakerRelativePermeability : public RelativePermeabilityBase static constexpr char const * volFracScaleString() { return "volFracScale"; } }; - arrayView1d< real64 const > getPhaseMinVolumeFraction() const override { return m_phaseMinVolumeFraction; }; - real64 getWettingPhaseMinVolumeFraction() const override { integer ipWetting; - std::tie( ipWetting, std::ignore ) = wettingAndNonWettingPhaseIndices(); + std::tie( ipWetting, std::ignore ) = phaseIndex( getPhaseOrder()); return m_phaseMinVolumeFraction[ipWetting]; } real64 getNonWettingMinVolumeFraction() const override { integer ipNonWetting; - std::tie( std::ignore, ipNonWetting ) = wettingAndNonWettingPhaseIndices(); + std::tie( std::ignore, ipNonWetting ) = phaseIndex( getPhaseOrder()); return m_phaseMinVolumeFraction[ipNonWetting]; }; diff --git a/src/coreComponents/constitutive/relativePermeability/BrooksCoreyRelativePermeability.hpp b/src/coreComponents/constitutive/relativePermeability/BrooksCoreyRelativePermeability.hpp index 30ee2ff3692..2de3998a78e 100644 --- a/src/coreComponents/constitutive/relativePermeability/BrooksCoreyRelativePermeability.hpp +++ b/src/coreComponents/constitutive/relativePermeability/BrooksCoreyRelativePermeability.hpp @@ -105,19 +105,18 @@ class BrooksCoreyRelativePermeability : public RelativePermeabilityBase static constexpr char const * volFracScaleString() { return "volFracScale"; } }; //END_SPHINX_INCLUDE_01 - arrayView1d< real64 const > getPhaseMinVolumeFraction() const override { return m_phaseMinVolumeFraction; }; real64 getWettingPhaseMinVolumeFraction() const override { integer ipWetting; - std::tie( ipWetting, std::ignore ) = wettingAndNonWettingPhaseIndices(); + std::tie( ipWetting, std::ignore ) = phaseIndex( getPhaseOrder()); return m_phaseMinVolumeFraction[ipWetting]; } real64 getNonWettingMinVolumeFraction() const override { integer ipNonWetting; - std::tie( std::ignore, ipNonWetting ) = wettingAndNonWettingPhaseIndices(); + std::tie( std::ignore, ipNonWetting ) = phaseIndex( getPhaseOrder()); return m_phaseMinVolumeFraction[ipNonWetting]; }; diff --git a/src/coreComponents/constitutive/relativePermeability/BrooksCoreyStone2RelativePermeability.hpp b/src/coreComponents/constitutive/relativePermeability/BrooksCoreyStone2RelativePermeability.hpp index 47eefdd512a..2d83dcda597 100644 --- a/src/coreComponents/constitutive/relativePermeability/BrooksCoreyStone2RelativePermeability.hpp +++ b/src/coreComponents/constitutive/relativePermeability/BrooksCoreyStone2RelativePermeability.hpp @@ -138,7 +138,7 @@ class BrooksCoreyStone2RelativePermeability : public RelativePermeabilityBase static constexpr char const * volFracScaleString() { return "volFracScale"; } }; - arrayView1d< real64 const > getPhaseMinVolumeFraction() const override { return m_phaseMinVolumeFraction; }; + arrayView1d< real64 const > getPhaseMinVolumeFraction() const { return m_phaseMinVolumeFraction; }; real64 getWettingPhaseMinVolumeFraction() const override { diff --git a/src/coreComponents/constitutive/relativePermeability/KilloughHysteresis.hpp b/src/coreComponents/constitutive/relativePermeability/KilloughHysteresis.hpp index 58499b94b64..48d2d3deaa2 100644 --- a/src/coreComponents/constitutive/relativePermeability/KilloughHysteresis.hpp +++ b/src/coreComponents/constitutive/relativePermeability/KilloughHysteresis.hpp @@ -137,18 +137,8 @@ class KilloughHysteresis m_extremumPhaseVolFraction ), InputError ); - GEOS_THROW_IF( m_criticalImbibitionValue < 0 || m_criticalImbibitionValue > 1, - GEOS_FMT( "KilloughHysteresis: the critical imbibition relative permeability is equal to {} but must be between 0 an 1", - m_criticalImbibitionValue ), - InputError ); - GEOS_THROW_IF( m_criticalDrainageValue < 0 || m_criticalDrainageValue > 1, - GEOS_FMT( "KilloughHysteresis: the critical drainage relative permeability is equal to {} but must be between 0 an 1", - m_criticalDrainageValue ), - InputError ); - GEOS_THROW_IF( m_extremumValue < 0 || m_extremumValue > 1, - GEOS_FMT( "KilloughHysteresis: the extremum relative permeability is equal to {} but must be between 0 an 1", - m_extremumValue ), - InputError ); + // Note: value validation removed because this struct is used for both relative permeability (0-1 range) + // and capillary pressure (can be in Pa or other units, typically >> 1). The value range depends on the application. m_isWetting = m_criticalDrainagePhaseVolFraction > m_extremumPhaseVolFraction; diff --git a/src/coreComponents/constitutive/relativePermeability/RelativePermeabilityBase.cpp b/src/coreComponents/constitutive/relativePermeability/RelativePermeabilityBase.cpp index d93d335873e..57890850fab 100644 --- a/src/coreComponents/constitutive/relativePermeability/RelativePermeabilityBase.cpp +++ b/src/coreComponents/constitutive/relativePermeability/RelativePermeabilityBase.cpp @@ -153,6 +153,7 @@ std::tuple< integer, integer > RelativePermeabilityBase::wettingAndNonWettingPha return std::make_tuple( ipWetting, ipNonWetting ); } + } // namespace constitutive } // namespace geos diff --git a/src/coreComponents/constitutive/relativePermeability/RelativePermeabilityBase.hpp b/src/coreComponents/constitutive/relativePermeability/RelativePermeabilityBase.hpp index 0bc67b26a39..0600a41643a 100644 --- a/src/coreComponents/constitutive/relativePermeability/RelativePermeabilityBase.hpp +++ b/src/coreComponents/constitutive/relativePermeability/RelativePermeabilityBase.hpp @@ -165,8 +165,8 @@ class RelativePermeabilityBase : public ConstitutiveBase static std::tuple< integer, integer > phaseIndex( arrayView1d< integer const > const & phaseOrder ); arrayView1d< integer const > getPhaseOrder() const { return m_phaseOrder; } - virtual arrayView1d< real64 const > getPhaseMinVolumeFraction() const = 0; virtual real64 getWettingPhaseMinVolumeFraction() const = 0; + virtual real64 getNonWettingMinVolumeFraction() const = 0; std::tuple< integer, integer > wettingAndNonWettingPhaseIndices() const; @@ -217,6 +217,45 @@ class RelativePermeabilityBase : public ConstitutiveBase }; + +/// for use in RelpermDriver to browse the drainage curves +/// by setting the MaxHistoricalNonWettingSat to Snwmin and MinWettingSat to Sw +inline std::tuple< integer, integer > RelativePermeabilityBase::phaseIndex( arrayView1d< integer const > const & phaseOrder ) +{ + using PT = PhaseType; + integer const ipWater = phaseOrder[PT::WATER]; + integer const ipOil = phaseOrder[PT::OIL]; + integer const ipGas = phaseOrder[PT::GAS]; + + integer ipWetting = -1, ipNonWetting = -1; + + if( ipWater >= 0 && ipOil >= 0 && ipGas >= 0 ) + { + ipWetting = ipWater; + ipNonWetting = ipGas; + } + else if( ipWater < 0 ) + { + ipWetting = ipOil; + ipNonWetting = ipGas; + } + else if( ipOil < 0 ) + { + ipWetting = ipWater; + ipNonWetting = ipGas; + } + else if( ipGas < 0 ) + { + ipWetting = ipWater; + ipNonWetting = ipOil; + } + + //maybe a bit too pythonic + return std::make_tuple( ipWetting, ipNonWetting ); +} + + + } // namespace constitutive } // namespace geos diff --git a/src/coreComponents/constitutive/relativePermeability/TableRelativePermeability.hpp b/src/coreComponents/constitutive/relativePermeability/TableRelativePermeability.hpp index 7b2fb4d7bcd..255fa33aca3 100644 --- a/src/coreComponents/constitutive/relativePermeability/TableRelativePermeability.hpp +++ b/src/coreComponents/constitutive/relativePermeability/TableRelativePermeability.hpp @@ -142,19 +142,17 @@ class TableRelativePermeability : public RelativePermeabilityBase static constexpr char const * threePhaseInterpolatorString() { return "threePhaseInterpolator"; } }; - arrayView1d< real64 const > getPhaseMinVolumeFraction() const override { return m_phaseMinVolumeFraction; }; - real64 getWettingPhaseMinVolumeFraction() const override { integer ipWetting; - std::tie( ipWetting, std::ignore ) = wettingAndNonWettingPhaseIndices(); + std::tie( ipWetting, std::ignore ) = phaseIndex( getPhaseOrder()); return m_phaseMinVolumeFraction[ipWetting]; } real64 getNonWettingMinVolumeFraction() const override { integer ipNonWetting; - std::tie( std::ignore, ipNonWetting ) = wettingAndNonWettingPhaseIndices(); + std::tie( std::ignore, ipNonWetting ) = phaseIndex( getPhaseOrder()); return m_phaseMinVolumeFraction[ipNonWetting]; }; diff --git a/src/coreComponents/constitutive/relativePermeability/TableRelativePermeabilityHelpers.cpp b/src/coreComponents/constitutive/relativePermeability/TableRelativePermeabilityHelpers.cpp index 965c9278fc1..3d360fc6362 100644 --- a/src/coreComponents/constitutive/relativePermeability/TableRelativePermeabilityHelpers.cpp +++ b/src/coreComponents/constitutive/relativePermeability/TableRelativePermeabilityHelpers.cpp @@ -81,7 +81,7 @@ TableRelativePermeabilityHelpers::validateRelativePermeabilityTable( TableFuncti if( isZero( relPerm[i-1] ) && !isZero( relPerm[i] ) ) { phaseMinVolFrac = phaseVolFrac[i-1]; - phaseRelPermMinEndPoint = 0.; + phaseRelPermMinEndPoint = relPerm[i-1]; } } diff --git a/src/coreComponents/constitutive/relativePermeability/TableRelativePermeabilityHysteresis.cpp b/src/coreComponents/constitutive/relativePermeability/TableRelativePermeabilityHysteresis.cpp index 910c42cce41..69814ea81fd 100644 --- a/src/coreComponents/constitutive/relativePermeability/TableRelativePermeabilityHysteresis.cpp +++ b/src/coreComponents/constitutive/relativePermeability/TableRelativePermeabilityHysteresis.cpp @@ -22,7 +22,8 @@ #include "constitutive/relativePermeability/RelativePermeabilityFields.hpp" #include "constitutive/relativePermeability/TableRelativePermeabilityHelpers.hpp" #include "functions/FunctionManager.hpp" -#include "LogLevelsInfo.hpp" +#include "constitutiveDrivers/relativePermeability/RelpermDriver.hpp" +#include "constitutive/ConstitutiveManager.hpp" namespace geos { @@ -89,48 +90,68 @@ TableRelativePermeabilityHysteresis::TableRelativePermeabilityHysteresis( std::s "To neglect hysteresis on this phase, just use the same table name for the drainage and imbibition curves" ); // hysteresis input parameters - - registerWrapper( viewKeyStruct::landParameterString(), &m_landParam ). - setInputFlag( InputFlags::FALSE ). // will be deduced from tables + registerWrapper( viewKeyStruct::phaseHasHysteresisString(), &m_phaseHasHysteresis ). + setInputFlag( InputFlags::FALSE ) + . // will be deduced from tables setSizedFromParent( 0 ); - // forwarded to KilloughHysteresis - registerWrapper( KilloughHysteresis::viewKeyStruct::jerauldParameterAString(), &m_jerauldParam_a ). - setInputFlag( InputFlags::OPTIONAL ). - setApplyDefaultValue( 0.1 ). - setDescription( "First parameter (modification parameter) introduced by Jerauld in the Land trapping model (see RTD documentation)." ); - - registerWrapper( KilloughHysteresis::viewKeyStruct::jerauldParameterBString(), &m_jerauldParam_b ). - setInputFlag( InputFlags::OPTIONAL ). - setApplyDefaultValue( 0.0 ). - setDescription( "Second parameter (modification parameter) introduced by Jerauld in the Land trapping model (see RTD documentation)." ); - - registerWrapper( KilloughHysteresis::viewKeyStruct::killoughCurvatureParameterString(), &m_killoughCurvatureParamRelPerm ). - setInputFlag( InputFlags::OPTIONAL ). - setApplyDefaultValue( 1.0 ). - setDescription( "Curvature parameter introduced by Killough for wetting-phase hysteresis (see RTD documentation)." ); + registerField< fields::relperm::phaseMaxHistoricalVolFraction >( + &m_phaseMaxHistoricalVolFraction ); + registerField< fields::relperm::phaseMinHistoricalVolFraction >( + &m_phaseMinHistoricalVolFraction ); - // structs + /// Killough data registerWrapper( viewKeyStruct::drainageRelPermKernelWrappersString(), &m_drainageRelPermKernelWrappers ). setSizedFromParent( 0 ). - setRestartFlags( RestartFlags::NO_WRITE ); + setRestartFlags( + RestartFlags::NO_WRITE ); registerWrapper( viewKeyStruct::imbibitionRelPermKernelWrappersString(), &m_imbibitionRelPermKernelWrappers ). setSizedFromParent( 0 ). - setRestartFlags( RestartFlags::NO_WRITE ); + setRestartFlags( + RestartFlags::NO_WRITE ); - // Killough data - registerWrapper( viewKeyStruct::wettingCurveString(), &m_wettingCurve ). + registerWrapper( viewKeyStruct::landParameterString(), &m_landParam ). setInputFlag( InputFlags::FALSE ). // will be deduced from tables - setSizedFromParent( 0 ). - setRestartFlags( RestartFlags::NO_WRITE ); + setSizedFromParent( 0 ); + + + registerWrapper( viewKeyStruct::wettingCurveString(), &m_wettingCurve ). + setInputFlag( + InputFlags::FALSE ). // will be deduced from tables + setSizedFromParent( + 0 ) + .setRestartFlags( RestartFlags::NO_WRITE ); registerWrapper( viewKeyStruct::nonWettingCurveString(), &m_nonWettingCurve ). - setInputFlag( InputFlags::FALSE ). // will be deduced from tables - setSizedFromParent( 0 ). - setRestartFlags( RestartFlags::NO_WRITE ); + setInputFlag( + InputFlags::FALSE ). // will be deduced from tables + setSizedFromParent( + 0 ) + .setRestartFlags( RestartFlags::NO_WRITE ); + + //Forwarded to KilloughHysteresis + registerWrapper( KilloughHysteresis::viewKeyStruct::jerauldParameterAString(), &m_jerauldParam_a ). + setInputFlag( InputFlags::OPTIONAL ). + setApplyDefaultValue( 0.1 ). + setDescription( + "First parameter (modification parameter) introduced by Jerauld in the Land trapping model (see RTD documentation)." ); + + registerWrapper( KilloughHysteresis::viewKeyStruct::jerauldParameterBString(), &m_jerauldParam_b ). + setInputFlag( InputFlags::OPTIONAL ). + setApplyDefaultValue( 0.0 ). + setDescription( + "Second parameter (modification parameter) introduced by Jerauld in the Land trapping model (see RTD documentation)." ); + + registerWrapper(KilloughHysteresis::viewKeyStruct::killoughCurvatureParameterRelPermString(), &m_killoughCurvatureParamRelPerm ). + setInputFlag( + InputFlags::OPTIONAL ). + setApplyDefaultValue( + 1.0 ). + setDescription( + "Curvature parameter introduced by Killough for wetting-phase hysteresis (see RTD documentation)." ); registerWrapper( viewKeyStruct::waterOilMaxRelPermString(), &m_waterOilMaxRelPerm ). setInputFlag( InputFlags::FALSE ). // will be deduced from tables @@ -165,7 +186,6 @@ void TableRelativePermeabilityHysteresis::postInputInitialization() getFullName() ), InputError, getDataContext() ); - m_phaseMinVolumeFraction.resize( numPhases ); m_phaseHasHysteresis.resize( numPhases ); //initialize STONE-II only used var to avoid discrepancies in baselines @@ -235,7 +255,7 @@ void TableRelativePermeabilityHysteresis::postInputInitialization() InputError, getDataContext() ); //Killough section - KilloughHysteresis::postProcessInput( m_jerauldParam_a, m_jerauldParam_b, m_killoughCurvatureParamRelPerm ); + KilloughHysteresis::postProcessInput(m_jerauldParam_a, m_jerauldParam_b, m_killoughCurvatureParamRelPerm, 0.0); } void TableRelativePermeabilityHysteresis::initializePreSubGroups() @@ -262,33 +282,33 @@ void TableRelativePermeabilityHysteresis::checkExistenceAndValidateWettingRelPer using IPT = TableRelativePermeabilityHysteresis::ImbibitionPhasePairPhaseType; integer const numPhases = m_phaseNames.size(); integer ipWetting = -1, ipNonWetting = -1; - std::tie( ipWetting, ipNonWetting ) = RelativePermeabilityBase::wettingAndNonWettingPhaseIndices(); + std::tie( ipWetting, ipNonWetting ) = RelativePermeabilityBase::phaseIndex( m_phaseOrder ); // Step 1.a: take care of the two-phase case - real64 drainagePhaseMinVolFraction = -1; // output - real64 drainagePhaseMaxVolFraction = -1; - real64 drainagePhaseRelPermMinEndPoint = -1; - real64 drainagePhaseRelPermMaxEndPoint = -1; - - string const tableName = ( numPhases == 2 ) ? - m_drainageWettingNonWettingRelPermTableNames[0] : m_drainageWettingIntermediateRelPermTableNames[0]; - - checkExistenceAndValidateRelPermTable( tableName, // input - drainagePhaseMinVolFraction, // output + real64 drainagePhaseMinVolFraction, // output + drainagePhaseMaxVolFraction, + drainagePhaseRelPermMinEndPoint, + drainagePhaseRelPermMaxEndPoint; + GEOS_ASSERT( m_drainageWettingNonWettingRelPermTableNames.size() == 2 ); + auto tableName = ( numPhases == 2 ) ? m_drainageWettingNonWettingRelPermTableNames[0] : m_drainageWettingIntermediateRelPermTableNames[0]; +// integer const ipWetting = ( m_phaseOrder[PhaseType::WATER] >= 0 ) ? m_phaseOrder[PhaseType::WATER] : m_phaseOrder[PhaseType::OIL]; + checkExistenceAndValidateRelPermTable( tableName, // input + drainagePhaseMinVolFraction, // output drainagePhaseMaxVolFraction, drainagePhaseRelPermMinEndPoint, drainagePhaseRelPermMaxEndPoint ); - // imbibition if provided - real64 imbibitionPhaseMinVolFraction = drainagePhaseMinVolFraction; // output - real64 imbibitionPhaseMaxVolFraction = drainagePhaseMaxVolFraction; - real64 imbibitionPhaseRelPermMinEndPoint = drainagePhaseRelPermMinEndPoint; - real64 imbibitionPhaseRelPermMaxEndPoint = drainagePhaseRelPermMaxEndPoint; + + //imbibition if provided + real64 imbibitionPhaseMinVolFraction, // output + imbibitionPhaseMaxVolFraction, + imbibitionPhaseRelPermMinEndPoint, + imbibitionPhaseRelPermMaxEndPoint; if( m_phaseHasHysteresis[IPT::WETTING] ) { - checkExistenceAndValidateRelPermTable( m_imbibitionWettingRelPermTableName, // input - imbibitionPhaseMinVolFraction, // output + checkExistenceAndValidateRelPermTable( m_imbibitionWettingRelPermTableName, // input + imbibitionPhaseMinVolFraction, // output imbibitionPhaseMaxVolFraction, imbibitionPhaseRelPermMinEndPoint, imbibitionPhaseRelPermMaxEndPoint ); @@ -318,17 +338,9 @@ void TableRelativePermeabilityHysteresis::checkExistenceAndValidateWettingRelPer InputError, getDataContext() ); } - m_wettingCurve.setPoints( drainagePhaseMinVolFraction, drainagePhaseRelPermMinEndPoint, // same as imbibition min - imbibitionPhaseMaxVolFraction, imbibitionPhaseRelPermMaxEndPoint, - drainagePhaseMaxVolFraction, drainagePhaseRelPermMaxEndPoint ); - - m_phaseMinVolumeFraction[ipWetting] = drainagePhaseMinVolFraction; - - GEOS_LOG_LEVEL_RANK_0( logInfo::Init, GEOS_FMT( "Initializing wetting relperm curve with {(smin,krmin), (simax,krimax), (sdmax,krdmax)} : {({},{}),({},{}),({},{})}", - m_wettingCurve.m_extremumPhaseVolFraction, m_wettingCurve.m_extremumValue, - m_wettingCurve.m_criticalImbibitionPhaseVolFraction, m_wettingCurve.m_criticalImbibitionValue, - m_wettingCurve.m_criticalDrainagePhaseVolFraction, m_wettingCurve.m_criticalDrainageValue - )); + m_wettingCurve.setPoints( {drainagePhaseMinVolFraction, drainagePhaseRelPermMinEndPoint}, // extremum + {imbibitionPhaseMaxVolFraction, imbibitionPhaseRelPermMaxEndPoint}, // imbibition critical + {drainagePhaseMaxVolFraction, drainagePhaseRelPermMaxEndPoint} ); // drainage critical } void TableRelativePermeabilityHysteresis::checkExistenceAndValidateNonWettingRelPermTables() @@ -337,17 +349,17 @@ void TableRelativePermeabilityHysteresis::checkExistenceAndValidateNonWettingRel integer const numPhases = m_phaseNames.size(); integer ipWetting = -1, ipNonWetting = -1; - std::tie( ipWetting, ipNonWetting ) = RelativePermeabilityBase::wettingAndNonWettingPhaseIndices(); - - // treat drainage - real64 drainagePhaseMinVolFraction = -1; // output - real64 drainagePhaseMaxVolFraction = -1; - real64 drainagePhaseRelPermMinEndPoint = -1; - real64 drainagePhaseRelPermMaxEndPoint = -1; + std::tie( ipWetting, ipNonWetting ) = RelativePermeabilityBase::phaseIndex( m_phaseOrder ); + //treat drainage + real64 drainagePhaseMinVolFraction, // output + drainagePhaseMaxVolFraction, + drainagePhaseRelPermMinEndPoint, + drainagePhaseRelPermMaxEndPoint; // Step 1: Read the drainage for the non wetting phase - string const tableName = ( numPhases == 2 ) ? m_drainageWettingNonWettingRelPermTableNames[1] : - m_drainageNonWettingIntermediateRelPermTableNames[0]; + GEOS_ASSERT( m_drainageWettingNonWettingRelPermTableNames.size() == 2 ); + auto tableName = ( numPhases == 2 ) ? m_drainageWettingNonWettingRelPermTableNames[1] : + m_drainageNonWettingIntermediateRelPermTableNames[0]; checkExistenceAndValidateRelPermTable( tableName, // input drainagePhaseMinVolFraction, // output drainagePhaseMaxVolFraction, @@ -355,10 +367,10 @@ void TableRelativePermeabilityHysteresis::checkExistenceAndValidateNonWettingRel drainagePhaseRelPermMaxEndPoint ); // Step 2: validate non-wetting-phase imbibition relative permeability table - real64 imbibitionPhaseMinVolFraction = drainagePhaseMinVolFraction; // output - real64 imbibitionPhaseMaxVolFraction = drainagePhaseMaxVolFraction; - real64 imbibitionPhaseRelPermMinEndPoint = drainagePhaseRelPermMinEndPoint; - real64 imbibitionPhaseRelPermMaxEndPoint = drainagePhaseRelPermMaxEndPoint; + real64 imbibitionPhaseMinVolFraction, // output + imbibitionPhaseMaxVolFraction, + imbibitionPhaseRelPermMinEndPoint, + imbibitionPhaseRelPermMaxEndPoint; if( m_phaseHasHysteresis[IPT::NONWETTING] ) { @@ -395,17 +407,10 @@ void TableRelativePermeabilityHysteresis::checkExistenceAndValidateNonWettingRel } - m_nonWettingCurve.setPoints( drainagePhaseMaxVolFraction, drainagePhaseRelPermMaxEndPoint, // same as imbibition max - imbibitionPhaseMinVolFraction, imbibitionPhaseRelPermMinEndPoint, - drainagePhaseMinVolFraction, drainagePhaseRelPermMinEndPoint ); - - m_phaseMinVolumeFraction[ipNonWetting] = drainagePhaseMinVolFraction; - GEOS_LOG_LEVEL_RANK_0( logInfo::Init, GEOS_FMT( "Initializing non-wetting relperm curve with {(sdmin,krdmin), (simin,krimin), (smax,krmax)} : {({},{}),({},{}),({},{})}", - m_nonWettingCurve.m_criticalDrainagePhaseVolFraction, m_nonWettingCurve.m_criticalDrainageValue, - m_nonWettingCurve.m_criticalImbibitionPhaseVolFraction, m_nonWettingCurve.m_criticalImbibitionValue, - m_nonWettingCurve.m_extremumPhaseVolFraction, m_nonWettingCurve.m_extremumValue - )); + m_nonWettingCurve.setPoints( {drainagePhaseMaxVolFraction, drainagePhaseRelPermMaxEndPoint}, // extremum + {imbibitionPhaseMinVolFraction, imbibitionPhaseRelPermMinEndPoint}, // imbibition critical + {drainagePhaseMinVolFraction, drainagePhaseRelPermMinEndPoint} ); // drainage critical } @@ -414,19 +419,21 @@ void TableRelativePermeabilityHysteresis::checkExistenceAndValidateIntermediateR if( m_phaseNames.size() == 3 ) { - real64 drainagePhaseMinVolFraction, + + real64 drainagePhaseMinVolFraction, // drainagePhaseMaxVolFraction, drainagePhaseRelPermMinEndPoint, drainagePhaseRelPermMaxEndPoint; - // intermediate drainage from wetting + //intermediate drainage from wetting checkExistenceAndValidateRelPermTable( m_drainageWettingIntermediateRelPermTableNames[1], // input drainagePhaseMinVolFraction, // output drainagePhaseMaxVolFraction, drainagePhaseRelPermMinEndPoint, drainagePhaseRelPermMaxEndPoint ); + //?? checkExistenceAndValidateRelPermTable( m_drainageNonWettingIntermediateRelPermTableNames[1], // input drainagePhaseMinVolFraction, drainagePhaseMaxVolFraction, @@ -468,9 +475,8 @@ void TableRelativePermeabilityHysteresis::computeLandCoefficient() // For two-phase flow, we make sure that they are equal m_landParam.resize( 2 ); - // Note: for simplicity, the notations are taken classical reservoir notations (although this breaks our phaseVolFrac naming convention) + // Note: for simplicity, the notations are taken reservoir simulation literature (although this breaks our phaseVolFrac naming convention) using IPT = TableRelativePermeabilityHysteresis::ImbibitionPhasePairPhaseType; - KilloughHysteresis::computeLandCoefficient( m_wettingCurve, m_landParam[IPT::WETTING] ); KilloughHysteresis::computeLandCoefficient( m_nonWettingCurve, m_landParam[IPT::NONWETTING] ); } @@ -566,12 +572,14 @@ void TableRelativePermeabilityHysteresis::allocateConstitutiveData( Group & pare { integer const numPhases = numFluidPhases(); - m_phaseMinVolumeFraction.resize( numPhases ); - m_phaseMaxHistoricalVolFraction.resize( 0, numPhases ); - m_phaseMinHistoricalVolFraction.resize( 0, numPhases ); - RelativePermeabilityBase::allocateConstitutiveData( parent, numPts ); + m_phaseMaxHistoricalVolFraction.resize( parent.size(), numPhases ); + m_phaseMinHistoricalVolFraction.resize( parent.size(), numPhases ); + + m_phaseMaxHistoricalVolFraction.setValues< parallelDevicePolicy<> >( 0.0 ); + m_phaseMinHistoricalVolFraction.setValues< parallelDevicePolicy<> >( 1.0 ); + m_phaseMaxHistoricalVolFraction.setValues< parallelDevicePolicy<> >( 0.0 ); m_phaseMinHistoricalVolFraction.setValues< parallelDevicePolicy<> >( 1.0 ); } @@ -597,24 +605,25 @@ void TableRelativePermeabilityHysteresis::saveConvergedPhaseVolFractionState( ar } -TableRelativePermeabilityHysteresis::KernelWrapper::KernelWrapper( arrayView1d< TableFunction::KernelWrapper const > const & drainageRelPermKernelWrappers, - arrayView1d< TableFunction::KernelWrapper const > const & imbibitionRelPermKernelWrappers, - arrayView1d< integer const > const & phaseHasHysteresis, - arrayView1d< real64 const > const & landParam, +TableRelativePermeabilityHysteresis::KernelWrapper:: + KernelWrapper( arrayView1d< TableFunction::KernelWrapper const > const & drainageRelPermKernelWrappers, + arrayView1d< TableFunction::KernelWrapper const > const & imbibitionRelPermKernelWrappers, + arrayView1d< integer const > const & phaseHasHysteresis, + arrayView1d< real64 const > const & landParam, real64 const & jerauldParam_a, real64 const & jerauldParam_b, real64 const & killoughCurvatureParamRelPerm, KilloughHysteresis::HysteresisCurve const & wettingCurve, KilloughHysteresis::HysteresisCurve const & nonWettingCurve, - arrayView1d< integer const > const & phaseTypes, - arrayView1d< integer const > const & phaseOrder, - ThreePhaseInterpolator const & threePhaseInterpolator, - real64 const & waterOilRelPermMaxValue, - arrayView2d< real64 const, compflow::USD_PHASE > const & phaseMinHistoricalVolFraction, - arrayView2d< real64 const, compflow::USD_PHASE > const & phaseMaxHistoricalVolFraction, - arrayView3d< real64, relperm::USD_RELPERM > const & phaseTrappedVolFrac, - arrayView3d< real64, relperm::USD_RELPERM > const & phaseRelPerm, - arrayView4d< real64, relperm::USD_RELPERM_DS > const & dPhaseRelPerm_dPhaseVolFrac ) + arrayView1d< integer const > const & phaseTypes, + arrayView1d< integer const > const & phaseOrder, + ThreePhaseInterpolator const & threePhaseInterpolator, + real64 const & waterOilRelPermMaxValue, + arrayView2d< real64 const, compflow::USD_PHASE > const & phaseMinHistoricalVolFraction, + arrayView2d< real64 const, compflow::USD_PHASE > const & phaseMaxHistoricalVolFraction, + arrayView3d< real64, relperm::USD_RELPERM > const & phaseTrappedVolFrac, + arrayView3d< real64, relperm::USD_RELPERM > const & phaseRelPerm, + arrayView4d< real64, relperm::USD_RELPERM_DS > const & dPhaseRelPerm_dPhaseVolFrac ) : RelativePermeabilityBaseUpdate( phaseTypes, phaseOrder, diff --git a/src/coreComponents/constitutive/relativePermeability/TableRelativePermeabilityHysteresis.hpp b/src/coreComponents/constitutive/relativePermeability/TableRelativePermeabilityHysteresis.hpp index 3f0d5611f4c..5761d49c5fc 100644 --- a/src/coreComponents/constitutive/relativePermeability/TableRelativePermeabilityHysteresis.hpp +++ b/src/coreComponents/constitutive/relativePermeability/TableRelativePermeabilityHysteresis.hpp @@ -21,12 +21,11 @@ #define GEOS_CONSTITUTIVE_TABLERELATIVEPERMEABILITYHYSTERESIS_HPP -#include "constitutive/relativePermeability/KilloughHysteresis.hpp" +#include "constitutive/KilloughHysteresis.hpp" #include "constitutive/relativePermeability/RelativePermeabilityBase.hpp" #include "constitutive/relativePermeability/RelativePermeabilityInterpolators.hpp" #include "functions/TableFunction.hpp" - - +///helper model class with data struct for curves and computing recipe for trapped and Land Coeff namespace geos { @@ -79,8 +78,6 @@ class TableRelativePermeabilityHysteresis : public RelativePermeabilityBase virtual string getCatalogName() const override { return catalogName(); } - virtual void allocateConstitutiveData( dataRepository::Group & parent, localIndex const numPts ) override; - /// Type of kernel wrapper for in-kernel update class KernelWrapper final : public RelativePermeabilityBaseUpdate { @@ -140,7 +137,24 @@ class TableRelativePermeabilityHysteresis : public RelativePermeabilityBase real64 & phaseRelPerm, real64 & dPhaseRelPerm_dPhaseVolFrac ) const; - + /** + * @brief Function computing the trapped critical phase volume fraction (Sgcrt) + * @param[in] Scrd the drainage critical phase volume fraction + * @param[in] Shy the max historical phase volume fraction + * @param[in] Smx the max phase volume fraction (= end-point phase volume fraction) + * @param[in] jerauldParam_a first (modification) parameter proposed by Jerauld + * @param[in] jerauldParam_b second (exponent) parameter proposed by Jerauld + * @param[in] landParam Land trapping parameter + * @param[out] Scrt the trapped critical phase volume fraction + */ + GEOS_HOST_DEVICE + void computeTrappedCriticalPhaseVolFraction( real64 const & Scrd, + real64 const & Shy, + real64 const & Smx, + real64 const & jerauldParam_a, + real64 const & jerauldParam_b, + real64 const & landParam, + real64 & Scrt ) const; /** * @brief Function updating the relperm (and derivative) for the wetting phase in imbibition using Killough's method * @param[in] drainageRelPermKernelWrapper kernel wrapper storing the drainage relperm table for the wetting phase @@ -270,19 +284,13 @@ class TableRelativePermeabilityHysteresis : public RelativePermeabilityBase /// Trapping parameter from the Land model (typically called C) arrayView1d< real64 const > m_landParam; - /// Parameter a introduced by Jerauld in the Land model real64 const & m_jerauldParam_a; - /// Parameter b introduced by Jerauld in the Land model real64 const & m_jerauldParam_b; - /// Curvature parameter introduced for wetting phase hysteresis in Killough real64 const & m_killoughCurvatureParamRelPerm; - /// The wetting phase hysteretic curve KilloughHysteresis::HysteresisCurve const & m_wettingCurve; - - /// The non-wetting phase hysteretic curve KilloughHysteresis::HysteresisCurve const & m_nonWettingCurve; /// Minimum historical phase volume fraction for each phase @@ -307,17 +315,17 @@ class TableRelativePermeabilityHysteresis : public RelativePermeabilityBase struct viewKeyStruct : RelativePermeabilityBase::viewKeyStruct { - /// Land coefficient + ///Land Coeff static constexpr char const * landParameterString() { return "landParameter"; } - /// Hysteretic curves + ///and packed curves data struct static constexpr char const * wettingCurveString() { return "wettingCurve"; }; static constexpr char const * nonWettingCurveString() { return "nonWettingCurve"; }; - /// Flag to determine whether a phase has hysteresis or not + ///flag static constexpr char const * phaseHasHysteresisString() { return "phaseHasHysteresis"; } - /// Tables and associated wrappers + ///tables and assoc. wrappers static constexpr char const * drainageRelPermKernelWrappersString() { return "drainageRelPermWrappers"; } static constexpr char const * imbibitionRelPermKernelWrappersString() { return "imbibitionRelPermWrappers"; } @@ -334,18 +342,18 @@ class TableRelativePermeabilityHysteresis : public RelativePermeabilityBase }; - arrayView1d< real64 const > getPhaseMinVolumeFraction() const override { return m_phaseMinVolumeFraction; }; - real64 getWettingPhaseMinVolumeFraction() const override { - return m_wettingCurve.m_extremumPhaseVolFraction; + return m_wettingCurve.oppositeBoundPhaseVolFraction; } real64 getNonWettingMinVolumeFraction() const override { - return m_nonWettingCurve.m_criticalDrainagePhaseVolFraction; + return m_nonWettingCurve.oppositeBoundPhaseVolFraction; } + virtual void allocateConstitutiveData( Group & parent, localIndex const numPts ) override; + private: virtual void postInputInitialization() override; @@ -453,17 +461,10 @@ class TableRelativePermeabilityHysteresis : public RelativePermeabilityBase /// Maximum historical phase volume fraction for each phase array2d< real64, compflow::LAYOUT_PHASE > m_phaseMaxHistoricalVolFraction; - /// The wetting phase hysteretic curve KilloughHysteresis::HysteresisCurve m_wettingCurve; - - /// The non-wetting phase hysteretic curve KilloughHysteresis::HysteresisCurve m_nonWettingCurve; - /// Min phase volume fractions (deduced from the tables). With Baker, only the water phase entry is used - array1d< real64 > m_phaseMinVolumeFraction; - real64 m_waterOilMaxRelPerm; - ThreePhaseInterpolator m_threePhaseInterpolator; }; @@ -498,13 +499,13 @@ TableRelativePermeabilityHysteresis::KernelWrapper:: // if consistent, S should be equal to 1 - imbibitionPhaseMinVolNonWettingFraction for two-phase flow // (but wetting and nonwetting phase hysteresis are implemented in a decoupled fashion) real64 const S = phaseVolFraction; - real64 const Smxi = m_wettingCurve.m_criticalImbibitionPhaseVolFraction; - real64 const Smxd = m_wettingCurve.m_criticalDrainagePhaseVolFraction; + real64 const Smxi = m_wettingCurve.imbibitionExtremaPhaseVolFraction; + real64 const Smxd = m_wettingCurve.drainageExtremaPhaseVolFraction; // Swc is the common end min endpoint saturation for wetting curves - real64 const Swc = m_wettingCurve.m_extremumPhaseVolFraction; + real64 const Swc = m_wettingCurve.oppositeBoundPhaseVolFraction; - using IPT = ImbibitionPhasePairPhaseType; + using IPT = TableRelativePermeabilityHysteresis::ImbibitionPhasePairPhaseType; if( S <= Swc ) { phaseRelPerm = 0.0; @@ -512,12 +513,12 @@ TableRelativePermeabilityHysteresis::KernelWrapper:: } else if( S >= Smxd ) { - phaseRelPerm = m_wettingCurve.m_criticalDrainageValue; + phaseRelPerm = m_wettingCurve.drainageExtremaSCALValue; dPhaseRelPerm_dPhaseVolFrac = 0.0; } else { - real64 const krwei = m_wettingCurve.m_criticalImbibitionValue; + real64 const krwei = m_wettingCurve.imbibitionExtremaSCALValue; real64 const krwedAtSmxi = drainageRelPermKernelWrapper.compute( &Smxi ); // Step 1: Compute the new end point @@ -578,12 +579,12 @@ TableRelativePermeabilityHysteresis::KernelWrapper:: // Step 1: for a given value of the max historical saturation, Shy, compute the trapped critical saturation, Scrt, // using Land's method. The calculation includes the modifications from Jerauld. real64 const S = phaseVolFraction; - real64 const Scri = m_nonWettingCurve.m_criticalImbibitionPhaseVolFraction; - real64 const Smx = m_nonWettingCurve.m_extremumPhaseVolFraction; + real64 const Scri = m_nonWettingCurve.imbibitionExtremaPhaseVolFraction; + real64 const Smx = m_nonWettingCurve.oppositeBoundPhaseVolFraction; real64 const Shy = (phaseMaxHistoricalVolFraction < Smx) ? phaseMaxHistoricalVolFraction : Smx; // to make sure that Shy < Smax real64 Scrt = 0; - using IPT = ImbibitionPhasePairPhaseType; + using IPT = TableRelativePermeabilityHysteresis::ImbibitionPhasePairPhaseType; KilloughHysteresis::computeTrappedCriticalPhaseVolFraction( m_nonWettingCurve, Shy, m_landParam[IPT::NONWETTING], @@ -598,13 +599,13 @@ TableRelativePermeabilityHysteresis::KernelWrapper:: } else if( S >= Smx ) // S is above the max saturation, so we just skip the rest and set the relperm to the endpoint { - phaseRelPerm = m_nonWettingCurve.m_extremumValue; + phaseRelPerm = m_nonWettingCurve.oppositeBoundSCALValue; dPhaseRelPerm_dPhaseVolFrac = 0.0; } else { // Step 2: compute the normalized saturation, S_norm, at which the imbibition relperm curve will be evaluated. - real64 const ratio = ( Smx - Scri ) / ( Shy - Scrt ); + real64 const ratio = ( Smx - Scri ) / ( Shy - Scrt ); // non S-deps part (isolated for derivatives calculations) real64 const Snorm = Scri + ( S - Scrt ) * ratio; // normalized saturation real64 const dSnorm_dS = ratio; @@ -618,7 +619,7 @@ TableRelativePermeabilityHysteresis::KernelWrapper:: real64 const krdAtShy = drainageRelPermKernelWrapper.compute( &Shy ); // Step 5: evaluate the drainage relperm, krd(Smx), at the max drainage saturation, Smx. - real64 const krdAtSmx = m_nonWettingCurve.m_extremumValue; + real64 const krdAtSmx = m_nonWettingCurve.oppositeBoundSCALValue; // Step 6: apply the formula blending drainage and imbibition relperms from the Killough model. real64 const drainageRelPermRatio = krdAtShy / krdAtSmx; @@ -650,7 +651,7 @@ TableRelativePermeabilityHysteresis::KernelWrapper:: if( !m_phaseHasHysteresis[IPT::WETTING] || phaseVolFraction[ipWetting] <= phaseMinHistoricalVolFraction[ipWetting] + flowReversalBuffer ) { - phaseTrappedVolFrac[ipWetting] = LvArray::math::min( phaseVolFraction[ipWetting], m_wettingCurve.m_extremumPhaseVolFraction ); + phaseTrappedVolFrac[ipWetting] = LvArray::math::min( phaseVolFraction[ipWetting], m_wettingCurve.oppositeBoundPhaseVolFraction ); computeDrainageRelPerm( m_drainageRelPermKernelWrappers[TPT::WETTING], phaseVolFraction[ipWetting], phaseRelPerm[ipWetting], @@ -671,8 +672,8 @@ TableRelativePermeabilityHysteresis::KernelWrapper:: phaseVolFraction[ipNonWetting] >= phaseMaxHistoricalVolFraction[ipNonWetting] - flowReversalBuffer ) { // for reporting purposes, compute Sgcrt first - real64 const Shy = ( phaseVolFraction[ipNonWetting] < m_nonWettingCurve.m_extremumPhaseVolFraction ) - ? phaseVolFraction[ipNonWetting] : m_nonWettingCurve.m_extremumPhaseVolFraction; // to make sure that Shy < Smax + real64 const Shy = ( phaseVolFraction[ipNonWetting] < m_nonWettingCurve.oppositeBoundPhaseVolFraction ) + ? phaseVolFraction[ipNonWetting] : m_nonWettingCurve.oppositeBoundPhaseVolFraction; // to make sure that Shy < Smax real64 Scrt = 0; KilloughHysteresis::computeTrappedCriticalPhaseVolFraction( m_nonWettingCurve, Shy, @@ -727,7 +728,7 @@ TableRelativePermeabilityHysteresis::KernelWrapper:: if( !m_phaseHasHysteresis[IPT::WETTING] || phaseVolFraction[ipWetting] <= phaseMinHistoricalVolFraction[ipWetting] + flowReversalBuffer ) { - phaseTrappedVolFrac[ipWetting] = LvArray::math::min( m_wettingCurve.m_extremumPhaseVolFraction, phaseVolFraction[ipWetting] ); + phaseTrappedVolFrac[ipWetting] = LvArray::math::min( m_wettingCurve.oppositeBoundPhaseVolFraction, phaseVolFraction[ipWetting] ); computeDrainageRelPerm( m_drainageRelPermKernelWrappers[TPT::WETTING], phaseVolFraction[ipWetting], phaseRelPerm[ipWetting], @@ -756,8 +757,8 @@ TableRelativePermeabilityHysteresis::KernelWrapper:: phaseVolFraction[ipNonWetting] >= phaseMaxHistoricalVolFraction[ipNonWetting] - flowReversalBuffer ) { // 2.a) compute Sgcrt for reporting purposes - real64 const Shy = ( phaseVolFraction[ipNonWetting] < m_nonWettingCurve.m_extremumPhaseVolFraction) - ? phaseVolFraction[ipNonWetting] : m_nonWettingCurve.m_extremumPhaseVolFraction; // to make sure that Shy < Smax + real64 const Shy = ( phaseVolFraction[ipNonWetting] < m_nonWettingCurve.oppositeBoundPhaseVolFraction) + ? phaseVolFraction[ipNonWetting] : m_nonWettingCurve.oppositeBoundPhaseVolFraction; // to make sure that Shy < Smax real64 Scrt = 0; KilloughHysteresis::computeTrappedCriticalPhaseVolFraction( m_nonWettingCurve, Shy, @@ -792,7 +793,7 @@ TableRelativePermeabilityHysteresis::KernelWrapper:: // 3) Compute the "three-phase" oil relperm // use saturation-weighted interpolation - real64 const shiftedWettingVolFrac = (phaseVolFraction[ipWetting] - m_wettingCurve.m_extremumPhaseVolFraction); + real64 const shiftedWettingVolFrac = (phaseVolFraction[ipWetting] - m_wettingCurve.oppositeBoundPhaseVolFraction); if( m_threePhaseInterpolator == ThreePhaseInterpolator::BAKER ) { diff --git a/src/coreComponents/constitutive/relativePermeability/VanGenuchtenBakerRelativePermeability.hpp b/src/coreComponents/constitutive/relativePermeability/VanGenuchtenBakerRelativePermeability.hpp index 931f7d35b63..11bd206d906 100644 --- a/src/coreComponents/constitutive/relativePermeability/VanGenuchtenBakerRelativePermeability.hpp +++ b/src/coreComponents/constitutive/relativePermeability/VanGenuchtenBakerRelativePermeability.hpp @@ -140,19 +140,17 @@ class VanGenuchtenBakerRelativePermeability : public RelativePermeabilityBase }; - arrayView1d< real64 const > getPhaseMinVolumeFraction() const override { return m_phaseMinVolumeFraction; }; - real64 getWettingPhaseMinVolumeFraction() const override { integer ipWetting; - std::tie( ipWetting, std::ignore ) = wettingAndNonWettingPhaseIndices(); + std::tie( ipWetting, std::ignore ) = phaseIndex( getPhaseOrder()); return m_phaseMinVolumeFraction[ipWetting]; } real64 getNonWettingMinVolumeFraction() const override { integer ipNonWetting; - std::tie( std::ignore, ipNonWetting ) = wettingAndNonWettingPhaseIndices(); + std::tie( std::ignore, ipNonWetting ) = phaseIndex( getPhaseOrder()); return m_phaseMinVolumeFraction[ipNonWetting]; }; diff --git a/src/coreComponents/constitutive/relativePermeability/VanGenuchtenStone2RelativePermeability.hpp b/src/coreComponents/constitutive/relativePermeability/VanGenuchtenStone2RelativePermeability.hpp index 3927764f236..e91e4fb39bd 100644 --- a/src/coreComponents/constitutive/relativePermeability/VanGenuchtenStone2RelativePermeability.hpp +++ b/src/coreComponents/constitutive/relativePermeability/VanGenuchtenStone2RelativePermeability.hpp @@ -139,7 +139,7 @@ class VanGenuchtenStone2RelativePermeability : public RelativePermeabilityBase static constexpr char const * volFracScaleString() { return "volFracScale"; } }; - arrayView1d< real64 const > getPhaseMinVolumeFraction() const override { return m_phaseMinVolumeFraction; }; + arrayView1d< real64 const > getPhaseMinVolumeFraction() const { return m_phaseMinVolumeFraction; }; real64 getWettingPhaseMinVolumeFraction() const override { diff --git a/src/coreComponents/constitutive/relativePermeability/unitTests/constitutiveTestHelpers.hpp b/src/coreComponents/constitutive/relativePermeability/unitTests/constitutiveTestHelpers.hpp index e2ddfa05933..c5463cca49f 100644 --- a/src/coreComponents/constitutive/relativePermeability/unitTests/constitutiveTestHelpers.hpp +++ b/src/coreComponents/constitutive/relativePermeability/unitTests/constitutiveTestHelpers.hpp @@ -32,7 +32,7 @@ namespace geos { namespace testing { -void fillArray( array1d< real64_array > & arr, std::initializer_list< real64 > const & input_list ) +void fill_array( array1d< real64_array > & arr, std::initializer_list< real64 > const & input_list ) { arr.resize( 1 ); arr[0].resize( input_list.size()); @@ -42,7 +42,7 @@ void fillArray( array1d< real64_array > & arr, std::initializer_list< real64 > c } -void fillArray( real64_array & arr, std::initializer_list< real64 > const & input_list ) +void fill_array( real64_array & arr, std::initializer_list< real64 > const & input_list ) { arr.resize( input_list.size()); int j = 0; diff --git a/src/coreComponents/constitutive/unitTests/FluidModelTest_impl.hpp b/src/coreComponents/constitutive/unitTests/FluidModelTest_impl.hpp index 72cfec6501c..ce731e6b775 100644 --- a/src/coreComponents/constitutive/unitTests/FluidModelTest_impl.hpp +++ b/src/coreComponents/constitutive/unitTests/FluidModelTest_impl.hpp @@ -35,7 +35,7 @@ template< typename FLUID_TYPE, integer NUM_COMP, integer NUM_PHASE > FluidModelTest< FLUID_TYPE, NUM_COMP, NUM_PHASE >::FluidModelTest(): m_parent( "parent", m_node ) { - createFunctionManager(); + // createFunctionManager(); } template< typename FLUID_TYPE, integer NUM_COMP, integer NUM_PHASE > diff --git a/src/coreComponents/constitutive/unitTests/testCO2SpycherPruessModels.cpp.uncrustify b/src/coreComponents/constitutive/unitTests/testCO2SpycherPruessModels.cpp.uncrustify new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/coreComponents/constitutive/unitTests/testTwoPhaseFluid.cpp b/src/coreComponents/constitutive/unitTests/testTwoPhaseFluid.cpp new file mode 100644 index 00000000000..207668e29cb --- /dev/null +++ b/src/coreComponents/constitutive/unitTests/testTwoPhaseFluid.cpp @@ -0,0 +1,283 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * SPDX-License-Identifier: LGPL-2.1-only + * + * Copyright (c) 2016-2024 Lawrence Livermore National Security LLC + * Copyright (c) 2018-2024 TotalEnergies + * Copyright (c) 2018-2024 The Board of Trustees of the Leland Stanford Junior University + * Copyright (c) 2023-2024 Chevron + * Copyright (c) 2019- GEOS/GEOSX Contributors + * All rights reserved + * + * See top level LICENSE, COPYRIGHT, CONTRIBUTORS, NOTICE, and ACKNOWLEDGEMENTS files for details. + * ------------------------------------------------------------------------------------------------------------ + */ + +#include "constitutive/fluid/twophasefluid/TwoPhaseFluid.hpp" +#include "constitutive/fluid/twophasefluid/TwoPhaseFluidFields.hpp" + +// Only for fill +#include "unitTests/constitutiveTests/MultiFluidTest.hpp" + +// Only for initializeTable +#include "unitTests/constitutiveTests/constitutiveTestHelpers.hpp" + + +using namespace geos; +using namespace geos::testing; +using namespace geos::constitutive; +using namespace geos::dataRepository; /// Only for group definition + + +static constexpr char const * tableContentPhase0 = "# Pg(Pa) Dens(kg/m3) Visc(Pa.s)\n" + "0.22 0.00603 40203\n" + "0.3 0.04224 31311\n" + "0.5 0.15011 22423\n" + "0.6 0.22423 15011\n" + "0.8 0.31311 4224\n" + "1.0 0.40203 603"; + +static constexpr char const * tableContentPhase1 = "# Pg(Pa) Dens(kg/m3) Visc(Pa.s)\n" + "1.22 0.00603 0.22\n" + "1.3 0.04224 0.22\n" + "1.5 0.15011 0.22\n" + "1.6 0.22423 0.22\n" + "1.8 0.31311 0.22\n" + "2.0 0.40203 0.22"; + +template< bool FROM_TABLE > +class TwoPhaseFluidTest : public ConstitutiveTestBase< TwoPhaseFluid > +{ +public: + + TwoPhaseFluidTest() + { + if constexpr (!FROM_TABLE) + { + writeTableToFile( "phase0.txt", tableContentPhase0 ); + writeTableToFile( "phase1.txt", tableContentPhase1 ); + } + + m_parent.resize( 1 ); + string const fluidName = GEOS_FMT( "fluid{}", (FROM_TABLE ? "Tables" : "Files")); + m_model = makeTwoPhaseFluid( fluidName, m_parent ); + + m_parent.initialize(); + m_parent.initializePostInitialConditions(); + } + + ~TwoPhaseFluidTest() + { + if constexpr (!FROM_TABLE) + { + removeFile( "phase0.txt" ); + removeFile( "phase1.txt" ); + } + } + + constitutive::TwoPhaseFluid & getFluid() const { return *m_model; } + + dataRepository::Group & getParent() { return m_parent; } + + + void testDerivatives( constitutive::TwoPhaseFluid & fluid, + dataRepository::Group * parent, + real64 const pressure, + real64 const perturbParameter, + real64 const relTol, + real64 const absTol = std::numeric_limits< real64 >::max() ) + { + auto const & phaseNames = fluid.getReference< string_array >( TwoPhaseFluid::viewKeyStruct::phaseNamesString() ); + + // create a clone of the fluid to run updates on + string const fluidCopyName = fluid.getName() + "Copy"; + std::unique_ptr< constitutive::ConstitutiveBase > fluidCopyPtr = fluid.deliverClone( fluidCopyName, parent ); + constitutive::TwoPhaseFluid & fluidCopy = dynamicCast< constitutive::TwoPhaseFluid & >( *fluidCopyPtr ); + fluidCopy.initializePostSubGroups(); + + fluid.allocateConstitutiveData( fluid.getParent(), 1 ); + fluidCopy.allocateConstitutiveData( fluid.getParent(), 1 ); + + // extract data views from both fluids + #define GET_FLUID_DATA( FLUID, TRAIT ) \ + FLUID.getReference< TRAIT::type >( TRAIT::key() )[0][0] + + constitutive::MultiFluidVarSlice< real64, 1, constitutive::multifluid::USD_PHASE - 2, constitutive::multifluid::USD_PHASE_DC - 2 > phaseVisc { + GET_FLUID_DATA( fluid, fields::twophasefluid::phaseViscosity ), + GET_FLUID_DATA( fluid, fields::twophasefluid::dPhaseViscosity ) + }; + + constitutive::MultiFluidVarSlice< real64, 1, constitutive::multifluid::USD_PHASE - 2, constitutive::multifluid::USD_PHASE_DC - 2 > phaseDens { + GET_FLUID_DATA( fluid, fields::twophasefluid::phaseDensity ), + GET_FLUID_DATA( fluid, fields::twophasefluid::dPhaseDensity ) + }; + + auto const & phaseDensCopy = GET_FLUID_DATA( fluidCopy, fields::twophasefluid::phaseDensity ); + auto const & phaseViscCopy = GET_FLUID_DATA( fluidCopy, fields::twophasefluid::phaseViscosity ); +#undef GET_FLUID_DATA + + + // set the original fluid state to current + constitutive::constitutiveUpdatePassThru( fluid, [&] ( auto & castedFluid ) + { + typename TYPEOFREF( castedFluid ) ::KernelWrapper fluidWrapper = castedFluid.createKernelWrapper(); + fluidWrapper.update( 0, 0, pressure ); + } ); + + // now perturb variables and update the copied fluid's state + constitutive::constitutiveUpdatePassThru( fluidCopy, [&] ( auto & castedFluid ) + { + using Deriv = constitutive::multifluid::DerivativeOffset; + + typename TYPEOFREF( castedFluid ) ::KernelWrapper fluidWrapper = castedFluid.createKernelWrapper(); + + // to be able to use the checkDerivative utility function, we have to invert the layout + auto dPhaseDens = invertLayout( phaseDens.derivs.toSliceConst(), 2, 1 ); + auto dPhaseVisc = invertLayout( phaseVisc.derivs.toSliceConst(), 2, 1 ); + + // update pressure and check derivatives + real64 const dP = perturbParameter * (pressure + perturbParameter); + fluidWrapper.update( 0, 0, pressure + dP ); + + checkDerivative( phaseDensCopy.toSliceConst(), phaseDens.value.toSliceConst(), dPhaseDens[Deriv::dP].toSliceConst(), + dP, relTol, absTol, "phaseDens", "Pressure", phaseNames ); + checkDerivative( phaseViscCopy.toSliceConst(), phaseVisc.value.toSliceConst(), dPhaseVisc[Deriv::dP].toSliceConst(), + dP, relTol, absTol, "phaseVisc", "Pressure", phaseNames ); + } ); + } // void testDerivatives + + +protected: + static void writeTableToFile( string const & fileName, char const * content ) + { + std::ofstream os( fileName ); + ASSERT_TRUE( os.is_open() ); + os << content; + os.close(); + } + + static void removeFile( string const & fileName ) + { + int const ret = std::remove( fileName.c_str() ); + ASSERT_TRUE( ret == 0 ); + } + + +private: + static TwoPhaseFluid * makeTwoPhaseFluid( string const & name, Group & parent ); + +}; // class TwoPhaseFluidTest + + +template<> +TwoPhaseFluid * TwoPhaseFluidTest< true >::makeTwoPhaseFluid( string const & name, Group & parent ) +{ + // 1D table with linear interpolation + localIndex constexpr Naxis = 6; + localIndex constexpr NaxisSingle = 1; + + array1d< real64_array > densityCoordPhase0( 1 ); + fill< Naxis >( densityCoordPhase0[0], { 0.22, 0.3, 0.5, 0.6, 0.8, 1.0 } ); + real64_array densityValuesPhase0; + fill< Naxis >( densityValuesPhase0, { 0.00603, 0.04224, 0.04224, 0.22423, 0.31311, 0.40203 } ); + + array1d< real64_array > densityCoordPhase1( 1 ); + fill< Naxis >( densityCoordPhase1[0], { 1.22, 1.3, 1.5, 1.6, 1.8, 2.0 } ); + real64_array densityValuesPhase1; + fill< Naxis >( densityValuesPhase1, { 0.00603, 0.04224, 0.04224, 0.22423, 0.31311, 0.40203 } ); + + + array1d< real64_array > viscosityCoordPhase0( 1 ); + fill< Naxis >( viscosityCoordPhase0[0], { 0.22, 0.3, 0.5, 0.6, 0.8, 1.0 } ); + real64_array viscosityValuesPhase0; + fill< Naxis >( viscosityValuesPhase0, { 40203, 31311, 22423, 15011, 4224, 603 } ); + + array1d< real64_array > viscosityCoordPhase1( 1 ); + fill< NaxisSingle >( viscosityCoordPhase1[0], { 0.22 } ); + real64_array viscosityValuesPhase1; + fill< NaxisSingle >( viscosityValuesPhase1, { 45 } ); + + initializeTable( "densityTablePhase0", densityCoordPhase0, densityValuesPhase0 ); + initializeTable( "densityTablePhase1", densityCoordPhase1, densityValuesPhase1 ); + initializeTable( "viscosityTablePhase0", viscosityCoordPhase0, viscosityValuesPhase0 ); + initializeTable( "viscosityTablePhase1", viscosityCoordPhase1, viscosityValuesPhase1 ); + + + // 2) Set up the constitutive model + TwoPhaseFluid & fluid = parent.registerGroup< TwoPhaseFluid >( name ); + + string_array & phaseNames = fluid.getReference< string_array >( TwoPhaseFluid::viewKeyStruct::phaseNamesString() ); + phaseNames.emplace_back( "oil" ); + phaseNames.emplace_back( "water" ); + + string_array & densityTableNames = fluid.getReference< string_array >( TwoPhaseFluid::viewKeyStruct::densityTableNamesString() ); + densityTableNames.emplace_back( "densityTablePhase0" ); + densityTableNames.emplace_back( "densityTablePhase1" ); + + string_array & viscosityTableNames = fluid.getReference< string_array >( TwoPhaseFluid::viewKeyStruct::viscosityTableNamesString() ); + viscosityTableNames.emplace_back( "viscosityTablePhase0" ); + viscosityTableNames.emplace_back( "viscosityTablePhase1" ); + + fluid.postInputInitializationRecursive(); + return &fluid; +} + + +template<> +TwoPhaseFluid * TwoPhaseFluidTest< false >::makeTwoPhaseFluid( string const & name, Group & parent ) +{ + TwoPhaseFluid & fluid = parent.registerGroup< TwoPhaseFluid >( name ); + + path_array & tableNames = fluid.getReference< path_array >( TwoPhaseFluid::viewKeyStruct::tableFilesString() ); + fill< 2 >( tableNames, {"phase0.txt", "phase1.txt"} ); + + fluid.postInputInitializationRecursive(); + return &fluid; +} + + + +using TwoPhaseFluidTestFromFiles = TwoPhaseFluidTest< false >; +using TwoPhaseFluidTestFromTables = TwoPhaseFluidTest< true >; + + +TEST_F( TwoPhaseFluidTestFromTables, testNumericalDerivative_initFromTables ) +{ + auto & fluid = getFluid(); + real64 const eps = std::sqrt( std::numeric_limits< real64 >::epsilon()); + real64 constexpr relTol = 1.0e-8; + real64 constexpr absTol = 1.0e-8; + + for( real64 const pressure : { 0.55, 1.0, 10.0 } ) + { + testDerivatives( fluid, &getParent(), pressure, eps, relTol, absTol ); + } +} + + +TEST_F( TwoPhaseFluidTestFromFiles, testNumericalDerivative_initFromFiles ) +{ + auto & fluid = getFluid(); + real64 const eps = std::sqrt( std::numeric_limits< real64 >::epsilon()); + real64 constexpr relTol = 1.0e-8; + real64 constexpr absTol = 1.0e-8; + + for( real64 const pressure : { 0.55, 1.0, 10.0 } ) + { + testDerivatives( fluid, &getParent(), pressure, eps, relTol, absTol ); + } +} + + +int main( int argc, char * * argv ) +{ + ::testing::InitGoogleTest( &argc, argv ); + + conduit::Node conduitNode; + dataRepository::Group rootNode( "root", conduitNode ); + FunctionManager functionManager( "FunctionManager", &rootNode ); + + int const result = RUN_ALL_TESTS(); + + return result; +} diff --git a/src/coreComponents/constitutiveDrivers/relativePermeability/RelpermDriver.cpp b/src/coreComponents/constitutiveDrivers/relativePermeability/RelpermDriver.cpp index 49dca10b482..6bd500b6dfa 100644 --- a/src/coreComponents/constitutiveDrivers/relativePermeability/RelpermDriver.cpp +++ b/src/coreComponents/constitutiveDrivers/relativePermeability/RelpermDriver.cpp @@ -13,7 +13,9 @@ * ------------------------------------------------------------------------------------------------------------ */ -#include "common/MpiWrapper.hpp" +#include "RelpermDriver.hpp" + +//#include "common/MpiWrapper.hpp" #include "functions/FunctionManager.hpp" #include "functions/TableFunction.hpp" #include "constitutive/ConstitutiveManager.hpp" @@ -110,7 +112,7 @@ bool RelpermDriver::execute( const geos::real64 GEOS_UNUSED_PARAM( time_n ), { // this code only makes sense in serial - GEOS_THROW_IF( MpiWrapper::commRank() > 0, "RelpermDriver should only be run in serial", std::runtime_error ); + //GEOS_THROW_IF( MpiWrapper::commRank() > 0, "RelpermDriver should only be run in serial", std::runtime_error ); ConstitutiveManager diff --git a/src/coreComponents/constitutiveDrivers/relativePermeability/RelpermDriverRunTest.hpp b/src/coreComponents/constitutiveDrivers/relativePermeability/RelpermDriverRunTest.hpp index eabe5616eee..750587bab88 100644 --- a/src/coreComponents/constitutiveDrivers/relativePermeability/RelpermDriverRunTest.hpp +++ b/src/coreComponents/constitutiveDrivers/relativePermeability/RelpermDriverRunTest.hpp @@ -19,7 +19,7 @@ #include "constitutiveDrivers/relativePermeability/RelpermDriver.hpp" #include "constitutive/relativePermeability/RelativePermeabilityFields.hpp" #include "constitutive/relativePermeability/Layouts.hpp" -#include "constitutive/relativePermeability/KilloughHysteresis.hpp" +#include "constitutive/KilloughHysteresis.hpp" namespace geos @@ -96,10 +96,6 @@ RelpermDriver::runTest( RELPERM_TYPE & relperm, arrayView2d< real64, compflow::USD_PHASE > phaseMaxHistoricalVolFraction = relperm.template getField< fields::relperm::phaseMaxHistoricalVolFraction >().reference(); arrayView2d< real64, compflow::USD_PHASE > phaseMinHistoricalVolFraction = relperm.template getField< fields::relperm::phaseMinHistoricalVolFraction >().reference(); -// arrayView1d< real64 > const drainagePhaseMinVolFraction = relperm.template getReference< array1d< real64 > >( -// constitutive::TableRelativePermeabilityHysteresis::viewKeyStruct::drainagePhaseMinVolumeFractionString()); -// arrayView1d< real64 > const drainagePhaseMaxVolFraction = relperm.template getReference< array1d< real64 > >( -// constitutive::TableRelativePermeabilityHysteresis::viewKeyStruct::drainagePhaseMaxVolumeFractionString()); constitutive::KilloughHysteresis::HysteresisCurve const wettingCurve = relperm.template getReference< constitutive::KilloughHysteresis::HysteresisCurve >( constitutive::TableRelativePermeabilityHysteresis::viewKeyStruct::wettingCurveString()); @@ -109,14 +105,15 @@ RelpermDriver::runTest( RELPERM_TYPE & relperm, { if( phaseHasHysteresis[ipNonWetting] ) { - phaseMaxHistoricalVolFraction[0][ipNonWetting] = nonWettingCurve.m_extremumPhaseVolFraction; + phaseMaxHistoricalVolFraction[0][ipNonWetting] = nonWettingCurve.oppositeBoundPhaseVolFraction; GEOS_LOG( GEOS_FMT( "New max non-wetting phase historical phase volume fraction: {}", phaseMaxHistoricalVolFraction[0][ipNonWetting] ) ); } if( phaseHasHysteresis[ipWetting] ) { - phaseMinHistoricalVolFraction[0][ipWetting] = wettingCurve.m_extremumPhaseVolFraction; + phaseMinHistoricalVolFraction[0][ipWetting] = wettingCurve.oppositeBoundPhaseVolFraction; GEOS_LOG( GEOS_FMT( "New min wetting phase historical phase volume fraction: {}", phaseMinHistoricalVolFraction[0][ipWetting] ) ); } + } @@ -140,13 +137,12 @@ RelpermDriver::runTest( RELPERM_TYPE & relperm, { if( phaseHasHysteresis[ipNonWetting] ) { - - phaseMaxHistoricalVolFraction[0][ipNonWetting] = nonWettingCurve.m_criticalDrainagePhaseVolFraction; + phaseMaxHistoricalVolFraction[0][ipNonWetting] = nonWettingCurve.drainageExtremaPhaseVolFraction; GEOS_LOG( GEOS_FMT( "New max non-wetting phase historical phase volume fraction: {}", phaseMaxHistoricalVolFraction[0][ipNonWetting] ) ); } if( phaseHasHysteresis[ipWetting] ) { - phaseMinHistoricalVolFraction[0][ipWetting] = wettingCurve.m_criticalDrainagePhaseVolFraction; + phaseMinHistoricalVolFraction[0][ipWetting] = wettingCurve.drainageExtremaPhaseVolFraction; GEOS_LOG( GEOS_FMT( "New min wetting phase historical phase volume fraction: {}", phaseMinHistoricalVolFraction[0][ipWetting] ) ); } } diff --git a/src/coreComponents/finiteVolume/CellElementStencilTPFA.hpp b/src/coreComponents/finiteVolume/CellElementStencilTPFA.hpp index 88fa3bb2b27..5c468d293cf 100644 --- a/src/coreComponents/finiteVolume/CellElementStencilTPFA.hpp +++ b/src/coreComponents/finiteVolume/CellElementStencilTPFA.hpp @@ -70,6 +70,20 @@ class CellElementStencilTPFAWrapper : public StencilWrapperBase< TwoPointStencil CoefficientAccessor< arrayView3d< real64 const > > const & dCoeff_dVar, real64 ( &weight )[1][2], real64 ( &dWeight_dVar )[1][2] ) const; +/** + * @brief Compute half weights and derivatives w.r.t to one variable. + * @param[in] iconn connection index + * @param[in] coefficient view accessor to the coefficient used to compute the weights + * @param[in] dCoeff_dVar view accessor to the derivative of the coefficient w.r.t to the variable + * @param[out] weight view weights + * @param[out] dWeight_dVar derivative of the weights w.r.t to the variable + */ + GEOS_HOST_DEVICE + void computeHalfWeights( localIndex const iconn, + CoefficientAccessor< arrayView3d< real64 const > > const & coefficient, + CoefficientAccessor< arrayView3d< real64 const > > const & dCoeff_dVar, + real64 ( &weight )[1][2], + real64 ( &dWeight_dVar )[1][2] ) const; /** * @brief Compute weights and derivatives w.r.t to one variable without coefficient @@ -293,6 +307,92 @@ CellElementStencilTPFAWrapper:: } } +GEOS_HOST_DEVICE +inline void +CellElementStencilTPFAWrapper:: + computeHalfWeights( localIndex const iconn, + CoefficientAccessor< arrayView3d< real64 const > > const & coefficient, + CoefficientAccessor< arrayView3d< real64 const > > const & dCoeff_dVar, + real64 (& weight)[1][2], + real64 (& dWeight_dVar )[1][2] ) const +{ + real64 halfWeight[2]; + real64 dHalfWeight_dVar[2]; + + // real64 const tolerance = 1e-30 * lengthTolerance; // TODO: choice of constant based on physics? + + for( localIndex i = 0; i < 2; ++i ) + { + localIndex const er = m_elementRegionIndices[iconn][i]; + localIndex const esr = m_elementSubRegionIndices[iconn][i]; + localIndex const ei = m_elementIndices[iconn][i]; + + halfWeight[i] = m_weights[iconn][i]; + dHalfWeight_dVar[i] = m_weights[iconn][i]; + + // Proper computation + real64 faceNormal[3]; + LvArray::tensorOps::copy< 3 >( faceNormal, m_faceNormal[iconn] ); + if( LvArray::tensorOps::AiBi< 3 >( m_cellToFaceVec[iconn][i], faceNormal ) < 0.0 ) + { + LvArray::tensorOps::scale< 3 >( faceNormal, -1 ); + } + + real64 faceConormal[3]; + real64 dFaceConormal_dVar[3]; + LvArray::tensorOps::hadamardProduct< 3 >( faceConormal, coefficient[er][esr][ei][0], faceNormal ); + LvArray::tensorOps::hadamardProduct< 3 >( dFaceConormal_dVar, dCoeff_dVar[er][esr][ei][0], faceNormal ); + halfWeight[i] *= LvArray::tensorOps::AiBi< 3 >( m_cellToFaceVec[iconn][i], faceConormal ); + dHalfWeight_dVar[i] *= LvArray::tensorOps::AiBi< 3 >( m_cellToFaceVec[iconn][i], dFaceConormal_dVar ); + + // correct negative weight issue arising from non-K-orthogonal grids + // if( halfWeight[i] < 0.0 ) + // { + // LvArray::tensorOps::hadamardProduct< 3 >( faceConormal, + // coefficient[er][esr][ei][0], + // m_cellToFaceVec[iconn][i] ); + // LvArray::tensorOps::hadamardProduct< 3 >( dFaceConormal_dVar, + // dCoeff_dVar[er][esr][ei][0], + // m_cellToFaceVec[iconn][i] ); + // halfWeight[i] = m_weights[iconn][i]; + // dHalfWeight_dVar[i] = m_weights[iconn][i]; + // halfWeight[i] *= LvArray::tensorOps::AiBi< 3 >( m_cellToFaceVec[iconn][i], faceConormal ); + // dHalfWeight_dVar[i] *= LvArray::tensorOps::AiBi< 3 >( m_cellToFaceVec[iconn][i], dFaceConormal_dVar ); + // } + } + + // // Do harmonic and arithmetic averaging + // real64 const product = halfWeight[0]*halfWeight[1]; + // real64 const sum = halfWeight[0]+halfWeight[1]; + + // real64 const harmonicWeight = sum > 0 ? product / sum : 0.0; + // real64 const arithmeticWeight = sum / 2; + + // real64 dHarmonicWeight_dVar[2]; + // real64 dArithmeticWeight_dVar[2]; + + // dHarmonicWeight_dVar[0] = sum > 0 ? (dHalfWeight_dVar[0]*sum*halfWeight[1] - dHalfWeight_dVar[0]*halfWeight[0]*halfWeight[1]) / ( + // sum*sum ) : 0.0; + // dHarmonicWeight_dVar[1] = sum > 0 ? (dHalfWeight_dVar[1]*sum*halfWeight[0] - dHalfWeight_dVar[1]*halfWeight[1]*halfWeight[0]) / ( + // sum*sum ) : 0.0; + + // dArithmeticWeight_dVar[0] = dHalfWeight_dVar[0] / 2; + // dArithmeticWeight_dVar[1] = dHalfWeight_dVar[1] / 2; + + // real64 const meanPermCoeff = 1.0; //TODO make it a member if it is really necessary + + // real64 const value = meanPermCoeff * harmonicWeight + (1 - meanPermCoeff) * arithmeticWeight; + for( localIndex ke = 0; ke < 2; ++ke ) + { + // weight[0][ke] = m_transMultiplier[iconn] * value * (ke == 0 ? 1 : -1); + weight[0][ke] = m_transMultiplier[iconn] * halfWeight[ke] * (ke == 0 ? 1 : -1); + + // real64 const dValue_dVar = meanPermCoeff * dHarmonicWeight_dVar[ke] + (1 - meanPermCoeff) * dArithmeticWeight_dVar[ke]; + // dWeight_dVar[0][ke] = m_transMultiplier[iconn] * dValue_dVar; + dWeight_dVar[0][ke] = m_transMultiplier[iconn] * dHalfWeight_dVar[ke]; + } +} + GEOS_HOST_DEVICE inline void CellElementStencilTPFAWrapper:: diff --git a/src/coreComponents/finiteVolume/EmbeddedSurfaceToCellStencil.hpp b/src/coreComponents/finiteVolume/EmbeddedSurfaceToCellStencil.hpp index 7f9c1562366..4a191ca7614 100644 --- a/src/coreComponents/finiteVolume/EmbeddedSurfaceToCellStencil.hpp +++ b/src/coreComponents/finiteVolume/EmbeddedSurfaceToCellStencil.hpp @@ -98,6 +98,22 @@ class EmbeddedSurfaceToCellStencilWrapper : public StencilWrapperBase< TwoPointS real64 ( &weight )[1][2], real64 ( &dWeight_dVar )[1][2] ) const; + /** + * @brief Compute half weigths and derivatives w.r.t to one variable. + * @param[in] iconn connection index + * @param[in] coefficient view accessor to the coefficient used to compute the weights + * @param[in] dCoeff_dVar view accessor to the derivative of the coefficient w.r.t to the variable + * @param[out] weight view weights + * @param[out] dWeight_dVar derivative of the weigths w.r.t to the variable + */ + + GEOS_HOST_DEVICE + void computeHalfWeights( localIndex const iconn, + CoefficientAccessor< arrayView3d< real64 const > > const & coefficient, + CoefficientAccessor< arrayView3d< real64 const > > const & dCoeff_dVar, + real64 ( &weight )[1][2], + real64 ( &dWeight_dVar )[1][2] ) const; + /** * @brief Compute weigths and derivatives w.r.t to one variable without coefficient * Used in ReactiveCompositionalMultiphaseOBL solver for thermal transmissibility computation: @@ -243,6 +259,42 @@ EmbeddedSurfaceToCellStencilWrapper:: dWeight_dVar[0][1] = ( t0 * dt1 * sumOfTrans - dt1 * t0 * t1 ) / ( sumOfTrans * sumOfTrans ); } +GEOS_HOST_DEVICE +inline void +EmbeddedSurfaceToCellStencilWrapper:: + computeHalfWeights( localIndex iconn, + CoefficientAccessor< arrayView3d< real64 const > > const & coefficient, + CoefficientAccessor< arrayView3d< real64 const > > const & dCoeff_dVar, + real64 ( & weight )[1][2], + real64 ( & dWeight_dVar )[1][2] ) const +{ + localIndex const er0 = m_elementRegionIndices[iconn][0]; + localIndex const esr0 = m_elementSubRegionIndices[iconn][0]; + localIndex const ei0 = m_elementIndices[iconn][0]; + + localIndex const er1 = m_elementRegionIndices[iconn][1]; + localIndex const esr1 = m_elementSubRegionIndices[iconn][1]; + localIndex const ei1 = m_elementIndices[iconn][1]; + + // Will change when implementing collocation points. Will use fracture normal to project the permeability + real64 const t0 = m_weights[iconn][0] * LvArray::tensorOps::l2Norm< 3 >( coefficient[er0][esr0][ei0][0] ); + // We consider the 3rd component of the permeability which is the normal one. + real64 const t1 = m_weights[iconn][1] * coefficient[er1][esr1][ei1][0][2]; + + real64 const sumOfTrans = t0+t1; + real64 const value = t0*t1/sumOfTrans; + + weight[0][0] = value; + weight[0][1] = -value; + + // We consider the 3rd component of the permeability which is the normal one. + real64 const dt0 = m_weights[iconn][0] * dCoeff_dVar[er0][esr0][ei0][0][0]; + real64 const dt1 = m_weights[iconn][1] * dCoeff_dVar[er1][esr1][ei1][0][2]; + + dWeight_dVar[0][0] = ( dt0 * t1 * sumOfTrans - dt0 * t0 * t1 ) / ( sumOfTrans * sumOfTrans ); + dWeight_dVar[0][1] = ( t0 * dt1 * sumOfTrans - dt1 * t0 * t1 ) / ( sumOfTrans * sumOfTrans ); +} + GEOS_HOST_DEVICE inline void EmbeddedSurfaceToCellStencilWrapper:: diff --git a/src/coreComponents/finiteVolume/FaceElementToCellStencil.hpp b/src/coreComponents/finiteVolume/FaceElementToCellStencil.hpp index 6920e52b3bf..e86e6724bed 100644 --- a/src/coreComponents/finiteVolume/FaceElementToCellStencil.hpp +++ b/src/coreComponents/finiteVolume/FaceElementToCellStencil.hpp @@ -106,6 +106,21 @@ class FaceElementToCellStencilWrapper : public StencilWrapperBase< TwoPointStenc real64 ( &weight )[1][2], real64 ( &dWeight_dVar )[1][2] ) const; +/** + * @brief Compute half weigths and derivatives w.r.t to one variable. + * @param[in] iconn connection index + * @param[in] coefficient view accessor to the coefficient used to compute the weights + * @param[in] dCoeff_dVar view accessor to the derivative of the coefficient w.r.t to the variable + * @param[out] weight view weights + * @param[out] dWeight_dVar derivative of the weigths w.r.t to the variable + */ + GEOS_HOST_DEVICE + void computeHalfWeights( localIndex iconn, + CoefficientAccessor< arrayView3d< real64 const > > const & coefficient, + CoefficientAccessor< arrayView3d< real64 const > > const & dCoeff_dVar, + real64 ( &weight )[1][2], + real64 ( &dWeight_dVar )[1][2] ) const; + /** * @brief Compute weigths and derivatives w.r.t to one variable without coefficient * Used in ReactiveCompositionalMultiphaseOBL solver for thermal transmissibility computation: @@ -297,6 +312,44 @@ inline void FaceElementToCellStencilWrapper:: dWeight_dVar[0][1] = m_transMultiplier[iconn] * ( t0 * dt1 * sumOfTrans - dt1 * t0 * t1 ) / ( sumOfTrans * sumOfTrans ); } +GEOS_HOST_DEVICE +inline void FaceElementToCellStencilWrapper:: + computeHalfWeights( localIndex const iconn, + CoefficientAccessor< arrayView3d< real64 const > > const & coefficient, + CoefficientAccessor< arrayView3d< real64 const > > const & dCoeff_dVar, + real64 ( & weight )[1][2], + real64 ( & dWeight_dVar )[1][2] ) const +{ + localIndex const er0 = m_elementRegionIndices[iconn][0]; + localIndex const esr0 = m_elementSubRegionIndices[iconn][0]; + localIndex const ei0 = m_elementIndices[iconn][0]; + + localIndex const er1 = m_elementRegionIndices[iconn][1]; + localIndex const esr1 = m_elementSubRegionIndices[iconn][1]; + localIndex const ei1 = m_elementIndices[iconn][1]; + + real64 faceConormal[3]; + + // Will change when implementing collocation points. + LvArray::tensorOps::hadamardProduct< 3 >( faceConormal, coefficient[er0][esr0][ei0][0], m_faceNormal[iconn] ); + real64 const t0 = m_weights[iconn][0] * LvArray::tensorOps::AiBi< 3 >( m_cellToFaceVec[iconn], faceConormal ); + // We consider the 3rd component of the permeability which is the normal one. + real64 const t1 = m_weights[iconn][1] * coefficient[er1][esr1][ei1][0][2]; + + real64 const sumOfTrans = t0+t1; + real64 const value = m_transMultiplier[iconn]*t0*t1/sumOfTrans; + + weight[0][0] = value; + weight[0][1] = -value; + + // We consider the 3rd component of the permeability which is the normal one. + real64 const dt0 = m_weights[iconn][0] * dCoeff_dVar[er0][esr0][ei0][0][0]; + real64 const dt1 = m_weights[iconn][1] * dCoeff_dVar[er1][esr1][ei1][0][2]; + + dWeight_dVar[0][0] = ( dt0 * t1 * sumOfTrans - dt0 * t0 * t1 ) / ( sumOfTrans * sumOfTrans ); + dWeight_dVar[0][1] = ( t0 * dt1 * sumOfTrans - dt1 * t0 * t1 ) / ( sumOfTrans * sumOfTrans ); +} + GEOS_HOST_DEVICE inline void FaceElementToCellStencilWrapper diff --git a/src/coreComponents/finiteVolume/SurfaceElementStencil.hpp b/src/coreComponents/finiteVolume/SurfaceElementStencil.hpp index 5de67fe2f11..22942a1a648 100644 --- a/src/coreComponents/finiteVolume/SurfaceElementStencil.hpp +++ b/src/coreComponents/finiteVolume/SurfaceElementStencil.hpp @@ -110,6 +110,21 @@ class SurfaceElementStencilWrapper : public StencilWrapperBase< SurfaceElementSt real64 ( &weight )[maxNumConnections][2], real64 ( &dWeight_dVar )[maxNumConnections][2] ) const; + /** + * @brief Compute weights and derivatives w.r.t to one variable. + * @param[in] iconn connection index + * @param[in] coefficient view accessor to the coefficient used to compute the weights + * @param[in] dCoeff_dVar view accessor to the derivative of the coefficient w.r.t to the variable + * @param[out] weight view weights + * @param[out] dWeight_dVar derivative of the weights w.r.t to the variable + */ + GEOS_HOST_DEVICE + void computeHalfWeights( localIndex iconn, + CoefficientAccessor< arrayView3d< real64 const > > const & coefficient, + CoefficientAccessor< arrayView3d< real64 const > > const & dCoeff_dVar, + real64 ( &weight )[maxNumConnections][2], + real64 ( &dWeight_dVar )[maxNumConnections][2] ) const; + /** * @brief Compute weights and derivatives w.r.t to one variable without coefficient * Used in ReactiveCompositionalMultiphaseOBL solver for thermal transmissibility computation: @@ -364,6 +379,70 @@ SurfaceElementStencilWrapper:: } } +GEOS_HOST_DEVICE +inline void +SurfaceElementStencilWrapper:: + computeHalfWeights( localIndex iconn, + CoefficientAccessor< arrayView3d< real64 const > > const & coefficient, + CoefficientAccessor< arrayView3d< real64 const > > const & dCoeff_dVar, + real64 ( & weight )[maxNumConnections][2], + real64 ( & dWeight_dVar )[maxNumConnections][2] ) const +{ + + real64 sumOfTrans = 0.0; + for( localIndex k=0; k coordinates_dw; // Swc = 0.22 // consistent with Swmaxd = 1-Sgcrd = 1-0 = 1 - geos::testing::fillArray( coordinates_dw, - {.22, .25, .3, .35, .4, .45, .5, .55, .6, .65, .66, .68, .72, .82, .91, 1.} ); + geosx::testing::fill_array( coordinates_dw, {.22, .25, .3, .35, .4, .45, .5, .55, .6, .65, .66, .68, .72, .82, .91, 1.} ); array1d< real64_array > coordinates_iw; // Swc = 0.22 // consistent with Swmaxi = 1-Sgcri = 1-0.3 = 0.7 - geos::testing::fillArray( coordinates_iw, {.22, .25, .3, .35, .4, .45, .5, .55, .6, .65, .66, .7} ); + geosx::testing::fill_array( coordinates_iw, {.22, .25, .3, .35, .4, .45, .5, .55, .6, .65, .66, .7} ); // Gas phase saturation, fifth column of Table 2 array1d< real64_array > coordinates_dg; // Sgcrd = 0.0 // consistent with Swc = 0.22 - geos::testing::fillArray( coordinates_dg, - {0.000, 0.010, 0.030, 0.050, 0.100, 0.150, 0.200, 0.250, 0.300, 0.350, 0.400, 0.450, - 0.500, - 0.550, 0.600, 0.650, 0.700, 0.750, 0.780} ); + geosx::testing::fill_array( coordinates_dg, + {0.000, 0.010, 0.030, 0.050, 0.100, 0.150, 0.200, 0.250, 0.300, 0.350, 0.400, 0.450, 0.500, + 0.550, 0.600, 0.650, 0.700, 0.750, 0.780} ); array1d< real64_array > coordinates_ig; // Sgcri = 0.30; // consistent with Swc = 0.22 - geos::testing::fillArray( coordinates_ig, - {0.300, 0.350, 0.400, 0.450, 0.500, 0.550, 0.600, 0.650, 0.700, 0.750, 0.78} ); + geosx::testing::fill_array( coordinates_ig, {0.300, 0.350, 0.400, 0.450, 0.500, 0.550, 0.600, 0.650, 0.700, 0.750, 0.78} ); // then define the bounding drainage and imbibibition relative permeability // Water phase drainage relperm real64_array drainageValues_w; - geos::testing::fillArray( drainageValues_w, {0.00000, 0.00100, 0.00300, 0.01000, 0.01800, 0.03500, 0.04000, 0.05700, - 0.08800, 0.14500, 0.16000, 0.19000, 0.26300, 0.45500, 0.69200, 1.} ); + geosx::testing::fill_array( drainageValues_w, {0.00000, 0.00100, 0.00300, 0.01000, 0.01800, 0.03500, 0.04000, 0.05700, + 0.08800, 0.14500, 0.16000, 0.19000, 0.26300, 0.45500, 0.69200, 1.} ); // Gas phase drainage relperm, seventh column of Table 2 real64_array drainageValues_g; - geos::testing::fillArray( drainageValues_g, {0.00000, 0.00200, 0.00700, 0.01000, 0.02000, 0.04000, 0.07500, - 0.12700, 0.18000, 0.24000, 0.31000, 0.37300, 0.46000, 0.55000, - 0.64000, 0.73000, 0.82500, 0.92000, 1.00000} ); + geosx::testing::fill_array( drainageValues_g, {0.00000, 0.00200, 0.00700, 0.01000, 0.02000, 0.04000, 0.07500, + 0.12700, 0.18000, 0.24000, 0.31000, 0.37300, 0.46000, 0.55000, + 0.64000, 0.73000, 0.82500, 0.92000, 1.00000} ); real64_array imbibitionValues_w; - geos::testing::fillArray( imbibitionValues_w, {0, 0.0156, 0.0680, 0.1409, 0.2296, 0.3317, 0.4455, 0.5700, - 0.7044, 0.8479, 0.8776, 0.9382} ); + geosx::testing::fill_array( imbibitionValues_w, {0, 0.0156, 0.0680, 0.1409, 0.2296, 0.3317, 0.4455, 0.5700, + 0.7044, 0.8479, 0.8776, 0.9382} ); real64_array imbibitionValues_g; - geos::testing::fillArray( imbibitionValues_g, {0.0000, 0.03361965, 0.09509072, 0.17469281, 0.26895718, - 0.37587908, 0.49410588, 0.62264458, 0.76072577, 0.90773047, 1.} ); + geosx::testing::fill_array( imbibitionValues_g, {0.0000, 0.03361965, 0.09509072, 0.17469281, 0.26895718, + 0.37587908, 0.49410588, 0.62264458, 0.76072577, 0.90773047, 1.} ); initializeTable( "drainageWater_swg", coordinates_dw, diff --git a/src/coreComponents/integrationTests/fluidFlowTests/CMakeLists.txt b/src/coreComponents/integrationTests/fluidFlowTests/CMakeLists.txt index 951cac7c502..9a89d938e2e 100644 --- a/src/coreComponents/integrationTests/fluidFlowTests/CMakeLists.txt +++ b/src/coreComponents/integrationTests/fluidFlowTests/CMakeLists.txt @@ -4,6 +4,7 @@ set( gtest_geosx_tests testThermalSinglePhaseFlow.cpp testTransmissibility.cpp testImmiscibleMultiphaseFlow.cpp + testImmiscibleInterfaceConditions.cpp testSinglePhaseMFDPolyhedral.cpp testSinglePhaseReactiveTransport.cpp ) diff --git a/src/coreComponents/integrationTests/fluidFlowTests/testImmiscibleInterfaceConditions.cpp b/src/coreComponents/integrationTests/fluidFlowTests/testImmiscibleInterfaceConditions.cpp new file mode 100644 index 00000000000..b1816d04204 --- /dev/null +++ b/src/coreComponents/integrationTests/fluidFlowTests/testImmiscibleInterfaceConditions.cpp @@ -0,0 +1,764 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * SPDX-License-Identifier: LGPL-2.1-only + * + * Copyright (c) 2016-2024 Lawrence Livermore National Security LLC + * Copyright (c) 2018-2024 TotalEnergies + * Copyright (c) 2018-2024 The Board of Trustees of the Leland Stanford Junior University + * Copyright (c) 2023-2024 Chevron + * Copyright (c) 2019- GEOS/GEOSX Contributors + * All rights reserved + * + * See top level LICENSE, COPYRIGHT, CONTRIBUTORS, NOTICE, and ACKNOWLEDGEMENTS files for details. + * ------------------------------------------------------------------------------------------------------------ + */ +#define phase0MinSat1 0.0 +#define phase1MinSat1 0.0 +#define phase0MinSat2 0.0 +#define phase1MinSat2 0.0 + + +#include +#include + +#include "mainInterface/initialization.hpp" +#include "mainInterface/GeosxState.hpp" +#include "constitutive/fluid/twophaseimmisciblefluid/TwoPhaseImmiscibleFluid.hpp" +#include "physicsSolvers/PhysicsSolverManager.hpp" +#include "physicsSolvers/fluidFlow/ImmiscibleMultiphaseFlow.hpp" +#include "integrationTests/fluidFlowTests/testCompFlowUtils.hpp" +#include "constitutive/unitTests/FluidModelTest.hpp" +#include "constitutive/unitTests/FluidModelTest_impl.hpp" +#include "common/initializeEnvironment.hpp" +#include "constitutive/relativePermeability/unitTests/constitutiveTestHelpers.hpp" +#include "functions/FunctionManager.hpp" +#include "constitutive/capillaryPressure/CapillaryPressureFields.hpp" + +#include + + + +using namespace geos; +using namespace geos::dataRepository; +using namespace geos::constitutive; +using namespace geos::testing; + +CommandLineOptions g_commandLineOptions; + +TwoPhaseImmiscibleFluid * makeTwoPhaseImmiscibleFluid( TwoPhaseImmiscibleFluid & fluid ) +{ + + FunctionManager & functionManager = FunctionManager::getInstance(); + + // 1D table with linear interpolation + localIndex constexpr Naxis = 6; + localIndex constexpr NaxisSingle = 1; + + real64_array densityCoordPhase0; + // fill( densityCoordPhase0, Feed< Naxis >{ 0.22, 0.3, 0.5, 0.6, 0.8, 1.0 } ); + for (auto v : {0.0}) + densityCoordPhase0.emplace_back(v); + real64_array densityValuesPhase0; + // fill( densityValuesPhase0, Feed< Naxis >{ 0.00603, 0.04224, 0.04224, 0.22423, 0.31311, 0.40203 } ); + for (auto v : {1000.0}) + densityValuesPhase0.emplace_back(v); + + real64_array densityCoordPhase1; + // fill( densityCoordPhase1, Feed< Naxis >{ 1.22, 1.3, 1.5, 1.6, 1.8, 2.0 } ); + for (auto v : {0.0}) + densityCoordPhase1.emplace_back(v); + real64_array densityValuesPhase1; + // fill( densityValuesPhase1, Feed< Naxis >{ 0.00603, 0.04224, 0.04224, 0.22423, 0.31311, 0.40203 } ); + for (auto v : {100.0}) + densityValuesPhase1.emplace_back(v); + + real64_array viscosityCoordPhase0; + // fill( viscosityCoordPhase0, Feed< Naxis >{ 0.22, 0.3, 0.5, 0.6, 0.8, 1.0 } ); + for (auto v : {0.0}) + viscosityCoordPhase0.emplace_back(v); + real64_array viscosityValuesPhase0; + // fill( viscosityValuesPhase0, Feed< Naxis >{ 40203, 31311, 22423, 15011, 4224, 603 } ); + for (auto v : {0.001}) + viscosityValuesPhase0.emplace_back(v); + + real64_array viscosityCoordPhase1; + // fill( viscosityCoordPhase1, Feed< NaxisSingle >{ 0.22 } ); + for (auto v : {0.0}) + viscosityCoordPhase1.emplace_back(v); + + real64_array viscosityValuesPhase1; + // fill( viscosityValuesPhase1, Feed< NaxisSingle >{ 45 } ); + for (auto v : {0.001}) + viscosityValuesPhase1.emplace_back(v); + + TableFunction & table_density0 = dynamicCast< TableFunction & >( *functionManager.createChild( "TableFunction", "densityTablePhase0" ) ); + array1d coords_density0; + coords_density0.emplace_back(densityCoordPhase0); + table_density0.setTableCoordinates( coords_density0, { units::Dimensionless } ); + table_density0.setTableValues( densityValuesPhase0, units::Dimensionless ); + table_density0.reInitializeFunction(); + + table_density0.setInterpolationMethod( TableFunction::InterpolationType::Linear ); + + TableFunction & table_density1 = dynamicCast< TableFunction & >( *functionManager.createChild( "TableFunction", "densityTablePhase1" ) ); + array1d coords_density1; + coords_density1.emplace_back(densityCoordPhase1); + table_density1.setTableCoordinates( coords_density1, { units::Dimensionless } ); + table_density1.setTableValues( densityValuesPhase1, units::Dimensionless ); + table_density1.reInitializeFunction(); + + table_density1.setInterpolationMethod( TableFunction::InterpolationType::Linear ); + + TableFunction & table_viscosity0 = dynamicCast< TableFunction & >( *functionManager.createChild( "TableFunction", "viscosityTablePhase0" ) ); + array1d coords_viscosity0; + coords_viscosity0.emplace_back(viscosityCoordPhase0); + table_viscosity0.setTableCoordinates( coords_viscosity0, { units::Dimensionless } ); + table_viscosity0.setTableValues( viscosityValuesPhase0, units::Dimensionless ); + table_viscosity0.reInitializeFunction(); + + table_viscosity0.setInterpolationMethod( TableFunction::InterpolationType::Linear ); + + TableFunction & table_viscosity1 = dynamicCast< TableFunction & >( *functionManager.createChild( "TableFunction", "viscosityTablePhase1" ) ); + array1d coords_viscosity1; + coords_viscosity1.emplace_back(viscosityCoordPhase1); + table_viscosity1.setTableCoordinates( coords_viscosity1, { units::Dimensionless } ); + table_viscosity1.setTableValues( viscosityValuesPhase1, units::Dimensionless ); + table_viscosity1.reInitializeFunction(); + + table_viscosity1.setInterpolationMethod( TableFunction::InterpolationType::Linear ); + + + // createTable( "densityTablePhase0", densityCoordPhase0, densityValuesPhase0 ); + // createTable( "densityTablePhase1", densityCoordPhase1, densityValuesPhase1 ); + // createTable( "viscosityTablePhase0", viscosityCoordPhase0, viscosityValuesPhase0 ); + // createTable( "viscosityTablePhase1", viscosityCoordPhase1, viscosityValuesPhase1 ); + + // 2) Set up the constitutive model + + string_array & phaseNames = fluid.getReference< string_array >( TwoPhaseImmiscibleFluid::viewKeyStruct::phaseNamesString() ); + phaseNames.emplace_back( "water" ); + phaseNames.emplace_back( "gas" ); + + string_array & densityTableNames = fluid.getReference< string_array >( TwoPhaseImmiscibleFluid::viewKeyStruct::densityTableNamesString() ); + densityTableNames.emplace_back( "densityTablePhase0" ); + densityTableNames.emplace_back( "densityTablePhase1" ); + + string_array & viscosityTableNames = fluid.getReference< string_array >( TwoPhaseImmiscibleFluid::viewKeyStruct::viscosityTableNamesString() ); + viscosityTableNames.emplace_back( "viscosityTablePhase0" ); + viscosityTableNames.emplace_back( "viscosityTablePhase1" ); + + fluid.postInputInitializationRecursive(); + fluid.initialize(); // to test all the checks + + return &fluid; +} + +CapillaryPressureBase & makeJFunctionCapPressureTwoPhase( string const & name, Group & parent ) +{ + FunctionManager & functionManager = FunctionManager::getInstance(); + + // 1) First, define the tables + + // // 1D table, various interpolation methods + // localIndex const Naxis = 12; + + // // Setup table + // array1d< real64_array > coordinates; + // coordinates.resize( 1 ); + // coordinates[0].resize( Naxis ); + + // coordinates[0][0] = 0.0; + // coordinates[0][1] = 0.05; + // coordinates[0][2] = 0.15; + // coordinates[0][3] = 0.25; + // coordinates[0][4] = 0.35; + // coordinates[0][5] = 0.45; + // coordinates[0][6] = 0.55; + // coordinates[0][7] = 0.65; + // coordinates[0][8] = 0.75; + // coordinates[0][9] = 0.85; + // coordinates[0][10] = 0.95; + // coordinates[0][11] = 1.0; + + // real64_array values( Naxis ); + // values[0] = 4.331729359; + // values[1] = 3.523266264; + // values[2] = 2.677103439; + // values[3] = 2.356150157; + // values[4] = 2.166062360; + // values[5] = 2.034158727; + // values[6] = 1.934627222; + // values[7] = 1.855494313; + // values[8] = 1.790286970; + // values[9] = 1.735134860; + // values[10] = 1.687551617; + // values[11] = 1.666049754; + + + localIndex const Naxis = 2; + + // Setup table + array1d< real64_array > coordinates; + coordinates.resize( 1 ); + coordinates[0].resize( Naxis ); + + coordinates[0][0] = 0.0; + coordinates[0][1] = 1.0; + + real64_array values( Naxis ); + values[0] = 4.331729359; + values[1] = 1.666049754; + + + TableFunction & table_w = dynamicCast< TableFunction & >( *functionManager.createChild( "TableFunction", "water_jFunction" ) ); + table_w.setTableCoordinates( coordinates, { units::Dimensionless } ); + table_w.setTableValues( values, units::Dimensionless ); + table_w.reInitializeFunction(); + + table_w.setInterpolationMethod( TableFunction::InterpolationType::Linear ); + + // 2) Then set up the constitutive model + + JFunctionCapillaryPressure & capPressure = parent.registerGroup< JFunctionCapillaryPressure >( name ); + + string_array & phaseNames = capPressure.getReference< string_array >( CapillaryPressureBase::viewKeyStruct::phaseNamesString() ); + phaseNames.resize( 2 ); + phaseNames[0] = "water"; phaseNames[1] = "gas"; + + auto & waterTableName = capPressure.getReference< string >( JFunctionCapillaryPressure::viewKeyStruct::wettingNonWettingJFuncTableNameString() ); + waterTableName = "water_jFunction"; + + auto & surfaceTension = capPressure.getReference< real64 >( JFunctionCapillaryPressure::viewKeyStruct::wettingNonWettingSurfaceTensionString() ); + //surfaceTension = 23.86955676433857e-3; + surfaceTension = 0.02; + + auto & permeabilityDirection = + capPressure.getReference< JFunctionCapillaryPressure::PermeabilityDirection >( JFunctionCapillaryPressure::viewKeyStruct::permeabilityDirectionString() ); + permeabilityDirection = JFunctionCapillaryPressure::PermeabilityDirection::XY; + + capPressure.postInputInitializationRecursive(); + capPressure.initialize(); // to test all the checks + + return capPressure; +} + +CapillaryPressureBase & makeTableCapPressureTwoPhase1( string const & name, Group & parent ) +{ + FunctionManager & functionManager = FunctionManager::getInstance(); + + // 1) First, define the tables + + // 1D table, various interpolation methods + localIndex Naxis = 12; + + // Setup table + array1d< real64_array > coordinates; + coordinates.resize( 1 ); + coordinates[0].resize( Naxis ); + coordinates[0][0] = 0.0; + coordinates[0][1] = 0.05; + coordinates[0][2] = 0.15; + coordinates[0][3] = 0.25; + coordinates[0][4] = 0.35; + coordinates[0][5] = 0.45; + coordinates[0][6] = 0.55; + coordinates[0][7] = 0.65; + coordinates[0][8] = 0.75; + coordinates[0][9] = 0.85; + coordinates[0][10] = 0.95; + coordinates[0][11] = 1.0; + + real64_array values( Naxis ); + values[0] = 130000.0; + values[1] = 90572.79; + values[2] = 49307.11; + values[3] = 33654.85; + values[4] = 24384.64; + values[5] = 17951.96; + values[6] = 13098; + values[7] = 9238.84; + values[8] = 6058.81; + values[9] = 3369.14; + values[10] = 1048.6; + values[11] = 0.0; + + // localIndex const Naxis = 2; + + // // Setup table + // array1d< real64_array > coordinates; + // coordinates.resize( 1 ); + // coordinates[0].resize( Naxis ); + + // coordinates[0][0] = 0.0; + // coordinates[0][1] = 1.0; + + // real64_array values( Naxis ); + // values[0] = 129999.999994362; + // values[1] = 50000.0000139914; + + + TableFunction & table_w = dynamicCast< TableFunction & >( *functionManager.createChild( "TableFunction", "water_pc" ) ); + table_w.setTableCoordinates( coordinates, { units::Dimensionless } ); + table_w.setTableValues( values, units::Pressure ); + table_w.reInitializeFunction(); + + table_w.setInterpolationMethod( TableFunction::InterpolationType::Linear ); + + // 2) Then set up the constitutive model + + TableCapillaryPressure & capPressure = parent.registerGroup< TableCapillaryPressure >( name ); + + string_array & phaseNames = capPressure.getReference< string_array >( CapillaryPressureBase::viewKeyStruct::phaseNamesString() ); + phaseNames.resize( 2 ); + phaseNames[0] = "water"; phaseNames[1] = "gas"; + + auto & waterTableName = capPressure.getReference< string >( TableCapillaryPressure::viewKeyStruct::wettingNonWettingCapPresTableNameString() ); + waterTableName = "water_pc"; + + capPressure.postInputInitializationRecursive(); + capPressure.initialize(); // to test all the checks + return capPressure; +} + +CapillaryPressureBase & makeTableCapPressureTwoPhase2( string const & name, Group & parent ) +{ + FunctionManager & functionManager = FunctionManager::getInstance(); + + // 1) First, define the tables + + // 1D table, various interpolation methods + localIndex Naxis = 12; + + // Setup table + array1d< real64_array > coordinates; + coordinates.resize( 1 ); + coordinates[0].resize( Naxis ); + coordinates[0][0] = 0.0; + coordinates[0][1] = 0.05; + coordinates[0][2] = 0.15; + coordinates[0][3] = 0.25; + coordinates[0][4] = 0.35; + coordinates[0][5] = 0.45; + coordinates[0][6] = 0.55; + coordinates[0][7] = 0.65; + coordinates[0][8] = 0.75; + coordinates[0][9] = 0.85; + coordinates[0][10] = 0.95; + coordinates[0][11] = 1.0; + + real64_array values( Naxis ); + values[0] = 195000.0; + values[1] = 135859.25; + values[2] = 73960.67; + values[3] = 50482.28; + values[4] = 36576.96; + values[5] = 26927.94; + values[6] = 19647; + values[7] = 13858.26; + values[8] = 9088.2; + values[9] = 5053.72; + values[10] = 1572.9; + values[11] = 0.0; + + // localIndex const Naxis = 2; + + // // Setup table + // array1d< real64_array > coordinates; + // coordinates.resize( 1 ); + // coordinates[0].resize( Naxis ); + + // coordinates[0][0] = 0.0; + // coordinates[0][1] = 1.0; + + // real64_array values( Naxis ); + // values[0] = 129999.999994362; + // values[1] = 50000.0000139914; + + + TableFunction & table_w = dynamicCast< TableFunction & >( *functionManager.createChild( "TableFunction", "water_pc" ) ); + table_w.setTableCoordinates( coordinates, { units::Dimensionless } ); + table_w.setTableValues( values, units::Pressure ); + table_w.reInitializeFunction(); + + table_w.setInterpolationMethod( TableFunction::InterpolationType::Linear ); + + // 2) Then set up the constitutive model + + TableCapillaryPressure & capPressure = parent.registerGroup< TableCapillaryPressure >( name ); + + string_array & phaseNames = capPressure.getReference< string_array >( CapillaryPressureBase::viewKeyStruct::phaseNamesString() ); + phaseNames.resize( 2 ); + phaseNames[0] = "water"; phaseNames[1] = "gas"; + + auto & waterTableName = capPressure.getReference< string >( TableCapillaryPressure::viewKeyStruct::wettingNonWettingCapPresTableNameString() ); + waterTableName = "water_pc"; + + capPressure.postInputInitializationRecursive(); + capPressure.initialize(); // to test all the checks + return capPressure; +} + +CapillaryPressureBase & makeBrooksCoreyCapPressureTwoPhase1( string const & name, Group & parent ) +{ + BrooksCoreyCapillaryPressure & capPressure = parent.registerGroup< BrooksCoreyCapillaryPressure >( name ); + + string_array & phaseNames = capPressure.getReference< string_array >( CapillaryPressureBase::viewKeyStruct::phaseNamesString() ); + phaseNames.resize( 2 ); + phaseNames[0] = "water"; phaseNames[1] = "gas"; + + array1d< real64 > & phaseMinSat = capPressure.getReference< array1d< real64 > >( BrooksCoreyCapillaryPressure::viewKeyStruct::phaseMinVolumeFractionString() ); + phaseMinSat.resize( 2 ); + phaseMinSat[0] = phase0MinSat1; phaseMinSat[1] = phase1MinSat1; + + array1d< real64 > & phaseCapPressureExpInv = + capPressure.getReference< array1d< real64 > >( BrooksCoreyCapillaryPressure::viewKeyStruct::phaseCapPressureExponentInvString() ); + phaseCapPressureExpInv.resize( 2 ); + phaseCapPressureExpInv[0] = 4; phaseCapPressureExpInv[1] = 1; + + array1d< real64 > & phaseEntryPressure = capPressure.getReference< array1d< real64 > >( BrooksCoreyCapillaryPressure::viewKeyStruct::phaseEntryPressureString() ); + phaseEntryPressure.resize( 2 ); + phaseEntryPressure[0] = 4.0e3; phaseEntryPressure[1] = 0; + + real64 & capPressureEpsilon = capPressure.getReference< real64 >( BrooksCoreyCapillaryPressure::viewKeyStruct::capPressureEpsilonString() ); + capPressureEpsilon = 1.0e-8; + + capPressure.postInputInitializationRecursive(); + return capPressure; +} + +CapillaryPressureBase & makeBrooksCoreyCapPressureTwoPhase2( string const & name, Group & parent ) +{ + BrooksCoreyCapillaryPressure & capPressure = parent.registerGroup< BrooksCoreyCapillaryPressure >( name ); + + string_array & phaseNames = capPressure.getReference< string_array >( CapillaryPressureBase::viewKeyStruct::phaseNamesString() ); + phaseNames.resize( 2 ); + phaseNames[0] = "water"; phaseNames[1] = "gas"; + + array1d< real64 > & phaseMinSat = capPressure.getReference< array1d< real64 > >( BrooksCoreyCapillaryPressure::viewKeyStruct::phaseMinVolumeFractionString() ); + phaseMinSat.resize( 2 ); + phaseMinSat[0] = phase0MinSat2; phaseMinSat[1] = phase1MinSat2; + + array1d< real64 > & phaseCapPressureExpInv = + capPressure.getReference< array1d< real64 > >( BrooksCoreyCapillaryPressure::viewKeyStruct::phaseCapPressureExponentInvString() ); + phaseCapPressureExpInv.resize( 2 ); + phaseCapPressureExpInv[0] = 4; phaseCapPressureExpInv[1] = 1; + + array1d< real64 > & phaseEntryPressure = capPressure.getReference< array1d< real64 > >( BrooksCoreyCapillaryPressure::viewKeyStruct::phaseEntryPressureString() ); + phaseEntryPressure.resize( 2 ); + phaseEntryPressure[0] = 2.0e3; phaseEntryPressure[1] = 0; + + real64 & capPressureEpsilon = capPressure.getReference< real64 >( BrooksCoreyCapillaryPressure::viewKeyStruct::capPressureEpsilonString() ); + capPressureEpsilon = 1e-8; + + capPressure.postInputInitializationRecursive(); + return capPressure; +} + +RelativePermeabilityBase & makeBrooksCoreyRelPerm( string const & name, Group & parent ) +{ + BrooksCoreyRelativePermeability & relPerm = parent.registerGroup< BrooksCoreyRelativePermeability >( name ); + + string_array & phaseNames = relPerm.getReference< string_array >( RelativePermeabilityBase::viewKeyStruct::phaseNamesString() ); + phaseNames.resize( 2 ); + phaseNames[0] = "water"; phaseNames[1] = "gas"; + + array1d< real64 > & phaseMinSat = relPerm.getReference< array1d< real64 > >( BrooksCoreyRelativePermeability::viewKeyStruct::phaseMinVolumeFractionString() ); + phaseMinSat.resize( 2 ); + phaseMinSat[0] = phase0MinSat1; phaseMinSat[1] = phase1MinSat1; + + array1d< real64 > & phaseRelPermExp = relPerm.getReference< array1d< real64 > >( BrooksCoreyRelativePermeability::viewKeyStruct::phaseRelPermExponentString() ); + phaseRelPermExp.resize( 2 ); + phaseRelPermExp[0] = 2.0; phaseRelPermExp[1] = 2.0; + + array1d< real64 > & phaseRelPermMaxVal = relPerm.getReference< array1d< real64 > >( BrooksCoreyRelativePermeability::viewKeyStruct::phaseRelPermMaxValueString() ); + phaseRelPermMaxVal.resize( 2 ); + phaseRelPermMaxVal[0] = 1.0; phaseRelPermMaxVal[1] = 1.0; + + relPerm.postInputInitializationRecursive(); + return relPerm; +} + +class ImmiscibleInterfaceConditionsTest : public FluidModelTest< TwoPhaseImmiscibleFluid, 2 > +{ +public: + ImmiscibleInterfaceConditionsTest(): state( std::make_unique< CommandLineOptions >( g_commandLineOptions )), + m_parent( "TestParentGroup", m_node ) + + {} + +protected: + + static real64 constexpr time = 0.0; + static real64 constexpr dt = 1e4; + static real64 constexpr eps = std::numeric_limits< real64 >::epsilon(); + + GeosxState state; + ImmiscibleMultiphaseFlow *solver; + conduit::Node m_node; + dataRepository::Group m_parent; +}; + +real64 constexpr ImmiscibleInterfaceConditionsTest::time; +real64 constexpr ImmiscibleInterfaceConditionsTest::dt; +real64 constexpr ImmiscibleInterfaceConditionsTest::eps; + + + +TEST_F( ImmiscibleInterfaceConditionsTest, LocalNonlinearSolverConvergence ) +{ + + // using Base = FluidModelTest< TwoPhaseImmiscibleFluid, 2 >; + createFluid( "fluid", [this]( TwoPhaseImmiscibleFluid & fluid ){ + makeTwoPhaseImmiscibleFluid( fluid ); + + // getting constitutive models: + RelativePermeabilityBase & relPerm = makeBrooksCoreyRelPerm( "relPerm" , this->m_parent); + RelativePermeabilityBase * relPermPtr = &relPerm; + // CapillaryPressureBase & capPressure0 = makeJFunctionCapPressureTwoPhase( "capPressure0", this->m_parent ); + // CapillaryPressureBase * capPressurePtr0 = &capPressure0; + + CapillaryPressureBase & capPressure0 = makeTableCapPressureTwoPhase1( "capPressure0", this->m_parent ); + CapillaryPressureBase * capPressurePtr0 = &capPressure0; + + CapillaryPressureBase & capPressure1 = makeTableCapPressureTwoPhase2( "capPressure1", this->m_parent ); + CapillaryPressureBase * capPressurePtr1 = &capPressure1; + // CapillaryPressureBase & capPressure0 = makeBrooksCoreyCapPressureTwoPhase1( "capPressure0", this->m_parent ); + // CapillaryPressureBase * capPressurePtr0 = &capPressure0; + + // CapillaryPressureBase & capPressure1 = makeBrooksCoreyCapPressureTwoPhase2( "capPressure1", this->m_parent ); + // CapillaryPressureBase * capPressurePtr1 = &capPressure1; + + std::vector< RelativePermeabilityBase * > relPerms = {relPermPtr, relPermPtr}; + std::vector< CapillaryPressureBase * > capPressures = {capPressurePtr0, capPressurePtr1}; + std::vector< TwoPhaseImmiscibleFluid * > fluids = { &fluid, &fluid }; + // real64 uT = 3.2864545889999906e-05; + + real64 uT = 3.3e-2; +// real64 uT = 1e-7; + stdVector< real64 > saturations = {0.2, 0.4}; + stdVector< real64 > trappedSats1 = {phase0MinSat1, phase1MinSat1}; + stdVector< real64 > trappedSats2 = {phase0MinSat2, phase1MinSat2}; + stdVector< real64 > pressures = {1e7, 1e7}; + stdVector< real64 > JFMultipliers = {45016.662822296035, 30011.108548197357}; + stdVector< fields::cappres::ModeIndexType > modes = {static_cast(0), static_cast(0)}; + stdVector< real64 > transHats = {1.9738466000000002e-12, 4.4411548500000007e-12}; + stdVector< real64 > dTransHats_dP = {0.0, 0.0}; + stdVector< real64 > gravCoefHats = {490.5, 490.5}; + stdVector< real64 > gravCoefs = {465.97500000000002, 515.02499999999998}; + stdVector< real64 > cellCenterDuT = {0.0, 0.0, 0.0, 0.0}; // duT_dP[0], duT_dP[1], duT_dS[0], duT_dS[1] + stdVector< real64 > cellCenterDens = {1000.0, 800.0}; // density for each phase + stdVector< real64 > cellCenterDens_dP = {0.0, 0.0, 0.0, 0.0}; // dDens_dP[0][0], dDens_dP[0][1], dDens_dP[1][0], dDens_dP[1][1] + stdVector< real64 > phaseMaxHistVolFrac1 = {0.0, 0.0}; + stdVector< real64 > phaseMinHistVolFrac1 = {0.0, 0.0}; + stdVector< real64 > phaseMaxHistVolFrac2 = {0.0, 0.0}; + stdVector< real64 > phaseMinHistVolFrac2 = {0.0, 0.0}; + + stdVector< real64 > phi = {0.0, 0.0}; + stdVector< real64 > grad_phi_P = {0.0, 0.0, 0.0, 0.0}; + stdVector< real64 > grad_phi_S = {0.0, 0.0, 0.0, 0.0}; + bool converged = false; + + std::ofstream outFile( "local_solver_results.csv" ); + + + // Write data to the file + outFile << "Si"; + outFile << ","; + outFile << "Sj"; + outFile << ","; + outFile << "Fw_alpha"; + outFile << ","; + outFile << "Fn_alpha"; + outFile << ","; + outFile << "Residual_initial"; + outFile << ","; + outFile << "Pc_int"; + outFile << ","; + outFile << "Residual"; + outFile << ","; + outFile << "newton"; + outFile << std::endl; + + real64 const start_sat = 0.0; + real64 const end_sat = 1.0; + real64 const dS = 1e-2; + // real64 Si = 0.0; + // real64 Sj = 0.9; + + for( real64 Si = start_sat; Si <= end_sat + 1e-8; Si += dS ) + { + for( real64 Sj = start_sat; Sj <= end_sat + 1e-8; Sj += dS ) + { + saturations[0] = Si; + saturations[1] = Sj; + + auto t0 = std::chrono::high_resolution_clock::now(); + +// Call the GEOS local solver + geos::immiscibleMultiphaseKernels::local_solver( uT, saturations, pressures, JFMultipliers, trappedSats1, trappedSats2, modes, transHats, dTransHats_dP, gravCoefHats, gravCoefs, + cellCenterDuT, cellCenterDens, cellCenterDens_dP, relPerms, capPressures, fluids, phi, grad_phi_P, grad_phi_S, converged, + phaseMaxHistVolFrac1, phaseMinHistVolFrac1, phaseMaxHistVolFrac2, phaseMinHistVolFrac2 ); + +auto t1 = std::chrono::high_resolution_clock::now(); +std::chrono::duration elapsed = t1 - t0; +// std::cout << "Local solver time: " << elapsed.count() << " s" << std::endl; +EXPECT_TRUE( converged ) << "Local solver diverged for saturations Si=" << Si << ", Sj=" << Sj; + + // Write data to the file + outFile << GEOS_FMT( "{:10.10e}", saturations[0] ); + outFile << GEOS_FMT( ",{:10.10e}", saturations[1] ); + outFile << GEOS_FMT( ",{:10.10e}", phi[0] ); + outFile << GEOS_FMT( ",{:10.10e}", phi[1] ); + outFile << GEOS_FMT( ",{:10.10e}", grad_phi_P[0] ); + outFile << GEOS_FMT( ",{:10.10e}", grad_phi_P[1] ); + outFile << GEOS_FMT( ",{:10.10e}", grad_phi_P[2] ); + outFile << GEOS_FMT( ",{:10.10e}", grad_phi_P[3] ); + outFile << std::endl; + phi[0] = 0; + phi[1] = 0; + grad_phi_P[0] = 0; + grad_phi_P[1] = 0; + grad_phi_P[2] = 0; + grad_phi_P[3] = 0; + grad_phi_S[0] = 0; + grad_phi_S[1] = 0; + grad_phi_S[2] = 0; + grad_phi_S[3] = 0; + + } +} + + outFile.close(); + +} ); +} + +TEST_F( ImmiscibleInterfaceConditionsTest, LocalSolverResults ) +{ + + // using Base = FluidModelTest< TwoPhaseImmiscibleFluid, 2 >; + createFluid( "fluid", [this]( TwoPhaseImmiscibleFluid & fluid ){ + makeTwoPhaseImmiscibleFluid( fluid ); + + // getting constitutive models: + RelativePermeabilityBase & relPerm = makeBrooksCoreyRelPerm( "relPerm" , this->m_parent); + RelativePermeabilityBase * relPermPtr = &relPerm; + + CapillaryPressureBase & capPressure0 = makeBrooksCoreyCapPressureTwoPhase1( "capPressure0", this->m_parent ); + CapillaryPressureBase * capPressurePtr0 = &capPressure0; + + CapillaryPressureBase & capPressure1 = makeBrooksCoreyCapPressureTwoPhase2( "capPressure1", this->m_parent ); + CapillaryPressureBase * capPressurePtr1 = &capPressure1; + + std::vector< RelativePermeabilityBase * > relPerms = {relPermPtr, relPermPtr}; + std::vector< CapillaryPressureBase * > capPressures = {capPressurePtr0, capPressurePtr1}; + std::vector< TwoPhaseImmiscibleFluid * > fluids = { &fluid, &fluid }; + + real64 uT = 0.000001889581158; + + stdVector< real64 > saturations = {0.6, 0.3}; + stdVector< real64 > trappedSats1 = {phase0MinSat1, phase1MinSat1}; + stdVector< real64 > trappedSats2 = {phase0MinSat2, phase1MinSat2}; + stdVector< real64 > pressures = {1e7, 1e7}; + stdVector< real64 > JFMultipliers = {45016.662822296035, 30011.108548197357}; + stdVector< fields::cappres::ModeIndexType > modes = {static_cast(0), static_cast(0)}; + stdVector< real64 > transHats = {2.0e-12, 8.0e-12}; + stdVector< real64 > dTransHats_dP = {0.0, 0.0}; + stdVector< real64 > gravCoefHats = {49.05, 49.05}; + stdVector< real64 > gravCoefs = {46.5975, 51.5025}; + stdVector< real64 > cellCenterDuT = {8.32E-10, -8.32E-10, 0.0000063429744518, -0.0000012971521486}; // duT_dP[0], duT_dP[1], duT_dS[0], duT_dS[1] + stdVector< real64 > cellCenterDens = {1000.0, 100.0}; // density for each phase + stdVector< real64 > cellCenterDens_dP = {0.0, 0.0, 0.0, 0.0}; // dDens_dP[0][0], dDens_dP[0][1], dDens_dP[1][0], dDens_dP[1][1] + stdVector< real64 > phaseMaxHistVolFrac1 = {0.0, 0.0}; + stdVector< real64 > phaseMinHistVolFrac1 = {0.0, 0.0}; + stdVector< real64 > phaseMaxHistVolFrac2 = {0.0, 0.0}; + stdVector< real64 > phaseMinHistVolFrac2 = {0.0, 0.0}; + + stdVector< real64 > phi = {0.0, 0.0}; + stdVector< real64 > grad_phi_P = {0.0, 0.0, 0.0, 0.0}; + stdVector< real64 > grad_phi_S = {0.0, 0.0, 0.0, 0.0}; + bool converged = false; + + + +// Call the GEOS local solver + geos::immiscibleMultiphaseKernels::local_solver( uT, saturations, pressures, JFMultipliers, trappedSats1, trappedSats2, modes, transHats, dTransHats_dP, gravCoefHats, gravCoefs, + cellCenterDuT, cellCenterDens, cellCenterDens_dP, relPerms, capPressures, fluids, phi, grad_phi_P, grad_phi_S, converged, + phaseMaxHistVolFrac1, phaseMinHistVolFrac1, phaseMaxHistVolFrac2, phaseMinHistVolFrac2 ); + + real64 const tolerance_eps = std::sqrt( std::numeric_limits< real64 >::epsilon() ); + real64 const tol = 1e-4; + real64 const abs_tolerance = tolerance_eps * tol; + + EXPECT_NEAR( phi[0], 1.676451024635667e-03, abs_tolerance ); + EXPECT_NEAR( phi[1], 2.131301333609180e-05, abs_tolerance ); + EXPECT_NEAR( grad_phi_P[0], 5.760000000000000e-07, abs_tolerance ); + EXPECT_NEAR( grad_phi_P[1], -5.760000000000000e-07, abs_tolerance ); + EXPECT_NEAR( grad_phi_P[2], 2.560000000000001e-08, abs_tolerance ); + EXPECT_NEAR( grad_phi_P[3], -2.560000000000001e-08, abs_tolerance ); + EXPECT_NEAR( grad_phi_S[0], 7.268012258072125e-03, abs_tolerance ); + EXPECT_NEAR( grad_phi_S[1], -8.980284105794445e-04, abs_tolerance ); + EXPECT_NEAR( grad_phi_S[2], -9.250378062747074e-05, abs_tolerance ); + EXPECT_NEAR( grad_phi_S[3], -3.991237380353088e-05, abs_tolerance ); + + +} ); +} + + + +int main( int argc, char * *argv ) +{ + ::testing::InitGoogleTest( &argc, argv ); + g_commandLineOptions = *geos::basicSetup( argc, argv ); + int const result = RUN_ALL_TESTS(); + geos::basicCleanup(); + return result; +} + +// maybe needed later on +// TEST_F( CapillaryPressureTest, numericalDerivatives_jFunctionCapPressureTwoPhase ) +// { +// initialize( makeJFunctionCapPressureTwoPhase( "capPressure", m_parent ) ); + +// // here, we have to apply a special treatment to this test +// // to make sure that the J-function multiplier is initialized using initializeRockState +// // this requires calling allocateConstitutiveData in advance (it will be called again later, in the "test" function) + +// // setup some values for porosity and permeability +// array2d< real64 > porosity; +// porosity.resize( 1, 1 ); +// porosity[0][0] = 0.13496794266569806; +// array3d< real64 > permeability; +// permeability.resize( 1, 1, 3 ); +// permeability[0][0][0] = 0.1722194e-15; +// permeability[0][0][1] = 0.3423156e-15; +// permeability[0][0][2] = 0.2324191e-15; + +// // initialize the J-function multiplier (done on GPU if GPU is available) +// m_model->allocateConstitutiveData( m_parent, 1 ); +// m_model->initializeRockState( porosity.toViewConst(), permeability.toViewConst() ); + +// // move the multiplier back to the CPU since the test is performed on the CPU +// auto & jFuncMultiplier = +// m_model->getReference< array2d< real64 > >( fields::cappres::jFuncMultiplier::key() ); +// jFuncMultiplier.move( hostMemorySpace, false ); + +// // we are ready to proceed to the test + +// real64 const eps = std::sqrt( std::numeric_limits< real64 >::epsilon() ); +// real64 const tol = 1e-4; + +// real64 const start_sat = 0.3; +// real64 const end_sat = 0.9; +// real64 const dS = 1e-1; +// array1d< real64 > sat( 2 ); +// sat[0] = start_sat; sat[1] = 1-sat[0]; +// while( sat[0] <= end_sat ) +// { +// test( sat, eps, tol ); +// sat[0] += dS; +// sat[1] = 1 - sat[0]; +// } + +// } \ No newline at end of file diff --git a/src/coreComponents/physicsSolvers/fluidFlow/CMakeLists.txt b/src/coreComponents/physicsSolvers/fluidFlow/CMakeLists.txt index 6eb55e9775a..b0bcc0bd271 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/CMakeLists.txt +++ b/src/coreComponents/physicsSolvers/fluidFlow/CMakeLists.txt @@ -29,8 +29,6 @@ set( fluidFlowSolvers_headers ImmiscibleMultiphaseFlow.hpp ImmiscibleMultiphaseFlowFields.hpp LogLevelsInfo.hpp - ReactiveCompositionalMultiphaseOBL.hpp - ReactiveCompositionalMultiphaseOBLFields.hpp SourceFluxStatistics.hpp SinglePhaseBase.hpp SinglePhaseBaseFields.hpp @@ -65,18 +63,6 @@ set( fluidFlowSolvers_headers kernels/singlePhase/ThermalFluxComputeKernel.hpp kernels/singlePhase/proppant/ProppantBaseKernels.hpp kernels/singlePhase/proppant/ProppantFluxKernels.hpp - kernels/singlePhase/reactive/AccumulationKernels.hpp - kernels/singlePhase/reactive/DirichletFluxComputeKernel.hpp - kernels/singlePhase/reactive/FluidUpdateKernel.hpp - kernels/singlePhase/reactive/FluxComputeKernel.hpp - kernels/singlePhase/reactive/KernelLaunchSelectors.hpp - kernels/singlePhase/reactive/ReactionUpdateKernel.hpp - kernels/singlePhase/reactive/ResidualNormKernel.hpp - kernels/singlePhase/reactive/SourceFluxComputeKernel.hpp - kernels/singlePhase/reactive/ThermalAccumulationKernels.hpp - kernels/singlePhase/reactive/ThermalDirichletFluxComputeKernel.hpp - kernels/singlePhase/reactive/ThermalFluxComputeKernel.hpp - kernels/singlePhase/reactive/ThermalSourceFluxComputeKernel.hpp kernels/compositional/AccumulationKernel.hpp kernels/compositional/AquiferBCKernel.hpp kernels/compositional/PPUPhaseFlux.hpp @@ -104,7 +90,6 @@ set( fluidFlowSolvers_headers kernels/compositional/PotGrad.hpp kernels/compositional/PPUPhaseFlux.hpp kernels/compositional/PropertyKernelBase.hpp - kernels/compositional/ReactiveCompositionalMultiphaseOBLKernels.hpp kernels/compositional/RelativePermeabilityUpdateKernel.hpp kernels/compositional/ResidualNormKernel.hpp kernels/compositional/SolidInternalEnergyUpdateKernel.hpp @@ -145,14 +130,12 @@ set( fluidFlowSolvers_sources CompositionalMultiphaseStatistics.cpp CompositionalMultiphaseHybridFVM.cpp ImmiscibleMultiphaseFlow.cpp - ReactiveCompositionalMultiphaseOBL.cpp FlowSolverBase.cpp SinglePhaseBase.cpp SinglePhaseStatistics.cpp SinglePhaseFVM.cpp SinglePhaseHybridFVM.cpp SinglePhaseProppantBase.cpp - SinglePhaseReactiveTransport.cpp SourceFluxStatistics.cpp StencilDataCollection.cpp kernels/singlePhase/proppant/ProppantFluxKernels.cpp @@ -174,8 +157,10 @@ file( READ "${CMAKE_CURRENT_LIST_DIR}/kernelSpecs.json" kernelSpecs ) set( kernelTemplateFileList "" ) # Keep only the templates that are unrelated to hybrid flux/dirichlet -list( APPEND kernelTemplateFileList - ReactiveCompositionalMultiphaseOBLKernels.cpp.template ) +if( ENABLE_HPCREACT AND EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/../constitutive/HPCReact/CMakeLists.txt" ) + list( APPEND kernelTemplateFileList + ReactiveCompositionalMultiphaseOBLKernels.cpp.template ) +endif() foreach( kernelTemplateFile ${kernelTemplateFileList} ) get_filename_component( jsonKey ${kernelTemplateFile} NAME_WE ) @@ -188,6 +173,31 @@ foreach( kernelTemplateFile ${kernelTemplateFileList} ) list(APPEND fluidFlowSolvers_sources ${sourceFiles}) endforeach() +# Conditionally add reactive transport files when HPCReact is available +if( ENABLE_HPCREACT AND EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/../constitutive/HPCReact/CMakeLists.txt" ) + list( APPEND fluidFlowSolvers_headers + ReactiveCompositionalMultiphaseOBL.hpp + ReactiveCompositionalMultiphaseOBLFields.hpp + kernels/singlePhase/reactive/AccumulationKernels.hpp + kernels/singlePhase/reactive/DirichletFluxComputeKernel.hpp + kernels/singlePhase/reactive/FluidUpdateKernel.hpp + kernels/singlePhase/reactive/FluxComputeKernel.hpp + kernels/singlePhase/reactive/KernelLaunchSelectors.hpp + kernels/singlePhase/reactive/ReactionUpdateKernel.hpp + kernels/singlePhase/reactive/ResidualNormKernel.hpp + kernels/singlePhase/reactive/SourceFluxComputeKernel.hpp + kernels/singlePhase/reactive/ThermalAccumulationKernels.hpp + kernels/singlePhase/reactive/ThermalDirichletFluxComputeKernel.hpp + kernels/singlePhase/reactive/ThermalFluxComputeKernel.hpp + kernels/singlePhase/reactive/ThermalSourceFluxComputeKernel.hpp + kernels/compositional/ReactiveCompositionalMultiphaseOBLKernels.hpp + SinglePhaseReactiveTransport.hpp ) + + list( APPEND fluidFlowSolvers_sources + ReactiveCompositionalMultiphaseOBL.cpp + SinglePhaseReactiveTransport.cpp ) +endif() + # TODO: The two kernels below have non-matching file names and JSON keys. # Either fix them to follow pattern, or come up with another mechanism. diff --git a/src/coreComponents/physicsSolvers/fluidFlow/CompositionalMultiphaseBase.cpp b/src/coreComponents/physicsSolvers/fluidFlow/CompositionalMultiphaseBase.cpp index ec8268e0079..d71740eb5fe 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/CompositionalMultiphaseBase.cpp +++ b/src/coreComponents/physicsSolvers/fluidFlow/CompositionalMultiphaseBase.cpp @@ -998,12 +998,24 @@ void CompositionalMultiphaseBase::initializeFluidState( MeshLevel & mesh, // We postpone the other constitutive models for now // Now, we initialize and update each constitutive model one by one - // initialized phase volume fraction arrayView2d< real64 const, compflow::USD_PHASE > const phaseVolFrac = - subRegion.template getField< flow::phaseVolumeFraction >(); + subRegion.template getField< fields::flow::phaseVolumeFraction >(); - // Initialize/update the relative permeability model using the initial phase volume fraction + // 4.2 Save the computed porosity into the old porosity + // + // Note: + // - This must be called after updatePorosityAndPermeability + // - This step depends on porosity + string const & solidName = subRegion.template getReference< string >( viewKeyStruct::solidNamesString() ); + CoupledSolidBase const & porousMaterial = getConstitutiveModel< CoupledSolidBase >( subRegion, solidName ); + porousMaterial.initializeState(); + + // 4.3 Initialize/update the relative permeability model using the initial phase volume fraction + // This is needed to handle relative permeability hysteresis + // Also, initialize the fluid model (to compute the initial total mass density, needed to compute the body force increment in + // coupled simulations) + // // Note: // - This must be called after updatePhaseVolumeFraction // - This step depends on phaseVolFraction @@ -2858,6 +2870,7 @@ void CompositionalMultiphaseBase::implicitStepComplete( real64 const & time, CapillaryPressureBase const & capPressureMaterial = getConstitutiveModel< CapillaryPressureBase >( subRegion, capPressName ); capPressureMaterial.saveConvergedRockState( porosity, permeability ); + capPressureMaterial.saveConvergedPhaseVolFractionState(phaseVolFrac); } // Step 6: if the thermal option is on, send the converged porosity and phase volume fraction to the thermal conductivity model diff --git a/src/coreComponents/physicsSolvers/fluidFlow/CompositionalMultiphaseBase.hpp b/src/coreComponents/physicsSolvers/fluidFlow/CompositionalMultiphaseBase.hpp index 227055bf3ed..a03fbf63a99 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/CompositionalMultiphaseBase.hpp +++ b/src/coreComponents/physicsSolvers/fluidFlow/CompositionalMultiphaseBase.hpp @@ -20,6 +20,11 @@ #ifndef GEOS_PHYSICSSOLVERS_FLUIDFLOW_COMPOSITIONALMULTIPHASEBASE_HPP_ #define GEOS_PHYSICSSOLVERS_FLUIDFLOW_COMPOSITIONALMULTIPHASEBASE_HPP_ +#include "common/DataLayouts.hpp" +#include "constitutive/fluid/multifluid/Layouts.hpp" +#include "constitutive/relativePermeability/Layouts.hpp" +#include "constitutive/capillaryPressure/Layouts.hpp" +#include "fieldSpecification/FieldSpecificationManager.hpp" #include "physicsSolvers/fluidFlow/FlowSolverBase.hpp" #include "common/DataLayouts.hpp" #include "constitutive/fluid/multifluid/Layouts.hpp" diff --git a/src/coreComponents/physicsSolvers/fluidFlow/ImmiscibleMultiphaseFlow.cpp b/src/coreComponents/physicsSolvers/fluidFlow/ImmiscibleMultiphaseFlow.cpp index a198a78cb17..5fc1155a274 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/ImmiscibleMultiphaseFlow.cpp +++ b/src/coreComponents/physicsSolvers/fluidFlow/ImmiscibleMultiphaseFlow.cpp @@ -17,402 +17,585 @@ * @file ImmiscibleMultiphaseFlow.cpp */ -#include "ImmiscibleMultiphaseFlow.hpp" - -#include "FlowSolverBaseFields.hpp" -#include "physicsSolvers/fluidFlow/ImmiscibleMultiphaseFlowFields.hpp" -#include "physicsSolvers/PhysicsSolverBaseKernels.hpp" -#include "physicsSolvers/fluidFlow/CompositionalMultiphaseUtilities.hpp" -#include "physicsSolvers/fluidFlow/kernels/immiscibleMultiphase/ImmiscibleMultiphaseKernels.hpp" -#include "physicsSolvers/fluidFlow/kernels/compositional/RelativePermeabilityUpdateKernel.hpp" -#include "physicsSolvers/fluidFlow/kernels/compositional/CapillaryPressureUpdateKernel.hpp" -#include "constitutive/capillaryPressure/CapillaryPressureSelector.hpp" -#include "constitutive/relativePermeability/RelativePermeabilitySelector.hpp" - -#include "fieldSpecification/EquilibriumInitialCondition.hpp" -#include "fieldSpecification/SourceFluxBoundaryCondition.hpp" -#include "physicsSolvers/fluidFlow/SourceFluxStatistics.hpp" -#include "physicsSolvers/LogLevelsInfo.hpp" - -#include "constitutive/ConstitutivePassThru.hpp" -#include "constitutive/fluid/twophaseimmisciblefluid/TwoPhaseImmiscibleFluid.hpp" - -#include - -#if defined( __INTEL_COMPILER ) -#pragma GCC optimize "O0" -#endif - -namespace geos -{ - -using namespace dataRepository; -using namespace constitutive; -using namespace fields::immiscibleMultiphaseFlow; -using namespace immiscibleMultiphaseKernels; - - -ImmiscibleMultiphaseFlow::ImmiscibleMultiphaseFlow( const string & name, - Group * const parent ) - : - FlowSolverBase( name, parent ), - m_numPhases( 2 ), - m_hasCapPressure( false ), - m_useTotalMassEquation ( 1 ) -{ - this->registerWrapper( viewKeyStruct::inputTemperatureString(), &m_inputTemperature ). - setInputFlag( InputFlags::REQUIRED ). - setDescription( "Temperature" ); - - this->registerWrapper( viewKeyStruct::useTotalMassEquationString(), &m_useTotalMassEquation ). - setSizedFromParent( 0 ). - setInputFlag( InputFlags::OPTIONAL ). - setApplyDefaultValue( 1 ). - setDescription( "Flag indicating whether total mass equation is used" ); - - this->registerWrapper( viewKeyStruct::gravityDensitySchemeString(), &m_gravityDensityScheme ). - setSizedFromParent( 0 ). - setInputFlag( InputFlags::OPTIONAL ). - setApplyDefaultValue( GravityDensityScheme::ArithmeticAverage ). - setDescription( "Scheme for density treatment in gravity" ); - - this->registerWrapper( viewKeyStruct::solutionChangeScalingFactorString(), &m_solutionChangeScalingFactor ). - setSizedFromParent( 0 ). - setInputFlag( InputFlags::OPTIONAL ). - setApplyDefaultValue( 0.5 ). - setDescription( "Damping factor for solution change targets" ); - this->registerWrapper( viewKeyStruct::targetRelativePresChangeString(), &m_targetRelativePresChange ). - setSizedFromParent( 0 ). - setInputFlag( InputFlags::OPTIONAL ). - setApplyDefaultValue( 0.2 ). - setDescription( "Target (relative) change in pressure in a time step (expected value between 0 and 1)" ); - this->registerWrapper( viewKeyStruct::targetPhaseVolFracChangeString(), &m_targetPhaseVolFracChange ). - setSizedFromParent( 0 ). - setInputFlag( InputFlags::OPTIONAL ). - setApplyDefaultValue( 0.2 ). - setDescription( "Target (absolute) change in the phase volume fraction within a single time step." ); -} - -void ImmiscibleMultiphaseFlow::postInputInitialization() -{ - FlowSolverBase::postInputInitialization(); -} - -void ImmiscibleMultiphaseFlow::registerDataOnMesh( Group & meshBodies ) -{ - FlowSolverBase::registerDataOnMesh( meshBodies ); - - // 0. Find a "reference" fluid model name (at this point, models are already attached to subregions) - forDiscretizationOnMeshTargets( meshBodies, [&]( string const &, - MeshLevel & mesh, - string_array const & regionNames ) - { - mesh.getElemManager().forElementSubRegions( regionNames, - [&]( localIndex const, - ElementSubRegionBase & subRegion ) - { - // If at least one region has a capillary pressure model, consider it enabled for all - string const capPresName = getConstitutiveName< CapillaryPressureBase >( subRegion ); - if( !capPresName.empty() ) - { - m_hasCapPressure = true; - } - } ); - } ); - - m_numDofPerCell = m_numPhases; - - // 2. Register and resize all fields as necessary - forDiscretizationOnMeshTargets( meshBodies, [&]( string const &, - MeshLevel & mesh, - string_array const & regionNames ) - { - mesh.getElemManager().forElementSubRegions( regionNames, - [&]( localIndex const, - ElementSubRegionBase & subRegion ) - { - if( m_hasCapPressure ) + #include "ImmiscibleMultiphaseFlow.hpp" + + #include "FlowSolverBaseFields.hpp" + #include "physicsSolvers/fluidFlow/ImmiscibleMultiphaseFlowFields.hpp" + #include "physicsSolvers/PhysicsSolverBaseKernels.hpp" + #include "physicsSolvers/fluidFlow/CompositionalMultiphaseUtilities.hpp" + #include "physicsSolvers/fluidFlow/kernels/immiscibleMultiphase/ImmiscibleMultiphaseKernels.hpp" + #include "physicsSolvers/fluidFlow/kernels/immiscibleMultiphase/CapillaryPressureUpdateKernel.hpp" + #include "physicsSolvers/fluidFlow/kernels/compositional/ThermalAccumulationKernel.hpp" + #include "physicsSolvers/fluidFlow/kernels/compositional/RelativePermeabilityUpdateKernel.hpp" + #include "constitutive/ConstitutiveManager.hpp" + #include "constitutive/capillaryPressure/CapillaryPressureFields.hpp" + #include "constitutive/capillaryPressure/CapillaryPressureSelector.hpp" + #include "constitutive/relativePermeability/RelativePermeabilitySelector.hpp" + + #include "fieldSpecification/EquilibriumInitialCondition.hpp" + #include "fieldSpecification/SourceFluxBoundaryCondition.hpp" + #include "physicsSolvers/fluidFlow/SourceFluxStatistics.hpp" + #include "physicsSolvers/LogLevelsInfo.hpp" + + #include "constitutive/ConstitutivePassThru.hpp" + #include "constitutive/fluid/twophaseimmisciblefluid/TwoPhaseImmiscibleFluid.hpp" + + #include + + #if defined( __INTEL_COMPILER ) + #pragma GCC optimize "O0" + #endif + + namespace geos + { + + using namespace dataRepository; + using namespace constitutive; + using namespace fields::immiscibleMultiphaseFlow; + using namespace immiscibleMultiphaseKernels; + + + ImmiscibleMultiphaseFlow::ImmiscibleMultiphaseFlow( const string & name, + Group * const parent ) + : + FlowSolverBase( name, parent ), + m_numPhases( 2 ), + m_hasCapPressure( false ), + m_useTotalMassEquation ( 1 ) + { + this->registerWrapper( viewKeyStruct::inputTemperatureString(), &m_inputTemperature ). + setInputFlag( InputFlags::REQUIRED ). + setDescription( "Temperature" ); + + this->registerWrapper( viewKeyStruct::useTotalMassEquationString(), &m_useTotalMassEquation ). + setSizedFromParent( 0 ). + setInputFlag( InputFlags::OPTIONAL ). + setApplyDefaultValue( 1 ). + setDescription( "Flag indicating whether total mass equation is used" ); + + this->registerWrapper( viewKeyStruct::gravityDensitySchemeString(), &m_gravityDensityScheme ). + setSizedFromParent( 0 ). + setInputFlag( InputFlags::OPTIONAL ). + setApplyDefaultValue( GravityDensityScheme::ArithmeticAverage ). + setDescription( "Scheme for density treatment in gravity" ); + + this->registerWrapper( viewKeyStruct::solutionChangeScalingFactorString(), &m_solutionChangeScalingFactor ). + setSizedFromParent( 0 ). + setInputFlag( InputFlags::OPTIONAL ). + setApplyDefaultValue( 0.5 ). + setDescription( "Damping factor for solution change targets" ); + this->registerWrapper( viewKeyStruct::targetRelativePresChangeString(), &m_targetRelativePresChange ). + setSizedFromParent( 0 ). + setInputFlag( InputFlags::OPTIONAL ). + setApplyDefaultValue( 0.2 ). + setDescription( "Target (relative) change in pressure in a time step (expected value between 0 and 1)" ); + this->registerWrapper( viewKeyStruct::targetPhaseVolFracChangeString(), &m_targetPhaseVolFracChange ). + setSizedFromParent( 0 ). + setInputFlag( InputFlags::OPTIONAL ). + setApplyDefaultValue( 0.2 ). + setDescription( "Target (absolute) change in phase volume fraction in a time step" ); + + this->registerWrapper( viewKeyStruct::interfaceFaceSetNamesString(), + &m_interfaceFaceSetNames ). + setInputFlag( InputFlags::OPTIONAL ). + setDescription( "Names of the interface face sets" ); + } + + void ImmiscibleMultiphaseFlow::postInputInitialization() + { + FlowSolverBase::postInputInitialization(); + } + + void ImmiscibleMultiphaseFlow::registerDataOnMesh( Group & meshBodies ) + { + FlowSolverBase::registerDataOnMesh( meshBodies ); + + // 0. Find a "reference" fluid model name (at this point, models are already attached to subregions) + forDiscretizationOnMeshTargets( meshBodies, [&]( string const &, + MeshLevel & mesh, + string_array const & regionNames ) + { + mesh.getElemManager().forElementSubRegions( regionNames, + [&]( localIndex const, + ElementSubRegionBase & subRegion ) + { + // If at least one region has a capillary pressure model, consider it enabled for all + string const capPresName = getConstitutiveName< CapillaryPressureBase >( subRegion ); + if( !capPresName.empty() ) + { + m_hasCapPressure = true; + } + } ); + } ); + + m_numDofPerCell = m_numPhases; + + // 2. Register and resize all fields as necessary + forDiscretizationOnMeshTargets( meshBodies, [&]( string const &, + MeshLevel & mesh, + string_array const & regionNames ) + { + mesh.getElemManager().forElementSubRegions( regionNames, + [&]( localIndex const, + ElementSubRegionBase & subRegion ) + { + if( m_hasCapPressure ) + { + subRegion.registerWrapper< string >( viewKeyStruct::capPressureNamesString() ). + setPlotLevel( PlotLevel::NOPLOT ). + setRestartFlags( RestartFlags::NO_WRITE ). + setSizedFromParent( 0 ). + setDescription( "Name of the capillary pressure constitutive model to use" ). + reference(); + + string & capPresName = subRegion.getReference< string >( viewKeyStruct::capPressureNamesString() ); + capPresName = getConstitutiveName< CapillaryPressureBase >( subRegion ); + GEOS_THROW_IF( capPresName.empty(), + GEOS_FMT( "{}: Capillary pressure model not found on subregion {}", + getDataContext(), subRegion.getDataContext() ), + InputError ); + } + + // The resizing of the arrays needs to happen here, before the call to initializePreSubGroups, + // to make sure that the dimensions are properly set before the timeHistoryOutput starts its initialization. + subRegion.registerField< phaseVolumeFraction >( getName() ). + reference().resizeDimension< 1 >( m_numPhases ); + + subRegion.registerField< phaseVolumeFraction_n >( getName() ). + reference().resizeDimension< 1 >( m_numPhases ); + + subRegion.registerField< bcPhaseVolumeFraction >( getName() ). + reference().resizeDimension< 1 >( m_numPhases ); + + subRegion.registerField< phaseMass >( getName() ). + reference().resizeDimension< 1 >( m_numPhases ); + + subRegion.registerField< phaseMass_n >( getName() ). + reference().resizeDimension< 1 >( m_numPhases ); + + subRegion.registerField< phaseMobility >( getName() ). + reference().resizeDimension< 1 >( m_numPhases ); + + subRegion.registerField< dPhaseMobility >( getName() ). + reference().resizeDimension< 1, 2 >( m_numPhases, m_numPhases ); // dP, dS + + } ); + + } ); + } + + void ImmiscibleMultiphaseFlow::setConstitutiveNames( ElementSubRegionBase & subRegion ) const + { + setConstitutiveName< TwoPhaseImmiscibleFluid >( subRegion, viewKeyStruct::fluidNamesString(), "two phase immiscible fluid" ); + + setConstitutiveName< RelativePermeabilityBase >( subRegion, viewKeyStruct::relPermNamesString(), "relative permeability" ); + } + + void ImmiscibleMultiphaseFlow::initializePreSubGroups() + { + m_linearSolverParameters.get().mgr.strategy = LinearSolverParameters::MGR::StrategyType::immiscibleMultiphaseFVM; + + FlowSolverBase::initializePreSubGroups(); + + DomainPartition & domain = this->getGroupByPath< DomainPartition >( "/Problem/domain" ); + + forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &, + MeshLevel & mesh, + string_array const & regionNames ) + { + mesh.getElemManager().forElementSubRegions( regionNames, + [&]( localIndex const, + ElementSubRegionBase & subRegion ) + { + arrayView1d< real64 > const temp = subRegion.getField< fields::flow::temperature >(); + temp.setValues< parallelHostPolicy >( m_inputTemperature ); + } ); + } ); + + // ***** Create FaceElements ***** + forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &, + MeshLevel & meshLevel, + string_array const & GEOS_UNUSED_PARAM( regionNames )) + { + + FaceManager const & faceManager = meshLevel.getFaceManager(); + Group const & faceSetGroup = faceManager.sets(); + ElementRegionManager & elemManager = meshLevel.getElemManager(); + m_interfaceConstitutivePairs.resize( m_interfaceFaceSetNames.size() ); + + // this is the FaceElement Level + for( size_t surfaceRegionIndex=0; surfaceRegionIndex < m_interfaceFaceSetNames.size(); ++surfaceRegionIndex ) + { + string const & faceSetName = m_interfaceFaceSetNames[surfaceRegionIndex]; + SortedArrayView< localIndex const > const & faceSet = faceSetGroup.getReference< SortedArray< localIndex > >( faceSetName ); + SurfaceElementRegion & faceRegion = elemManager.getRegion< SurfaceElementRegion >( faceSetName ); + + for( localIndex const faceIndex : faceSet ) + { + localIndex const faceIndices[2] = { faceIndex, faceIndex }; + faceRegion.addToSurfaceMesh( &faceManager, faceIndices ); + } + + FaceElementSubRegion const & faceSubRegion = faceRegion.getUniqueSubRegion< FaceElementSubRegion >(); + FixedToManyElementRelation const & faceElementsToCells = faceSubRegion.getToCellRelation(); + + // Precompute numRegions once (it's constant for all face elements) + localIndex const numRegions = elemManager.numRegions(); + constexpr int MAX_REASONABLE_REGION_INDEX = 100000; + + std::function< std::tuple< CellElementSubRegion *, CellElementSubRegion * >(localIndex) > getSubregions = [&]( localIndex surfaceSubRegionIndex ) -> std::tuple< CellElementSubRegion *, + CellElementSubRegion * > { - subRegion.registerWrapper< string >( viewKeyStruct::capPressureNamesString() ). - setPlotLevel( PlotLevel::NOPLOT ). - setRestartFlags( RestartFlags::NO_WRITE ). - setSizedFromParent( 0 ). - setDescription( "Name of the capillary pressure constitutive model to use" ). - reference(); - - string & capPresName = subRegion.getReference< string >( viewKeyStruct::capPressureNamesString() ); - capPresName = getConstitutiveName< CapillaryPressureBase >( subRegion ); - GEOS_THROW_IF( capPresName.empty(), - GEOS_FMT( "{}: Capillary pressure model not found on subregion {}", - getDataContext(), subRegion.getDataContext() ), - InputError ); - } - - // The resizing of the arrays needs to happen here, before the call to initializePreSubGroups, - // to make sure that the dimensions are properly set before the timeHistoryOutput starts its initialization. - subRegion.registerField< phaseVolumeFraction >( getName() ). - reference().resizeDimension< 1 >( m_numPhases ); - - subRegion.registerField< phaseVolumeFraction_n >( getName() ). - reference().resizeDimension< 1 >( m_numPhases ); - - subRegion.registerField< bcPhaseVolumeFraction >( getName() ). - reference().resizeDimension< 1 >( m_numPhases ); - - subRegion.registerField< phaseMass >( getName() ). - reference().resizeDimension< 1 >( m_numPhases ); - - subRegion.registerField< phaseMass_n >( getName() ). - reference().resizeDimension< 1 >( m_numPhases ); - subRegion.registerField< phaseMobility >( getName() ). - reference().resizeDimension< 1 >( m_numPhases ); - - subRegion.registerField< dPhaseMobility >( getName() ). - reference().resizeDimension< 1, 2 >( m_numPhases, m_numPhases ); // dP, dS - - } ); - - } ); -} - -void ImmiscibleMultiphaseFlow::setConstitutiveNames( ElementSubRegionBase & subRegion ) const -{ - setConstitutiveName< TwoPhaseImmiscibleFluid >( subRegion, viewKeyStruct::fluidNamesString(), "two phase immiscible fluid" ); - - setConstitutiveName< RelativePermeabilityBase >( subRegion, viewKeyStruct::relPermNamesString(), "relative permeability" ); -} - -void ImmiscibleMultiphaseFlow::initializePreSubGroups() -{ - m_linearSolverParameters.get().mgr.strategy = LinearSolverParameters::MGR::StrategyType::immiscibleMultiphaseFVM; - - FlowSolverBase::initializePreSubGroups(); - - DomainPartition & domain = this->getGroupByPath< DomainPartition >( "/Problem/domain" ); - - forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &, - MeshLevel & mesh, - string_array const & regionNames ) - { - mesh.getElemManager().forElementSubRegions( regionNames, - [&]( localIndex const, - ElementSubRegionBase & subRegion ) - { - arrayView1d< real64 > const temp = subRegion.getField< fields::flow::temperature >(); - temp.setValues< parallelHostPolicy >( m_inputTemperature ); - } ); - } ); -} - - -void ImmiscibleMultiphaseFlow::updateFluidModel( ObjectManagerBase & dataGroup ) const -{ - GEOS_MARK_FUNCTION; + int regionIdx0 = faceElementsToCells.m_toElementRegion[surfaceSubRegionIndex][0]; + int regionIdx1 = faceElementsToCells.m_toElementRegion[surfaceSubRegionIndex][1]; + int subRegionIdx0 = faceElementsToCells.m_toElementSubRegion[surfaceSubRegionIndex][0]; + int subRegionIdx1 = faceElementsToCells.m_toElementSubRegion[surfaceSubRegionIndex][1]; - arrayView1d< real64 const > const pres = dataGroup.getField< fields::flow::pressure >(); - - TwoPhaseImmiscibleFluid & fluid = getConstitutiveModel< TwoPhaseImmiscibleFluid >( dataGroup, dataGroup.getReference< string >( viewKeyStruct::fluidNamesString() ) ); - - constitutiveUpdatePassThru( fluid, [&] ( auto & castedFluid ) - { - using FluidType = TYPEOFREF( castedFluid ); - typename FluidType::KernelWrapper fluidWrapper = castedFluid.createKernelWrapper(); - - FluidUpdateKernel::launch< parallelDevicePolicy<> >( dataGroup.size(), fluidWrapper, pres ); - } ); -} - - -void ImmiscibleMultiphaseFlow::updateRelPermModel( ObjectManagerBase & dataGroup ) const -{ - GEOS_MARK_FUNCTION; - - - GEOS_UNUSED_VAR( dataGroup ); - - arrayView2d< real64 const, immiscibleFlow::USD_PHASE > const phaseVolFrac = - dataGroup.getField< fields::immiscibleMultiphaseFlow::phaseVolumeFraction >(); - - string const & relPermName = dataGroup.getReference< string >( viewKeyStruct::relPermNamesString() ); - RelativePermeabilityBase & relPerm = getConstitutiveModel< RelativePermeabilityBase >( dataGroup, relPermName ); + // Fast validation checks (ordered from cheapest to most expensive) + if( regionIdx0 < 0 || regionIdx1 < 0 || + subRegionIdx0 < 0 || subRegionIdx1 < 0 ) + { + return std::make_tuple( nullptr, nullptr ); + } + + if( regionIdx0 > MAX_REASONABLE_REGION_INDEX || regionIdx1 > MAX_REASONABLE_REGION_INDEX ) + { + return std::make_tuple( nullptr, nullptr ); + } + + if( static_cast< localIndex >( regionIdx0 ) >= numRegions || + static_cast< localIndex >( regionIdx1 ) >= numRegions ) + { + return std::make_tuple( nullptr, nullptr ); + } - constitutive::constitutiveUpdatePassThru( relPerm, [&] ( auto & castedRelPerm ) - { - typename TYPEOFREF( castedRelPerm ) ::KernelWrapper relPermWrapper = castedRelPerm.createKernelWrapper(); + // Try to get regions - they might not exist on this MPI rank even if index is in range + ElementRegionBase * region0BasePtr = nullptr; + ElementRegionBase * region1BasePtr = nullptr; + + try + { + region0BasePtr = &elemManager.getRegion< ElementRegionBase >( regionIdx0 ); + region1BasePtr = &elemManager.getRegion< ElementRegionBase >( regionIdx1 ); + } + catch( std::exception const & ) + { + return std::make_tuple( nullptr, nullptr ); + } + + // Check if they are CellElementRegion (interface conditions only apply to cell-to-cell interfaces) + CellElementRegion * cellRegion0 = dynamic_cast< CellElementRegion * >( region0BasePtr ); + CellElementRegion * cellRegion1 = dynamic_cast< CellElementRegion * >( region1BasePtr ); + + if( cellRegion0 == nullptr || cellRegion1 == nullptr ) + { + return std::make_tuple( nullptr, nullptr ); + } - isothermalCompositionalMultiphaseBaseKernels:: - RelativePermeabilityUpdateKernel:: - launch< parallelDevicePolicy<> >( dataGroup.size(), - relPermWrapper, - phaseVolFrac ); - } ); -} + // Validate subregion indices before accessing + if( static_cast< localIndex >( subRegionIdx0 ) >= cellRegion0->numSubRegions() || + static_cast< localIndex >( subRegionIdx1 ) >= cellRegion1->numSubRegions() ) + { + return std::make_tuple( nullptr, nullptr ); + } -void ImmiscibleMultiphaseFlow::updateCapPressureModel( ObjectManagerBase & dataGroup ) const + CellElementSubRegion * subRegion0 = &cellRegion0->getSubRegion< CellElementSubRegion >( subRegionIdx0 ); + CellElementSubRegion * subRegion1 = &cellRegion1->getSubRegion< CellElementSubRegion >( subRegionIdx1 ); + return std::make_tuple( subRegion0, subRegion1 ); + }; + + // std::tuple< CellElementSubRegion *, CellElementSubRegion * > subRegionPair = getSubregions( surfaceRegionIndex ); + // CellElementSubRegion * subRegion0 = std::get< 0 >( subRegionPair ); + // CellElementSubRegion * subRegion1 = std::get< 1 >( subRegionPair ); + + // // get constitutives by type and name: relPerms, capPressures, Fluids (three pointers) + // std::string & relPermName0 = subRegion0->getReference< std::string >( viewKeyStruct::relPermNamesString()); + // std::string & relPermName1 = subRegion1->getReference< std::string >( viewKeyStruct::relPermNamesString()); + // RelativePermeabilityBase * relPerm0 = &getConstitutiveModel< RelativePermeabilityBase >( *subRegion0, relPermName0 ); + // RelativePermeabilityBase * relPerm1 = &getConstitutiveModel< RelativePermeabilityBase >( *subRegion1, relPermName1 ); + + // std::string & cappresName0 = subRegion0->getReference< std::string >( viewKeyStruct::capPressureNamesString()); + // std::string & cappresName1 = subRegion1->getReference< std::string >( viewKeyStruct::capPressureNamesString()); + // CapillaryPressureBase * capPressure0 = &getConstitutiveModel< CapillaryPressureBase >( *subRegion0, cappresName0 ); + // CapillaryPressureBase * capPressure1 = &getConstitutiveModel< CapillaryPressureBase >( *subRegion1, cappresName1 ); + + // std::string & fluidName0 = subRegion0->getReference< std::string >( viewKeyStruct::fluidNamesString() ); + // std::string & fluidName1 = subRegion1->getReference< std::string >( viewKeyStruct::fluidNamesString() ); + + // TwoPhaseImmiscibleFluid * fluid0 = &getConstitutiveModel< TwoPhaseImmiscibleFluid >( *subRegion0, fluidName0 ); + // TwoPhaseImmiscibleFluid * fluid1 = &getConstitutiveModel< TwoPhaseImmiscibleFluid >( *subRegion1, fluidName1 ); + + // m_interfaceConstitutivePairs[surfaceRegionIndex][0] = std::make_tuple( relPerm0, capPressure0, fluid0 ); + // m_interfaceConstitutivePairs[surfaceRegionIndex][1] = std::make_tuple( relPerm1, capPressure1, fluid1 ); + // Find a representative face element that connects two CellElementRegion objects +// (not SurfaceElementRegion, which we don't handle for interface conditions) +CellElementSubRegion * subRegion0 = nullptr; +CellElementSubRegion * subRegion1 = nullptr; +bool foundValidFei = false; + +// Single loop to find a valid face element (avoids redundant scanning) +for( localIndex i = 0; i < faceElementsToCells.size(); ++i ) { - GEOS_MARK_FUNCTION; - - if( m_hasCapPressure ) + // Quick check: must have two adjacent cells with non-negative region indices + if( faceElementsToCells.m_toElementRegion[i].size() >= 2 && + faceElementsToCells.m_toElementRegion[i][0] >= 0 && + faceElementsToCells.m_toElementRegion[i][1] >= 0 ) { - arrayView2d< real64 const, immiscibleFlow::USD_PHASE > const phaseVolFrac = - dataGroup.getField< fields::immiscibleMultiphaseFlow::phaseVolumeFraction >(); - - string const & cappresName = dataGroup.getReference< string >( viewKeyStruct::capPressureNamesString() ); - CapillaryPressureBase & capPressure = getConstitutiveModel< CapillaryPressureBase >( dataGroup, cappresName ); - - constitutive::constitutiveUpdatePassThru( capPressure, [&] ( auto & castedCapPres ) + std::tuple< CellElementSubRegion *, CellElementSubRegion * > subRegionPair = getSubregions( i ); + CellElementSubRegion * testSubRegion0 = std::get< 0 >( subRegionPair ); + CellElementSubRegion * testSubRegion1 = std::get< 1 >( subRegionPair ); + + if( testSubRegion0 != nullptr && testSubRegion1 != nullptr ) { - typename TYPEOFREF( castedCapPres ) ::KernelWrapper capPresWrapper = castedCapPres.createKernelWrapper(); - - isothermalCompositionalMultiphaseBaseKernels:: - CapillaryPressureUpdateKernel:: - launch< parallelDevicePolicy<> >( dataGroup.size(), - capPresWrapper, - phaseVolFrac ); - } ); + subRegion0 = testSubRegion0; + subRegion1 = testSubRegion1; + foundValidFei = true; + break; + } } } - -void ImmiscibleMultiphaseFlow::updateFluidState( ElementSubRegionBase & subRegion ) const -{ - GEOS_MARK_FUNCTION; - - updateFluidModel( subRegion ); - updateVolumeConstraint( subRegion ); - updatePhaseMass( subRegion ); - updateRelPermModel( subRegion ); - updatePhaseMobility( subRegion ); - updateCapPressureModel( subRegion ); -} - - -void ImmiscibleMultiphaseFlow::updatePhaseMass( ElementSubRegionBase & subRegion ) const +// If no valid face element connecting two CellElementRegion objects, skip this surface region +if( !foundValidFei ) { - GEOS_MARK_FUNCTION; - - string const & solidName = subRegion.getReference< string >( viewKeyStruct::solidNamesString() ); - string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString() ); - - TwoPhaseImmiscibleFluid const & fluid = getConstitutiveModel< TwoPhaseImmiscibleFluid >( subRegion, fluidName ); - CoupledSolidBase const & solid = getConstitutiveModel< CoupledSolidBase >( subRegion, solidName ); - - arrayView1d< real64 const > const volume = subRegion.getElementVolume(); - arrayView2d< real64 const > const porosity = solid.getPorosity(); - - arrayView2d< real64 const, immiscibleFlow::USD_PHASE > const phaseVolFrac= subRegion.getField< fields::immiscibleMultiphaseFlow::phaseVolumeFraction >(); - arrayView3d< real64 const, multifluid::USD_PHASE > phaseDens = fluid.phaseDensity(); - arrayView2d< real64, immiscibleFlow::USD_PHASE > const phaseMass = subRegion.getField< fields::immiscibleMultiphaseFlow::phaseMass >(); - - // Might be needed for geomechanics????? if so, need to change the accumulation as well? - //arrayView1d< real64 > const deltaVolume = subRegion.getField< fields::flow::deltaVolume >(); - - forAll< parallelDevicePolicy<> >( subRegion.size(), [=] GEOS_HOST_DEVICE ( localIndex const ei ) - { - real64 const poreVolume = volume[ei] * porosity[ei][0]; - for( integer ip = 0; ip < 2; ++ip ) - { - phaseMass[ei][ip] = poreVolume * phaseVolFrac[ei][ip] * phaseDens[ei][0][ip]; - } - } ); + continue; } - -void ImmiscibleMultiphaseFlow::updatePhaseMobility( ObjectManagerBase & dataGroup ) const -{ - GEOS_MARK_FUNCTION; - - // note that the phase mobility computed here also includes phase density - string const & fluidName = dataGroup.getReference< string >( viewKeyStruct::fluidNamesString() ); - TwoPhaseImmiscibleFluid const & fluid = getConstitutiveModel< TwoPhaseImmiscibleFluid >( dataGroup, fluidName ); - - string const & relpermName = dataGroup.getReference< string >( viewKeyStruct::relPermNamesString() ); - RelativePermeabilityBase const & relperm = getConstitutiveModel< RelativePermeabilityBase >( dataGroup, relpermName ); - - immiscibleMultiphaseKernels:: - PhaseMobilityKernelFactory:: - createAndLaunch< parallelDevicePolicy<> >( m_numPhases, - dataGroup, - fluid, - relperm ); -} - -void ImmiscibleMultiphaseFlow::initializeFluidState( MeshLevel & mesh, - string_array const & regionNames ) -{ - GEOS_MARK_FUNCTION; - - mesh.getElemManager().forElementSubRegions( regionNames, - [&]( localIndex const, - ElementSubRegionBase & subRegion ) - { - // 2. Assume global component fractions have been prescribed. - // Initialize constitutive state to get fluid density. - updateFluidModel( subRegion ); - - } ); - - // for some reason CUDA does not want the host_device lambda to be defined inside the generic lambda - // I need the exact type of the subRegion for updateSolidflowProperties to work well. - mesh.getElemManager().forElementSubRegions< CellElementSubRegion, - SurfaceElementSubRegion >( regionNames, [&]( localIndex const, - auto & subRegion ) - { - // 4. Initialize/update dependent state quantities - - // 4.1 Update the constitutive models that only depend on - // - the primary variables - // - the fluid constitutive quantities (as they have already been updated) - // We postpone the other constitutive models for now - // In addition, to avoid multiplying permeability/porosity bay netToGross in the assembly kernel, we do it once and for all here - arrayView1d< real64 const > const netToGross = subRegion.template getField< fields::flow::netToGross >(); - CoupledSolidBase const & porousSolid = - getConstitutiveModel< CoupledSolidBase >( subRegion, subRegion.template getReference< string >( viewKeyStruct::solidNamesString() ) ); - PermeabilityBase const & permeabilityModel = - getConstitutiveModel< PermeabilityBase >( subRegion, subRegion.template getReference< string >( viewKeyStruct::permeabilityNamesString() ) ); - permeabilityModel.scaleHorizontalPermeability( netToGross ); - porousSolid.scaleReferencePorosity( netToGross ); - saveConvergedState( subRegion ); // necessary for a meaningful porosity update in sequential schemes - updatePorosityAndPermeability( subRegion ); - - // Now, we initialize and update each constitutive model one by one - - // 4.2 Save the computed porosity into the old porosity - // - // Note: - // - This must be called after updatePorosityAndPermeability - // - This step depends on porosity - string const & solidName = subRegion.template getReference< string >( viewKeyStruct::solidNamesString() ); - CoupledSolidBase const & porousMaterial = getConstitutiveModel< CoupledSolidBase >( subRegion, solidName ); - porousMaterial.initializeState(); - - // 4.3 Initialize/update the relative permeability model using the initial phase volume fraction - // This is needed to handle relative permeability hysteresis - // Also, initialize the fluid model - // - // Note: - // - This must be called after updateVolumeConstraint - // - This step depends on phaseVolFraction - - // initialized phase volume fraction - arrayView2d< real64 const, immiscibleFlow::USD_PHASE > const phaseVolFrac = - subRegion.template getField< fields::immiscibleMultiphaseFlow::phaseVolumeFraction >(); - - string const & relpermName = subRegion.template getReference< string >( viewKeyStruct::relPermNamesString() ); - RelativePermeabilityBase & relPermMaterial = - getConstitutiveModel< RelativePermeabilityBase >( subRegion, relpermName ); - relPermMaterial.saveConvergedPhaseVolFractionState( phaseVolFrac ); // this needs to happen before calling updateRelPermModel - updateRelPermModel( subRegion ); - relPermMaterial.saveConvergedState(); // this needs to happen after calling updateRelPermModel - - // 4.4 Then, we initialize/update the capillary pressure model - // - // Note: - // - This must be called after updatePorosityAndPermeability - // - This step depends on porosity and permeability - if( m_hasCapPressure ) - { +// get constitutives by type and name: relPerms, capPressures, Fluids (three pointers) +std::string & relPermName0 = subRegion0->getReference< std::string >( viewKeyStruct::relPermNamesString()); +std::string & relPermName1 = subRegion1->getReference< std::string >( viewKeyStruct::relPermNamesString()); +RelativePermeabilityBase * relPerm0 = &getConstitutiveModel< RelativePermeabilityBase >( *subRegion0, relPermName0 ); +RelativePermeabilityBase * relPerm1 = &getConstitutiveModel< RelativePermeabilityBase >( *subRegion1, relPermName1 ); + +std::string & cappresName0 = subRegion0->getReference< std::string >( viewKeyStruct::capPressureNamesString()); +std::string & cappresName1 = subRegion1->getReference< std::string >( viewKeyStruct::capPressureNamesString()); +CapillaryPressureBase * capPressure0 = &getConstitutiveModel< CapillaryPressureBase >( *subRegion0, cappresName0 ); +CapillaryPressureBase * capPressure1 = &getConstitutiveModel< CapillaryPressureBase >( *subRegion1, cappresName1 ); + +std::string & fluidName0 = subRegion0->getReference< std::string >( viewKeyStruct::fluidNamesString() ); +std::string & fluidName1 = subRegion1->getReference< std::string >( viewKeyStruct::fluidNamesString() ); + +TwoPhaseImmiscibleFluid * fluid0 = &getConstitutiveModel< TwoPhaseImmiscibleFluid >( *subRegion0, fluidName0 ); +TwoPhaseImmiscibleFluid * fluid1 = &getConstitutiveModel< TwoPhaseImmiscibleFluid >( *subRegion1, fluidName1 ); + +m_interfaceConstitutivePairs[surfaceRegionIndex][0] = std::make_tuple( relPerm0, capPressure0, fluid0 ); +m_interfaceConstitutivePairs[surfaceRegionIndex][1] = std::make_tuple( relPerm1, capPressure1, fluid1 ); + + } + } ); + + } + + + void ImmiscibleMultiphaseFlow::updateFluidModel( ObjectManagerBase & dataGroup ) const + { + GEOS_MARK_FUNCTION; + + arrayView1d< real64 const > const pres = dataGroup.getField< fields::flow::pressure >(); + + TwoPhaseImmiscibleFluid & fluid = getConstitutiveModel< TwoPhaseImmiscibleFluid >( dataGroup, dataGroup.getReference< string >( viewKeyStruct::fluidNamesString() ) ); + + constitutiveUpdatePassThru( fluid, [&] ( auto & castedFluid ) + { + using FluidType = TYPEOFREF( castedFluid ); + typename FluidType::KernelWrapper fluidWrapper = castedFluid.createKernelWrapper(); + + FluidUpdateKernel::launch< parallelDevicePolicy<> >( dataGroup.size(), fluidWrapper, pres ); + } ); + } + + + void ImmiscibleMultiphaseFlow::updateRelPermModel( ObjectManagerBase & dataGroup ) const + { + GEOS_MARK_FUNCTION; + + + GEOS_UNUSED_VAR( dataGroup ); + + arrayView2d< real64 const, immiscibleFlow::USD_PHASE > const phaseVolFrac = + dataGroup.getField< fields::immiscibleMultiphaseFlow::phaseVolumeFraction >(); + + string const & relPermName = dataGroup.getReference< string >( viewKeyStruct::relPermNamesString() ); + RelativePermeabilityBase & relPerm = getConstitutiveModel< RelativePermeabilityBase >( dataGroup, relPermName ); + + constitutive::constitutiveUpdatePassThru( relPerm, [&] ( auto & castedRelPerm ) + { + typename TYPEOFREF( castedRelPerm ) ::KernelWrapper relPermWrapper = castedRelPerm.createKernelWrapper(); + + isothermalCompositionalMultiphaseBaseKernels:: + RelativePermeabilityUpdateKernel:: + launch< parallelDevicePolicy<> >( dataGroup.size(), + relPermWrapper, + phaseVolFrac ); + } ); + } + + void ImmiscibleMultiphaseFlow::updateCapPressureModel( ObjectManagerBase & dataGroup ) const + { + GEOS_MARK_FUNCTION; + + if( m_hasCapPressure ) + { + arrayView2d< real64 const, immiscibleFlow::USD_PHASE > const phaseVolFrac = + dataGroup.getField< fields::immiscibleMultiphaseFlow::phaseVolumeFraction >(); + + string const & cappresName = dataGroup.getReference< string >( viewKeyStruct::capPressureNamesString() ); + CapillaryPressureBase & capPressure = getConstitutiveModel< CapillaryPressureBase >( dataGroup, cappresName ); + + constitutive::constitutiveUpdatePassThru( capPressure, [&] ( auto & castedCapPres ) + { + typename TYPEOFREF( castedCapPres ) ::KernelWrapper capPresWrapper = castedCapPres.createKernelWrapper(); + + // isothermalCompositionalMultiphaseBaseKernels:: + immiscibleMultiphaseKernels:: + CapillaryPressureUpdateKernel:: + launch< parallelDevicePolicy<> >( dataGroup.size(), + capPresWrapper, + phaseVolFrac ); + } ); + } + } + + + void ImmiscibleMultiphaseFlow::updateFluidState( ElementSubRegionBase & subRegion ) const + { + GEOS_MARK_FUNCTION; + + updateFluidModel( subRegion ); + updateVolumeConstraint( subRegion ); + updatePhaseMass( subRegion ); + updateRelPermModel( subRegion ); + updatePhaseMobility( subRegion ); + updateCapPressureModel( subRegion ); + } + + + void ImmiscibleMultiphaseFlow::updatePhaseMass( ElementSubRegionBase & subRegion ) const + { + GEOS_MARK_FUNCTION; + + string const & solidName = subRegion.getReference< string >( viewKeyStruct::solidNamesString() ); + string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString() ); + + TwoPhaseImmiscibleFluid const & fluid = getConstitutiveModel< TwoPhaseImmiscibleFluid >( subRegion, fluidName ); + CoupledSolidBase const & solid = getConstitutiveModel< CoupledSolidBase >( subRegion, solidName ); + + arrayView1d< real64 const > const volume = subRegion.getElementVolume(); + arrayView2d< real64 const > const porosity = solid.getPorosity(); + + arrayView2d< real64 const, immiscibleFlow::USD_PHASE > const phaseVolFrac= subRegion.getField< fields::immiscibleMultiphaseFlow::phaseVolumeFraction >(); + arrayView3d< real64 const, multifluid::USD_PHASE > phaseDens = fluid.phaseDensity(); + arrayView2d< real64, immiscibleFlow::USD_PHASE > const phaseMass = subRegion.getField< fields::immiscibleMultiphaseFlow::phaseMass >(); + + // Might be needed for geomechanics????? if so, need to change the accumulation as well? + //arrayView1d< real64 > const deltaVolume = subRegion.getField< fields::flow::deltaVolume >(); + + forAll< parallelDevicePolicy<> >( subRegion.size(), [=] GEOS_HOST_DEVICE ( localIndex const ei ) + { + real64 const poreVolume = volume[ei] * porosity[ei][0]; + for( integer ip = 0; ip < 2; ++ip ) + { + phaseMass[ei][ip] = poreVolume * phaseVolFrac[ei][ip] * phaseDens[ei][0][ip]; + } + } ); + } + + + void ImmiscibleMultiphaseFlow::updatePhaseMobility( ObjectManagerBase & dataGroup ) const + { + GEOS_MARK_FUNCTION; + + // note that the phase mobility computed here also includes phase density + string const & fluidName = dataGroup.getReference< string >( viewKeyStruct::fluidNamesString() ); + TwoPhaseImmiscibleFluid const & fluid = getConstitutiveModel< TwoPhaseImmiscibleFluid >( dataGroup, fluidName ); + + string const & relpermName = dataGroup.getReference< string >( viewKeyStruct::relPermNamesString() ); + RelativePermeabilityBase const & relperm = getConstitutiveModel< RelativePermeabilityBase >( dataGroup, relpermName ); + + immiscibleMultiphaseKernels:: + PhaseMobilityKernelFactory:: + createAndLaunch< parallelDevicePolicy<> >( m_numPhases, + dataGroup, + fluid, + relperm ); + } + + void ImmiscibleMultiphaseFlow::initializeFluidState( MeshLevel & mesh, + string_array const & regionNames ) + { + GEOS_MARK_FUNCTION; + + mesh.getElemManager().forElementSubRegions( regionNames, + [&]( localIndex const, + ElementSubRegionBase & subRegion ) + { + // 2. Assume global component fractions have been prescribed. + // Initialize constitutive state to get fluid density. + updateFluidModel( subRegion ); + + } ); + + // for some reason CUDA does not want the host_device lambda to be defined inside the generic lambda + // I need the exact type of the subRegion for updateSolidflowProperties to work well. + mesh.getElemManager().forElementSubRegions< CellElementSubRegion, + SurfaceElementSubRegion >( regionNames, [&]( localIndex const, + auto & subRegion ) + { + // 4. Initialize/update dependent state quantities + + // 4.1 Update the constitutive models that only depend on + // - the primary variables + // - the fluid constitutive quantities (as they have already been updated) + // We postpone the other constitutive models for now + // In addition, to avoid multiplying permeability/porosity bay netToGross in the assembly kernel, we do it once and for all here + arrayView1d< real64 const > const netToGross = subRegion.template getField< fields::flow::netToGross >(); + CoupledSolidBase const & porousSolid = + getConstitutiveModel< CoupledSolidBase >( subRegion, subRegion.template getReference< string >( viewKeyStruct::solidNamesString() ) ); + PermeabilityBase const & permeabilityModel = + getConstitutiveModel< PermeabilityBase >( subRegion, subRegion.template getReference< string >( viewKeyStruct::permeabilityNamesString() ) ); + permeabilityModel.scaleHorizontalPermeability( netToGross ); + porousSolid.scaleReferencePorosity( netToGross ); + saveConvergedState( subRegion ); // necessary for a meaningful porosity update in sequential schemes + updatePorosityAndPermeability( subRegion ); + + // Now, we initialize and update each constitutive model one by one + + // 4.2 Save the computed porosity into the old porosity + // + // Note: + // - This must be called after updatePorosityAndPermeability + // - This step depends on porosity + string const & solidName = subRegion.template getReference< string >( viewKeyStruct::solidNamesString() ); + CoupledSolidBase const & porousMaterial = getConstitutiveModel< CoupledSolidBase >( subRegion, solidName ); + porousMaterial.initializeState(); + + // 4.3 Initialize/update the relative permeability model using the initial phase volume fraction + // This is needed to handle relative permeability hysteresis + // Also, initialize the fluid model + // + // Note: + // - This must be called after updateVolumeConstraint + // - This step depends on phaseVolFraction + + // initialized phase volume fraction + arrayView2d< real64 const, immiscibleFlow::USD_PHASE > const phaseVolFrac = + subRegion.template getField< fields::immiscibleMultiphaseFlow::phaseVolumeFraction >(); + + string const & relpermName = subRegion.template getReference< string >( viewKeyStruct::relPermNamesString() ); + RelativePermeabilityBase & relPermMaterial = + getConstitutiveModel< RelativePermeabilityBase >( subRegion, relpermName ); + relPermMaterial.saveConvergedPhaseVolFractionState( phaseVolFrac ); // this needs to happen before calling updateRelPermModel + updateRelPermModel( subRegion ); + relPermMaterial.saveConvergedState(); // this needs to happen after calling updateRelPermModel + + // 4.4 Then, we initialize/update the capillary pressure model + // + // Note: + // - This must be called after updatePorosityAndPermeability + // - This step depends on porosity and permeability + if( m_hasCapPressure ) + { // initialized porosity arrayView2d< real64 const > const porosity = porousMaterial.getPorosity(); @@ -425,854 +608,939 @@ void ImmiscibleMultiphaseFlow::initializeFluidState( MeshLevel & mesh, CapillaryPressureBase const & capPressureMaterial = getConstitutiveModel< CapillaryPressureBase >( subRegion, capPressureName ); capPressureMaterial.initializeRockState( porosity, permeability ); // this needs to happen before calling updateCapPressureModel + capPressureMaterial.saveConvergedPhaseVolFractionState( phaseVolFrac ); updateCapPressureModel( subRegion ); - } - - // 4.5 Update the phase mobility - // - // Note: - // - This must be called after updateRelPermModel - // - This step depends phaseRelPerm - updatePhaseMobility( subRegion ); - - } ); - - // 5. Save initial pressure - mesh.getElemManager().forElementSubRegions( regionNames, [&]( localIndex const, - ElementSubRegionBase & subRegion ) - { - arrayView1d< real64 const > const pres = subRegion.getField< fields::flow::pressure >(); - arrayView1d< real64 > const initPres = subRegion.getField< fields::flow::initialPressure >(); - arrayView1d< real64 const > const temp = subRegion.getField< fields::flow::temperature >(); - arrayView1d< real64 > const initTemp = subRegion.template getField< fields::flow::initialTemperature >(); - initPres.setValues< parallelDevicePolicy<> >( pres ); - initTemp.setValues< parallelDevicePolicy<> >( temp ); - - // TODO: Missing updatePhaseMass? - } ); -} - - -void ImmiscibleMultiphaseFlow::initializePostInitialConditionsPreSubGroups() -{ - GEOS_MARK_FUNCTION; - - FlowSolverBase::initializePostInitialConditionsPreSubGroups(); - - DomainPartition & domain = this->getGroupByPath< DomainPartition >( "/Problem/domain" ); - - forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &, - MeshLevel & mesh, - string_array const & regionNames ) - { - FieldIdentifiers fieldsToBeSync; - fieldsToBeSync.addElementFields( { fields::flow::pressure::key(), - fields::immiscibleMultiphaseFlow::phaseVolumeFraction::key() }, - regionNames ); - - CommunicationTools::getInstance().synchronizeFields( fieldsToBeSync, mesh, domain.getNeighbors(), false ); - } ); - - initializeState( domain ); -} - - -void -ImmiscibleMultiphaseFlow::implicitStepSetup( real64 const & GEOS_UNUSED_PARAM( time_n ), - real64 const & GEOS_UNUSED_PARAM( dt ), - DomainPartition & domain ) -{ - forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &, - MeshLevel & mesh, - string_array const & regionNames ) - { - mesh.getElemManager().forElementSubRegions< CellElementSubRegion, - SurfaceElementSubRegion >( regionNames, - [&]( localIndex const, - auto & subRegion ) - { - saveConvergedState( subRegion ); - - // update porosity, permeability - updatePorosityAndPermeability( subRegion ); - // update all fluid properties - updateVolumeConstraint( subRegion ); - updateFluidState( subRegion ); - - // after the update, save the new saturation - arrayView2d< real64 const, immiscibleFlow::USD_PHASE > const phaseVolFrac = - subRegion.template getField< fields::immiscibleMultiphaseFlow::phaseVolumeFraction >(); - - arrayView2d< real64, immiscibleFlow::USD_PHASE > const phaseVolFrac_n = - subRegion.template getField< fields::immiscibleMultiphaseFlow::phaseVolumeFraction_n >(); - phaseVolFrac_n.setValues< parallelDevicePolicy<> >( phaseVolFrac ); - - arrayView2d< real64 const, immiscibleFlow::USD_PHASE > const & phaseMass = - subRegion.template getField< fields::immiscibleMultiphaseFlow::phaseMass >(); - - arrayView2d< real64, immiscibleFlow::USD_PHASE > const & phaseMass_n = - subRegion.template getField< fields::immiscibleMultiphaseFlow::phaseMass_n >(); - phaseMass_n.setValues< parallelDevicePolicy<> >( phaseMass ); - - } ); - } ); -} - -void ImmiscibleMultiphaseFlow::assembleSystem( real64 const GEOS_UNUSED_PARAM( time_n ), - real64 const dt, - DomainPartition & domain, - DofManager const & dofManager, - CRSMatrixView< real64, globalIndex const > const & localMatrix, - arrayView1d< real64 > const & localRhs ) -{ - GEOS_MARK_FUNCTION; - - assembleAccumulationTerm( domain, - dofManager, - localMatrix, - localRhs ); - - - assembleFluxTerms( dt, - domain, - dofManager, - localMatrix, - localRhs ); -} - - - -void ImmiscibleMultiphaseFlow::assembleAccumulationTerm( DomainPartition & domain, - DofManager const & dofManager, - CRSMatrixView< real64, globalIndex const > const & localMatrix, - arrayView1d< real64 > const & localRhs ) const -{ - GEOS_MARK_FUNCTION; - - - forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &, - MeshLevel const & mesh, - string_array const & regionNames ) - { - mesh.getElemManager().forElementSubRegions( regionNames, - [&]( localIndex const, - ElementSubRegionBase const & subRegion ) - { - string const dofKey = dofManager.getKey( viewKeyStruct::elemDofFieldString() ); - string const & solidName = subRegion.getReference< string >( viewKeyStruct::solidNamesString() ); - string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString() ); - - TwoPhaseImmiscibleFluid const & fluid = getConstitutiveModel< TwoPhaseImmiscibleFluid >( subRegion, fluidName ); - CoupledSolidBase const & solid = getConstitutiveModel< CoupledSolidBase >( subRegion, solidName ); - - immiscibleMultiphaseKernels:: - AccumulationKernelFactory:: - createAndLaunch< parallelDevicePolicy<> >( m_numPhases, - dofManager.rankOffset(), - m_useTotalMassEquation, - dofKey, - subRegion, - fluid, - solid, - localMatrix, - localRhs ); - - } ); - } ); -} - -void ImmiscibleMultiphaseFlow::assembleFluxTerms( real64 const dt, - DomainPartition const & domain, - DofManager const & dofManager, - CRSMatrixView< real64, globalIndex const > const & localMatrix, - arrayView1d< real64 > const & localRhs ) const -{ - GEOS_MARK_FUNCTION; - - NumericalMethodsManager const & numericalMethodManager = domain.getNumericalMethodManager(); - FiniteVolumeManager const & fvManager = numericalMethodManager.getFiniteVolumeManager(); - FluxApproximationBase const & fluxApprox = fvManager.getFluxApproximation( m_discretizationName ); - - string const & dofKey = dofManager.getKey( viewKeyStruct::elemDofFieldString() ); - - forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &, + } + + // 4.5 Update the phase mobility + // + // Note: + // - This must be called after updateRelPermModel + // - This step depends phaseRelPerm + updatePhaseMobility( subRegion ); + + } ); + + // 5. Save initial pressure + mesh.getElemManager().forElementSubRegions( regionNames, [&]( localIndex const, + ElementSubRegionBase & subRegion ) + { + arrayView1d< real64 const > const pres = subRegion.getField< fields::flow::pressure >(); + arrayView1d< real64 > const initPres = subRegion.getField< fields::flow::initialPressure >(); + arrayView1d< real64 const > const temp = subRegion.getField< fields::flow::temperature >(); + arrayView1d< real64 > const initTemp = subRegion.template getField< fields::flow::initialTemperature >(); + initPres.setValues< parallelDevicePolicy<> >( pres ); + initTemp.setValues< parallelDevicePolicy<> >( temp ); + + // TODO: Missing updatePhaseMass? + } ); + } + + + void ImmiscibleMultiphaseFlow::initializePostInitialConditionsPreSubGroups() + { + GEOS_MARK_FUNCTION; + + FlowSolverBase::initializePostInitialConditionsPreSubGroups(); + + DomainPartition & domain = this->getGroupByPath< DomainPartition >( "/Problem/domain" ); + + forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &, + MeshLevel & mesh, + string_array const & regionNames ) + { + FieldIdentifiers fieldsToBeSync; + fieldsToBeSync.addElementFields( { fields::flow::pressure::key(), + fields::immiscibleMultiphaseFlow::phaseVolumeFraction::key() }, + regionNames ); + + CommunicationTools::getInstance().synchronizeFields( fieldsToBeSync, mesh, domain.getNeighbors(), false ); + } ); + + // Retrieve the numerical methods and finite volume manager + FiniteVolumeManager const & fvManager = domain.getNumericalMethodManager().getFiniteVolumeManager(); + FluxApproximationBase const & fluxApprox = fvManager.getFluxApproximation( m_discretizationName ); + const geos::string flux_approximation_name = fluxApprox.getName(); + + // Clear the existing mapping between connector indices and interface region indices + m_interfaceRegionByConnector.clear(); + forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( std::string const &, + MeshLevel & meshLevel, + string_array const & GEOS_UNUSED_PARAM( regionNames )) + { + // Access the face manager and retrieve the face set group for the current mesh level + FaceManager const & faceManager = meshLevel.getFaceManager(); + Group const & faceSetGroup = faceManager.sets(); + + // Access the connector indices map (face index → connector index) + Group & stencilGroup = + meshLevel.getGroup( FluxApproximationBase::groupKeyStruct::stencilMeshGroupString()) + .getGroup( flux_approximation_name ); + CellElementStencilTPFA & stencil = + stencilGroup.getReference< CellElementStencilTPFA >( + FluxApproximationBase::viewKeyStruct::cellStencilString()); + unordered_map< localIndex, localIndex > const & connectorIndices = stencil.getConnectorIndices(); + + // for all interface face sets to map connector indices to their corresponding interface region indices + for( size_t surfaceRegionIndex = 0; surfaceRegionIndex < m_interfaceFaceSetNames.size(); ++surfaceRegionIndex ) + { + // Iterate over each face and associate its connector index + std::string const & faceSetName = m_interfaceFaceSetNames[surfaceRegionIndex]; + for( localIndex kf : faceSetGroup.getReference< SortedArray< localIndex > >( faceSetName )) + { + auto it = connectorIndices.find( kf ); + if( it != connectorIndices.end()) + { + // Map the connector index to the corresponding surface region index + m_interfaceRegionByConnector[it->second] = surfaceRegionIndex; + } + } + } + } ); + + + initializeState( domain ); + } + + + void + ImmiscibleMultiphaseFlow::implicitStepSetup( real64 const & GEOS_UNUSED_PARAM( time_n ), + real64 const & GEOS_UNUSED_PARAM( dt ), + DomainPartition & domain ) + { + forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &, + MeshLevel & mesh, + string_array const & regionNames ) + { + mesh.getElemManager().forElementSubRegions< CellElementSubRegion, + SurfaceElementSubRegion >( regionNames, + [&]( localIndex const, + auto & subRegion ) + { + saveConvergedState( subRegion ); + + // update porosity, permeability + updatePorosityAndPermeability( subRegion ); + // update all fluid properties + updateVolumeConstraint( subRegion ); + updateFluidState( subRegion ); + + // after the update, save the new saturation + arrayView2d< real64 const, immiscibleFlow::USD_PHASE > const phaseVolFrac = + subRegion.template getField< fields::immiscibleMultiphaseFlow::phaseVolumeFraction >(); + + arrayView2d< real64, immiscibleFlow::USD_PHASE > const phaseVolFrac_n = + subRegion.template getField< fields::immiscibleMultiphaseFlow::phaseVolumeFraction_n >(); + phaseVolFrac_n.setValues< parallelDevicePolicy<> >( phaseVolFrac ); + + arrayView2d< real64 const, immiscibleFlow::USD_PHASE > const & phaseMass = + subRegion.template getField< fields::immiscibleMultiphaseFlow::phaseMass >(); + + arrayView2d< real64, immiscibleFlow::USD_PHASE > const & phaseMass_n = + subRegion.template getField< fields::immiscibleMultiphaseFlow::phaseMass_n >(); + phaseMass_n.setValues< parallelDevicePolicy<> >( phaseMass ); + + } ); + } ); + } + + void ImmiscibleMultiphaseFlow::assembleSystem( real64 const GEOS_UNUSED_PARAM( time_n ), + real64 const dt, + DomainPartition & domain, + DofManager const & dofManager, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) + { + GEOS_MARK_FUNCTION; + + assembleAccumulationTerm( domain, + dofManager, + localMatrix, + localRhs ); + + + assembleFluxTerms( dt, + domain, + dofManager, + localMatrix, + localRhs ); + } + + + + void ImmiscibleMultiphaseFlow::assembleAccumulationTerm( DomainPartition & domain, + DofManager const & dofManager, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) const + { + GEOS_MARK_FUNCTION; + + + forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &, MeshLevel const & mesh, - string_array const & ) - { - fluxApprox.forAllStencils( mesh, [&]( auto & stencil ) - { - typename TYPEOFREF( stencil ) ::KernelWrapper stencilWrapper = stencil.createKernelWrapper(); - immiscibleMultiphaseKernels:: - FluxComputeKernelFactory::createAndLaunch< parallelDevicePolicy<> >( m_numPhases, - dofManager.rankOffset(), - dofKey, - m_hasCapPressure, - m_useTotalMassEquation, - m_gravityDensityScheme == GravityDensityScheme::PhasePresence, - getName(), - mesh.getElemManager(), - stencilWrapper, - dt, - localMatrix.toViewConstSizes(), - localRhs.toView() ); - } ); - } ); -} - -void ImmiscibleMultiphaseFlow::setupDofs( DomainPartition const & domain, - DofManager & dofManager ) const -{ - GEOS_UNUSED_VAR( domain, dofManager ); - // add a field for the cell-centered degrees of freedom - dofManager.addField( viewKeyStruct::elemDofFieldString(), - FieldLocation::Elem, - m_numDofPerCell, - getMeshTargets() ); - - //// this call with instruct GEOS to reorder the dof numbers - //dofManager.setLocalReorderingType( viewKeyStruct::elemDofFieldString(), - // DofManager::LocalReorderingType::ReverseCutHillMcKee ); - - NumericalMethodsManager const & numericalMethodManager = domain.getNumericalMethodManager(); - FiniteVolumeManager const & fvManager = numericalMethodManager.getFiniteVolumeManager(); - FluxApproximationBase const & fluxApprox = fvManager.getFluxApproximation( m_discretizationName ); - dofManager.addCoupling( viewKeyStruct::elemDofFieldString(), fluxApprox ); -} - -void ImmiscibleMultiphaseFlow::applyBoundaryConditions( real64 const time_n, - real64 const dt, - DomainPartition & domain, - DofManager const & dofManager, - CRSMatrixView< real64, globalIndex const > const & localMatrix, - arrayView1d< real64 > const & localRhs ) -{ - GEOS_MARK_FUNCTION; - - // apply pressure boundary conditions. - applyDirichletBC( time_n, dt, dofManager, domain, localMatrix.toViewConstSizes(), localRhs.toView() ); - - // apply flux boundary conditions - applySourceFluxBC( time_n, dt, dofManager, domain, localMatrix.toViewConstSizes(), localRhs.toView() ); -} - - -namespace -{ -char const bcLogMessage[] = - "ImmiscibleMultiphaseFlow {}: at time {}s, " - "the <{}> boundary condition '{}' is applied to the element set '{}' in subRegion '{}'. " - "\nThe scale of this boundary condition is {} and multiplies the value of the provided function (if any). " - "\nThe total number of target elements (including ghost elements) is {}. " - "\nNote that if this number is equal to zero for all subRegions, the boundary condition will not be applied on this element set."; -} - -bool ImmiscibleMultiphaseFlow::validateDirichletBC( DomainPartition & domain, - real64 const time ) const -{ - constexpr integer MAX_NP = 2; - FieldSpecificationManager & fsManager = FieldSpecificationManager::getInstance(); - - bool bcConsistent = true; - - forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &, - MeshLevel & mesh, - string_array const & ) - { - // map: regionName -> subRegionName -> setName -> numPhases to check pressure/phase are present consistent - map< string, map< string, map< string, ComponentMask< MAX_NP > > > > bcPresCompStatusMap; - - // 1. Check pressure Dirichlet BCs - fsManager.apply< ElementSubRegionBase >( time, - mesh, - fields::flow::pressure::key(), - [&]( FieldSpecificationBase const &, - string const & setName, - SortedArrayView< localIndex const > const &, - ElementSubRegionBase & subRegion, - string const & ) - { - // Check whether pressure has already been applied to this set - string const & subRegionName = subRegion.getName(); - string const & regionName = subRegion.getParent().getParent().getName(); - - auto & subRegionSetMap = bcPresCompStatusMap[regionName][subRegionName]; - if( subRegionSetMap.count( setName ) > 0 ) - { - bcConsistent = false; - GEOS_WARNING( BCMessage::pressureConflict( regionName, subRegionName, setName, - fields::flow::pressure::key() ) ); - } - subRegionSetMap[setName].setNumComp( m_numPhases ); - } ); - // 2. Check saturation Dirichlet BCs - fsManager.apply< ElementSubRegionBase >( time, - mesh, - fields::immiscibleMultiphaseFlow::phaseVolumeFraction::key(), - [&] ( FieldSpecificationBase const & fs, - string const & setName, - SortedArrayView< localIndex const > const &, - ElementSubRegionBase & subRegion, - string const & ) + string_array const & regionNames ) + { + mesh.getElemManager().forElementSubRegions( regionNames, + [&]( localIndex const, + ElementSubRegionBase const & subRegion ) + { + string const dofKey = dofManager.getKey( viewKeyStruct::elemDofFieldString() ); + string const & solidName = subRegion.getReference< string >( viewKeyStruct::solidNamesString() ); + string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString() ); + + TwoPhaseImmiscibleFluid const & fluid = getConstitutiveModel< TwoPhaseImmiscibleFluid >( subRegion, fluidName ); + CoupledSolidBase const & solid = getConstitutiveModel< CoupledSolidBase >( subRegion, solidName ); + + immiscibleMultiphaseKernels:: + AccumulationKernelFactory:: + createAndLaunch< parallelDevicePolicy<> >( m_numPhases, + dofManager.rankOffset(), + m_useTotalMassEquation, + dofKey, + subRegion, + fluid, + solid, + localMatrix, + localRhs ); + + } ); + } ); + } + + void ImmiscibleMultiphaseFlow::assembleFluxTerms( real64 const dt, + DomainPartition & domain, + DofManager const & dofManager, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) const + { + GEOS_MARK_FUNCTION; + + NumericalMethodsManager const & numericalMethodManager = domain.getNumericalMethodManager(); + FiniteVolumeManager const & fvManager = numericalMethodManager.getFiniteVolumeManager(); + FluxApproximationBase const & fluxApprox = fvManager.getFluxApproximation( m_discretizationName ); + + string const & dofKey = dofManager.getKey( viewKeyStruct::elemDofFieldString() ); + + + forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &, + MeshLevel & mesh, + string_array const & regionNames ) + { + if( m_hasCapPressure ) { - string const & subRegionName = subRegion.getName( ); - string const & regionName = subRegion.getParent().getParent().getName(); - integer const comp = fs.getComponent(); - - auto & subRegionSetMap = bcPresCompStatusMap[regionName][subRegionName]; - if( subRegionSetMap.count( setName ) == 0 ) + // Get the first subregion to pass to the kernel factory (only used for domainSize, not for filtering connections) + ElementSubRegionBase const * firstSubRegion = nullptr; + mesh.getElemManager().forElementSubRegions( regionNames, + [&]( localIndex const, + ElementSubRegionBase const & subRegion ) { - bcConsistent = false; - GEOS_WARNING( BCMessage::missingPressure( regionName, subRegionName, setName, - fields::flow::pressure::key() ) ); - } - if( comp < 0 || comp >= m_numPhases ) - { - bcConsistent = false; - GEOS_WARNING( BCMessage::invalidComponentIndex( comp, fs.getName(), - fields::immiscibleMultiphaseFlow::phaseVolumeFraction::key() ) ); - return; // can't check next part with invalid component id - } - - ComponentMask< MAX_NP > & compMask = subRegionSetMap[setName]; - if( compMask[comp] ) - { - bcConsistent = false; - fsManager.forSubGroups< EquilibriumInitialCondition >( [&] ( EquilibriumInitialCondition const & bc ) + if( firstSubRegion == nullptr ) { - string_array const & componentNames = bc.getComponentNames(); - GEOS_WARNING( BCMessage::conflictingComposition( comp, componentNames[comp], - regionName, subRegionName, setName, - fields::immiscibleMultiphaseFlow::phaseVolumeFraction::key() ) ); - } ); - } - compMask.set( comp ); - } ); + firstSubRegion = &subRegion; + } + } ); - // 3.2 Check consistency between composition BC applied to sets - // Note: for a temperature-only boundary condition, this loop does not do anything - for( auto const & regionEntry : bcPresCompStatusMap ) - { - for( auto const & subRegionEntry : regionEntry.second ) + fluxApprox.forAllStencils( mesh, [&]( auto & stencil ) { - for( auto const & setEntry : subRegionEntry.second ) - { - ComponentMask< MAX_NP > const & compMask = setEntry.second; - - fsManager.forSubGroups< EquilibriumInitialCondition >( [&] ( EquilibriumInitialCondition const & fs ) - { - string_array const & componentNames = fs.getComponentNames(); - for( size_t ic = 0; ic < componentNames.size(); ic++ ) - { - if( !compMask[ic] ) - { - bcConsistent = false; - GEOS_WARNING( BCMessage::notAppliedOnRegion( ic, componentNames[ic], - regionEntry.first, subRegionEntry.first, setEntry.first, - fields::immiscibleMultiphaseFlow::phaseVolumeFraction::key() ) ); - } - } - } ); - } - } + typename TYPEOFREF( stencil ) ::KernelWrapper stencilWrapper = stencil.createKernelWrapper(); + immiscibleMultiphaseKernels:: + FluxComputeKernelFactory::createAndLaunch< parallelDevicePolicy<> >( m_numPhases, + dofManager.rankOffset(), + dofKey, + m_hasCapPressure, + m_useTotalMassEquation, + m_gravityDensityScheme == GravityDensityScheme::PhasePresence, + getName(), + mesh.getElemManager(), + stencilWrapper, + m_interfaceFaceSetNames, + m_interfaceConstitutivePairs, + m_interfaceRegionByConnector, + *firstSubRegion, + dt, + localMatrix.toViewConstSizes(), + localRhs.toView() ); + } ); } - } ); - - return bcConsistent; -} - -void ImmiscibleMultiphaseFlow::applyDirichletBC( real64 const time_n, - real64 const dt, - DofManager const & dofManager, - DomainPartition & domain, - CRSMatrixView< real64, globalIndex const > const & localMatrix, - arrayView1d< real64 > const & localRhs ) const -{ - GEOS_MARK_FUNCTION; - - // Only validate BC at the beginning of Newton loop - if( m_nonlinearSolverParameters.m_numNewtonIterations == 0 ) - { - bool const bcConsistent = validateDirichletBC( domain, time_n + dt ); - GEOS_ERROR_IF( !bcConsistent, GEOS_FMT( "ImmiscibleMultiphaseFlow {}: inconsistent boundary conditions", getDataContext() ) ); - } - - FieldSpecificationManager & fsManager = FieldSpecificationManager::getInstance(); - - forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &, - MeshLevel & mesh, - string_array const & ) - { - - // 1. Apply pressure Dirichlet BCs, store in a separate field - applyFieldValue< ElementSubRegionBase >( time_n, dt, mesh, bcLogMessage, - fields::flow::pressure::key(), fields::flow::bcPressure::key() ); - // 2. Apply saturation BC (phase volume fraction) and store in a separate field - applyFieldValue< ElementSubRegionBase >( time_n, dt, mesh, bcLogMessage, - fields::immiscibleMultiphaseFlow::phaseVolumeFraction::key(), - fields::immiscibleMultiphaseFlow::bcPhaseVolumeFraction::key() ); - - globalIndex const rankOffset = dofManager.rankOffset(); - string const dofKey = dofManager.getKey( viewKeyStruct::elemDofFieldString() ); - - // 3. Call constitutive update - fsManager.apply< ElementSubRegionBase >( time_n + dt, - mesh, - fields::flow::pressure::key(), - [&] ( FieldSpecificationBase const &, - string const &, - SortedArrayView< localIndex const > const & targetSet, + else + { + fluxApprox.forAllStencils( mesh, [&]( auto & stencil ) + { + typename TYPEOFREF( stencil ) ::KernelWrapper stencilWrapper = stencil.createKernelWrapper(); + immiscibleMultiphaseKernels:: + FluxComputeKernelFactory::createAndLaunch< parallelDevicePolicy<> >( m_numPhases, + dofManager.rankOffset(), + dofKey, + m_hasCapPressure, + m_useTotalMassEquation, + m_gravityDensityScheme == GravityDensityScheme::PhasePresence, + getName(), + mesh.getElemManager(), + stencilWrapper, + dt, + localMatrix.toViewConstSizes(), + localRhs.toView() ); + } ); + } + + } ); + } + + // Ryan: Looks like this will need to be overwritten as well... + // I have left the CompositionalMultiphaseFVM implementation for reference + void ImmiscibleMultiphaseFlow::setupDofs( DomainPartition const & domain, + DofManager & dofManager ) const + { + GEOS_UNUSED_VAR( domain, dofManager ); + // add a field for the cell-centered degrees of freedom + dofManager.addField( viewKeyStruct::elemDofFieldString(), + FieldLocation::Elem, + m_numDofPerCell, + getMeshTargets() ); + + //// this call with instruct GEOS to reorder the dof numbers + //dofManager.setLocalReorderingType( viewKeyStruct::elemDofFieldString(), + // DofManager::LocalReorderingType::ReverseCutHillMcKee ); + + NumericalMethodsManager const & numericalMethodManager = domain.getNumericalMethodManager(); + FiniteVolumeManager const & fvManager = numericalMethodManager.getFiniteVolumeManager(); + FluxApproximationBase const & fluxApprox = fvManager.getFluxApproximation( m_discretizationName ); + dofManager.addCoupling( viewKeyStruct::elemDofFieldString(), fluxApprox ); + } + + void ImmiscibleMultiphaseFlow::applyBoundaryConditions( real64 const time_n, + real64 const dt, + DomainPartition & domain, + DofManager const & dofManager, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) + { + GEOS_MARK_FUNCTION; + + // apply pressure boundary conditions. + applyDirichletBC( time_n, dt, dofManager, domain, localMatrix.toViewConstSizes(), localRhs.toView() ); + + // apply flux boundary conditions + applySourceFluxBC( time_n, dt, dofManager, domain, localMatrix.toViewConstSizes(), localRhs.toView() ); + } + + + namespace + { + char const bcLogMessage[] = + "ImmiscibleMultiphaseFlow {}: at time {}s, " + "the <{}> boundary condition '{}' is applied to the element set '{}' in subRegion '{}'. " + "\nThe scale of this boundary condition is {} and multiplies the value of the provided function (if any). " + "\nThe total number of target elements (including ghost elements) is {}. " + "\nNote that if this number is equal to zero for all subRegions, the boundary condition will not be applied on this element set."; + } + + bool ImmiscibleMultiphaseFlow::validateDirichletBC( DomainPartition & domain, + real64 const time ) const + { + constexpr integer MAX_NP = 2; + FieldSpecificationManager & fsManager = FieldSpecificationManager::getInstance(); + + bool bcConsistent = true; + + forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &, + MeshLevel & mesh, + string_array const & ) + { + // map: regionName -> subRegionName -> setName -> numPhases to check pressure/phase are present consistent + map< string, map< string, map< string, ComponentMask< MAX_NP > > > > bcPresCompStatusMap; + + // 1. Check pressure Dirichlet BCs + fsManager.apply< ElementSubRegionBase >( time, + mesh, + fields::flow::pressure::key(), + [&]( FieldSpecificationBase const &, + string const & setName, + SortedArrayView< localIndex const > const &, ElementSubRegionBase & subRegion, string const & ) - { - - arrayView1d< real64 const > const bcPres = - subRegion.getReference< array1d< real64 > >( fields::flow::bcPressure::key() ); - arrayView2d< real64 const, immiscibleFlow::USD_PHASE > const bcPhaseVolFraction = - subRegion.getReference< array2d< real64, immiscibleFlow::LAYOUT_PHASE > >( - fields::immiscibleMultiphaseFlow::bcPhaseVolumeFraction::key() ); - - arrayView1d< integer const > const ghostRank = - subRegion.getReference< array1d< integer > >( ObjectManagerBase::viewKeyStruct::ghostRankString() ); - arrayView1d< globalIndex const > const dofNumber = - subRegion.getReference< array1d< globalIndex > >( dofKey ); - arrayView1d< real64 const > const pres = - subRegion.getReference< array1d< real64 > >( fields::flow::pressure::key() ); - arrayView2d< real64 const, immiscibleFlow::USD_PHASE > const phaseVolFraction = - subRegion.getReference< array2d< real64, immiscibleFlow::LAYOUT_PHASE > >( - fields::immiscibleMultiphaseFlow::phaseVolumeFraction::key() ); - - integer const numPhase = m_numPhases; - - - forAll< parallelDevicePolicy<> >( targetSet.size(), [=] GEOS_HOST_DEVICE ( localIndex const a ) - { - localIndex const ei = targetSet[a]; - if( ghostRank[ei] >= 0 ) - { - return; - } - - globalIndex const dofIndex = dofNumber[ei]; - localIndex const localRow = dofIndex - rankOffset; - real64 rhsValue; - - // 3.1. Apply pressure value to the matrix/rhs - FieldSpecificationEqual::SpecifyFieldValue( dofIndex, - rankOffset, - localMatrix, - rhsValue, - bcPres[ei], - pres[ei] ); - localRhs[localRow] = rhsValue; - - // 3.2. For each phase, apply target saturation value - for( integer ip = 0; ip < numPhase-1; ++ip ) - { - FieldSpecificationEqual::SpecifyFieldValue( dofIndex + ip + 1, - rankOffset, - localMatrix, - rhsValue, - bcPhaseVolFraction[ei][ip], - phaseVolFraction[ei][ip] ); - localRhs[localRow + ip + 1] = rhsValue; - } - } ); - } ); - } ); -} - -void ImmiscibleMultiphaseFlow::applySourceFluxBC( real64 const time, + { + // Check whether pressure has already been applied to this set + string const & subRegionName = subRegion.getName(); + string const & regionName = subRegion.getParent().getParent().getName(); + + auto & subRegionSetMap = bcPresCompStatusMap[regionName][subRegionName]; + if( subRegionSetMap.count( setName ) > 0 ) + { + bcConsistent = false; + GEOS_WARNING( BCMessage::pressureConflict( regionName, subRegionName, setName, + fields::flow::pressure::key() ) ); + } + subRegionSetMap[setName].setNumComp( m_numPhases ); + } ); + // 2. Check saturation Dirichlet BCs + fsManager.apply< ElementSubRegionBase >( time, + mesh, + fields::immiscibleMultiphaseFlow::phaseVolumeFraction::key(), + [&] ( FieldSpecificationBase const & fs, + string const & setName, + SortedArrayView< localIndex const > const &, + ElementSubRegionBase & subRegion, + string const & ) + { + string const & subRegionName = subRegion.getName( ); + string const & regionName = subRegion.getParent().getParent().getName(); + integer const comp = fs.getComponent(); + + auto & subRegionSetMap = bcPresCompStatusMap[regionName][subRegionName]; + if( subRegionSetMap.count( setName ) == 0 ) + { + bcConsistent = false; + GEOS_WARNING( BCMessage::missingPressure( regionName, subRegionName, setName, + fields::flow::pressure::key() ) ); + } + if( comp < 0 || comp >= m_numPhases ) + { + bcConsistent = false; + GEOS_WARNING( BCMessage::invalidComponentIndex( comp, fs.getName(), + fields::immiscibleMultiphaseFlow::phaseVolumeFraction::key() ) ); + return; // can't check next part with invalid component id + } + + ComponentMask< MAX_NP > & compMask = subRegionSetMap[setName]; + if( compMask[comp] ) + { + bcConsistent = false; + fsManager.forSubGroups< EquilibriumInitialCondition >( [&] ( EquilibriumInitialCondition const & bc ) + { + string_array const & componentNames = bc.getComponentNames(); + GEOS_WARNING( BCMessage::conflictingComposition( comp, componentNames[comp], + regionName, subRegionName, setName, + fields::immiscibleMultiphaseFlow::phaseVolumeFraction::key() ) ); + } ); + } + compMask.set( comp ); + } ); + + // 3.2 Check consistency between composition BC applied to sets + // Note: for a temperature-only boundary condition, this loop does not do anything + for( auto const & regionEntry : bcPresCompStatusMap ) + { + for( auto const & subRegionEntry : regionEntry.second ) + { + for( auto const & setEntry : subRegionEntry.second ) + { + ComponentMask< MAX_NP > const & compMask = setEntry.second; + + fsManager.forSubGroups< EquilibriumInitialCondition >( [&] ( EquilibriumInitialCondition const & fs ) + { + string_array const & componentNames = fs.getComponentNames(); + for( size_t ic = 0; ic < componentNames.size(); ic++ ) + { + if( !compMask[ic] ) + { + bcConsistent = false; + GEOS_WARNING( BCMessage::notAppliedOnRegion( ic, componentNames[ic], + regionEntry.first, subRegionEntry.first, setEntry.first, + fields::immiscibleMultiphaseFlow::phaseVolumeFraction::key() ) ); + } + } + } ); + } + } + } + } ); + + return bcConsistent; + } + + void ImmiscibleMultiphaseFlow::applyDirichletBC( real64 const time_n, real64 const dt, DofManager const & dofManager, DomainPartition & domain, CRSMatrixView< real64, globalIndex const > const & localMatrix, arrayView1d< real64 > const & localRhs ) const -{ - GEOS_MARK_FUNCTION; - - FieldSpecificationManager & fsManager = FieldSpecificationManager::getInstance(); - - string const dofKey = dofManager.getKey( viewKeyStruct::elemDofFieldString() ); - - // Step 1: count individual source flux boundary conditions - - stdMap< string, localIndex > bcNameToBcId; - localIndex bcCounter = 0; - + { + GEOS_MARK_FUNCTION; + + // Only validate BC at the beginning of Newton loop + if( m_nonlinearSolverParameters.m_numNewtonIterations == 0 ) + { + bool const bcConsistent = validateDirichletBC( domain, time_n + dt ); + GEOS_ERROR_IF( !bcConsistent, GEOS_FMT( "ImmiscibleMultiphaseFlow {}: inconsistent boundary conditions", getDataContext() ) ); + } + + FieldSpecificationManager & fsManager = FieldSpecificationManager::getInstance(); + + forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &, + MeshLevel & mesh, + string_array const & ) + { + + // 1. Apply pressure Dirichlet BCs, store in a separate field + applyFieldValue< ElementSubRegionBase >( time_n, dt, mesh, bcLogMessage, + fields::flow::pressure::key(), fields::flow::bcPressure::key() ); + // 2. Apply saturation BC (phase volume fraction) and store in a separate field + applyFieldValue< ElementSubRegionBase >( time_n, dt, mesh, bcLogMessage, + fields::immiscibleMultiphaseFlow::phaseVolumeFraction::key(), + fields::immiscibleMultiphaseFlow::bcPhaseVolumeFraction::key() ); + + globalIndex const rankOffset = dofManager.rankOffset(); + string const dofKey = dofManager.getKey( viewKeyStruct::elemDofFieldString() ); + + // 3. Call constitutive update + fsManager.apply< ElementSubRegionBase >( time_n + dt, + mesh, + fields::flow::pressure::key(), + [&] ( FieldSpecificationBase const &, + string const &, + SortedArrayView< localIndex const > const & targetSet, + ElementSubRegionBase & subRegion, + string const & ) + { + + arrayView1d< real64 const > const bcPres = + subRegion.getReference< array1d< real64 > >( fields::flow::bcPressure::key() ); + arrayView2d< real64 const, immiscibleFlow::USD_PHASE > const bcPhaseVolFraction = + subRegion.getReference< array2d< real64, immiscibleFlow::LAYOUT_PHASE > >( + fields::immiscibleMultiphaseFlow::bcPhaseVolumeFraction::key() ); + + arrayView1d< integer const > const ghostRank = + subRegion.getReference< array1d< integer > >( ObjectManagerBase::viewKeyStruct::ghostRankString() ); + arrayView1d< globalIndex const > const dofNumber = + subRegion.getReference< array1d< globalIndex > >( dofKey ); + arrayView1d< real64 const > const pres = + subRegion.getReference< array1d< real64 > >( fields::flow::pressure::key() ); + arrayView2d< real64 const, immiscibleFlow::USD_PHASE > const phaseVolFraction = + subRegion.getReference< array2d< real64, immiscibleFlow::LAYOUT_PHASE > >( + fields::immiscibleMultiphaseFlow::phaseVolumeFraction::key() ); + + integer const numPhase = m_numPhases; + + + forAll< parallelDevicePolicy<> >( targetSet.size(), [=] GEOS_HOST_DEVICE ( localIndex const a ) + { + localIndex const ei = targetSet[a]; + if( ghostRank[ei] >= 0 ) + { + return; + } + + globalIndex const dofIndex = dofNumber[ei]; + localIndex const localRow = dofIndex - rankOffset; + real64 rhsValue; + + // 3.1. Apply pressure value to the matrix/rhs + FieldSpecificationEqual::SpecifyFieldValue( dofIndex, + rankOffset, + localMatrix, + rhsValue, + bcPres[ei], + pres[ei] ); + localRhs[localRow] = rhsValue; + + // 3.2. For each phase, apply target saturation value + for( integer ip = 0; ip < numPhase-1; ++ip ) + { + FieldSpecificationEqual::SpecifyFieldValue( dofIndex + ip + 1, + rankOffset, + localMatrix, + rhsValue, + bcPhaseVolFraction[ei][ip], + phaseVolFraction[ei][ip] ); + localRhs[localRow + ip + 1] = rhsValue; + } + } ); + } ); + } ); + } + + void ImmiscibleMultiphaseFlow::applySourceFluxBC( real64 const time, + real64 const dt, + DofManager const & dofManager, + DomainPartition & domain, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) const + { + GEOS_MARK_FUNCTION; + + FieldSpecificationManager & fsManager = FieldSpecificationManager::getInstance(); + + string const dofKey = dofManager.getKey( viewKeyStruct::elemDofFieldString() ); + + // Step 1: count individual source flux boundary conditions + + stdMap< string, localIndex > bcNameToBcId; + localIndex bcCounter = 0; + fsManager.forSubGroups< SourceFluxBoundaryCondition >( [&] ( SourceFluxBoundaryCondition const & bc ) { // collect all the bc names to idx bcNameToBcId.insert( {bc.getName(), bcCounter} ); bcCounter++; } ); - - if( bcCounter == 0 ) - { - return; - } - - // Step 2: count the set size for each source flux (each source flux may have multiple target sets) - - array1d< globalIndex > bcAllSetsSize( bcNameToBcId.size() ); - - computeSourceFluxSizeScalingFactor( time, - dt, - domain, - bcNameToBcId, - bcAllSetsSize.toView() ); - - // Step 3: we are ready to impose the boundary condition, normalized by the set size - - forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &, - MeshLevel & mesh, - string_array const & ) - { - - fsManager.apply< ElementSubRegionBase, - SourceFluxBoundaryCondition >( time + dt, - mesh, - SourceFluxBoundaryCondition::catalogName(), - [&]( SourceFluxBoundaryCondition const & fs, - string const & setName, - SortedArrayView< localIndex const > const & targetSet, - ElementSubRegionBase & subRegion, - string const & ) - { - if( fs.getLogLevel() >= 1 && m_nonlinearSolverParameters.m_numNewtonIterations == 0 ) - { - globalIndex const numTargetElems = MpiWrapper::sum< globalIndex >( targetSet.size() ); - GEOS_LOG_RANK_0( GEOS_FMT( bcLogMessage, - getName(), time+dt, fs.getCatalogName(), fs.getName(), - setName, subRegion.getName(), fs.getScale(), numTargetElems ) ); - } - - if( targetSet.size() == 0 ) - { - return; - } - if( !subRegion.hasWrapper( dofKey ) ) - { - if( fs.getLogLevel() >= 1 ) - { - GEOS_LOG_RANK( GEOS_FMT( "{}: trying to apply SourceFlux, but its targetSet named '{}' intersects with non-simulated region named '{}'.", - getDataContext(), setName, subRegion.getName() ) ); - } - return; - } - - arrayView1d< globalIndex const > const dofNumber = subRegion.getReference< array1d< globalIndex > >( dofKey ); - arrayView1d< integer const > const ghostRank = subRegion.ghostRank(); - - // Step 3.1: get the values of the source boundary condition that need to be added to the rhs - - array1d< globalIndex > dofArray( targetSet.size() ); - array1d< real64 > rhsContributionArray( targetSet.size() ); - arrayView1d< real64 > rhsContributionArrayView = rhsContributionArray.toView(); - localIndex const rankOffset = dofManager.rankOffset(); - - RAJA::ReduceSum< parallelDeviceReduce, real64 > massProd( 0.0 ); - - // note that the dofArray will not be used after this step (simpler to use dofNumber instead) - fs.computeRhsContribution< FieldSpecificationAdd, - parallelDevicePolicy<> >( targetSet.toViewConst(), - time + dt, - dt, - subRegion, - dofNumber, - rankOffset, - localMatrix, - dofArray.toView(), - rhsContributionArrayView, - [] GEOS_HOST_DEVICE ( localIndex const ) - { - return 0.0; - } ); - - // Step 3.2: we are ready to add the right-hand side contributions, taking into account our equation layout - - // get the normalizer - real64 const sizeScalingFactor = bcAllSetsSize[bcNameToBcId.at( fs.getName())]; - integer const fluidPhaseId = fs.getComponent(); - integer const numFluidPhases = m_numPhases; - integer useTotalMassEquation = m_useTotalMassEquation; - forAll< parallelDevicePolicy<> >( targetSet.size(), [sizeScalingFactor, - targetSet, - rankOffset, - ghostRank, - fluidPhaseId, - numFluidPhases, - useTotalMassEquation, - dofNumber, - rhsContributionArrayView, - localRhs, - massProd] GEOS_HOST_DEVICE ( localIndex const a ) - { - // we need to filter out ghosts here, because targetSet may contain them - localIndex const ei = targetSet[a]; - if( ghostRank[ei] >= 0 ) - { - return; - } - - real64 const rhsValue = rhsContributionArrayView[a] / sizeScalingFactor; // scale the contribution by the sizeScalingFactor here! - massProd += rhsValue; - if( useTotalMassEquation > 0 ) - { - // for all "fluid components", we add the value to the total mass balance equation - globalIndex const totalMassBalanceRow = dofNumber[ei] - rankOffset; - localRhs[totalMassBalanceRow] += rhsValue; - if( fluidPhaseId < numFluidPhases - 1 ) - { - globalIndex const compMassBalanceRow = totalMassBalanceRow + fluidPhaseId + 1; // component mass bal equations are shifted - localRhs[compMassBalanceRow] += rhsValue; - } - } - else - { - globalIndex const compMassBalanceRow = dofNumber[ei] - rankOffset + fluidPhaseId; - localRhs[compMassBalanceRow] += rhsValue; - } - } ); - - SourceFluxStatsAggregator::forAllFluxStatWrappers( subRegion, fs.getName(), - [&]( SourceFluxStatsAggregator::WrappedStats & wrapper ) - { - // set the new sub-region statistics for this timestep - array1d< real64 > massProdArr{ m_numPhases }; - massProdArr[fluidPhaseId] = massProd.get(); - wrapper.gatherTimeStepStats( time, dt, massProdArr.toViewConst(), targetSet.size() ); - } ); - } ); - } ); -} - -real64 ImmiscibleMultiphaseFlow::calculateResidualNorm( real64 const & GEOS_UNUSED_PARAM( time_n ), - real64 const & GEOS_UNUSED_PARAM( dt ), - DomainPartition const & domain, - DofManager const & dofManager, - arrayView1d< real64 const > const & localRhs ) -{ - GEOS_MARK_FUNCTION; - array1d< real64 > localResidualNorm; - array1d< real64 > localResidualNormalizer; - localResidualNorm.resize( numNorm ); - localResidualNormalizer.resize( numNorm ); - - physicsSolverBaseKernels::NormType const normType = getNonlinearSolverParameters().normType(); - - globalIndex const rankOffset = dofManager.rankOffset(); - string const dofKey = dofManager.getKey( viewKeyStruct::elemDofFieldString() ); - - forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &, - MeshLevel const & mesh, - string_array const & regionNames ) - { - mesh.getElemManager().forElementSubRegions( regionNames, - [&]( localIndex const, - ElementSubRegionBase const & subRegion ) - { - real64 subRegionResidualNorm[numNorm]{}; - real64 subRegionResidualNormalizer[numNorm]{}; - - string const & solidName = subRegion.getReference< string >( viewKeyStruct::solidNamesString() ); - CoupledSolidBase const & solid = getConstitutiveModel< CoupledSolidBase >( subRegion, solidName ); - - // step 1: compute the norm in the subRegion - - real64 subRegionFlowResidualNorm[1]{}; - real64 subRegionFlowResidualNormalizer[1]{}; - - immiscibleMultiphaseKernels:: - ResidualNormKernelFactory::createAndLaunch< parallelDevicePolicy<> >( normType, - 2, - rankOffset, - dofKey, - localRhs, - subRegion, - solid, - m_nonlinearSolverParameters.m_minNormalizer, - subRegionFlowResidualNorm, - subRegionFlowResidualNormalizer ); - subRegionResidualNorm[0] = subRegionFlowResidualNorm[0]; - subRegionResidualNormalizer[0] = subRegionFlowResidualNormalizer[0]; - - // step 2: first reduction across meshBodies/regions/subRegions - if( normType == physicsSolverBaseKernels::NormType::Linf ) - { - physicsSolverBaseKernels::LinfResidualNormHelper:: - updateLocalNorm< numNorm >( subRegionResidualNorm, localResidualNorm ); - } - else - { - physicsSolverBaseKernels::L2ResidualNormHelper:: - updateLocalNorm< numNorm >( subRegionResidualNorm, subRegionResidualNormalizer, localResidualNorm, localResidualNormalizer ); - } - } ); - } ); - - real64 residualNorm = 0.0; - residualNorm = localResidualNorm[0]; - if( normType == physicsSolverBaseKernels::NormType::Linf ) - { - physicsSolverBaseKernels::LinfResidualNormHelper::computeGlobalNorm( localResidualNorm[0], residualNorm ); - } - else - { - physicsSolverBaseKernels::L2ResidualNormHelper::computeGlobalNorm( localResidualNorm[0], localResidualNormalizer[0], residualNorm ); - } - + + if( bcCounter == 0 ) + { + return; + } + + // Step 2: count the set size for each source flux (each source flux may have multiple target sets) + + array1d< globalIndex > bcAllSetsSize( bcNameToBcId.size() ); + + computeSourceFluxSizeScalingFactor( time, + dt, + domain, + bcNameToBcId, + bcAllSetsSize.toView() ); + + // Step 3: we are ready to impose the boundary condition, normalized by the set size + + forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &, + MeshLevel & mesh, + string_array const & ) + { + + fsManager.apply< ElementSubRegionBase, + SourceFluxBoundaryCondition >( time + dt, + mesh, + SourceFluxBoundaryCondition::catalogName(), + [&]( SourceFluxBoundaryCondition const & fs, + string const & setName, + SortedArrayView< localIndex const > const & targetSet, + ElementSubRegionBase & subRegion, + string const & ) + { + if( fs.getLogLevel() >= 1 && m_nonlinearSolverParameters.m_numNewtonIterations == 0 ) + { + globalIndex const numTargetElems = MpiWrapper::sum< globalIndex >( targetSet.size() ); + GEOS_LOG_RANK_0( GEOS_FMT( bcLogMessage, + getName(), time+dt, fs.getCatalogName(), fs.getName(), + setName, subRegion.getName(), fs.getScale(), numTargetElems ) ); + } + + if( targetSet.size() == 0 ) + { + return; + } + if( !subRegion.hasWrapper( dofKey ) ) + { + if( fs.getLogLevel() >= 1 ) + { + GEOS_LOG_RANK( GEOS_FMT( "{}: trying to apply SourceFlux, but its targetSet named '{}' intersects with non-simulated region named '{}'.", + getDataContext(), setName, subRegion.getName() ) ); + } + return; + } + + arrayView1d< globalIndex const > const dofNumber = subRegion.getReference< array1d< globalIndex > >( dofKey ); + arrayView1d< integer const > const ghostRank = subRegion.ghostRank(); + + // Step 3.1: get the values of the source boundary condition that need to be added to the rhs + + array1d< globalIndex > dofArray( targetSet.size() ); + array1d< real64 > rhsContributionArray( targetSet.size() ); + arrayView1d< real64 > rhsContributionArrayView = rhsContributionArray.toView(); + localIndex const rankOffset = dofManager.rankOffset(); + + RAJA::ReduceSum< parallelDeviceReduce, real64 > massProd( 0.0 ); + + // note that the dofArray will not be used after this step (simpler to use dofNumber instead) + fs.computeRhsContribution< FieldSpecificationAdd, + parallelDevicePolicy<> >( targetSet.toViewConst(), + time + dt, + dt, + subRegion, + dofNumber, + rankOffset, + localMatrix, + dofArray.toView(), + rhsContributionArrayView, + [] GEOS_HOST_DEVICE ( localIndex const ) + { + return 0.0; + } ); + + // Step 3.2: we are ready to add the right-hand side contributions, taking into account our equation layout + + // get the normalizer + real64 const sizeScalingFactor = bcAllSetsSize[bcNameToBcId.at( fs.getName())]; + integer const fluidPhaseId = fs.getComponent(); + integer const numFluidPhases = m_numPhases; + integer useTotalMassEquation = m_useTotalMassEquation; + forAll< parallelDevicePolicy<> >( targetSet.size(), [sizeScalingFactor, + targetSet, + rankOffset, + ghostRank, + fluidPhaseId, + numFluidPhases, + useTotalMassEquation, + dofNumber, + rhsContributionArrayView, + localRhs, + massProd] GEOS_HOST_DEVICE ( localIndex const a ) + { + // we need to filter out ghosts here, because targetSet may contain them + localIndex const ei = targetSet[a]; + if( ghostRank[ei] >= 0 ) + { + return; + } + + real64 const rhsValue = rhsContributionArrayView[a] / sizeScalingFactor; // scale the contribution by the sizeScalingFactor here! + massProd += rhsValue; + if( useTotalMassEquation > 0 ) + { + // for all "fluid components", we add the value to the total mass balance equation + globalIndex const totalMassBalanceRow = dofNumber[ei] - rankOffset; + localRhs[totalMassBalanceRow] += rhsValue; + if( fluidPhaseId < numFluidPhases - 1 ) + { + globalIndex const compMassBalanceRow = totalMassBalanceRow + fluidPhaseId + 1; // component mass bal equations are shifted + localRhs[compMassBalanceRow] += rhsValue; + } + } + else + { + globalIndex const compMassBalanceRow = dofNumber[ei] - rankOffset + fluidPhaseId; + localRhs[compMassBalanceRow] += rhsValue; + } + } ); + + SourceFluxStatsAggregator::forAllFluxStatWrappers( subRegion, fs.getName(), + [&]( SourceFluxStatsAggregator::WrappedStats & wrapper ) + { + // set the new sub-region statistics for this timestep + array1d< real64 > massProdArr{ m_numPhases }; + massProdArr[fluidPhaseId] = massProd.get(); + wrapper.gatherTimeStepStats( time, dt, massProdArr.toViewConst(), targetSet.size() ); + } ); + } ); + } ); + } + + real64 ImmiscibleMultiphaseFlow::calculateResidualNorm( real64 const & GEOS_UNUSED_PARAM( time_n ), + real64 const & GEOS_UNUSED_PARAM( dt ), + DomainPartition const & domain, + DofManager const & dofManager, + arrayView1d< real64 const > const & localRhs ) + { + GEOS_MARK_FUNCTION; + array1d< real64 > localResidualNorm; + array1d< real64 > localResidualNormalizer; + localResidualNorm.resize( numNorm ); + localResidualNormalizer.resize( numNorm ); + + physicsSolverBaseKernels::NormType const normType = getNonlinearSolverParameters().normType(); + + globalIndex const rankOffset = dofManager.rankOffset(); + string const dofKey = dofManager.getKey( viewKeyStruct::elemDofFieldString() ); + + forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &, + MeshLevel const & mesh, + string_array const & regionNames ) + { + mesh.getElemManager().forElementSubRegions( regionNames, + [&]( localIndex const, + ElementSubRegionBase const & subRegion ) + { + real64 subRegionResidualNorm[numNorm]{}; + real64 subRegionResidualNormalizer[numNorm]{}; + + string const & solidName = subRegion.getReference< string >( viewKeyStruct::solidNamesString() ); + CoupledSolidBase const & solid = getConstitutiveModel< CoupledSolidBase >( subRegion, solidName ); + + // step 1: compute the norm in the subRegion + + real64 subRegionFlowResidualNorm[1]{}; + real64 subRegionFlowResidualNormalizer[1]{}; + + immiscibleMultiphaseKernels:: + ResidualNormKernelFactory::createAndLaunch< parallelDevicePolicy<> >( normType, + 2, + rankOffset, + dofKey, + localRhs, + subRegion, + solid, + m_nonlinearSolverParameters.m_minNormalizer, + subRegionFlowResidualNorm, + subRegionFlowResidualNormalizer ); + subRegionResidualNorm[0] = subRegionFlowResidualNorm[0]; + subRegionResidualNormalizer[0] = subRegionFlowResidualNormalizer[0]; + + // step 2: first reduction across meshBodies/regions/subRegions + if( normType == physicsSolverBaseKernels::NormType::Linf ) + { + physicsSolverBaseKernels::LinfResidualNormHelper:: + updateLocalNorm< numNorm >( subRegionResidualNorm, localResidualNorm ); + } + else + { + physicsSolverBaseKernels::L2ResidualNormHelper:: + updateLocalNorm< numNorm >( subRegionResidualNorm, subRegionResidualNormalizer, localResidualNorm, localResidualNormalizer ); + } + } ); + } ); + + real64 residualNorm = 0.0; + residualNorm = localResidualNorm[0]; + if( normType == physicsSolverBaseKernels::NormType::Linf ) + { + physicsSolverBaseKernels::LinfResidualNormHelper::computeGlobalNorm( localResidualNorm[0], residualNorm ); + } + else + { + physicsSolverBaseKernels::L2ResidualNormHelper::computeGlobalNorm( localResidualNorm[0], localResidualNormalizer[0], residualNorm ); + } + GEOS_LOG_LEVEL_RANK_0_NLR( logInfo::ResidualNorm, GEOS_FMT( " ( R{} ) = ( {:4.2e} )", coupledSolverAttributePrefix(), residualNorm )) - - getConvergenceStats().setResidualValue( GEOS_FMT( "R{}", coupledSolverAttributePrefix()), residualNorm ); - - return residualNorm; -} - -void ImmiscibleMultiphaseFlow::applySystemSolution( DofManager const & dofManager, - arrayView1d< real64 const > const & localSolution, - real64 const scalingFactor, - real64 const dt, - DomainPartition & domain ) -{ - GEOS_UNUSED_VAR( dt ); - - DofManager::CompMask pressureMask( m_numDofPerCell, 0, 1 ); - - // 1. apply the pressure update - dofManager.addVectorToField( localSolution, - viewKeyStruct::elemDofFieldString(), - fields::flow::pressure::key(), - scalingFactor, - pressureMask ); - - // 2. apply the phaseVolumeFraction update - dofManager.addVectorToField( localSolution, - viewKeyStruct::elemDofFieldString(), - fields::immiscibleMultiphaseFlow::phaseVolumeFraction::key(), - scalingFactor, - ~pressureMask ); - - // 3. synchronize - forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &, + + return residualNorm; + } + + void ImmiscibleMultiphaseFlow::applySystemSolution( DofManager const & dofManager, + arrayView1d< real64 const > const & localSolution, + real64 const scalingFactor, + real64 const dt, + DomainPartition & domain ) + { + GEOS_UNUSED_VAR( dt ); + + DofManager::CompMask pressureMask( m_numDofPerCell, 0, 1 ); + + // 1. apply the pressure update + dofManager.addVectorToField( localSolution, + viewKeyStruct::elemDofFieldString(), + fields::flow::pressure::key(), + scalingFactor, + pressureMask ); + + // 2. apply the phaseVolumeFraction update + dofManager.addVectorToField( localSolution, + viewKeyStruct::elemDofFieldString(), + fields::immiscibleMultiphaseFlow::phaseVolumeFraction::key(), + scalingFactor, + ~pressureMask ); + + // 3. synchronize + forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &, + MeshLevel & mesh, + string_array const & regionNames ) + { + std::vector< string > fields{ fields::flow::pressure::key(), fields::immiscibleMultiphaseFlow::phaseVolumeFraction::key() }; + + FieldIdentifiers fieldsToBeSync; + fieldsToBeSync.addElementFields( fields, regionNames ); + + CommunicationTools::getInstance().synchronizeFields( fieldsToBeSync, mesh, domain.getNeighbors(), true ); + } ); + } + + + void ImmiscibleMultiphaseFlow::updateVolumeConstraint( ElementSubRegionBase & subRegion ) const + { + GEOS_MARK_FUNCTION; + + arrayView2d< real64, immiscibleFlow::USD_PHASE > const phaseVolumeFraction = subRegion.getField< fields::immiscibleMultiphaseFlow::phaseVolumeFraction >(); + + forAll< parallelDevicePolicy<> >( subRegion.size(), [=] GEOS_HOST_DEVICE ( localIndex const ei ) + { + phaseVolumeFraction[ei][0] = fmin( 1.0, fmax( phaseVolumeFraction[ei][0], 0.0 )); ; + phaseVolumeFraction[ei][1] = 1.0 - phaseVolumeFraction[ei][0]; + } ); + } + + + void ImmiscibleMultiphaseFlow::resetStateToBeginningOfStep( DomainPartition & domain ) + { + GEOS_MARK_FUNCTION; + + forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &, MeshLevel & mesh, string_array const & regionNames ) - { - stdVector< string > fields{ fields::flow::pressure::key(), fields::immiscibleMultiphaseFlow::phaseVolumeFraction::key() }; - - FieldIdentifiers fieldsToBeSync; - fieldsToBeSync.addElementFields( fields, regionNames ); - - CommunicationTools::getInstance().synchronizeFields( fieldsToBeSync, mesh, domain.getNeighbors(), true ); - } ); -} - - -void ImmiscibleMultiphaseFlow::updateVolumeConstraint( ElementSubRegionBase & subRegion ) const -{ - GEOS_MARK_FUNCTION; - - arrayView2d< real64, immiscibleFlow::USD_PHASE > const phaseVolumeFraction = subRegion.getField< fields::immiscibleMultiphaseFlow::phaseVolumeFraction >(); - - forAll< parallelDevicePolicy<> >( subRegion.size(), [=] GEOS_HOST_DEVICE ( localIndex const ei ) - { - phaseVolumeFraction[ei][1] = 1.0 - phaseVolumeFraction[ei][0]; - } ); -} - - -void ImmiscibleMultiphaseFlow::resetStateToBeginningOfStep( DomainPartition & domain ) -{ - GEOS_MARK_FUNCTION; - - forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &, - MeshLevel & mesh, - string_array const & regionNames ) - { - mesh.getElemManager().forElementSubRegions< CellElementSubRegion, - SurfaceElementSubRegion >( regionNames, - [&]( localIndex const, - auto & subRegion ) - { - arrayView1d< real64 > const & pres = - subRegion.template getField< fields::flow::pressure >(); - arrayView1d< real64 const > const & pres_n = - subRegion.template getField< fields::flow::pressure_n >(); - pres.setValues< parallelDevicePolicy<> >( pres_n ); - - // after the update, save the new saturation - arrayView2d< real64 const, immiscibleFlow::USD_PHASE > const phaseVolFrac_n = - subRegion.template getField< fields::immiscibleMultiphaseFlow::phaseVolumeFraction_n >(); - - arrayView2d< real64, immiscibleFlow::USD_PHASE > const phaseVolFrac = - subRegion.template getField< fields::immiscibleMultiphaseFlow::phaseVolumeFraction >(); - phaseVolFrac.setValues< parallelDevicePolicy<> >( phaseVolFrac_n ); - - arrayView2d< real64 const, immiscibleFlow::USD_PHASE > const & phaseMass_n = - subRegion.template getField< fields::immiscibleMultiphaseFlow::phaseMass_n >(); - - arrayView2d< real64, immiscibleFlow::USD_PHASE > const & phaseMass = - subRegion.template getField< fields::immiscibleMultiphaseFlow::phaseMass >(); - phaseMass.setValues< parallelDevicePolicy<> >( phaseMass_n ); - - if( m_isThermal ) - { - arrayView1d< real64 > const & temp = - subRegion.template getField< fields::flow::temperature >(); - arrayView1d< real64 const > const & temp_n = - subRegion.template getField< fields::flow::temperature_n >(); - temp.setValues< parallelDevicePolicy<> >( temp_n ); - } - - // update porosity, permeability - updatePorosityAndPermeability( subRegion ); - // update all fluid properties - updateFluidState( subRegion ); - } ); - } ); -} - -void ImmiscibleMultiphaseFlow::implicitStepComplete( real64 const & time, - real64 const & dt, - DomainPartition & domain ) -{ - // Step 1: save the converged aquifer state - // note: we have to save the aquifer state **before** updating the pressure, - // otherwise the aquifer flux is saved with the wrong pressure time level - saveAquiferConvergedState( time, dt, domain ); - - forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &, - MeshLevel & mesh, - string_array const & regionNames ) - { - mesh.getElemManager().forElementSubRegions( regionNames, - [&]( localIndex const, - ElementSubRegionBase & subRegion ) - { - // Step 3: save the converged solid state - string const & solidName = subRegion.getReference< string >( viewKeyStruct::solidNamesString() ); - CoupledSolidBase const & porousMaterial = getConstitutiveModel< CoupledSolidBase >( subRegion, solidName ); - if( m_keepVariablesConstantDuringInitStep ) - { - porousMaterial.ignoreConvergedState(); // newPorosity <- porosity_n - } - else - { - porousMaterial.saveConvergedState(); // porosity_n <- porosity - } - - // Step 4: save converged state for the relperm model to handle hysteresis - arrayView2d< real64 const, immiscibleFlow::USD_PHASE > const phaseVolFrac = - subRegion.getField< fields::immiscibleMultiphaseFlow::phaseVolumeFraction >(); - string const & relPermName = subRegion.getReference< string >( viewKeyStruct::relPermNamesString() ); - RelativePermeabilityBase const & relPermMaterial = - getConstitutiveModel< RelativePermeabilityBase >( subRegion, relPermName ); - relPermMaterial.saveConvergedPhaseVolFractionState( phaseVolFrac ); - - // Step 5: if capillary pressure is supported, send the converged porosity and permeability to the capillary pressure model - // note: this is needed when the capillary pressure depends on porosity and permeability (Leverett J-function for instance) - if( m_hasCapPressure ) - { + { + mesh.getElemManager().forElementSubRegions< CellElementSubRegion, + SurfaceElementSubRegion >( regionNames, + [&]( localIndex const, + auto & subRegion ) + { + arrayView1d< real64 > const & pres = + subRegion.template getField< fields::flow::pressure >(); + arrayView1d< real64 const > const & pres_n = + subRegion.template getField< fields::flow::pressure_n >(); + pres.setValues< parallelDevicePolicy<> >( pres_n ); + + // after the update, save the new saturation + arrayView2d< real64 const, immiscibleFlow::USD_PHASE > const phaseVolFrac_n = + subRegion.template getField< fields::immiscibleMultiphaseFlow::phaseVolumeFraction_n >(); + + arrayView2d< real64, immiscibleFlow::USD_PHASE > const phaseVolFrac = + subRegion.template getField< fields::immiscibleMultiphaseFlow::phaseVolumeFraction >(); + phaseVolFrac.setValues< parallelDevicePolicy<> >( phaseVolFrac_n ); + + arrayView2d< real64 const, immiscibleFlow::USD_PHASE > const & phaseMass_n = + subRegion.template getField< fields::immiscibleMultiphaseFlow::phaseMass_n >(); + + arrayView2d< real64, immiscibleFlow::USD_PHASE > const & phaseMass = + subRegion.template getField< fields::immiscibleMultiphaseFlow::phaseMass >(); + phaseMass.setValues< parallelDevicePolicy<> >( phaseMass_n ); + + if( m_isThermal ) + { + arrayView1d< real64 > const & temp = + subRegion.template getField< fields::flow::temperature >(); + arrayView1d< real64 const > const & temp_n = + subRegion.template getField< fields::flow::temperature_n >(); + temp.setValues< parallelDevicePolicy<> >( temp_n ); + } + + // update porosity, permeability + updatePorosityAndPermeability( subRegion ); + // update all fluid properties + updateFluidState( subRegion ); + } ); + } ); + } + + void ImmiscibleMultiphaseFlow::implicitStepComplete( real64 const & time, + real64 const & dt, + DomainPartition & domain ) + { + // Step 1: save the converged aquifer state + // note: we have to save the aquifer state **before** updating the pressure, + // otherwise the aquifer flux is saved with the wrong pressure time level + saveAquiferConvergedState( time, dt, domain ); + + forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &, + MeshLevel & mesh, + string_array const & regionNames ) + { + mesh.getElemManager().forElementSubRegions( regionNames, + [&]( localIndex const, + ElementSubRegionBase & subRegion ) + { + // Step 3: save the converged solid state + string const & solidName = subRegion.getReference< string >( viewKeyStruct::solidNamesString() ); + CoupledSolidBase const & porousMaterial = getConstitutiveModel< CoupledSolidBase >( subRegion, solidName ); + if( m_keepVariablesConstantDuringInitStep ) + { + porousMaterial.ignoreConvergedState(); // newPorosity <- porosity_n + } + else + { + porousMaterial.saveConvergedState(); // porosity_n <- porosity + } + + // Step 4: save converged state for the relperm model to handle hysteresis + arrayView2d< real64 const, immiscibleFlow::USD_PHASE > const phaseVolFrac = + subRegion.getField< fields::immiscibleMultiphaseFlow::phaseVolumeFraction >(); + string const & relPermName = subRegion.getReference< string >( viewKeyStruct::relPermNamesString() ); + RelativePermeabilityBase const & relPermMaterial = + getConstitutiveModel< RelativePermeabilityBase >( subRegion, relPermName ); + relPermMaterial.saveConvergedPhaseVolFractionState( phaseVolFrac ); + + // Step 5: if capillary pressure is supported, send the converged porosity and permeability to the capillary pressure model + // note: this is needed when the capillary pressure depends on porosity and permeability (Leverett J-function for instance) + if( m_hasCapPressure ) + { arrayView2d< real64 const > const porosity = porousMaterial.getPorosity(); string const & permName = subRegion.getReference< string >( viewKeyStruct::permeabilityNamesString() ); @@ -1284,127 +1552,128 @@ void ImmiscibleMultiphaseFlow::implicitStepComplete( real64 const & time, CapillaryPressureBase const & capPressureMaterial = getConstitutiveModel< CapillaryPressureBase >( subRegion, capPressName ); capPressureMaterial.saveConvergedRockState( porosity, permeability ); - } - } ); - } ); - -} - -void ImmiscibleMultiphaseFlow::saveConvergedState( ElementSubRegionBase & subRegion ) const -{ - FlowSolverBase::saveConvergedState( subRegion ); - - arrayView2d< real64 const, immiscibleFlow::USD_PHASE > const phaseVolFrac = - subRegion.getField< fields::immiscibleMultiphaseFlow::phaseVolumeFraction >(); - arrayView2d< real64, immiscibleFlow::USD_PHASE > const phaseVolFrac_n = - subRegion.getField< fields::immiscibleMultiphaseFlow::phaseVolumeFraction_n >(); - phaseVolFrac_n.setValues< parallelDevicePolicy<> >( phaseVolFrac ); - - arrayView2d< real64 const, immiscibleFlow::USD_PHASE > const phaseMass = - subRegion.getField< fields::immiscibleMultiphaseFlow::phaseMass >(); - arrayView2d< real64, immiscibleFlow::USD_PHASE > const phaseMass_n = - subRegion.getField< fields::immiscibleMultiphaseFlow::phaseMass_n >(); - phaseMass_n.setValues< parallelDevicePolicy<> >( phaseMass ); - -} - -void ImmiscibleMultiphaseFlow::updateState( DomainPartition & domain ) -{ - GEOS_MARK_FUNCTION; - - forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &, - MeshLevel & mesh, - string_array const & regionNames ) - { - mesh.getElemManager().forElementSubRegions< CellElementSubRegion, - SurfaceElementSubRegion >( regionNames, [&]( localIndex const, - auto & subRegion ) - { - // update porosity, permeability, and solid internal energy - updatePorosityAndPermeability( subRegion ); - // update all fluid properties - updateVolumeConstraint( subRegion ); - updateFluidState( subRegion ); - } ); - } ); -} - -real64 ImmiscibleMultiphaseFlow::setNextDtBasedOnStateChange( real64 const & currentDt, - DomainPartition & domain ) -{ - if( m_targetRelativePresChange >= 1.0 && - m_targetPhaseVolFracChange >= 1.0 ) - { - return LvArray::NumericLimits< real64 >::max; - } - - real64 maxRelativePresChange = 0.0; - real64 maxAbsolutePhaseVolFracChange = 0.0; - - integer const numPhase = m_numPhases; - - forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &, - MeshLevel & mesh, - string_array const & regionNames ) - { - mesh.getElemManager().forElementSubRegions( regionNames, - [&]( localIndex const, - ElementSubRegionBase & subRegion ) - { - arrayView1d< integer const > const ghostRank = subRegion.ghostRank(); - - arrayView1d< real64 const > const pres = subRegion.getField< fields::flow::pressure >(); - arrayView1d< real64 const > const pres_n = subRegion.getField< fields::flow::pressure_n >(); - arrayView2d< real64 const, immiscibleFlow::USD_PHASE > const phaseVolFrac = - subRegion.getField< fields::immiscibleMultiphaseFlow::phaseVolumeFraction >(); - arrayView2d< real64, immiscibleFlow::USD_PHASE > const phaseVolFrac_n = - subRegion.getField< fields::immiscibleMultiphaseFlow::phaseVolumeFraction_n >(); - - RAJA::ReduceMax< parallelDeviceReduce, real64 > subRegionMaxPresChange( 0.0 ); - RAJA::ReduceMax< parallelDeviceReduce, real64 > subRegionMaxPhaseVolFracChange( 0.0 ); - - forAll< parallelDevicePolicy<> >( subRegion.size(), [=] GEOS_HOST_DEVICE ( localIndex const ei ) - { - if( ghostRank[ei] < 0 ) - { - // switch from relative to absolute when values less than 1 - subRegionMaxPresChange.max( LvArray::math::abs( pres[ei] - pres_n[ei] ) / LvArray::math::max( LvArray::math::abs( pres_n[ei] ), 1.0 ) ); - for( integer ip = 0; ip < numPhase; ++ip ) - { - subRegionMaxPhaseVolFracChange.max( LvArray::math::abs( phaseVolFrac[ei][ip] - phaseVolFrac_n[ei][ip] ) ); - } - } - } ); - - maxRelativePresChange = LvArray::math::max( maxRelativePresChange, subRegionMaxPresChange.get() ); - maxAbsolutePhaseVolFracChange = LvArray::math::max( maxAbsolutePhaseVolFracChange, subRegionMaxPhaseVolFracChange.get() ); - - } ); - } ); - - maxRelativePresChange = MpiWrapper::max( maxRelativePresChange ); - maxAbsolutePhaseVolFracChange = MpiWrapper::max( maxAbsolutePhaseVolFracChange ); - - GEOS_LOG_LEVEL_RANK_0( logInfo::TimeStep, GEOS_FMT( "{}: max relative pressure change during time step = {} %", - getName(), GEOS_FMT( "{:.{}f}", 100*maxRelativePresChange, 3 ) ) ); - GEOS_LOG_LEVEL_RANK_0( logInfo::TimeStep, GEOS_FMT( "{}: max absolute phase volume fraction change during time step = {}", - getName(), GEOS_FMT( "{:.{}f}", maxAbsolutePhaseVolFracChange, 3 ) ) ); - - real64 const eps = LvArray::NumericLimits< real64 >::epsilon; - - real64 const nextDtPressure = currentDt * ( 1.0 + m_solutionChangeScalingFactor ) * m_targetRelativePresChange - / std::max( eps, maxRelativePresChange + m_solutionChangeScalingFactor * m_targetRelativePresChange ); - if( m_nonlinearSolverParameters.getLogLevel() > 0 ) - GEOS_LOG_RANK_0( GEOS_FMT( "{}: next time step based on pressure change = {}", getName(), nextDtPressure )); - real64 const nextDtPhaseVolFrac = currentDt * ( 1.0 + m_solutionChangeScalingFactor ) * m_targetPhaseVolFracChange - / std::max( eps, maxAbsolutePhaseVolFracChange + m_solutionChangeScalingFactor * m_targetPhaseVolFracChange ); - if( m_nonlinearSolverParameters.getLogLevel() > 0 ) - GEOS_LOG_RANK_0( GEOS_FMT( "{}: next time step based on phase volume fraction change = {}", getName(), nextDtPhaseVolFrac )); - - return std::min( nextDtPressure, nextDtPhaseVolFrac ); - -} - -REGISTER_CATALOG_ENTRY( PhysicsSolverBase, ImmiscibleMultiphaseFlow, string const &, Group * const ) - -} // namespace geos + capPressureMaterial.saveConvergedPhaseVolFractionState( phaseVolFrac ); + } + } ); + } ); + } + + void ImmiscibleMultiphaseFlow::saveConvergedState( ElementSubRegionBase & subRegion ) const + { + FlowSolverBase::saveConvergedState( subRegion ); + + arrayView2d< real64 const, immiscibleFlow::USD_PHASE > const phaseVolFrac = + subRegion.getField< fields::immiscibleMultiphaseFlow::phaseVolumeFraction >(); + arrayView2d< real64, immiscibleFlow::USD_PHASE > const phaseVolFrac_n = + subRegion.getField< fields::immiscibleMultiphaseFlow::phaseVolumeFraction_n >(); + phaseVolFrac_n.setValues< parallelDevicePolicy<> >( phaseVolFrac ); + + arrayView2d< real64 const, immiscibleFlow::USD_PHASE > const phaseMass = + subRegion.getField< fields::immiscibleMultiphaseFlow::phaseMass >(); + arrayView2d< real64, immiscibleFlow::USD_PHASE > const phaseMass_n = + subRegion.getField< fields::immiscibleMultiphaseFlow::phaseMass_n >(); + phaseMass_n.setValues< parallelDevicePolicy<> >( phaseMass ); + + } + + void ImmiscibleMultiphaseFlow::updateState( DomainPartition & domain ) + { + GEOS_MARK_FUNCTION; + + forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &, + MeshLevel & mesh, + string_array const & regionNames ) + { + mesh.getElemManager().forElementSubRegions< CellElementSubRegion, + SurfaceElementSubRegion >( regionNames, [&]( localIndex const, + auto & subRegion ) + { + // update porosity, permeability, and solid internal energy + updatePorosityAndPermeability( subRegion ); + // update all fluid properties + updateVolumeConstraint( subRegion ); + updateFluidState( subRegion ); + } ); + } ); + } + + real64 ImmiscibleMultiphaseFlow::setNextDtBasedOnStateChange( real64 const & currentDt, + DomainPartition & domain ) + { + if( m_targetRelativePresChange >= 1.0 && + m_targetPhaseVolFracChange >= 1.0 ) + { + return LvArray::NumericLimits< real64 >::max; + } + + real64 maxRelativePresChange = 0.0; + real64 maxAbsolutePhaseVolFracChange = 0.0; + + integer const numPhase = m_numPhases; + + forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &, + MeshLevel & mesh, + string_array const & regionNames ) + { + mesh.getElemManager().forElementSubRegions( regionNames, + [&]( localIndex const, + ElementSubRegionBase & subRegion ) + { + arrayView1d< integer const > const ghostRank = subRegion.ghostRank(); + + arrayView1d< real64 const > const pres = subRegion.getField< fields::flow::pressure >(); + arrayView1d< real64 const > const pres_n = subRegion.getField< fields::flow::pressure_n >(); + arrayView2d< real64 const, immiscibleFlow::USD_PHASE > const phaseVolFrac = + subRegion.getField< fields::immiscibleMultiphaseFlow::phaseVolumeFraction >(); + arrayView2d< real64, immiscibleFlow::USD_PHASE > const phaseVolFrac_n = + subRegion.getField< fields::immiscibleMultiphaseFlow::phaseVolumeFraction_n >(); + + RAJA::ReduceMax< parallelDeviceReduce, real64 > subRegionMaxPresChange( 0.0 ); + RAJA::ReduceMax< parallelDeviceReduce, real64 > subRegionMaxPhaseVolFracChange( 0.0 ); + + forAll< parallelDevicePolicy<> >( subRegion.size(), [=] GEOS_HOST_DEVICE ( localIndex const ei ) + { + if( ghostRank[ei] < 0 ) + { + // switch from relative to absolute when values less than 1 + subRegionMaxPresChange.max( LvArray::math::abs( pres[ei] - pres_n[ei] ) / LvArray::math::max( LvArray::math::abs( pres_n[ei] ), 1.0 ) ); + for( integer ip = 0; ip < numPhase; ++ip ) + { + subRegionMaxPhaseVolFracChange.max( LvArray::math::abs( phaseVolFrac[ei][ip] - phaseVolFrac_n[ei][ip] ) ); + } + } + } ); + + maxRelativePresChange = LvArray::math::max( maxRelativePresChange, subRegionMaxPresChange.get() ); + maxAbsolutePhaseVolFracChange = LvArray::math::max( maxAbsolutePhaseVolFracChange, subRegionMaxPhaseVolFracChange.get() ); + + } ); + } ); + + maxRelativePresChange = MpiWrapper::max( maxRelativePresChange ); + maxAbsolutePhaseVolFracChange = MpiWrapper::max( maxAbsolutePhaseVolFracChange ); + + GEOS_LOG_LEVEL_RANK_0( logInfo::TimeStep, GEOS_FMT( "{}: max relative pressure change during time step = {} %", + getName(), GEOS_FMT( "{:.{}f}", 100*maxRelativePresChange, 3 ) ) ); + GEOS_LOG_LEVEL_RANK_0( logInfo::TimeStep, GEOS_FMT( "{}: max absolute phase volume fraction change during time step = {}", + getName(), GEOS_FMT( "{:.{}f}", maxAbsolutePhaseVolFracChange, 3 ) ) ); + + real64 const eps = LvArray::NumericLimits< real64 >::epsilon; + + real64 const nextDtPressure = currentDt * ( 1.0 + m_solutionChangeScalingFactor ) * m_targetRelativePresChange + / std::max( eps, maxRelativePresChange + m_solutionChangeScalingFactor * m_targetRelativePresChange ); + if( m_nonlinearSolverParameters.getLogLevel() > 0 ) + GEOS_LOG_RANK_0( GEOS_FMT( "{}: next time step based on pressure change = {}", getName(), nextDtPressure )); + real64 const nextDtPhaseVolFrac = currentDt * ( 1.0 + m_solutionChangeScalingFactor ) * m_targetPhaseVolFracChange + / std::max( eps, maxAbsolutePhaseVolFracChange + m_solutionChangeScalingFactor * m_targetPhaseVolFracChange ); + if( m_nonlinearSolverParameters.getLogLevel() > 0 ) + GEOS_LOG_RANK_0( GEOS_FMT( "{}: next time step based on phase volume fraction change = {}", getName(), nextDtPhaseVolFrac )); + + return std::min( nextDtPressure, nextDtPhaseVolFrac ); + + } + + REGISTER_CATALOG_ENTRY( PhysicsSolverBase, ImmiscibleMultiphaseFlow, string const &, Group * const ) + + } // namespace geos + diff --git a/src/coreComponents/physicsSolvers/fluidFlow/ImmiscibleMultiphaseFlow.hpp b/src/coreComponents/physicsSolvers/fluidFlow/ImmiscibleMultiphaseFlow.hpp index 7526130316c..b3769837600 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/ImmiscibleMultiphaseFlow.hpp +++ b/src/coreComponents/physicsSolvers/fluidFlow/ImmiscibleMultiphaseFlow.hpp @@ -27,6 +27,12 @@ namespace geos { + +namespace constitutive +{ +class ConstitutiveBase; +} // namespace constitutive + //START_SPHINX_INCLUDE_00 /** * @class ImmiscibleMultiphaseFlow @@ -160,12 +166,11 @@ class ImmiscibleMultiphaseFlow : public FlowSolverBase * @param matrix the system matrix * @param rhs the system right-hand side vector */ - virtual void - assembleFluxTerms( real64 const dt, - DomainPartition const & domain, - DofManager const & dofManager, - CRSMatrixView< real64, globalIndex const > const & localMatrix, - arrayView1d< real64 > const & localRhs ) const; + void assembleFluxTerms( real64 const dt, + DomainPartition & domain, + DofManager const & dofManager, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) const; /** * @brief Function to perform the Application of Dirichlet type BC's @@ -215,6 +220,10 @@ class ImmiscibleMultiphaseFlow : public FlowSolverBase struct viewKeyStruct : public FlowSolverBase::viewKeyStruct { // inputs + static constexpr char const * capPressureNamesString() { return "capPressureNames"; } + static constexpr char const * relPermNamesString() { return "relPermNames"; } + static constexpr char const * elemDofFieldString() { return "elemDofField"; } + static constexpr char const * interfaceFaceSetNamesString() { return "interfaceFaceSetNames"; } // density averaging scheme static constexpr char const * gravityDensitySchemeString() { return "gravityDensityScheme"; } @@ -228,9 +237,9 @@ class ImmiscibleMultiphaseFlow : public FlowSolverBase static constexpr char const * maxRelativePresChangeString() { return "maxRelativePressureChange"; } static constexpr char const * useTotalMassEquationString() { return "useTotalMassEquation"; } - static constexpr char const * capPressureNamesString() { return "capillary_pressure"; } - static constexpr char const * relPermNamesString() { return "relative_permeability"; } - static constexpr char const * elemDofFieldString() { return "elemDofField"; } +// static constexpr char const * capPressureNamesString() { return "capillary_pressure"; } +// static constexpr char const * relPermNamesString() { return "relative_permeability"; } +// static constexpr char const * elemDofFieldString() { return "elemDofField"; } }; @@ -300,6 +309,15 @@ class ImmiscibleMultiphaseFlow : public FlowSolverBase /// damping factor for solution change targets real64 m_solutionChangeScalingFactor; + string_array m_interfaceFaceSetNames; + + stdVector< std::array< std::tuple< constitutive::RelativePermeabilityBase *, + constitutive::CapillaryPressureBase *, + constitutive::TwoPhaseImmiscibleFluid * >, 2 > > m_interfaceConstitutivePairs; + + unordered_map< localIndex, localIndex > m_interfaceRegionByConnector; + unordered_map< localIndex, localIndex > m_connectorIndicesByInterfaceRegion; + private: diff --git a/src/coreComponents/physicsSolvers/fluidFlow/kernels/immiscibleMultiphase/CapillaryPressureUpdateKernel.hpp b/src/coreComponents/physicsSolvers/fluidFlow/kernels/immiscibleMultiphase/CapillaryPressureUpdateKernel.hpp new file mode 100644 index 00000000000..2a02f536ea9 --- /dev/null +++ b/src/coreComponents/physicsSolvers/fluidFlow/kernels/immiscibleMultiphase/CapillaryPressureUpdateKernel.hpp @@ -0,0 +1,73 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * SPDX-License-Identifier: LGPL-2.1-only + * + * Copyright (c) 2016-2024 Lawrence Livermore National Security LLC + * Copyright (c) 2018-2024 TotalEnergies + * Copyright (c) 2018-2024 The Board of Trustees of the Leland Stanford Junior University + * Copyright (c) 2023-2024 Chevron + * Copyright (c) 2019- GEOS/GEOSX Contributors + * All rights reserved + * + * See top level LICENSE, COPYRIGHT, CONTRIBUTORS, NOTICE, and ACKNOWLEDGEMENTS files for details. + * ------------------------------------------------------------------------------------------------------------ + */ + +/** + * @file CapillaryPressureUpdateKernel.hpp + */ + +#ifndef GEOS_PHYSICSSOLVERS_FLUIDFLOW_IMMISCIBLEMULTIPHASE_CAPILLARYPRESSUREUPDATEKERNEL_HPP +#define GEOS_PHYSICSSOLVERS_FLUIDFLOW_IMMISCIBLEMULTIPHASE_CAPILLARYPRESSUREUPDATEKERNEL_HPP + +#include "common/DataTypes.hpp" +#include "common/GEOS_RAJA_Interface.hpp" + +namespace geos +{ + +namespace immiscibleMultiphaseKernels +{ + +/******************************** CapillaryPressureUpdateKernel ********************************/ + +struct CapillaryPressureUpdateKernel +{ + template< typename POLICY, typename CAPPRES_WRAPPER > + static void + launch( localIndex const size, + CAPPRES_WRAPPER const & capPresWrapper, + arrayView2d< real64 const, immiscibleFlow::USD_PHASE > const & phaseVolFrac ) + { + forAll< POLICY >( size, [=] GEOS_HOST_DEVICE ( localIndex const k ) + { + for( localIndex q = 0; q < capPresWrapper.numGauss(); ++q ) + { + capPresWrapper.update( k, q, phaseVolFrac[k] ); + } + } ); + } + + template< typename POLICY, typename CAPPRES_WRAPPER > + static void + launch( SortedArrayView< localIndex const > const & targetSet, + CAPPRES_WRAPPER const & capPresWrapper, + arrayView2d< real64 const, immiscibleFlow::USD_PHASE > const & phaseVolFrac ) + { + forAll< POLICY >( targetSet.size(), [=] GEOS_HOST_DEVICE ( localIndex const a ) + { + localIndex const k = targetSet[a]; + for( localIndex q = 0; q < capPresWrapper.numGauss(); ++q ) + { + capPresWrapper.update( k, q, phaseVolFrac[k] ); + } + } ); + } +}; + +} // namespace immiscibleMultiphaseKernels + +} // namespace geos + + +#endif //GEOS_PHYSICSSOLVERS_FLUIDFLOW_IMMISCIBLEMULTIPHASE_CAPILLARYPRESSUREUPDATEKERNEL_HPP diff --git a/src/coreComponents/physicsSolvers/fluidFlow/kernels/immiscibleMultiphase/ImmiscibleMultiphaseKernels.hpp b/src/coreComponents/physicsSolvers/fluidFlow/kernels/immiscibleMultiphase/ImmiscibleMultiphaseKernels.hpp index ebb78332b43..378048270f3 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/kernels/immiscibleMultiphase/ImmiscibleMultiphaseKernels.hpp +++ b/src/coreComponents/physicsSolvers/fluidFlow/kernels/immiscibleMultiphase/ImmiscibleMultiphaseKernels.hpp @@ -17,39 +17,1587 @@ * @file ImmiscibleMultiphaseKernels.hpp */ + #ifndef GEOS_PHYSICSSOLVERS_FLUIDFLOW_MULTIPHASEKERNELS_HPP #define GEOS_PHYSICSSOLVERS_FLUIDFLOW_MULTIPHASEKERNELS_HPP -#include "codingUtilities/Utilities.hpp" -#include "common/DataLayouts.hpp" -#include "common/DataTypes.hpp" -#include "common/GEOS_RAJA_Interface.hpp" + + #include "codingUtilities/Utilities.hpp" + #include "common/DataLayouts.hpp" + #include "common/DataTypes.hpp" + #include "common/GEOS_RAJA_Interface.hpp" + #include "constitutive/fluid/twophaseimmisciblefluid/TwoPhaseImmiscibleFluid.hpp" + #include "constitutive/solid/CoupledSolidBase.hpp" + #include "constitutive/fluid/twophaseimmisciblefluid/TwoPhaseImmiscibleFluidFields.hpp" + #include "constitutive/capillaryPressure/CapillaryPressureFields.hpp" + #include "constitutive/capillaryPressure/CapillaryPressureBase.hpp" + #include "constitutive/capillaryPressure/JFunctionCapillaryPressure.hpp" + #include "constitutive/capillaryPressure/TableCapillaryPressure.hpp" + #include "constitutive/capillaryPressure/TableCapillaryPressureHysteresis.hpp" + #include "constitutive/permeability/PermeabilityBase.hpp" + #include "constitutive/permeability/PermeabilityFields.hpp" + #include "constitutive/relativePermeability/RelativePermeabilityBase.hpp" + #include "constitutive/relativePermeability/RelativePermeabilityFields.hpp" + #include "constitutive/ConstitutiveManager.hpp" +#include "constitutive/capillaryPressure/CapillaryPressureSelector.hpp" +#include "constitutive/relativePermeability/RelativePermeabilitySelector.hpp" + +#include "constitutive/ConstitutivePassThru.hpp" #include "constitutive/fluid/twophaseimmisciblefluid/TwoPhaseImmiscibleFluid.hpp" -#include "constitutive/solid/CoupledSolidBase.hpp" -#include "constitutive/fluid/twophaseimmisciblefluid/TwoPhaseImmiscibleFluidFields.hpp" -#include "constitutive/capillaryPressure/CapillaryPressureFields.hpp" -#include "constitutive/capillaryPressure/CapillaryPressureBase.hpp" -#include "constitutive/permeability/PermeabilityBase.hpp" -#include "constitutive/permeability/PermeabilityFields.hpp" -#include "constitutive/relativePermeability/RelativePermeabilityBase.hpp" -#include "constitutive/relativePermeability/RelativePermeabilityFields.hpp" -#include "fieldSpecification/AquiferBoundaryCondition.hpp" -#include "finiteVolume/BoundaryStencil.hpp" -#include "finiteVolume/FluxApproximationBase.hpp" -#include "linearAlgebra/interfaces/InterfaceTypes.hpp" -#include "physicsSolvers/PhysicsSolverBaseKernels.hpp" -#include "physicsSolvers/fluidFlow/FlowSolverBaseFields.hpp" -#include "physicsSolvers/fluidFlow/ImmiscibleMultiphaseFlowFields.hpp" -#include "physicsSolvers/fluidFlow/CompositionalMultiphaseUtilities.hpp" -#include "physicsSolvers/fluidFlow/StencilAccessors.hpp" -#include "physicsSolvers/fluidFlow/kernels/immiscibleMultiphase/KernelLaunchSelectors.hpp" + + + #include "fieldSpecification/AquiferBoundaryCondition.hpp" + #include "finiteVolume/BoundaryStencil.hpp" + #include "finiteVolume/CellElementStencilTPFA.hpp" + #include "finiteVolume/FluxApproximationBase.hpp" + #include "linearAlgebra/interfaces/InterfaceTypes.hpp" + #include "physicsSolvers/PhysicsSolverBaseKernels.hpp" + #include "physicsSolvers/fluidFlow/FlowSolverBaseFields.hpp" + #include "physicsSolvers/fluidFlow/ImmiscibleMultiphaseFlowFields.hpp" + #include "physicsSolvers/fluidFlow/CompositionalMultiphaseUtilities.hpp" + #include "physicsSolvers/fluidFlow/StencilAccessors.hpp" + #include "physicsSolvers/fluidFlow/kernels/compositional/RelativePermeabilityUpdateKernel.hpp" + #include "physicsSolvers/fluidFlow/kernels/compositional/CapillaryPressureUpdateKernel.hpp" + #include "physicsSolvers/PhysicsSolverBaseKernels.hpp" + #include "physicsSolvers/fluidFlow/kernels/immiscibleMultiphase/KernelLaunchSelectors.hpp" + #include "physicsSolvers/fluidFlow/kernels/immiscibleMultiphase/KernelLaunchSelectors.hpp" + namespace geos { namespace immiscibleMultiphaseKernels { + using namespace constitutive; +GEOS_HOST_DEVICE +inline +static void local_solver( real64 uT, stdVector< real64 > saturations, stdVector< real64 > pressures, stdVector< real64 > JFMultipliers, stdVector< real64 > trappedSats1, + stdVector< real64 > trappedSats2, stdVector< fields::cappres::ModeIndexType> modes, stdVector< real64 > transHat, stdVector< real64 > dTransHat_dP, stdVector< real64 > gravCoefHat, stdVector< real64 > gravCoef, + stdVector< real64 > cellCenterDuT, stdVector< real64 > cellCenterDens, stdVector< real64 > cellCenterDens_dP, + std::vector< RelativePermeabilityBase * > relPerms, std::vector< CapillaryPressureBase * > capPressures, + std::vector< TwoPhaseImmiscibleFluid * > fluids, std::vector< real64 > &phi, std::vector< real64 > &grad_phi_P, std::vector< real64 > &grad_phi_S, bool &converged, + stdVector< real64 > const & phaseMaxHistVolFrac1, stdVector< real64 > const & phaseMinHistVolFrac1, + stdVector< real64 > const & phaseMaxHistVolFrac2, stdVector< real64 > const & phaseMinHistVolFrac2 ) +{ + + // getting wrappers: + + constitutive::constitutiveUpdatePassThru( *capPressures[0], [&] ( auto & castedCapPres1 ) + { + auto capPresWrapper1 = castedCapPres1.createKernelWrapper(); + + + constitutive::constitutiveUpdatePassThru( *capPressures[1], [&] ( auto & castedCapPres2 ) + { + auto capPresWrapper2 = castedCapPres2.createKernelWrapper(); + + + constitutive::constitutiveUpdatePassThru( *relPerms[0], [&] ( auto & castedRelPerm1 ) + { + auto relPermWrapper1 = castedRelPerm1.createKernelWrapper(); + + + constitutive::constitutiveUpdatePassThru( *relPerms[1], [&] ( auto & castedRelPerm2 ) + { + auto relPermWrapper2 = castedRelPerm2.createKernelWrapper(); + + auto fluidWrapper1 = fluids[0]->createKernelWrapper(); + auto fluidWrapper2 = fluids[1]->createKernelWrapper(); + + + std::ofstream outFile( "iterations2.csv" ); + + + // Write data to the file + outFile << "Jacobian"; + outFile << ","; + outFile << "residual"; + outFile << ","; + outFile << "Fw_alpha"; + outFile << ","; + outFile << "Fw_beta"; + outFile << ","; + outFile << "Pc_int"; + outFile << ","; + outFile << "Pc_int1"; + outFile << ","; + outFile << "Pc_int2"; + outFile << ","; + outFile << "Fn_alpha"; + outFile << ","; + outFile << "Fn_beta"; + outFile << ","; + outFile << "Vw_alpha"; + outFile << ","; + outFile << "Vn_alpha"; + outFile << ","; + outFile << "Vw_beta"; + outFile << ","; + outFile << "Vn_beta"; + outFile << ","; + outFile << "Gw_alpha"; + outFile << ","; + outFile << "Gn_alpha"; + outFile << ","; + outFile << "Gw_beta"; + outFile << ","; + outFile << "Gn_beta"; + outFile << ","; + outFile << "Cw_alpha"; + outFile << ","; + outFile << "Cn_alpha"; + outFile << ","; + outFile << "Cw_beta"; + outFile << ","; + outFile << "Cn_beta"; + outFile << ","; + outFile << "transHats0"; + outFile << ","; + outFile << "transHats1"; + outFile << ","; + outFile << "gravCoefHats"; + outFile << ","; + outFile << "gravCoef0"; + outFile << ","; + outFile << "gravCoef1"; + outFile << ","; + outFile << "uT"; + outFile << ","; + outFile << "duT_dP0"; + outFile << ","; + outFile << "duT_dS0"; + outFile << ","; + outFile << "duT_dP1"; + outFile << ","; + outFile << "duT_dS1"; + outFile << std::endl; + + + + // nonlinear solver's parameters + real64 tol = 1.0e-9; + int max_iter = 50; + converged = 0; + bool damping = true; + bool bisection = false; + // bool lineSearch = false; + bool newton_path = false; + + // Local newton loop: + + // Use of the capillary pressure kernel wrapper + + StackArray< real64, 2, 2, immiscibleFlow::LAYOUT_PHASE > phaseVolFrac1( 1, 2 ); + StackArray< real64, 3, 2, constitutive::cappres::LAYOUT_CAPPRES > capPres1( 1, 1, 2 ); + StackArray< real64, 4, 4, constitutive::cappres::LAYOUT_CAPPRES_DS > dPhaseVolFrac_dCapPres1( 1, 1, 2, 2 ); + StackArray< real64, 4, 4, constitutive::cappres::LAYOUT_CAPPRES_DS > dCapPres1_dPhaseVolFrac( 1, 1, 2, 2 ); + StackArray< real64, 3, 2, constitutive::relperm::LAYOUT_RELPERM > trappedVolFrac1( 1, 1, 2 ); + StackArray< real64, 2, 2, immiscibleFlow::LAYOUT_PHASE > facePhaseVolFrac1( 1, 2 ); + StackArray< real64, 3, 2, constitutive::relperm::LAYOUT_RELPERM > faceTrappedVolFrac1( 1, 1, 2 ); + StackArray< real64, 3, 2, constitutive::cappres::LAYOUT_CAPPRES > faceCapPres1( 1, 1, 2 ); + StackArray< real64, 4, 4, constitutive::cappres::LAYOUT_CAPPRES_DS > dfacePhaseVolFrac_dCapPres1( 1, 1, 2, 2 ); + StackArray< real64, 4, 4, constitutive::cappres::LAYOUT_CAPPRES_DS > dCapPres1_dfacePhaseVolFrac( 1, 1, 2, 2 ); + StackArray< real64, 2, 2, immiscibleFlow::LAYOUT_PHASE > phaseVolFrac2( 1, 2 ); + StackArray< real64, 3, 2, constitutive::cappres::LAYOUT_CAPPRES > capPres2( 1, 1, 2 ); + StackArray< real64, 4, 4, constitutive::cappres::LAYOUT_CAPPRES_DS > dPhaseVolFrac_dCapPres2( 1, 1, 2, 2 ); + StackArray< real64, 4, 4, constitutive::cappres::LAYOUT_CAPPRES_DS > dCapPres2_dPhaseVolFrac( 1, 1, 2, 2 ); + StackArray< real64, 3, 2, constitutive::relperm::LAYOUT_RELPERM > trappedVolFrac2( 1, 1, 2 ); + StackArray< real64, 2, 2, immiscibleFlow::LAYOUT_PHASE > facePhaseVolFrac2( 1, 2 ); + StackArray< real64, 3, 2, constitutive::relperm::LAYOUT_RELPERM > faceTrappedVolFrac2( 1, 1, 2 ); + StackArray< real64, 3, 2, constitutive::cappres::LAYOUT_CAPPRES > faceCapPres2( 1, 1, 2 ); + StackArray< real64, 4, 4, constitutive::cappres::LAYOUT_CAPPRES_DS > dfacePhaseVolFrac_dCapPres2( 1, 1, 2, 2 ); + StackArray< real64, 4, 4, constitutive::cappres::LAYOUT_CAPPRES_DS > dCapPres2_dfacePhaseVolFrac( 1, 1, 2, 2 ); + StackArray< real64, 1, 2 > JFunc1( 2 ); + StackArray< real64, 1, 2 > JFunc2( 2 ); + + + StackArray< real64, 2, 2, immiscibleFlow::LAYOUT_PHASE > phaseMaxHistoricalVolFraction1( 1, 2 ); + StackArray< real64, 2, 2, immiscibleFlow::LAYOUT_PHASE > phaseMinHistoricalVolFraction1( 1, 2 ); + StackArray< real64, 2, 2, immiscibleFlow::LAYOUT_PHASE > phaseMaxHistoricalVolFraction2( 1, 2 ); + StackArray< real64, 2, 2, immiscibleFlow::LAYOUT_PHASE > phaseMinHistoricalVolFraction2( 1, 2 ); + + if( phaseMaxHistVolFrac1.size() >= 2 && phaseMaxHistVolFrac1[0] >= 0.0 ) + { + + phaseMaxHistoricalVolFraction1[0][0] = phaseMaxHistVolFrac1[0]; + phaseMaxHistoricalVolFraction1[0][1] = phaseMaxHistVolFrac1[1]; + phaseMinHistoricalVolFraction1[0][0] = phaseMinHistVolFrac1[0]; + phaseMinHistoricalVolFraction1[0][1] = phaseMinHistVolFrac1[1]; + } + else + { + + phaseMaxHistoricalVolFraction1[0][0] = saturations[0]; + phaseMaxHistoricalVolFraction1[0][1] = 1.0 - saturations[0]; + phaseMinHistoricalVolFraction1[0][0] = saturations[0]; + phaseMinHistoricalVolFraction1[0][1] = 1.0 - saturations[0]; + } + + if( phaseMaxHistVolFrac2.size() >= 2 && phaseMaxHistVolFrac2[0] >= 0.0 ) + { + + phaseMaxHistoricalVolFraction2[0][0] = phaseMaxHistVolFrac2[0]; + phaseMaxHistoricalVolFraction2[0][1] = phaseMaxHistVolFrac2[1]; + phaseMinHistoricalVolFraction2[0][0] = phaseMinHistVolFrac2[0]; + phaseMinHistoricalVolFraction2[0][1] = phaseMinHistVolFrac2[1]; + } + else + { + phaseMaxHistoricalVolFraction2[0][0] = saturations[1]; + phaseMaxHistoricalVolFraction2[0][1] = 1.0 - saturations[1]; + phaseMinHistoricalVolFraction2[0][0] = saturations[1]; + phaseMinHistoricalVolFraction2[0][1] = 1.0 - saturations[1]; + } + + // compute relative permeability for both cell centers: + + trappedVolFrac1[0][0][0] = trappedSats1[0]; + trappedVolFrac1[0][0][1] = trappedSats1[1]; + + trappedVolFrac2[0][0][0] = trappedSats2[0]; + trappedVolFrac2[0][0][1] = trappedSats2[1]; + + faceTrappedVolFrac1[0][0][0] = trappedSats1[0]; + faceTrappedVolFrac1[0][0][1] = trappedSats1[1]; + + faceTrappedVolFrac2[0][0][0] = trappedSats2[0]; + faceTrappedVolFrac2[0][0][1] = trappedSats2[1]; + + phaseVolFrac1[0][0] = saturations[0]; + phaseVolFrac1[0][1] = 1.0 - saturations[0]; + + phaseVolFrac2[0][0] = saturations[1]; + phaseVolFrac2[0][1] = 1.0 - saturations[1]; + + real64 Pc1_min = 0.0; + real64 Pc2_min = 0.0; + real64 Pc1_max = 0.0; + real64 Pc2_max = 0.0; + + real64 density2[2]{}; + real64 dDens_dP2[2][2]{}; + + density2[0] = cellCenterDens[0]; + density2[1] = cellCenterDens[1]; + + dDens_dP2[0][0] = cellCenterDens_dP[0]; + dDens_dP2[0][1] = cellCenterDens_dP[1]; + dDens_dP2[1][0] = cellCenterDens_dP[2]; + dDens_dP2[1][1] = cellCenterDens_dP[3]; + + JFunc1[0] = JFMultipliers[0]; + JFunc2[0] = JFMultipliers[1]; + + using T1 = std::decay_t< decltype(castedCapPres1) >; + if constexpr (std::is_same_v< T1, JFunctionCapillaryPressure >) { + capPresWrapper1.compute( phaseVolFrac1[0], + JFunc1.toSliceConst(), + capPres1[0][0], + dCapPres1_dPhaseVolFrac[0][0] ); + + facePhaseVolFrac1[0][1] = 0.0; + facePhaseVolFrac1[0][0] = 1.0; + capPresWrapper1.compute( facePhaseVolFrac1[0], + JFunc1.toSliceConst(), + faceCapPres1[0][0], + dCapPres1_dfacePhaseVolFrac[0][0] ); + Pc1_min = faceCapPres1[0][0][0]; + facePhaseVolFrac1[0][1] = 1.0; + facePhaseVolFrac1[0][0] = 0.0; + capPresWrapper1.compute( facePhaseVolFrac1[0], + JFunc1.toSliceConst(), + faceCapPres1[0][0], + dCapPres1_dfacePhaseVolFrac[0][0] ); + Pc1_max = faceCapPres1[0][0][0]; + + } + else if constexpr (std::is_same_v< T1, TableCapillaryPressureHysteresis >) { + capPresWrapper1.compute( phaseVolFrac1[0], + phaseMaxHistoricalVolFraction1[0], + phaseMinHistoricalVolFraction1[0], + trappedVolFrac1[0][0], + capPres1[0][0], + dCapPres1_dPhaseVolFrac[0][0], + modes[0] ); + + + facePhaseVolFrac1[0][1] = 0.0; + facePhaseVolFrac1[0][0] = 1.0; + capPresWrapper1.compute( facePhaseVolFrac1[0], + phaseMaxHistoricalVolFraction1[0], + phaseMinHistoricalVolFraction1[0], + faceTrappedVolFrac1[0][0], + faceCapPres1[0][0], + dCapPres1_dfacePhaseVolFrac[0][0], + modes[0] ); + Pc1_min = faceCapPres1[0][0][0]; + + facePhaseVolFrac1[0][1] = 1.0; + facePhaseVolFrac1[0][0] = 0.0; + capPresWrapper1.compute( facePhaseVolFrac1[0], + phaseMaxHistoricalVolFraction1[0], + phaseMinHistoricalVolFraction1[0], + faceTrappedVolFrac1[0][0], + faceCapPres1[0][0], + dCapPres1_dfacePhaseVolFrac[0][0], + modes[0] ); + Pc1_max = faceCapPres1[0][0][0]; + + // For hysteresis models, also evaluate at historical saturation range endpoints + // to ensure we capture the full range of the current curve + if (phaseMinHistoricalVolFraction1[0][0] >= 0.0 && phaseMaxHistoricalVolFraction1[0][0] >= 0.0) { + // Evaluate at min historical saturation + real64 const S_min_hist = phaseMinHistoricalVolFraction1[0][0]; + if (S_min_hist > 0.0 && S_min_hist < 1.0) { + facePhaseVolFrac1[0][0] = S_min_hist; + facePhaseVolFrac1[0][1] = 1.0 - S_min_hist; + capPresWrapper1.compute( facePhaseVolFrac1[0], + phaseMaxHistoricalVolFraction1[0], + phaseMinHistoricalVolFraction1[0], + faceTrappedVolFrac1[0][0], + faceCapPres1[0][0], + dCapPres1_dfacePhaseVolFrac[0][0], + modes[0] ); + Pc1_min = LvArray::math::min(Pc1_min, faceCapPres1[0][0][0]); + Pc1_max = LvArray::math::max(Pc1_max, faceCapPres1[0][0][0]); + } + + // Evaluate at max historical saturation + real64 const S_max_hist = phaseMaxHistoricalVolFraction1[0][0]; + if (S_max_hist > 0.0 && S_max_hist < 1.0) { + facePhaseVolFrac1[0][0] = S_max_hist; + facePhaseVolFrac1[0][1] = 1.0 - S_max_hist; + capPresWrapper1.compute( facePhaseVolFrac1[0], + phaseMaxHistoricalVolFraction1[0], + phaseMinHistoricalVolFraction1[0], + faceTrappedVolFrac1[0][0], + faceCapPres1[0][0], + dCapPres1_dfacePhaseVolFrac[0][0], + modes[0] ); + Pc1_min = LvArray::math::min(Pc1_min, faceCapPres1[0][0][0]); + Pc1_max = LvArray::math::max(Pc1_max, faceCapPres1[0][0][0]); + } + } + } + else + { + capPresWrapper1.compute( phaseVolFrac1[0], + capPres1[0][0], + dCapPres1_dPhaseVolFrac[0][0] ); + + facePhaseVolFrac1[0][1] = 0.0; + facePhaseVolFrac1[0][0] = 1.0; + capPresWrapper1.compute( facePhaseVolFrac1[0], + faceCapPres1[0][0], + dCapPres1_dfacePhaseVolFrac[0][0] ); + Pc1_min = faceCapPres1[0][0][0]; + facePhaseVolFrac1[0][1] = 1.0; + facePhaseVolFrac1[0][0] = 0.0; + capPresWrapper1.compute( facePhaseVolFrac1[0], + faceCapPres1[0][0], + dCapPres1_dfacePhaseVolFrac[0][0] ); + Pc1_max = faceCapPres1[0][0][0]; + } + + using T2 = std::decay_t< decltype(castedCapPres2) >; + if constexpr (std::is_same_v< T2, JFunctionCapillaryPressure >) { + // evaluating cell-center Pc: + + capPresWrapper2.compute( phaseVolFrac2[0], + JFunc2.toSliceConst(), + capPres2[0][0], + dCapPres2_dPhaseVolFrac[0][0] ); + +// finding endpoints: + + facePhaseVolFrac2[0][1] = 0.0; + facePhaseVolFrac2[0][0] = 1.0; + capPresWrapper2.compute( facePhaseVolFrac2[0], + JFunc2.toSliceConst(), + faceCapPres2[0][0], + dCapPres2_dfacePhaseVolFrac[0][0] ); + Pc2_min = faceCapPres2[0][0][0]; + facePhaseVolFrac2[0][1] = 1.0; + facePhaseVolFrac2[0][0] = 0.0; + capPresWrapper2.compute( facePhaseVolFrac2[0], + JFunc2.toSliceConst(), + faceCapPres2[0][0], + dCapPres2_dfacePhaseVolFrac[0][0] ); + Pc2_max = faceCapPres2[0][0][0]; + + } + else if constexpr (std::is_same_v< T2, TableCapillaryPressureHysteresis >) { + capPresWrapper2.compute( phaseVolFrac2[0], + phaseMaxHistoricalVolFraction2[0], + phaseMinHistoricalVolFraction2[0], + trappedVolFrac2[0][0], + capPres2[0][0], + dCapPres2_dPhaseVolFrac[0][0], + modes[1] ); + + + facePhaseVolFrac2[0][1] = 0.0; + facePhaseVolFrac2[0][0] = 1.0; + capPresWrapper2.compute( facePhaseVolFrac2[0], + phaseMaxHistoricalVolFraction2[0], + phaseMinHistoricalVolFraction2[0], + faceTrappedVolFrac2[0][0], + faceCapPres2[0][0], + dCapPres2_dfacePhaseVolFrac[0][0], + modes[1] ); + Pc2_min = faceCapPres2[0][0][0]; + + facePhaseVolFrac2[0][1] = 1.0; + facePhaseVolFrac2[0][0] = 0.0; + capPresWrapper2.compute( facePhaseVolFrac2[0], + phaseMaxHistoricalVolFraction2[0], + phaseMinHistoricalVolFraction2[0], + faceTrappedVolFrac2[0][0], + faceCapPres2[0][0], + dCapPres2_dfacePhaseVolFrac[0][0], + modes[1] ); + Pc2_max = faceCapPres2[0][0][0]; + + // For hysteresis models, also evaluate at historical saturation range endpoints + // to ensure we capture the full range of the current curve + if (phaseMinHistoricalVolFraction2[0][0] >= 0.0 && phaseMaxHistoricalVolFraction2[0][0] >= 0.0) { + // Evaluate at min historical saturation + real64 const S_min_hist = phaseMinHistoricalVolFraction2[0][0]; + if (S_min_hist > 0.0 && S_min_hist < 1.0) { + facePhaseVolFrac2[0][0] = S_min_hist; + facePhaseVolFrac2[0][1] = 1.0 - S_min_hist; + capPresWrapper2.compute( facePhaseVolFrac2[0], + phaseMaxHistoricalVolFraction2[0], + phaseMinHistoricalVolFraction2[0], + faceTrappedVolFrac2[0][0], + faceCapPres2[0][0], + dCapPres2_dfacePhaseVolFrac[0][0], + modes[1] ); + Pc2_min = LvArray::math::min(Pc2_min, faceCapPres2[0][0][0]); + Pc2_max = LvArray::math::max(Pc2_max, faceCapPres2[0][0][0]); + } + + // Evaluate at max historical saturation + real64 const S_max_hist = phaseMaxHistoricalVolFraction2[0][0]; + if (S_max_hist > 0.0 && S_max_hist < 1.0) { + facePhaseVolFrac2[0][0] = S_max_hist; + facePhaseVolFrac2[0][1] = 1.0 - S_max_hist; + capPresWrapper2.compute( facePhaseVolFrac2[0], + phaseMaxHistoricalVolFraction2[0], + phaseMinHistoricalVolFraction2[0], + faceTrappedVolFrac2[0][0], + faceCapPres2[0][0], + dCapPres2_dfacePhaseVolFrac[0][0], + modes[1] ); + Pc2_min = LvArray::math::min(Pc2_min, faceCapPres2[0][0][0]); + Pc2_max = LvArray::math::max(Pc2_max, faceCapPres2[0][0][0]); + } + } + } + else + { + // evaluating cell-center Pc: + + capPresWrapper2.compute( phaseVolFrac2[0], + capPres2[0][0], + dCapPres2_dPhaseVolFrac[0][0] ); + +// finding endpoints: + + facePhaseVolFrac2[0][1] = 0.0; + facePhaseVolFrac2[0][0] = 1.0; + capPresWrapper2.compute( facePhaseVolFrac2[0], + faceCapPres2[0][0], + dCapPres2_dfacePhaseVolFrac[0][0] ); + Pc2_min = faceCapPres2[0][0][0]; + facePhaseVolFrac2[0][1] = 1.0; + facePhaseVolFrac2[0][0] = 0.0; + capPresWrapper2.compute( facePhaseVolFrac2[0], + faceCapPres2[0][0], + dCapPres2_dfacePhaseVolFrac[0][0] ); + Pc2_max = faceCapPres2[0][0][0]; + } + + // Use of the relative permeability kernel wrapper + + + StackArray< real64, 3, 2, constitutive::relperm::LAYOUT_RELPERM > faceRelPerm1( 1, 1, 2 ); + StackArray< real64, 4, 4, constitutive::relperm::LAYOUT_RELPERM_DS > dfacePhaseRelPerm1_dPhaseVolFrac( 1, 1, 2, 2 ); + StackArray< real64, 3, 2, constitutive::relperm::LAYOUT_RELPERM > relPerm1( 1, 1, 2 ); + StackArray< real64, 4, 4, constitutive::relperm::LAYOUT_RELPERM_DS > dPhaseRelPerm1_dPhaseVolFrac( 1, 1, 2, 2 ); + StackArray< real64, 3, 2, constitutive::relperm::LAYOUT_RELPERM > faceRelPerm2( 1, 1, 2 ); + StackArray< real64, 4, 4, constitutive::relperm::LAYOUT_RELPERM_DS > dfacePhaseRelPerm2_dPhaseVolFrac( 1, 1, 2, 2 ); + StackArray< real64, 3, 2, constitutive::relperm::LAYOUT_RELPERM > relPerm2( 1, 1, 2 ); + StackArray< real64, 4, 4, constitutive::relperm::LAYOUT_RELPERM_DS > dPhaseRelPerm2_dPhaseVolFrac( 1, 1, 2, 2 ); + + + using T5 = std::decay_t< decltype(castedRelPerm1) >; + if constexpr (std::is_same_v< T5, constitutive::TableRelativePermeabilityHysteresis >) { + relPermWrapper1.compute( phaseVolFrac1[0], + phaseMaxHistoricalVolFraction1[0], + phaseMinHistoricalVolFraction1[0], + trappedVolFrac1[0][0], + relPerm1[0][0], + dPhaseRelPerm1_dPhaseVolFrac[0][0] ); + + } + else + { + + relPermWrapper1.compute( phaseVolFrac1[0], + trappedVolFrac1[0][0], + relPerm1[0][0], + dPhaseRelPerm1_dPhaseVolFrac[0][0] ); + } + + using T6 = std::decay_t< decltype(castedRelPerm2) >; + if constexpr (std::is_same_v< T6, constitutive::TableRelativePermeabilityHysteresis >) { + relPermWrapper2.compute( phaseVolFrac2[0], + phaseMaxHistoricalVolFraction2[0], + phaseMinHistoricalVolFraction2[0], + trappedVolFrac2[0][0], + relPerm2[0][0], + dPhaseRelPerm2_dPhaseVolFrac[0][0] ); + + } + else + { + + relPermWrapper2.compute( phaseVolFrac2[0], + trappedVolFrac2[0][0], + relPerm2[0][0], + dPhaseRelPerm2_dPhaseVolFrac[0][0] ); + } + + + // Use of the fluid model kernel wrapper + StackArray< real64, 3, 2, constitutive::multifluid::LAYOUT_PHASE > phaseDensity1( 1, 1, 2 ); + StackArray< real64, 3, 2, constitutive::multifluid::LAYOUT_PHASE > phaseViscosity1( 1, 1, 2 ); + StackArray< real64, 4, 4, constitutive::multifluid::LAYOUT_PHASE_DC > dPhaseDens1_dP( 1, 1, 2, 2 ); + StackArray< real64, 4, 4, constitutive::multifluid::LAYOUT_PHASE_DC > dPhaseVisc1_dP( 1, 1, 2, 2 ); + StackArray< real64, 3, 2, constitutive::multifluid::LAYOUT_PHASE > phaseDensity2( 1, 1, 2 ); + StackArray< real64, 3, 2, constitutive::multifluid::LAYOUT_PHASE > phaseViscosity2( 1, 1, 2 ); + StackArray< real64, 4, 4, constitutive::multifluid::LAYOUT_PHASE_DC > dPhaseDens2_dP( 1, 1, 2, 2 ); + StackArray< real64, 4, 4, constitutive::multifluid::LAYOUT_PHASE_DC > dPhaseVisc2_dP( 1, 1, 2, 2 ); + + // Declare the MultiFluidVar (PhaseProp) type + TwoPhaseImmiscibleFluid::PhaseProp phaseDensity_temp1; + TwoPhaseImmiscibleFluid::PhaseProp phaseViscosity_temp1; + phaseDensity_temp1.value.resize( 1, 1, 2 ); // or whatever sizes you need + phaseDensity_temp1.derivs.resize( 1, 1, 2, 2 ); // make sure all dims > 0 + phaseViscosity_temp1.value.resize( 1, 1, 2 ); // or whatever sizes you need + phaseViscosity_temp1.derivs.resize( 1, 1, 2, 2 ); // make sure all dims > 0 + + auto phaseDensitySlice0 = TwoPhaseImmiscibleFluid::PhaseProp::SliceType( + phaseDensity_temp1.value[0][0], phaseDensity_temp1.derivs[0][0] ); + auto phaseViscositySlice0 = TwoPhaseImmiscibleFluid::PhaseProp::SliceType( + phaseViscosity_temp1.value[0][0], phaseViscosity_temp1.derivs[0][0] ); + fluidWrapper1.compute( pressures[0], phaseDensitySlice0, phaseViscositySlice0 ); + + // Temporary: + phaseDensity1[0][0][0] = phaseDensitySlice0.value[0]; + phaseDensity1[0][0][1] = phaseDensitySlice0.value[1]; + dPhaseDens1_dP[0][0][0][0] = phaseDensitySlice0.derivs[0][0]; + dPhaseDens1_dP[0][0][0][1] = 0; + dPhaseDens1_dP[0][0][1][0] = phaseDensitySlice0.derivs[1][0]; + dPhaseDens1_dP[0][0][1][1] = 0; + + phaseViscosity1[0][0][0] = phaseViscositySlice0.value[0]; + phaseViscosity1[0][0][1] = phaseViscositySlice0.value[1]; + dPhaseVisc1_dP[0][0][0][0] = phaseViscositySlice0.derivs[0][0]; + dPhaseVisc1_dP[0][0][0][1] = 0; + dPhaseVisc1_dP[0][0][1][0] = phaseViscositySlice0.derivs[1][0]; + dPhaseVisc1_dP[0][0][1][1] = 0; + + auto phaseDensitySlice1 = TwoPhaseImmiscibleFluid::PhaseProp::SliceType( + phaseDensity_temp1.value[0][0], phaseDensity_temp1.derivs[0][0] ); + auto phaseViscositySlice1 = TwoPhaseImmiscibleFluid::PhaseProp::SliceType( + phaseViscosity_temp1.value[0][0], phaseViscosity_temp1.derivs[0][0] ); + fluidWrapper2.compute( pressures[1], phaseDensitySlice1, phaseViscositySlice1 ); + + + phaseDensity2[0][0][0] = phaseDensitySlice1.value[0]; + phaseDensity2[0][0][1] = phaseDensitySlice1.value[1]; + dPhaseDens2_dP[0][0][0][0] = phaseDensitySlice1.derivs[0][0]; + dPhaseDens2_dP[0][0][0][1] = 0; + dPhaseDens2_dP[0][0][1][0] = phaseDensitySlice1.derivs[1][0]; + dPhaseDens2_dP[0][0][1][1] = 0; + + phaseViscosity2[0][0][0] = phaseViscositySlice1.value[0]; + phaseViscosity2[0][0][1] = phaseViscositySlice1.value[1]; + dPhaseVisc2_dP[0][0][0][0] = phaseViscositySlice1.derivs[0][0]; + dPhaseVisc2_dP[0][0][0][1] = 0; + dPhaseVisc2_dP[0][0][1][0] = phaseViscositySlice1.derivs[1][0]; + dPhaseVisc2_dP[0][0][1][1] = 0; + + // clear working arrays + real64 halfFluxVal[2][2]{}; + real64 dhalfFlux1_dP[2][2]{}; + real64 dhalfFlux1_dS[2][2]{}; + real64 dhalfFlux2_dP[2][2]{}; + real64 dhalfFlux2_dS[2][2]{}; + real64 dhalfFlux_duT[2][2]{}; + real64 dhalfFlux_dpc[2][2]{}; + + //new + real64 fluxVal[2]{}; + real64 dFlux_dP[2][2]{}; + real64 dFlux_dS[2][2]{}; + + real64 duT_dP[2]{}; + real64 duT_dS[2]{}; + + duT_dP[0] = cellCenterDuT[0]; + duT_dP[1] = cellCenterDuT[1]; + + duT_dS[0] = cellCenterDuT[2]; + duT_dS[1] = cellCenterDuT[3]; + + // initial guess: + + real64 const Pc1 = capPres1[0][0][0]; + real64 const Pc2 = capPres2[0][0][0]; + + real64 Pc_int = ( Pc1 + Pc2 ) / 2.0; + + real64 Pc_min_all = fmax( Pc1_min, Pc2_min ); + real64 Pc_max_all = fmin( Pc1_max, Pc2_max ); + + if( Pc_int < Pc_min_all || Pc_int > Pc_max_all ) + { + Pc_int = ( Pc_min_all + Pc_max_all ) / 2.0; + } + + // While loop (newton loop) + int iter = 0; + int div = 0; + // int ext_iter0 = 0; + // int ext_iter1 = 0; + real64 next_Pc_int = 0.0; + real64 old_Pc_int = 0.0; + real64 old_residual = 0.0; + + if (bisection) { + Pc_int = fmax( Pc1_max, Pc2_max ); + next_Pc_int = fmin( Pc1_min, Pc2_min ); + } + + if (newton_path){ + Pc_int = fmax( Pc1_max, Pc2_max ); + Pc_int = 2.0e5; + next_Pc_int = (fmax( Pc1_max, Pc2_max ) - fmin( Pc1_min, Pc2_min )) / (max_iter - 1); + next_Pc_int = (2.0e5 - 5.0e4) / (max_iter - 1); + } + + real64 Pc_int_iterate = Pc_int; + + while( iter < max_iter ) + { + + Pc_int_iterate = Pc_int; + + // clear working arrays + real64 density[2]{}; + real64 dDens_dP[2][2]{}; + real64 gravityCof[2]{}; + real64 viscosity[2]{}; + real64 dVisc_dP[2][2]{}; + + real64 viscous[2][2]{}; + real64 bouyancy[2][2]{}; + real64 capillarity[2][2]{}; + + real64 dV1_dS[2][2]{}; + real64 dG1_dS[2][2]{}; + real64 dC1_dS[2][2]{}; + real64 dV2_dS[2][2]{}; + real64 dG2_dS[2][2]{}; + real64 dC2_dS[2][2]{}; + real64 dV1_dpc[2][2]{}; + real64 dG1_dpc[2][2]{}; + real64 dC1_dpc[2][2]{}; + real64 dV2_dpc[2][2]{}; + real64 dG2_dpc[2][2]{}; + real64 dC2_dpc[2][2]{}; + + real64 local_residual = 0; + real64 local_jacobian = 0; + + // truncate the capillary pressure iterate (ensures the inverse will compute a saturation bounded between 0 and 1): + + Pc_int = fmin( fmax( Pc1_max, Pc2_max ), fmax( Pc_int, fmin( Pc1_min, Pc2_min ) )); + + faceCapPres1[0][0][0] = fmin( Pc1_max, fmax( Pc_int, Pc1_min)); + faceCapPres2[0][0][0] = fmin( Pc2_max, fmax( Pc_int, Pc2_min)); + + // Compute the inverse: + + JFunc1[0] = JFMultipliers[0]; + JFunc2[0] = JFMultipliers[1]; + + using T3 = std::decay_t< decltype(castedCapPres1) >; + if constexpr (std::is_same_v< T3, JFunctionCapillaryPressure >) { + capPresWrapper1.computeInv( facePhaseVolFrac1[0], + JFunc1.toSliceConst(), + faceCapPres1[0][0], + dfacePhaseVolFrac_dCapPres1[0][0] ); + facePhaseVolFrac1[0][0] = fmin( 1.0, fmax( facePhaseVolFrac1[0][0], 0.0 )); + facePhaseVolFrac1[0][1] = fmin( 1.0, fmax( facePhaseVolFrac1[0][1], 0.0 )); + //get derivatives: + capPresWrapper1.compute( facePhaseVolFrac1[0], + JFunc1.toSliceConst(), + faceCapPres1[0][0], + dCapPres1_dfacePhaseVolFrac[0][0] ); + + } + else if constexpr (std::is_same_v< T3, TableCapillaryPressureHysteresis >) { + capPresWrapper1.computeInv( facePhaseVolFrac1[0], + phaseMaxHistoricalVolFraction1[0], + phaseMinHistoricalVolFraction1[0], + faceTrappedVolFrac1[0][0], + faceCapPres1[0][0], + dCapPres1_dfacePhaseVolFrac[0][0], + modes[0] ); + facePhaseVolFrac1[0][0] = fmin( 1.0, fmax( facePhaseVolFrac1[0][0], 0.0 )); + facePhaseVolFrac1[0][1] = fmin( 1.0, fmax( facePhaseVolFrac1[0][1], 0.0 )); + capPresWrapper1.compute( facePhaseVolFrac1[0], + phaseMaxHistoricalVolFraction1[0], + phaseMinHistoricalVolFraction1[0], + faceTrappedVolFrac1[0][0], + faceCapPres1[0][0], + dCapPres1_dfacePhaseVolFrac[0][0], + modes[0] ); + + } + else + { + capPresWrapper1.computeInv( facePhaseVolFrac1[0], + faceCapPres1[0][0], + dfacePhaseVolFrac_dCapPres1[0][0] ); + facePhaseVolFrac1[0][0] = fmin( 1.0, fmax( facePhaseVolFrac1[0][0], 0.0 )); + facePhaseVolFrac1[0][1] = fmin( 1.0, fmax( facePhaseVolFrac1[0][1], 0.0 )); + //get derivatives: + capPresWrapper1.compute( facePhaseVolFrac1[0], + faceCapPres1[0][0], + dCapPres1_dfacePhaseVolFrac[0][0] ); + } + + using T4 = std::decay_t< decltype(castedCapPres2) >; + if constexpr (std::is_same_v< T4, JFunctionCapillaryPressure >) { + // evaluating cell-center Pc: + capPresWrapper2.computeInv( facePhaseVolFrac2[0], + JFunc2.toSliceConst(), + faceCapPres2[0][0], + dfacePhaseVolFrac_dCapPres2[0][0] ); + facePhaseVolFrac2[0][0] = fmin( 1.0, fmax( facePhaseVolFrac2[0][0], 0.0 )); + facePhaseVolFrac2[0][1] = fmin( 1.0, fmax( facePhaseVolFrac2[0][1], 0.0 )); + +//get derivatives: + + capPresWrapper2.compute( facePhaseVolFrac2[0], + JFunc2.toSliceConst(), + faceCapPres2[0][0], + dCapPres2_dfacePhaseVolFrac[0][0] ); + + } + else if constexpr (std::is_same_v< T4, TableCapillaryPressureHysteresis >) { + capPresWrapper2.computeInv( facePhaseVolFrac2[0], + phaseMaxHistoricalVolFraction2[0], + phaseMinHistoricalVolFraction2[0], + faceTrappedVolFrac2[0][0], + faceCapPres2[0][0], + dCapPres2_dfacePhaseVolFrac[0][0], + modes[1] ); + facePhaseVolFrac2[0][0] = fmin( 1.0, fmax( facePhaseVolFrac2[0][0], 0.0 )); + facePhaseVolFrac2[0][1] = fmin( 1.0, fmax( facePhaseVolFrac2[0][1], 0.0 )); + capPresWrapper2.compute( facePhaseVolFrac2[0], + phaseMaxHistoricalVolFraction2[0], + phaseMinHistoricalVolFraction2[0], + faceTrappedVolFrac2[0][0], + faceCapPres2[0][0], + dCapPres2_dfacePhaseVolFrac[0][0], + modes[1] ); + + } + else + { + // evaluating cell-center Pc: + capPresWrapper2.computeInv( facePhaseVolFrac2[0], + faceCapPres2[0][0], + dfacePhaseVolFrac_dCapPres2[0][0] ); + facePhaseVolFrac2[0][0] = fmin( 1.0, fmax( facePhaseVolFrac2[0][0], 0.0 )); + facePhaseVolFrac2[0][1] = fmin( 1.0, fmax( facePhaseVolFrac2[0][1], 0.0 )); + +//get derivatives: + + capPresWrapper2.compute( facePhaseVolFrac2[0], + faceCapPres2[0][0], + dCapPres2_dfacePhaseVolFrac[0][0] ); + } + + // compute relative permeability for both faces: + + using T7 = std::decay_t< decltype(castedRelPerm1) >; + if constexpr (std::is_same_v< T7, constitutive::TableRelativePermeabilityHysteresis >) { + + relPermWrapper1.compute( facePhaseVolFrac1[0], + phaseMaxHistoricalVolFraction1[0], + phaseMinHistoricalVolFraction1[0], + faceTrappedVolFrac1[0][0], + faceRelPerm1[0][0], + dfacePhaseRelPerm1_dPhaseVolFrac[0][0] ); + + } + else + { + + relPermWrapper1.compute( facePhaseVolFrac1[0], + faceTrappedVolFrac1[0][0], + faceRelPerm1[0][0], + dfacePhaseRelPerm1_dPhaseVolFrac[0][0] ); + } + + using T8 = std::decay_t< decltype(castedRelPerm2) >; + if constexpr (std::is_same_v< T8, constitutive::TableRelativePermeabilityHysteresis >) { + + relPermWrapper2.compute( facePhaseVolFrac2[0], + phaseMaxHistoricalVolFraction2[0], + phaseMinHistoricalVolFraction2[0], + faceTrappedVolFrac2[0][0], + faceRelPerm2[0][0], + dfacePhaseRelPerm2_dPhaseVolFrac[0][0] ); + + } + else + { + + relPermWrapper2.compute( facePhaseVolFrac2[0], + faceTrappedVolFrac2[0][0], + faceRelPerm2[0][0], + dfacePhaseRelPerm2_dPhaseVolFrac[0][0] ); + } + + + // Brenier and Jaffre's PPU upwinding: + bool check = false; + bool k_up_0_w = 1; + bool k_up_1_w = 1; + bool k_up_0_n = 1; + bool k_up_1_n = 1; + + if( uT < 0.0 ) + { + k_up_0_w = 0; + k_up_1_w = 0; + k_up_0_n = 0; + k_up_1_n = 0; + } + + if( std::fabs( uT ) < 1e-20 ) + { + k_up_0_w = 1; + k_up_1_w = 1; + k_up_0_n = 0; + k_up_1_n = 0; + } + + + localIndex k_up_0[2] = {static_cast< localIndex >(!k_up_0_w), static_cast< localIndex >(!k_up_0_n)}; + localIndex k_up_1[2] = {static_cast< localIndex >(k_up_1_w), static_cast< localIndex >(k_up_1_n)}; + bool k_up_0_check[2] = {false, false}; + bool k_up_1_check[2] = {false, false}; + + while( !check ) + { + k_up_0_check[0] = false; + k_up_0_check[1] = false; + k_up_1_check[0] = false; + k_up_1_check[1] = false; + for( integer ix = 0; ix < 2; ++ix ) // for loop over each half flux + { + + // clear working arrays for each half flux: + real64 densMean[2]{}; + real64 dDensMean_dP[2][2]{}; + + real64 presGrad[2]{}; + real64 dPresGrad_dP[2][2]{}; + + real64 gravHead[2]{}; + real64 dGravHead_dP[2][2]{}; + + real64 capGrad[2]{}; + // real64 capPresIC[2][2]{}; + // real64 jFMultiplier[2][2]{}; + real64 dCapGrad_dP[2][2]{}; + real64 dCapGrad_dS[2][2]{}; + + real64 mobility[2]{}; + real64 dMob_dP[2][2]{}; + real64 dMob_dS[2][2]{}; + + real64 total_mobility = 0; + gravityCof[0] = 0; + gravityCof[1] = 0; + + for( integer ip = 0; ip < 2; ++ip ) // loop over phases + { + // calculate quantities on primary connected cells + if( ix == 0 ) + { + density[ip] = phaseDensity1[0][0][ip]; + dDens_dP[ip][ix] = dPhaseDens1_dP[0][0][ip][ip]; + + viscosity[ip] = phaseViscosity1[0][0][ip]; + dVisc_dP[ip][ix] = dPhaseVisc1_dP[0][0][ip][ip]; + } + else + { + density[ip] = phaseDensity2[0][0][ip]; + dDens_dP[ip][ix] = dPhaseDens2_dP[0][0][ip][ip]; + + viscosity[ip] = phaseViscosity2[0][0][ip]; + dVisc_dP[ip][ix] = dPhaseVisc2_dP[0][0][ip][ip]; + } + + densMean[ip] = density[ip]; + dDensMean_dP[ip][0] = dDens_dP[ip][ix]; + dDensMean_dP[ip][1] = dDens_dP[ip][ix]; + + //***** calculation of flux ***** + + // compute potential difference + real64 potScale = 0.0; + real64 dPresGrad_dTrans = 0.0; + real64 dGravHead_dTrans = 0.0; + real64 dCapGrad_dTrans = 0.0; + constexpr int signPotDiff[2] = {1, -1}; + constexpr int signTix[2] = {1, -1}; + + for( integer ke = 0; ke < 2; ++ke ) + { + + real64 const pressure = pressures[ix]; + presGrad[ip] += signTix[ke] * transHat[ix] * pressure; + dPresGrad_dTrans += signPotDiff[ke] * pressure; + dPresGrad_dP[ip][ke] = signTix[ke] * transHat[ix]; + + real64 gravD = 0.0; + + if( ke == 0 ) + { + gravD += signTix[ke] * transHat[ix] * gravCoef[ix]; + } + else + { + gravD += signTix[ke] * transHat[ix] * gravCoefHat[ix]; + } + + real64 pot = signTix[ke] * transHat[ix] * pressure - densMean[ip] * gravD; + + gravHead[ip] += densMean[ip] * gravD; + gravityCof[ip] += gravD; + + dGravHead_dTrans += signPotDiff[ke] * densMean[ip] * gravCoefHat[ix]; + + for( integer i = 0; i < 2; ++i ) + { + dGravHead_dP[ip][i] += dDensMean_dP[ip][i] * gravD; + } + + real64 capPres = capPres1[0][0][ip]; + + if( ke == 1 && ix == 0 ) + { + capPres = faceCapPres1[0][0][ip]; + } + else if( ke == 1 && ix == 1 ) + { + capPres = faceCapPres2[0][0][ip]; + } + else if( ke == 0 && ix == 1 ) + { + + capPres = capPres2[0][0][ip]; + } + + dCapGrad_dTrans -= signPotDiff[ke] * capPres; + pot -= signTix[ke] * transHat[ix] * capPres; + + capGrad[ip] -= signTix[ke] * transHat[ix] * capPres; + + potScale = fmax( potScale, fabs( pot ) ); + } + + for( integer ke = 0; ke < 2; ++ke ) + { + dPresGrad_dP[ip][ke] += signTix[ke] * dTransHat_dP[ix] * dPresGrad_dTrans; + + dGravHead_dP[ip][ke] += signTix[ke] * dTransHat_dP[ix] * dGravHead_dTrans; + + // real64 constexpr eps = 1e-18; + real64 dCapPres_dS = dCapPres1_dPhaseVolFrac[0][0][ip][ip]; + + if( ke == 1 && ix == 0 ) + { + dCapPres_dS = dCapPres1_dfacePhaseVolFrac[0][0][ip][ip]; + } + else if( ke == 1 && ix == 1 ) + { + dCapPres_dS = dCapPres2_dfacePhaseVolFrac[0][0][ip][ip]; + } + else if( ke == 0 && ix == 1 ) + { + dCapPres_dS = dCapPres2_dPhaseVolFrac[0][0][ip][ip]; + } + + dCapGrad_dP[ip][ke] += signTix[ke] * dTransHat_dP[ix] * dCapGrad_dTrans; + dCapGrad_dS[ip][ke] -= signTix[ke] * transHat[ix] * dCapPres_dS; + } + + // *** upwinding *** + // compute potential gradient + real64 potGrad = presGrad[ip] - gravHead[ip]; + + potGrad += capGrad[ip]; + + // choose upstream cell + constexpr int sign[2] = {1, -1}; + + if( k_up_0[ip] == 1 && ix == 0 ) + { + mobility[ip] = faceRelPerm1[0][0][ip] / viscosity[ip]; + dMob_dP[ip][k_up_0[ip]] = mobility[ip] * (-dVisc_dP[ip][ix] / viscosity[ip]); + + dMob_dS[ip][k_up_0[ip]] = sign[ip] * dfacePhaseRelPerm1_dPhaseVolFrac[0][0][ip][ip] / viscosity[ip]; + } + else if( k_up_1[ip] == 0 && ix == 1 ) + { + mobility[ip] = relPerm2[0][0][ip] / viscosity[ip]; + dMob_dP[ip][0] = mobility[ip] * (-dVisc_dP[ip][ix] / viscosity[ip]); + dMob_dS[ip][0] = sign[ip] * dPhaseRelPerm2_dPhaseVolFrac[0][0][ip][ip] / viscosity[ip]; + } + else if( k_up_1[ip] == 1 && ix == 1 ) + { + mobility[ip] = faceRelPerm2[0][0][ip] / viscosity[ip]; + dMob_dP[ip][1] = mobility[ip] * (-dVisc_dP[ip][ix] / viscosity[ip]); + dMob_dS[ip][1] = sign[ip] * dfacePhaseRelPerm2_dPhaseVolFrac[0][0][ip][ip] / viscosity[ip]; + } + else + { + mobility[ip] = relPerm1[0][0][ip] / viscosity[ip]; + dMob_dP[ip][ix] = mobility[ip] * (-dVisc_dP[ip][ix] / viscosity[ip]); + dMob_dS[ip][ix] = sign[ip] * dPhaseRelPerm1_dPhaseVolFrac[0][0][ip][ip] / viscosity[ip]; + } + real64 constexpr eps = 0.0; + total_mobility += mobility[ip] + eps; + } // loop over phases + + /// Three Forces Flux Contribution: 1- Viscous 2- Gravitational 3- Capillary + constexpr int sign[2] = {1, -1}; + // real64 constexpr eps = 0.0; + + // loop over phases + for( integer ip = 0; ip < 2; ++ip ) + { + // 1- Viscous: pressure gradient depends on all points in the stencil + viscous[ip][ix] = mobility[ip] / total_mobility * uT; + halfFluxVal[ip][ix] = viscous[ip][ix]; + + for( integer ke = 0; ke < 2; ++ke ) + { + real64 dV_dP = sign[ip] * (dMob_dP[0][ke] * mobility[1] - dMob_dP[1][ke] * mobility[0]) / (total_mobility * total_mobility) * uT; + real64 dV_dS = sign[ip] * (dMob_dS[0][ke] * mobility[1] - dMob_dS[1][ke] * mobility[0]) / (total_mobility * total_mobility) * uT; + real64 dV_du = mobility[ip] / total_mobility; + + if( ix == 0 ) + { + dV1_dS[ip][ke] = dV_dS; + dhalfFlux1_dP[ip][ke] = dV_dP; + dhalfFlux1_dS[ip][ke] = dV1_dS[ip][ke]; + dhalfFlux_duT[ip][ix] = dV_du; + dV1_dpc[ip][ke] = dV1_dS[ip][ke] * dfacePhaseVolFrac_dCapPres1[0][0][0][0]; + // GEOS_UNUSED_VAR( dV1_dpc[ip][ke] ); + GEOS_UNUSED_VAR( dhalfFlux_duT[ip][ix] ); + } + else + { + dV2_dS[ip][ke] = dV_dS; + dhalfFlux2_dP[ip][ke] = dV_dP; + dhalfFlux2_dS[ip][ke] = dV2_dS[ip][ke]; + dhalfFlux_duT[ip][ix]= dV_du; + dV2_dpc[ip][ke] = dV2_dS[ip][ke] * dfacePhaseVolFrac_dCapPres2[0][0][0][0]; + // GEOS_UNUSED_VAR( dV2_dpc[ip][ke] ); + GEOS_UNUSED_VAR( dhalfFlux_duT[ip][ix] ); + } + } + + // 2- Gravitational: gravitational head depends only on the two cells connected (same as mean density) + bouyancy[ip][ix] = -1.0 * sign[ix] * sign[ip] * mobility[0] * mobility[1] / total_mobility * gravityCof[0] * (density[0] - density[1]); + halfFluxVal[ip][ix] += bouyancy[ip][ix]; + + for( integer ke = 0; ke < 2; ++ke ) + { + real64 dG_dP = sign[ix] * sign[ip] * (dMob_dP[0][ke] * mobility[1] * mobility[1] + dMob_dP[1][ke] * mobility[0] * mobility[0]) / (total_mobility * total_mobility) * gravityCof[0] * + (density[0] - density[1]) + + sign[ix] * (mobility[0] * mobility[1]) / total_mobility * (dDens_dP[0][ix] - dDens_dP[1][ix]); + + real64 dG_dS = sign[ix] * sign[ip] * (dMob_dS[0][ke] * mobility[1] * mobility[1] + dMob_dS[1][ke] * mobility[0] * mobility[0]) / (total_mobility * total_mobility) * + gravityCof[0] * (density[0] - density[1]); + + if( ix == 0 ) + { + dG1_dS[ip][ke] = dG_dS; + dhalfFlux1_dP[ip][ke] -= dG_dP; + dhalfFlux1_dS[ip][ke] -= dG1_dS[ip][ke]; + dG1_dpc[ip][ke] = dG1_dS[ip][ke] * dfacePhaseVolFrac_dCapPres1[0][0][0][0]; + // GEOS_UNUSED_VAR( dG1_dpc[ip][ke] ); + } + else + { + dG2_dS[ip][ke] = dG_dS; + dhalfFlux2_dP[ip][ke] -= dG_dP; + dhalfFlux2_dS[ip][ke] -= dG2_dS[ip][ke]; + dG2_dpc[ip][ke] = dG2_dS[ip][ke] * dfacePhaseVolFrac_dCapPres2[0][0][0][0]; + // GEOS_UNUSED_VAR( dG2_dpc[ip][ke] ); + } + } + + // 3- Capillary: capillary pressure contribution + capillarity[ip][ix] = -1.0 * sign[ix] * sign[ip] * mobility[0] * mobility[1] / total_mobility * (capGrad[1] -capGrad[0]); + halfFluxVal[ip][ix] += capillarity[ip][ix]; + + for( integer ke = 0; ke < 2; ++ke ) + { + real64 dC_dP = sign[ix] * sign[ip] * (dMob_dP[0][ke] * mobility[1] * mobility[1] + dMob_dP[1][ke] * mobility[0] * mobility[0]) / (total_mobility * total_mobility) * + (capGrad[1] -capGrad[0]) + + sign[ix] * sign[ip] * mobility[0] * mobility[1] / total_mobility * (dCapGrad_dP[1][ke] - dCapGrad_dP[0][ke]); + + real64 dC_dS_term1 = sign[ix] * sign[ip] * (dMob_dS[0][ke] * mobility[1] * mobility[1] + dMob_dS[1][ke] * mobility[0] * mobility[0]) / (total_mobility * total_mobility) * + (capGrad[1] -capGrad[0]); + + real64 dC_dS_term2 = sign[ix] * sign[ip] * (mobility[0] * mobility[1]) / total_mobility; + + real64 dC_dS_term3 = (dCapGrad_dS[1][ke] - dCapGrad_dS[0][ke]); + + if( ix == 0 ) + { + dC1_dS[ip][ke] = dC_dS_term1 + dC_dS_term2 * dC_dS_term3; + dhalfFlux1_dP[ip][ke] -= dC_dP; + dhalfFlux1_dS[ip][ke] -= dC1_dS[ip][ke]; + if (std::fabs(facePhaseVolFrac1[0][0] - 1.0) > 1e-8){ + dC1_dpc[ip][ke] = dC1_dS[ip][ke] * dfacePhaseVolFrac_dCapPres1[0][0][0][0]; + } else { + dC1_dpc[ip][ke] = dC_dS_term1 * dfacePhaseVolFrac_dCapPres1[0][0][0][0]; + } + + // GEOS_UNUSED_VAR( dC1_dpc[ip][ke] ); + } + else + { + dC2_dS[ip][ke] = dC_dS_term1 + dC_dS_term2 * dC_dS_term3; + dhalfFlux2_dP[ip][ke] -= dC_dP; + dhalfFlux2_dS[ip][ke] -= dC2_dS[ip][ke]; + if (std::fabs(facePhaseVolFrac2[0][0] - 1.0) > 1e-8){ + dC2_dpc[ip][ke] = dC2_dS[ip][ke] * dfacePhaseVolFrac_dCapPres2[0][0][0][0]; + } else { + dC2_dpc[ip][ke] = dC_dS_term1 * dfacePhaseVolFrac_dCapPres2[0][0][0][0]; + } + // GEOS_UNUSED_VAR( dC2_dpc[ip][ke] ); + } + + } + + + if( halfFluxVal[ip][0] > 0.0 ) + { + k_up_0_check[ip] = true; + } + if( halfFluxVal[ip][1] > 0.0 ) + { + k_up_1_check[ip] = true; + } + + } // loop over phases + + } // loop over half fluxes + + check = true; + + // Brenier and Jaffre's PPU check: + bool flip0[2] = {0, 0}; + bool flip0_k_up[2] = {0, 0}; + bool flip1[2] = {0, 0}; + bool flip1_k_up[2] = {0, 0}; + // loop over phases + for( integer ip = 0; ip < 2; ++ip ) + { + bool k_up_0_b = !static_cast< bool >(k_up_0[ip]); + bool k_up_1_b = static_cast< bool >(k_up_1[ip]); + flip0_k_up[ip] = k_up_0_b; + flip1_k_up[ip] = k_up_1_b; + + if( std::fabs( uT ) < 1e-20 ) + { + + if((std::fabs( halfFluxVal[ip][0] ) < 1e-20) && (std::fabs( halfFluxVal[ip][1] ) < 1e-20)) + { + k_up_0_check[ip] = !k_up_0_b; + k_up_1_check[ip] = !k_up_1_b; + } + else + { + k_up_0_check[ip] = k_up_0_b; + k_up_1_check[ip] = k_up_1_b; + } + + if( k_up_0_check[ip] != k_up_0_b ) + { + flip0[ip] = 1; + check = false; + } + + if( k_up_1_check[ip] != k_up_1_b ) + { + flip1[ip] = 1; + check = false; + } + + } + else + { + if( std::fabs( halfFluxVal[ip][0] ) < 1e-20 ) + { + k_up_0_check[ip] = k_up_0_b; + } + + if( std::fabs( halfFluxVal[ip][1] ) < 1e-20 ) + { + k_up_1_check[ip] = k_up_1_b; + } + + if( k_up_0_check[ip] != k_up_0_b ) + { + k_up_0[ip] = static_cast< localIndex >(k_up_0_b); + check = false; + } + + if( k_up_1_check[ip] != k_up_1_b ) + { + k_up_1[ip] = static_cast< localIndex >(!k_up_1_b); + check = false; + } + } + + } + + if( flip0[0] || flip0[1] ) + { + k_up_0[0] = static_cast< localIndex >(flip0_k_up[0]); + k_up_0[1] = static_cast< localIndex >(flip0_k_up[1]); + } + + if( flip1[0] || flip1[1] ) + { + k_up_1[0] = static_cast< localIndex >(!flip1_k_up[0]); + k_up_1[1] = static_cast< localIndex >(!flip1_k_up[1]); + } + + } // while check for BJ PPU + + real64 constexpr eps2 = 4.9304e-32; + // real64 constexpr eps2 = 0.0; + // newton update + dhalfFlux_dpc[0][0] = dhalfFlux1_dS[0][1]*dfacePhaseVolFrac_dCapPres1[0][0][0][0]; + real64 dhalfFlux_dpc00 = dV1_dpc[0][1] - dG1_dpc[0][1] - dC1_dpc[0][1]; + dhalfFlux_dpc[0][1] = dhalfFlux2_dS[0][1]*dfacePhaseVolFrac_dCapPres2[0][0][0][0]; + real64 dhalfFlux_dpc01 = dV2_dpc[0][1] - dG2_dpc[0][1] - dC2_dpc[0][1]; + + dhalfFlux_dpc[1][0] = dhalfFlux1_dS[1][1]*dfacePhaseVolFrac_dCapPres1[0][0][0][0]; + real64 dhalfFlux_dpc10 = dV1_dpc[1][1] - dG1_dpc[1][1] - dC1_dpc[1][1]; + dhalfFlux_dpc[1][1] = dhalfFlux2_dS[1][1]*dfacePhaseVolFrac_dCapPres2[0][0][0][0]; + real64 dhalfFlux_dpc11 = dV2_dpc[1][1] - dG2_dpc[1][1] - dC2_dpc[1][1]; + + dhalfFlux_dpc[0][0] = dhalfFlux_dpc00; + dhalfFlux_dpc[0][1] = dhalfFlux_dpc01; + dhalfFlux_dpc[1][0] = dhalfFlux_dpc10; + dhalfFlux_dpc[1][1] = dhalfFlux_dpc11; + + local_jacobian = dhalfFlux_dpc[0][0] - dhalfFlux_dpc[0][1] + eps2; + local_residual = halfFluxVal[0][0] - halfFluxVal[0][1]; + + // if (iter == 1) { + // grad_phi[0] = local_residual; + // } + + // Check convergence + if( std::fabs( local_residual ) < tol ) + { + // if( std::fabs( dhalfFlux_dpc[0][0] - dhalfFlux_dpc[0][1] ) (max_iter - 2)) ) + { + if( div == 0 ) + { + iter = 0; + div++; + Pc_int = Pc_min_all; + } + else if( div == 1 ) + { + iter = 0; + div++; + Pc_int = Pc_max_all; + } + else if( div > 1 ) + { + local_jacobian = 0.0; + + iter = max_iter; + } + } + else + { + + real64 deltaPc = local_residual/local_jacobian; + + if( std::fabs( local_residual ) < tol ) + { + + converged = 1; + outFile << GEOS_FMT( "{:10.10e}", local_jacobian ); + outFile << GEOS_FMT( ",{:10.10e}", local_residual ); + outFile << GEOS_FMT( ",{:10.10e}", halfFluxVal[0][0] ); + outFile << GEOS_FMT( ",{:10.10e}", halfFluxVal[0][1] ); + outFile << GEOS_FMT( ",{:10.10e}", Pc_int_iterate ); + outFile << GEOS_FMT( ",{:10.10e}", faceCapPres1[0][0][0] ); + outFile << GEOS_FMT( ",{:10.10e}", faceCapPres2[0][0][0] ); + outFile << GEOS_FMT( ",{:10.10e}", halfFluxVal[1][0] ); + outFile << GEOS_FMT( ",{:10.10e}", halfFluxVal[1][1] ); + outFile << GEOS_FMT( ",{:10.10e}", viscous[0][0] ); + outFile << GEOS_FMT( ",{:10.10e}", viscous[1][0] ); + outFile << GEOS_FMT( ",{:10.10e}", viscous[0][1] ); + outFile << GEOS_FMT( ",{:10.10e}", viscous[1][1] ); + outFile << GEOS_FMT( ",{:10.10e}", bouyancy[0][0] ); + outFile << GEOS_FMT( ",{:10.10e}", bouyancy[1][0] ); + outFile << GEOS_FMT( ",{:10.10e}", bouyancy[0][1] ); + outFile << GEOS_FMT( ",{:10.10e}", bouyancy[1][1] ); + outFile << GEOS_FMT( ",{:10.10e}", capillarity[0][0] ); + outFile << GEOS_FMT( ",{:10.10e}", capillarity[1][0] ); + outFile << GEOS_FMT( ",{:10.10e}", capillarity[0][1] ); + outFile << GEOS_FMT( ",{:10.10e}", capillarity[1][1] ); + outFile << GEOS_FMT( ",{:10.10e}", transHat[0] ); + outFile << GEOS_FMT( ",{:10.10e}", transHat[1] ); + outFile << GEOS_FMT( ",{:10.10e}", gravCoefHat[0] ); + outFile << GEOS_FMT( ",{:10.10e}", gravCoef[0] ); + outFile << GEOS_FMT( ",{:10.10e}", gravCoef[1] ); + outFile << GEOS_FMT( ",{:10.10e}", uT ); + outFile << GEOS_FMT( ",{:10.10e}", duT_dP[0] ); + outFile << GEOS_FMT( ",{:10.10e}", duT_dS[0] ); + outFile << GEOS_FMT( ",{:10.10e}", duT_dP[1] ); + outFile << GEOS_FMT( ",{:10.10e}", duT_dS[1] ); + outFile << std::endl; + break; // Converged + } + + // Damping option: + if (damping) { + real64 max_dpc = fmax( fabs(dCapPres1_dfacePhaseVolFrac[0][0][0][0]), fabs(dCapPres2_dfacePhaseVolFrac[0][0][0][0])); + + real64 sign = std::copysign(1.0, deltaPc); + + deltaPc = fmin( fabs(deltaPc), max_dpc * 0.1 ); + deltaPc *= sign; + + } + + if (bisection && iter < 7){ + if ( iter == 0 ){ + + old_Pc_int = Pc_int; + Pc_int = next_Pc_int; + old_residual = local_residual; + + } else if (old_residual * local_residual < 0.0 ) { + + Pc_int = (next_Pc_int + old_Pc_int) / 2.0; + old_residual = local_residual; + old_Pc_int = next_Pc_int; + next_Pc_int = Pc_int; + + } else if (old_residual * local_residual > 0.0 ) { + + Pc_int = old_Pc_int; + old_residual = local_residual; + old_Pc_int = next_Pc_int; + next_Pc_int = Pc_int; + + } else { + + Pc_int = old_Pc_int; + old_residual = local_residual; + old_Pc_int = next_Pc_int; + next_Pc_int = Pc_int; + + } + + } else if (newton_path){ + Pc_int -= next_Pc_int; + + } else { + + Pc_int -= deltaPc; + + } + + + // truncate the updated capillary pressure (extended capillary pressure condition) for reporting/plotting: + + real64 faceCapPres1_plot = fmin( Pc1_max, fmax( Pc_int, Pc1_min )); + real64 faceCapPres2_plot = fmin( Pc2_max, fmax( Pc_int, Pc2_min )); + faceCapPres1_plot = fmin( Pc2_max, fmax( faceCapPres1_plot, Pc2_min )); + faceCapPres2_plot = fmin( Pc1_max, fmax( faceCapPres2_plot, Pc1_min )); + + // Write data to the file + outFile << GEOS_FMT( "{:10.10e}", local_jacobian ); + outFile << GEOS_FMT( ",{:10.10e}", local_residual ); + outFile << GEOS_FMT( ",{:10.10e}", halfFluxVal[0][0] ); + outFile << GEOS_FMT( ",{:10.10e}", halfFluxVal[0][1] ); + outFile << GEOS_FMT( ",{:10.10e}", Pc_int_iterate ); + outFile << GEOS_FMT( ",{:10.10e}", faceCapPres1[0][0][0] ); + outFile << GEOS_FMT( ",{:10.10e}", faceCapPres2[0][0][0] ); + outFile << GEOS_FMT( ",{:10.10e}", halfFluxVal[1][0] ); + outFile << GEOS_FMT( ",{:10.10e}", halfFluxVal[1][1] ); + outFile << GEOS_FMT( ",{:10.10e}", viscous[0][0] ); + outFile << GEOS_FMT( ",{:10.10e}", viscous[1][0] ); + outFile << GEOS_FMT( ",{:10.10e}", viscous[0][1] ); + outFile << GEOS_FMT( ",{:10.10e}", viscous[1][1] ); + outFile << GEOS_FMT( ",{:10.10e}", bouyancy[0][0] ); + outFile << GEOS_FMT( ",{:10.10e}", bouyancy[1][0] ); + outFile << GEOS_FMT( ",{:10.10e}", bouyancy[0][1] ); + outFile << GEOS_FMT( ",{:10.10e}", bouyancy[1][1] ); + outFile << GEOS_FMT( ",{:10.10e}", capillarity[0][0] ); + outFile << GEOS_FMT( ",{:10.10e}", capillarity[1][0] ); + outFile << GEOS_FMT( ",{:10.10e}", capillarity[0][1] ); + outFile << GEOS_FMT( ",{:10.10e}", capillarity[1][1] ); + outFile << GEOS_FMT( ",{:10.10e}", transHat[0] ); + outFile << GEOS_FMT( ",{:10.10e}", transHat[1] ); + outFile << GEOS_FMT( ",{:10.10e}", gravCoefHat[0] ); + outFile << GEOS_FMT( ",{:10.10e}", gravCoef[0] ); + outFile << GEOS_FMT( ",{:10.10e}", gravCoef[1] ); + outFile << GEOS_FMT( ",{:10.10e}", uT ); + outFile << GEOS_FMT( ",{:10.10e}", duT_dP[0] ); + outFile << GEOS_FMT( ",{:10.10e}", duT_dS[0] ); + outFile << GEOS_FMT( ",{:10.10e}", duT_dP[1] ); + outFile << GEOS_FMT( ",{:10.10e}", duT_dS[1] ); + outFile << std::endl; + + iter++; + + } + + + } // while loop + + + + if( converged ) + { + + // Global derivatives: + real64 constexpr eps3 = 4.9304e-32; + // real64 constexpr eps3 = 0.0; + + real64 const dPc_int_dS1 =(-1.0) * (dhalfFlux1_dS[0][0] + dhalfFlux_duT[0][0] * duT_dS[0] - dhalfFlux_duT[0][1] * duT_dS[0]) / (dhalfFlux_dpc[0][0] - dhalfFlux_dpc[0][1] + eps3); + real64 const dPc_int_dS2 =(-1.0) * (dhalfFlux_duT[0][0] * duT_dS[1] - dhalfFlux2_dS[0][0] - dhalfFlux_duT[0][1] * duT_dS[1]) / (dhalfFlux_dpc[0][0] - dhalfFlux_dpc[0][1] + eps3); + real64 const dPc_int_du =(-1.0) * (dhalfFlux_duT[0][0] - dhalfFlux_duT[0][1]) / (dhalfFlux_dpc[0][0] - dhalfFlux_dpc[0][1] + eps3); + + dFlux_dP[0][0] = (dhalfFlux_duT[0][0] * duT_dP[0] + dhalfFlux_dpc[0][0] * dPc_int_du * duT_dP[0]) * density2[0] + halfFluxVal[0][0] * dDens_dP2[0][0]; + dFlux_dS[0][0] = (dhalfFlux1_dS[0][0] + dhalfFlux_duT[0][0] * duT_dS[0] + dhalfFlux_dpc[0][0] * dPc_int_dS1) * density2[0]; + + dFlux_dP[0][1] = (dhalfFlux_duT[0][1] * duT_dP[1] + dhalfFlux_dpc[0][1] * dPc_int_du * duT_dP[1]) * density2[0] + halfFluxVal[0][1] * dDens_dP2[0][1]; + dFlux_dS[0][1] = (dhalfFlux2_dS[0][0] + dhalfFlux_duT[0][1] * duT_dS[1] + dhalfFlux_dpc[0][1] * dPc_int_dS2) * density2[0]; + + dFlux_dP[1][0] = (dhalfFlux_duT[1][0] * duT_dP[0] + dhalfFlux_dpc[1][0] * dPc_int_du * duT_dP[0]) * density2[1] + halfFluxVal[1][0] * dDens_dP2[1][0]; + dFlux_dS[1][0] = (dhalfFlux1_dS[1][0] + dhalfFlux_duT[1][0] * duT_dS[0] + dhalfFlux_dpc[1][0] * dPc_int_dS1) * density2[1]; + + dFlux_dP[1][1] = (dhalfFlux_duT[1][1] * duT_dP[1] + dhalfFlux_dpc[1][1] * dPc_int_du * duT_dP[1]) * density2[1] + halfFluxVal[1][1] * dDens_dP2[1][1]; + dFlux_dS[1][1] = (dhalfFlux2_dS[1][0] + dhalfFlux_duT[1][1] * duT_dS[1] + dhalfFlux_dpc[1][1] * dPc_int_dS2) * density2[1]; + + fluxVal[0] = halfFluxVal[0][0] * density2[0]; + fluxVal[1] = halfFluxVal[1][0] * density2[1]; + + } else { + + std::cout << "**********************Diverged*******************" << std::endl; + + } + + phi[0] = fluxVal[0]; + phi[1] = fluxVal[1]; + + grad_phi_P[0] = dFlux_dP[0][0]; + grad_phi_P[1] = dFlux_dP[0][1]; + grad_phi_P[2] = dFlux_dP[1][0]; + grad_phi_P[3] = dFlux_dP[1][1]; + + grad_phi_S[0] = dFlux_dS[0][0]; + grad_phi_S[1] = dFlux_dS[0][1]; + grad_phi_S[2] = dFlux_dS[1][0]; + grad_phi_S[3] = dFlux_dS[1][1]; + + +// Close the file after writing + outFile.close(); + + } ); + + } ); + + } ); + + } ); + + +} /******************************** FluxComputeKernelBase ********************************/ @@ -78,17 +1626,23 @@ class FluxComputeKernelBase fields::flow::gravityCoefficient, fields::immiscibleMultiphaseFlow::phaseVolumeFraction, fields::immiscibleMultiphaseFlow::phaseMobility, + fields::immiscibleMultiphaseFlow::phaseMass_n, fields::immiscibleMultiphaseFlow::dPhaseMobility >; using MultiphaseFluidAccessors = StencilMaterialAccessors< constitutive::TwoPhaseImmiscibleFluid, fields::twophaseimmisciblefluid::phaseDensity, - fields::twophaseimmisciblefluid::dPhaseDensity >; + fields::twophaseimmisciblefluid::dPhaseDensity, + fields::twophaseimmisciblefluid::phaseViscosity, + fields::twophaseimmisciblefluid::dPhaseViscosity >; using CapPressureAccessors = StencilMaterialAccessors< CapillaryPressureBase, fields::cappres::phaseCapPressure, - fields::cappres::dPhaseCapPressure_dPhaseVolFraction >; + fields::cappres::dPhaseCapPressure_dPhaseVolFraction + >; + // ,fields::cappres::jFuncMultiplier >; + using PermeabilityAccessors = StencilMaterialAccessors< PermeabilityBase, @@ -135,12 +1689,16 @@ class FluxComputeKernelBase m_gravCoef( multiPhaseFlowAccessors.get( fields::flow::gravityCoefficient {} ) ), m_pres( multiPhaseFlowAccessors.get( fields::flow::pressure {} ) ), m_phaseVolFrac( multiPhaseFlowAccessors.get( fields::immiscibleMultiphaseFlow::phaseVolumeFraction {} ) ), + m_phaseMass_n( multiPhaseFlowAccessors.get( fields::immiscibleMultiphaseFlow::phaseMass_n {} ) ), m_mob( multiPhaseFlowAccessors.get( fields::immiscibleMultiphaseFlow::phaseMobility {} ) ), m_dMob( multiPhaseFlowAccessors.get( fields::immiscibleMultiphaseFlow::dPhaseMobility {} ) ), m_dens( fluidAccessors.get( fields::twophaseimmisciblefluid::phaseDensity {} ) ), + m_visc( fluidAccessors.get( fields::twophaseimmisciblefluid::phaseViscosity {} ) ), m_dDens_dPres( fluidAccessors.get( fields::twophaseimmisciblefluid::dPhaseDensity {} ) ), + m_dVisc_dPres( fluidAccessors.get( fields::twophaseimmisciblefluid::dPhaseViscosity {} ) ), m_phaseCapPressure( capPressureAccessors.get( fields::cappres::phaseCapPressure {} ) ), m_dPhaseCapPressure_dPhaseVolFrac( capPressureAccessors.get( fields::cappres::dPhaseCapPressure_dPhaseVolFraction {} ) ), + // m_jFuncMultiplier( capPressureAccessors.get( fields::cappres::jFuncMultiplier {} ) ), m_localMatrix( localMatrix ), m_localRhs( localRhs ), m_hasCapPressure ( hasCapPressure ), @@ -174,21 +1732,26 @@ class FluxComputeKernelBase /// Views on pressure and phase volume fraction ElementViewConst< arrayView1d< real64 const > > const m_pres; ElementViewConst< arrayView2d< real64 const, immiscibleFlow::USD_PHASE > > const m_phaseVolFrac; + ElementViewConst< arrayView2d< real64 const, immiscibleFlow::USD_PHASE > > const m_phaseMass_n; /// Views on fluid mobility ElementViewConst< arrayView2d< real64 const, immiscibleFlow::USD_PHASE > > const m_mob; ElementViewConst< arrayView3d< real64 const, immiscibleFlow::USD_PHASE_DS > > const m_dMob; - /// Views on fluid density + /// Views on fluid density and viscosity ElementViewConst< arrayView3d< real64 const, constitutive::multifluid::USD_PHASE > > const m_dens; + ElementViewConst< arrayView3d< real64 const, constitutive::multifluid::USD_PHASE > > const m_visc; ElementViewConst< arrayView4d< real64 const, constitutive::multifluid::USD_PHASE_DC > > const m_dDens_dPres; + ElementViewConst< arrayView4d< real64 const, constitutive::multifluid::USD_PHASE_DC > > const m_dVisc_dPres; /// Views on capillary pressure ElementViewConst< arrayView3d< real64 const, cappres::USD_CAPPRES > > const m_phaseCapPressure; ElementViewConst< arrayView4d< real64 const, cappres::USD_CAPPRES_DS > > const m_dPhaseCapPressure_dPhaseVolFrac; + // ElementViewConst< arrayView2d< real64 const > > const m_jFuncMultiplier; // Residual and jacobian + /// View on the local CRS matrix CRSMatrixView< real64, globalIndex const > const m_localMatrix; /// View on the local RHS @@ -314,6 +1877,11 @@ class FluxComputeKernel : public FluxComputeKernelBase /// Derivatives of transmissibility with respect to pressure real64 dTrans_dPres[maxNumConns][2]{}; + /// Transmissibility + real64 transmissibilityHat[maxNumConns][2]{}; + /// Derivatives of transmissibility with respect to pressure + real64 dTransHat_dPres[maxNumConns][2]{}; + // Local degrees of freedom and local residual/jacobian /// Indices of the matrix rows/columns corresponding to the dofs in this face @@ -372,7 +1940,7 @@ class FluxComputeKernel : public FluxComputeKernelBase * @param[inout] stack the stack variables * @param[in] NoOpFunc the function used to customize the computation of the flux */ - template< typename FUNC = NoOpFunc > // should change to multiphase + template< typename FUNC = NoOpFunc > // should change to multiphase GEOS_HOST_DEVICE void computeFlux( localIndex const iconn, StackVariables & stack, @@ -437,22 +2005,22 @@ class FluxComputeKernel : public FluxComputeKernelBase continue; } - real64 const density = m_dens[seri[ke]][sesri[ke]][sei[ke]][0][ip]; // r = rho1 || rho2 - real64 const dDens_dP = m_dDens_dPres[seri[ke]][sesri[ke]][sei[ke]][0][ip][Deriv::dP]; // dr/dP = dr1/dP1 || dr2/dP + real64 const density = m_dens[seri[ke]][sesri[ke]][sei[ke]][0][ip]; // r = rho1 || rho2 + real64 const dDens_dP = m_dDens_dPres[seri[ke]][sesri[ke]][sei[ke]][0][ip][Deriv::dP]; // dr/dP = dr1/dP1 || dr2/dP // average density and derivatives - densMean[ip] += density; // rho = (rho1 + rho2) - dDensMean_dP[ip][ke] = dDens_dP; // drho/dP = { (dr1/dP1) , (dr2/dP2) } + densMean[ip] += density; // rho = (rho1 + rho2) + dDensMean_dP[ip][ke] = dDens_dP; // drho/dP = { (dr1/dP1) , (dr2/dP2) } denom++; } if( denom > 1 ) { - densMean[ip] /= denom; // rho = (rho1 + rho2) / denom + densMean[ip] /= denom; // rho = (rho1 + rho2) / denom for( integer ke = 0; ke < 2; ++ke ) { - dDensMean_dP[ip][ke] /= denom; // drho/dP = { (dr1/dP1) / denom , (dr2/dP2) / denom } + dDensMean_dP[ip][ke] /= denom; // drho/dP = { (dr1/dP1) / denom , (dr2/dP2) / denom } } } @@ -471,106 +2039,114 @@ class FluxComputeKernel : public FluxComputeKernelBase localIndex const esr = sesri[ke]; localIndex const ei = sei[ke]; - real64 const pressure = m_pres[er][esr][ei]; // P = P1 || P2 - presGrad[ip] += trans[ke] * pressure; // DPv = T (P1 - P2) - dPresGrad_dTrans += signPotDiff[ke] * pressure; // dDPv/dT = (P1 - P2) - dPresGrad_dP[ip][ke] = trans[ke]; // dDPv/dP = { T , -T } + real64 const pressure = m_pres[er][esr][ei]; // P = P1 || P2 + presGrad[ip] += trans[ke] * pressure; // DPv = T (P1 - P2) + dPresGrad_dTrans += signPotDiff[ke] * pressure; // dDPv/dT = (P1 - P2) + dPresGrad_dP[ip][ke] = trans[ke]; // dDPv/dP = { T , -T } - real64 const gravD = trans[ke] * m_gravCoef[er][esr][ei]; // D = T g z1 || -T g z2 - real64 pot = trans[ke] * pressure - densMean[ip] * gravD; // Phi = T P1 - rho T g z1 || -T P2 + rho T g z2 + real64 const gravD = trans[ke] * m_gravCoef[er][esr][ei]; // D = T g z1 || -T g z2 + real64 pot = trans[ke] * pressure - densMean[ip] * gravD; // Phi = T P1 - rho T g z1 || -T P2 + rho T g z2 - gravHead[ip] += densMean[ip] * gravD; // DPg = rho (T g z1 - T g z2) = T rho g (z1 - z2) - dGravHead_dTrans += signPotDiff[ke] * densMean[ip] * m_gravCoef[er][esr][ei]; // dDPg/dT = rho g z1 - rho g z2 = rho g (z1 - z2) + gravHead[ip] += densMean[ip] * gravD; // DPg = rho (T g z1 - T g z2) = T rho g (z1 - + // z2) + dGravHead_dTrans += signPotDiff[ke] * densMean[ip] * m_gravCoef[er][esr][ei]; // dDPg/dT = rho g z1 - rho g z2 = rho g (z1 - + // z2) for( integer i = 0; i < 2; ++i ) { - dGravHead_dP[ip][i] += dDensMean_dP[ip][i] * gravD; // dDPg/dP = {drho/dP1 * T g (z1 - z2) , drho/dP2 * T g (z1 - z2)} + dGravHead_dP[ip][i] += dDensMean_dP[ip][i] * gravD; // dDPg/dP = {drho/dP1 * T g (z1 - z2) , drho/dP2 * T g (z1 - z2)} } - if( m_hasCapPressure ) // check sign convention + if( m_hasCapPressure ) // check sign convention { - real64 const capPres = m_phaseCapPressure[er][esr][ei][0][ip]; // Pc = Pc1 || Pc2 - dCapGrad_dTrans -= signPotDiff[ke] * capPres; // dDPc/dT = (-Pc1 + Pc2) - pot -= trans[ke] * capPres; // Phi = T P1 - rho T g z1 - T Pc1 || -T P2 + rho T g z2 + T - // Pc2 - capGrad[ip] -= trans[ke] * capPres; // DPc = T (-Pc1 + Pc2) + real64 const capPres = m_phaseCapPressure[er][esr][ei][0][ip]; // Pc = Pc1 || Pc2 + dCapGrad_dTrans -= signPotDiff[ke] * capPres; // dDPc/dT = (-Pc1 + Pc2) + pot -= trans[ke] * capPres; // Phi = T P1 - rho T g z1 - T Pc1 || -T P2 + rho T g z2 + T + // Pc2 + capGrad[ip] -= trans[ke] * capPres; // DPc = T (-Pc1 + Pc2) } - potScale = fmax( potScale, fabs( pot ) ); // maxPhi = Phi1 > Phi2 ? Phi1 : Phi2 + potScale = fmax( potScale, fabs( pot ) ); // maxPhi = Phi1 > Phi2 ? Phi1 : Phi2 } for( integer ke = 0; ke < 2; ++ke ) { - dPresGrad_dP[ip][ke] += dTrans_dP[ke] * dPresGrad_dTrans; // dDPv/dP = { T + dT/dP1 * (P1 - P2) , -T + dT/dP2 * (P1 - P2)} - dGravHead_dP[ip][ke] += dTrans_dP[ke] * dGravHead_dTrans; // dDPg/dP = { drho/dP1 * T g (z1 - z2) + dT/dP1 * rho g (z1 - z2) , - // drho/dP2 * T g (z1 - z2) + dT/dP2 * rho g (z1 - z2) } + dPresGrad_dP[ip][ke] += dTrans_dP[ke] * dPresGrad_dTrans; // dDPv/dP = { T + dT/dP1 * (P1 - P2) , -T + dT/dP2 * (P1 - P2)} + dGravHead_dP[ip][ke] += dTrans_dP[ke] * dGravHead_dTrans; // dDPg/dP = { drho/dP1 * T g (z1 - z2) + dT/dP1 * rho g (z1 - z2) + // , + // drho/dP2 * T g (z1 - z2) + dT/dP2 * rho g (z1 - z2) + // } if( m_hasCapPressure ) { - real64 const dCapPres_dS = m_dPhaseCapPressure_dPhaseVolFrac[seri[ke]][sesri[ke]][sei[ke]][0][ip][ip]; // dPc/dS = dPc1/dS1 || - // dPc2/dS2 - dCapGrad_dP[ip][ke] += dTrans_dP[ke] * dCapGrad_dTrans; // dDPc/dP = { dT/dP1 * - // (-Pc1 + Pc2) , - // dT/dP2 * - // (-Pc1 + Pc2) } - dCapGrad_dS[ip][ke] -= trans[ke] * dCapPres_dS; // dDPc/dS = { -T * - // dPc1/dS1 , T * - // dPc2/dS2 } + real64 const dCapPres_dS = m_dPhaseCapPressure_dPhaseVolFrac[seri[ke]][sesri[ke]][sei[ke]][0][ip][ip]; // dPc/dS = dPc1/dS1 + // || + // dPc2/dS2 + dCapGrad_dP[ip][ke] += dTrans_dP[ke] * dCapGrad_dTrans; // dDPc/dP = { dT/dP1 + // * + // (-Pc1 + Pc2) , + // dT/dP2 + // * + // (-Pc1 + Pc2) } + dCapGrad_dS[ip][ke] -= trans[ke] * dCapPres_dS; // dDPc/dS = { -T * + // dPc1/dS1 , T * + // dPc2/dS2 } } } // *** upwinding *** // compute potential gradient - real64 potGrad = presGrad[ip] - gravHead[ip]; // DPhi = T (P1 - P2) - T rho g (z1 - z2) + real64 potGrad = presGrad[ip] - gravHead[ip]; // DPhi = T (P1 - P2) - T rho g (z1 - z2) if( m_hasCapPressure ) { - potGrad += capGrad[ip]; // DPhi = T (P1 - P2) - T rho g (z1 - z2) + T (-Pc1 + Pc2) + potGrad += capGrad[ip]; // DPhi = T (P1 - P2) - T rho g (z1 - z2) + T (-Pc1 + Pc2) } // compute upwinding tolerance real64 constexpr upwRelTol = 1e-8; - real64 const upwAbsTol = fmax( potScale * upwRelTol, LvArray::NumericLimits< real64 >::epsilon ); // abstol = maxPhi * tol > eps ? - // maxPhi * tol : eps + real64 const upwAbsTol = fmax( potScale * upwRelTol, LvArray::NumericLimits< real64 >::epsilon ); // abstol = maxPhi * tol > eps + // ? + // maxPhi * tol : eps // decide mobility coefficients - smooth variation in [-upwAbsTol; upwAbsTol] - real64 const alpha = ( potGrad + upwAbsTol ) / ( 2 * upwAbsTol ); // alpha = (DPhi + abstol) / abstol / 2 + real64 const alpha = ( potGrad + upwAbsTol ) / ( 2 * upwAbsTol ); // alpha = (DPhi + abstol) / abstol / 2 // choose upstream cell - if( alpha <= 0.0 || alpha >= 1.0 ) // no smoothing needed + if( alpha <= 0.0 || alpha >= 1.0 ) // no smoothing needed { - localIndex const k_up = 1 - localIndex( fmax( fmin( alpha, 1.0 ), 0.0 ) ); // 1 upwind -> k_up = 0 || 2 upwind -> k_up = 1 + localIndex const k_up = 1 - localIndex( fmax( fmin( alpha, 1.0 ), 0.0 ) ); // 1 upwind -> k_up = 0 || 2 upwind -> k_up = 1 - mobility[ip] = m_mob[seri[k_up]][sesri[k_up]][sei[k_up]][ip]; // M = Mupstream - dMob_dP[ip][k_up] = m_dMob[seri[k_up]][sesri[k_up]][sei[k_up]][ip][Deriv::dP]; // dM/dP = {dM/dP1 , 0} OR {0 , dM/dP2} - dMob_dS[ip][k_up] = m_dMob[seri[k_up]][sesri[k_up]][sei[k_up]][ip][Deriv::dS]; // dM/dS = {dM/dS1 , 0} OR {0 , dM/dS2} + mobility[ip] = m_mob[seri[k_up]][sesri[k_up]][sei[k_up]][ip]; // M = Mupstream + dMob_dP[ip][k_up] = m_dMob[seri[k_up]][sesri[k_up]][sei[k_up]][ip][Deriv::dP]; // dM/dP = {dM/dP1 , 0} OR {0 , dM/dP2} + dMob_dS[ip][k_up] = m_dMob[seri[k_up]][sesri[k_up]][sei[k_up]][ip][Deriv::dS]; // dM/dS = {dM/dS1 , 0} OR {0 , dM/dS2} } - else // perform smoothing + else // perform smoothing { real64 const mobWeights[2] = { alpha, 1.0 - alpha }; for( integer ke = 0; ke < 2; ++ke ) { - mobility[ip] += mobWeights[ke] * m_mob[seri[ke]][sesri[ke]][sei[ke]][ip]; // M = alpha * M1 + (1 - alpha) * M2 - dMob_dP[ip][ke] = mobWeights[ke] * m_dMob[seri[ke]][sesri[ke]][sei[ke]][ip][Deriv::dP]; // dM/dP = {alpha * dM1/dP1 , (1 - - // alpha) * dM2/dP2} - dMob_dS[ip][ke] = mobWeights[ke] * m_dMob[seri[ke]][sesri[ke]][sei[ke]][ip][Deriv::dS]; // dM/dP = {alpha * dM1/dS1 , (1 - - // alpha) * dM2/dS2} + mobility[ip] += mobWeights[ke] * m_mob[seri[ke]][sesri[ke]][sei[ke]][ip]; // M = alpha * M1 + (1 - alpha) * M2 + dMob_dP[ip][ke] = mobWeights[ke] * m_dMob[seri[ke]][sesri[ke]][sei[ke]][ip][Deriv::dP]; // dM/dP = {alpha * dM1/dP1 , (1 - + // alpha) * dM2/dP2} + dMob_dS[ip][ke] = mobWeights[ke] * m_dMob[seri[ke]][sesri[ke]][sei[ke]][ip][Deriv::dS]; // dM/dP = {alpha * dM1/dS1 , (1 - + // alpha) * dM2/dS2} } } // pressure gradient depends on all points in the stencil for( integer ke = 0; ke < 2; ++ke ) { - dFlux_dP[ip][ke] += dPresGrad_dP[ip][ke]; // dF/dP = { T + dT/dP1 * (P1 - P2) , - // -T + dT/dP2 * (P1 - P2) } + dFlux_dP[ip][ke] += dPresGrad_dP[ip][ke]; // dF/dP = { T + dT/dP1 * (P1 - P2) , + // -T + dT/dP2 * (P1 - P2) } } // gravitational head depends only on the two cells connected (same as mean density) for( integer ke = 0; ke < 2; ++ke ) { - dFlux_dP[ip][ke] -= dGravHead_dP[ip][ke]; // dF/dP = { T + dT/dP1 * (P1 - P2) - drho/dP1 * T g (z1 - z2) - dT/dP1 * rho g (z1 - - // z2) , - // -T + dT/dP2 * (P1 - P2) - drho/dP2 * T g (z1 - z2) - dT/dP2 * rho g (z1 - - // z2) } + dFlux_dP[ip][ke] -= dGravHead_dP[ip][ke]; // dF/dP = { T + dT/dP1 * (P1 - P2) - drho/dP1 * T g (z1 - z2) - dT/dP1 * rho g (z1 - + // z2) , + // -T + dT/dP2 * (P1 - P2) - drho/dP2 * T g (z1 - z2) - dT/dP2 * rho g (z1 - + // z2) } } // capillary pressure contribution @@ -578,41 +2154,60 @@ class FluxComputeKernel : public FluxComputeKernelBase { for( integer ke = 0; ke < 2; ++ke ) { - dFlux_dP[ip][ke] += dCapGrad_dP[ip][ke]; // dF/dP = { T + dT/dP1 * (P1 - P2) - drho/dP1 * T g (z1 - z2) - dT/dP1 * rho g (z1 - // - z2) + dT/dP1 * (-Pc1 + Pc2) , - // -T + dT/dP2 * (P1 - P2) - drho/dP2 * T g (z1 - z2) - dT/dP2 * rho g (z1 - // - z2) + dT/dP2 * (-Pc1 + Pc2) } + dFlux_dP[ip][ke] += dCapGrad_dP[ip][ke]; // dF/dP = { T + dT/dP1 * (P1 - P2) - drho/dP1 * T g (z1 - z2) - dT/dP1 * rho g (z1 + // - z2) + dT/dP1 * (-Pc1 + Pc2) , + // -T + dT/dP2 * (P1 - P2) - drho/dP2 * T g (z1 - z2) - dT/dP2 * rho g (z1 + // - z2) + dT/dP2 * (-Pc1 + Pc2) } - dFlux_dS[ip][ke] += dCapGrad_dS[ip][ke]; // dF/dS = { T * -dPc/dS1 , T * dPc/dS2 } + dFlux_dS[ip][ke] += dCapGrad_dS[ip][ke]; // dF/dS = { T * -dPc/dS1 , T * dPc/dS2 } } } // compute the flux and derivatives using upstream cell mobility - fluxVal[ip] = mobility[ip] * potGrad; // F = M * DPhi + fluxVal[ip] = mobility[ip] * potGrad; // F = M * DPhi + + // DEBUG: Print flux computation for regular kernel + printf( "REGULAR_KERNEL_FLUX: iconn=%d, ip=%d, mobility[%d]=%.10e, potGrad=%.10e, fluxVal[%d]=%.10e\n", + static_cast(iconn), + static_cast(ip), + static_cast(ip), + mobility[ip], + potGrad, + static_cast(ip), + fluxVal[ip] ); for( integer ke = 0; ke < 2; ++ke ) { - dFlux_dP[ip][ke] *= mobility[ip]; // dF/dP = { M [ T + dT/dP1 * (P1 - P2) - drho/dP1 * T g (z1 - z2) - dT/dP1 * rho g (z1 - - // z2) + dT/dP1 * (-Pc1 + Pc2)] , - // M [-T + dT/dP2 * (P1 - P2) - drho/dP2 * T g (z1 - z2) - dT/dP2 * rho g (z1 - - // z2) + dT/dP2 * (-Pc1 + Pc2)] } + dFlux_dP[ip][ke] *= mobility[ip]; // dF/dP = { M [ T + dT/dP1 * (P1 - P2) - drho/dP1 * T g (z1 - z2) - dT/dP1 * rho g (z1 - + // z2) + dT/dP1 * (-Pc1 + Pc2)] , + // M [-T + dT/dP2 * (P1 - P2) - drho/dP2 * T g (z1 - z2) - dT/dP2 * rho g (z1 - + // z2) + dT/dP2 * (-Pc1 + Pc2)] } - dFlux_dS[ip][ke] *= mobility[ip]; // dF/dS = { M [T * -dPc/dS1] , M [T * dPc/dS2] } + dFlux_dS[ip][ke] *= mobility[ip]; // dF/dS = { M [T * -dPc/dS1] , M [T * dPc/dS2] } } // add contribution from upstream cell mobility derivatives for( integer ke = 0; ke < 2; ++ke ) { - dFlux_dP[ip][ke] += dMob_dP[ip][ke] * potGrad; // dF/dP = { M [ T + dT/dP1 * (P1 - P2) - drho1/dP * T g (z1 - z2) - dT/dP1 * - // rho g (z1 - z2) + dT/dP1 * (-Pc1 + Pc2)] + dM/dP1 * DPhi , - // M [-T + dT/dP2 * (P1 - P2) - drho2/dP * T g (z1 - z2) - dT/dP2 * - // rho g (z1 - z2) + dT/dP2 * (-Pc1 + Pc2)] + dM/dP2 * DPhi } + dFlux_dP[ip][ke] += dMob_dP[ip][ke] * potGrad; // dF/dP = { M [ T + dT/dP1 * (P1 - P2) - drho1/dP * T g (z1 - z2) - dT/dP1 * + // rho g (z1 - z2) + dT/dP1 * (-Pc1 + Pc2)] + dM/dP1 * DPhi , + // M [-T + dT/dP2 * (P1 - P2) - drho2/dP * T g (z1 - z2) - dT/dP2 * + // rho g (z1 - z2) + dT/dP2 * (-Pc1 + Pc2)] + dM/dP2 * DPhi } - dFlux_dS[ip][ke] += dMob_dS[ip][ke] * potGrad; // dF/dS = { M [T * -dPc/dS1] + dM/dS1 * DPhi , M [T * dPc/dS2] + dM/dS2 * DPhi - // } + dFlux_dS[ip][ke] += dMob_dS[ip][ke] * potGrad; // dF/dS = { M [T * -dPc/dS1] + dM/dS1 * DPhi , M [T * dPc/dS2] + dM/dS2 * DPhi + // } } // populate local flux vector and derivatives + printf( "REGULAR_KERNEL_ACCUM: iconn=%d, ip=%d, k[0]=%d, k[1]=%d, fluxVal[%d]=%.10e, dt=%.10e, adding=%.10e\n", + static_cast(iconn), + static_cast(ip), + static_cast(k[0]), + static_cast(k[1]), + static_cast(ip), + fluxVal[ip], + m_dt, + m_dt * fluxVal[ip] ); stack.localFlux[k[0]*numEqn + ip] += m_dt * fluxVal[ip]; stack.localFlux[k[1]*numEqn + ip] -= m_dt * fluxVal[ip]; @@ -630,14 +2225,14 @@ class FluxComputeKernel : public FluxComputeKernelBase } // Customize the kernel with this lambda - kernelOp( k, seri, sesri, sei, connectionIndex, alpha, mobility, potGrad, fluxVal, dFlux_dP, dFlux_dS ); // Not sure what this - // does + kernelOp( k, seri, sesri, sei, connectionIndex, alpha, mobility, potGrad, fluxVal, dFlux_dP, dFlux_dS ); // Not sure what this + // does - } // loop over phases + } // loop over phases connectionIndex++; } - } // loop over connection elements + } // loop over connection elements } /** @@ -645,7 +2240,7 @@ class FluxComputeKernel : public FluxComputeKernelBase * @param[in] iconn the connection index * @param[inout] stack the stack variables */ - template< typename FUNC = NoOpFunc > // should change to multiphase + template< typename FUNC = NoOpFunc > // should change to multiphase GEOS_HOST_DEVICE void complete( localIndex const iconn, StackVariables & stack, @@ -732,60 +2327,844 @@ class FluxComputeKernel : public FluxComputeKernelBase }; -/****************************************** */ - /** - * @class FluxComputeKernelFactory + * @class FaceBasedAssemblyInterfaceConditionKernel + * @tparam NUM_DOF number of degrees of freedom + * @tparam STENCILWRAPPER the type of the stencil wrapper + * @tparam CAPPRESWRAPPER the type of the capillary pressure wrapper + * @tparam RELPERMWRAPPER the type of the realtive permeability wrapper + * @brief Define the interface for the assembly kernel in charge of flux terms */ -class FluxComputeKernelFactory +// template< integer NUM_EQN, integer NUM_DOF, typename STENCILWRAPPER, typename CAPPRESWRAPPER, typename RELPERMWRAPPER > +template< integer NUM_EQN, integer NUM_DOF, typename STENCILWRAPPER > +class FluxComputeInterfaceConditionKernel : public FluxComputeKernel< NUM_EQN, NUM_DOF, STENCILWRAPPER > { public: + using AbstractBase = FluxComputeKernelBase; + using DofNumberAccessor = AbstractBase::DofNumberAccessor; + using ImmiscibleMultiphaseFlowAccessors = AbstractBase::ImmiscibleMultiphaseFlowAccessors; + using MultiphaseFluidAccessors = AbstractBase::MultiphaseFluidAccessors; + using CapPressureAccessors = AbstractBase::CapPressureAccessors; + using PermeabilityAccessors = AbstractBase::PermeabilityAccessors; + + using Base = FluxComputeKernel< NUM_EQN, NUM_DOF, STENCILWRAPPER >; + using Deriv = typename Base::Deriv; + using StackVariables = typename Base::StackVariables; + using Base::numEqn; + using Base::numDof; + using Base::m_stencilWrapper; + using Base::m_dofNumber; + using Base::m_rankOffset; + using Base::m_localMatrix; + using Base::m_localRhs; + using Base::m_numPhases; + using Base::m_permeability; + using Base::m_dPerm_dPres; + using Base::m_phaseVolFrac; + using Base::m_phaseMass_n; + using Base::m_phaseCapPressure; + // using Base::m_jFuncMultiplier; + using Base::m_dPhaseCapPressure_dPhaseVolFrac; + using Base::m_dens; + using Base::m_dDens_dPres; + using Base::m_visc; + using Base::m_dVisc_dPres; + using Base::m_dMob; + using Base::m_mob; + using Base::m_gravCoef; + using Base::m_ghostRank; + using Base::m_dt; + using Base::m_hasCapPressure; + using Base::m_useTotalMassEquation; + using Base::m_checkPhasePresenceInGravity; + using Base::m_seri; + using Base::m_sesri; + using Base::m_sei; + using Base::m_pres; + /** - * @brief Create a new kernel and launch - * @tparam POLICY the policy used in the RAJA kernel - * @tparam STENCILWRAPPER the type of the stencil wrapper + * @brief Constructor for the kernel interface + * @param[in] numPhases number of fluid phases * @param[in] rankOffset the offset of my MPI rank - * @param[in] dofKey string to get the element degrees of freedom numbers - * @param[in] solverName name of the solver (to name accessors) - * @param[in] elemManager reference to the element region manager * @param[in] stencilWrapper reference to the stencil wrapper + * @param[in] capPressureWrapper reference to the capillary pressure wrapper + * @param[in] dofNumberAccessor + * @param[in] multiPhaseFlowAccessors + * @param[in] fluidAccessors + * @param[in] capPressureAccessors + * @param[in] permeabilityAccessors * @param[in] dt time step size * @param[inout] localMatrix the local CRS matrix * @param[inout] localRhs the local right-hand side vector + * @param[in] hasCapPressure flags for capillary pressure + * @param[in] useTotalMassEquation flags for using total velocity formulation */ - template< typename POLICY, typename STENCILWRAPPER > - static void - createAndLaunch( integer const numPhases, - globalIndex const rankOffset, - string const & dofKey, - integer const hasCapPressure, - integer const useTotalMassEquation, - integer const checkPhasePresenceInGravity, - string const & solverName, - ElementRegionManager const & elemManager, - STENCILWRAPPER const & stencilWrapper, - real64 const & dt, - CRSMatrixView< real64, globalIndex const > const & localMatrix, - arrayView1d< real64 > const & localRhs ) + FluxComputeInterfaceConditionKernel( integer const numPhases, + globalIndex const rankOffset, + STENCILWRAPPER const & stencilWrapper, + // CAPPRESWRAPPER const & capPressureWrapper, + // RELPERMWRAPPER const & relPermWrapper, + DofNumberAccessor const & dofNumberAccessor, + ImmiscibleMultiphaseFlowAccessors const & multiPhaseFlowAccessors, + MultiphaseFluidAccessors const & fluidAccessors, + CapPressureAccessors const & capPressureAccessors, + PermeabilityAccessors const & permeabilityAccessors, + real64 const & dt, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs, + integer const hasCapPressure, + integer const useTotalMassEquation, + integer const checkPhasePresenceInGravity, + string_array const & interfaceFaceSetNames, + stdVector< std::array< std::tuple< constitutive::RelativePermeabilityBase *, + constitutive::CapillaryPressureBase *, + constitutive::TwoPhaseImmiscibleFluid * >, 2 > > const & interfaceConstitutivePairs, + unordered_map< localIndex, localIndex > const & interfaceRegionByConnector, + // std::tuple< constitutive::RelativePermeabilityBase *, + // constitutive::CapillaryPressureBase *, + // constitutive::TwoPhaseImmiscibleFluid * > const & interfaceConstitutivePairs_temp, + localIndex const GEOS_UNUSED_PARAM( domainSize ) ) + : Base( numPhases, + rankOffset, + stencilWrapper, + dofNumberAccessor, + multiPhaseFlowAccessors, + fluidAccessors, + capPressureAccessors, + permeabilityAccessors, + dt, + localMatrix, + localRhs, + hasCapPressure, + useTotalMassEquation, + checkPhasePresenceInGravity ), + // m_capPressureWrapper( capPressureWrapper ), + // m_relPermWrapper( relPermWrapper ), + m_interfaceFaceSetNames( interfaceFaceSetNames ), + m_interfaceConstitutivePairs( interfaceConstitutivePairs ), + m_interfaceRegionByConnector( interfaceRegionByConnector ) + // , + // m_interfaceConstitutivePairs_temp( interfaceConstitutivePairs_temp ) + {} + + /** + * @brief Compute the local flux contributions to the residual and Jacobian + * @tparam FUNC the type of the function that can be used to customize the computation of the flux + * @param[in] iconn the connection index + * @param[inout] stack the stack variables + * @param[in] NoOpFunc the function used to customize the computation of the flux + */ + + template< typename FUNC = NoOpFunc > // should change to multiphase + GEOS_HOST_DEVICE + void computeFlux( localIndex const iconn, + StackVariables & stack, + FUNC && kernelOp = NoOpFunc{} ) const { - integer constexpr NUM_EQN = 2; - integer constexpr NUM_DOF = 2; - ElementRegionManager::ElementViewAccessor< arrayView1d< globalIndex const > > dofNumberAccessor = - elemManager.constructArrayViewAccessor< globalIndex, 1 >( dofKey ); - dofNumberAccessor.setName( solverName + "/accessors/" + dofKey ); + bool connectorHasInterfaceConditionQ = false; + bool anyInterfaceConditionsQ = not m_interfaceConstitutivePairs.empty(); + if (anyInterfaceConditionsQ) { + connectorHasInterfaceConditionQ = + m_interfaceRegionByConnector.find(iconn) != m_interfaceRegionByConnector.end(); + } - using kernelType = FluxComputeKernel< NUM_EQN, NUM_DOF, STENCILWRAPPER >; - typename kernelType::ImmiscibleMultiphaseFlowAccessors flowAccessors( elemManager, solverName ); - typename kernelType::MultiphaseFluidAccessors fluidAccessors( elemManager, solverName ); - typename kernelType::CapPressureAccessors capPressureAccessors( elemManager, solverName ); - typename kernelType::PermeabilityAccessors permAccessors( elemManager, solverName ); - kernelType kernel( numPhases, rankOffset, stencilWrapper, dofNumberAccessor, - flowAccessors, fluidAccessors, capPressureAccessors, permAccessors, - dt, localMatrix, localRhs, hasCapPressure, useTotalMassEquation, - checkPhasePresenceInGravity ); + + // if (connectorHasInterfaceConditionQ){ + // // Improved transmission conditions + // int ammar_code = 0; + // }else{ + // // Regular contribution + // int standard_code = 0; + // } + + m_stencilWrapper.computeWeights( iconn, + m_permeability, + m_dPerm_dPres, + stack.transmissibility, + stack.dTrans_dPres ); + + localIndex k[2]; + localIndex connectionIndex = 0; + + // one-sided transmissibility + m_stencilWrapper.computeHalfWeights( iconn, + m_permeability, + m_dPerm_dPres, + stack.transmissibilityHat, + stack.dTransHat_dPres ); + + + + for( k[0] = 0; k[0] < stack.numFluxElems; ++k[0] ) + { + for( k[1] = k[0] + 1; k[1] < stack.numFluxElems; ++k[1] ) + { + // clear working arrays + real64 densMean[numEqn]{}; + real64 dDensMean_dP[numEqn][2]{}; + + real64 presGrad[numEqn]{}; + real64 dPresGrad_dP[numEqn][2]{}; + + real64 gravHead[numEqn]{}; + real64 dGravHead_dP[numEqn][2]{}; + + real64 capGrad[numEqn]{}; + // real64 capPresIC[numEqn][2]{}; + // real64 jFMultiplier[numEqn][2]{}; + real64 dCapGrad_dP[numEqn][2]{}; + real64 dCapGrad_dS[numEqn][2]{}; + + real64 fluxVal[numEqn]{}; + real64 dFlux_dP[numEqn][2]{}; + real64 dFlux_dS[numEqn][2]{}; + + real64 mobility[numEqn]{}; + real64 dMob_dP[numEqn][2]{}; + real64 dMob_dS[numEqn][2]{}; + + real64 density2[numEqn]{}; + real64 dDens_dP2[numEqn][2]{}; + real64 viscosity[numEqn]{}; + real64 dVisc_dP[numEqn][2]{}; + real64 gravCoef2[numEqn]{}; + real64 gravCoefHat[numEqn]{}; + + real64 uT = 0; + // real64 total_mobility = 0; + real64 duT_dP[numEqn]{}; + real64 duT_dS[numEqn]{}; + + real64 potGrad_ip[numEqn]{}; + real64 alpha_ip[numEqn]{}; + + real64 const trans[2] = { stack.transmissibility[connectionIndex][0], stack.transmissibility[connectionIndex][1] }; + real64 const dTrans_dP[2] = { stack.dTrans_dPres[connectionIndex][0], stack.dTrans_dPres[connectionIndex][1] }; + + real64 const transHat[2] = { stack.transmissibilityHat[connectionIndex][0], stack.transmissibilityHat[connectionIndex][1] * -1.0}; + real64 const dTransHat_dP[2] = { stack.dTransHat_dPres[connectionIndex][0], stack.dTransHat_dPres[connectionIndex][1] * -1.0}; + + // cell indices + localIndex const seri[2] = {m_seri( iconn, k[0] ), m_seri( iconn, k[1] )}; + localIndex const sesri[2] = {m_sesri( iconn, k[0] ), m_sesri( iconn, k[1] )}; + localIndex const sei[2] = {m_sei( iconn, k[0] ), m_sei( iconn, k[1] )}; + + stdVector< real64 > saturations = {m_phaseVolFrac[seri[0]][sesri[0]][sei[0]][0], m_phaseVolFrac[seri[1]][sesri[1]][sei[1]][0] }; + stdVector< real64 > pressures = {m_pres[seri[0]][sesri[0]][sei[0]], m_pres[seri[1]][sesri[1]][sei[1]] }; + // bool isJfunction = 0; + + // loop over phases + for( integer ip = 0; ip < m_numPhases; ++ip ) + { + // calculate quantities on primary connected cells + integer denom = 0; + for( integer ke = 0; ke < 2; ++ke ) + { + // density + bool const phaseExists = (m_phaseVolFrac[seri[ke]][sesri[ke]][sei[ke]][ip] > 0); + if( m_checkPhasePresenceInGravity && !phaseExists ) + { + continue; + } + + real64 const density = m_dens[seri[ke]][sesri[ke]][sei[ke]][0][ip]; // r = rho1 || rho2 + real64 const dDens_dP = m_dDens_dPres[seri[ke]][sesri[ke]][sei[ke]][0][ip][Deriv::dP]; // dr/dP = dr1/dP1 || dr2/dP + // average density and derivatives + densMean[ip] += density; // rho = (rho1 + rho2) + dDensMean_dP[ip][ke] = dDens_dP; // drho/dP = { (dr1/dP1) , (dr2/dP2) } + denom++; + } + + if( denom > 1 ) + { + densMean[ip] /= denom; // rho = (rho1 + rho2) / denom + for( integer ke = 0; ke < 2; ++ke ) + { + dDensMean_dP[ip][ke] /= denom; // drho/dP = { (dr1/dP1) / denom , (dr2/dP2) / denom } + } + } + + //***** calculation of flux ***** + + // compute potential difference + real64 potScale = 0.0; + real64 dPresGrad_dTrans = 0.0; + real64 dGravHead_dTrans = 0.0; + real64 dCapGrad_dTrans = 0.0; + gravCoefHat[0] = 0; + gravCoefHat[1] = 0; + constexpr int signPotDiff[2] = {1, -1}; + + for( integer ke = 0; ke < 2; ++ke ) + { + localIndex const er = seri[ke]; + localIndex const esr = sesri[ke]; + localIndex const ei = sei[ke]; + + real64 const pressure = m_pres[er][esr][ei]; // P = P1 || P2 + presGrad[ip] += trans[ke] * pressure; // DPv = T (P1 - P2) + dPresGrad_dTrans += signPotDiff[ke] * pressure; // dDPv/dT = (P1 - P2) + dPresGrad_dP[ip][ke] = trans[ke]; // dDPv/dP = { T , -T } + + real64 const gravD = trans[ke] * m_gravCoef[er][esr][ei]; // D = T g z1 || -T g z2 + real64 pot = trans[ke] * pressure - densMean[ip] * gravD; // Phi = T P1 - rho T g z1 || -T P2 + rho T g z2 + gravCoefHat[0] += m_gravCoef[er][esr][ei] * 0.5; + gravCoefHat[1] += m_gravCoef[er][esr][ei] * 0.5; + + gravCoef2[ke] = m_gravCoef[er][esr][ei]; + + gravHead[ip] += densMean[ip] * gravD; // DPg = rho (T g z1 - T g z2) = T rho g (z1 - z2) + dGravHead_dTrans += signPotDiff[ke] * densMean[ip] * m_gravCoef[er][esr][ei]; // dDPg/dT = rho g z1 - rho g z2 = rho g (z1 - z2) + + for( integer i = 0; i < 2; ++i ) + { + dGravHead_dP[ip][i] += dDensMean_dP[ip][i] * gravD; // dDPg/dP = {drho/dP1 * T g (z1 - z2) , drho/dP2 * T g (z1 - z2)} + } + + if( m_hasCapPressure ) // check sign convention + { + real64 const capPres = m_phaseCapPressure[er][esr][ei][0][ip]; // Pc = Pc1 || Pc2 + // jFMultiplier[ip][ke] = m_jFuncMultiplier[er][esr][ei][0]; + + // capPresIC[ip][ke] = capPres; + dCapGrad_dTrans -= signPotDiff[ke] * capPres; // dDPc/dT = (-Pc1 + Pc2) + pot -= trans[ke] * capPres; // Phi = T P1 - rho T g z1 - T Pc1 || -T P2 + rho T g z2 + T + // Pc2 + capGrad[ip] -= trans[ke] * capPres; // DPc = T (-Pc1 + Pc2) + } + + potScale = fmax( potScale, fabs( pot ) ); // maxPhi = Phi1 > Phi2 ? Phi1 : Phi2 + } + + + for( integer ke = 0; ke < 2; ++ke ) + { + dPresGrad_dP[ip][ke] += dTrans_dP[ke] * dPresGrad_dTrans; // dDPv/dP = { T + dT/dP1 * (P1 - P2) , -T + dT/dP2 * (P1 - P2)} + dGravHead_dP[ip][ke] += dTrans_dP[ke] * dGravHead_dTrans; // dDPg/dP = { drho/dP1 * T g (z1 - z2) + dT/dP1 * rho g (z1 - z2) , + // drho/dP2 * T g (z1 - z2) + dT/dP2 * rho g (z1 - z2) } + if( m_hasCapPressure ) + { + real64 const dCapPres_dS = m_dPhaseCapPressure_dPhaseVolFrac[seri[ke]][sesri[ke]][sei[ke]][0][ip][ip]; // dPc/dS = dPc1/dS1 || + // dPc2/dS2 + dCapGrad_dP[ip][ke] += dTrans_dP[ke] * dCapGrad_dTrans; // dDPc/dP = { dT/dP1 * + // (-Pc1 + Pc2) , + // dT/dP2 * + // (-Pc1 + Pc2) } + dCapGrad_dS[ip][ke] -= trans[ke] * dCapPres_dS; // dDPc/dS = { -T * + // dPc1/dS1 , T * + // dPc2/dS2 } + } + } + + // *** upwinding *** + // compute potential gradient + real64 potGrad = presGrad[ip] - gravHead[ip]; // DPhi = T (P1 - P2) - T rho g (z1 - z2) + if( m_hasCapPressure ) + { + potGrad += capGrad[ip]; // DPhi = T (P1 - P2) - T rho g (z1 - z2) + T (-Pc1 + Pc2) + } + + // compute upwinding tolerance + real64 constexpr upwRelTol = 1e-8; + real64 const upwAbsTol = fmax( potScale * upwRelTol, LvArray::NumericLimits< real64 >::epsilon ); // abstol = maxPhi * tol > eps ? + // maxPhi * tol : eps + + // decide mobility coefficients - smooth variation in [-upwAbsTol; upwAbsTol] + real64 alpha = ( potGrad + upwAbsTol ) / ( 2 * upwAbsTol ); // alpha = (DPhi + abstol) / abstol / 2 + + // choose upstream cell + if( alpha <= 0.0 || alpha >= 1.0 ) // no smoothing needed + { + localIndex const k_up = 1 - localIndex( fmax( fmin( alpha, 1.0 ), 0.0 ) ); // 1 upwind -> k_up = 0 || 2 upwind -> k_up = 1 + + mobility[ip] = m_mob[seri[k_up]][sesri[k_up]][sei[k_up]][ip]; // M = Mupstream + density2[ip] = m_dens[seri[k_up]][sesri[k_up]][sei[k_up]][0][ip]; // r = rho1 || rho2 + dDens_dP2[ip][k_up] = m_dDens_dPres[seri[k_up]][sesri[k_up]][sei[k_up]][0][ip][Deriv::dP]; // dr/dP = dr1/dP1 || dr2/dP + viscosity[ip] = m_visc[seri[k_up]][sesri[k_up]][sei[k_up]][0][ip]; + dVisc_dP[ip][k_up] = m_dVisc_dPres[seri[k_up]][sesri[k_up]][sei[k_up]][0][ip][Deriv::dP]; + dMob_dP[ip][k_up] = m_dMob[seri[k_up]][sesri[k_up]][sei[k_up]][ip][Deriv::dP]; // dM/dP = {dM/dP1 , 0} OR {0 , dM/dP2} + dMob_dS[ip][k_up] = m_dMob[seri[k_up]][sesri[k_up]][sei[k_up]][ip][Deriv::dS]; // dM/dS = {dM/dS1 , 0} OR {0 , dM/dS2} + } + else // perform smoothing + { + real64 const mobWeights[2] = { alpha, 1.0 - alpha }; + for( integer ke = 0; ke < 2; ++ke ) + { + mobility[ip] += mobWeights[ke] * m_mob[seri[ke]][sesri[ke]][sei[ke]][ip]; // M = alpha * M1 + (1 - alpha) * M2 + density2[ip] += mobWeights[ke] * m_dens[seri[ke]][sesri[ke]][sei[ke]][0][ip]; // r = rho1 || rho2 + dDens_dP2[ip][ke] = mobWeights[ke] * m_dDens_dPres[seri[ke]][sesri[ke]][sei[ke]][0][ip][Deriv::dP]; // dr/dP = dr1/dP1 || + // dr2/dP + viscosity[ip] = m_visc[seri[ke]][sesri[ke]][sei[ke]][0][ip]; + dVisc_dP[ip][ke] = m_dVisc_dPres[seri[ke]][sesri[ke]][sei[ke]][0][ip][Deriv::dP]; + dMob_dP[ip][ke] = mobWeights[ke] * m_dMob[seri[ke]][sesri[ke]][sei[ke]][ip][Deriv::dP]; // dM/dP = {alpha * dM1/dP1 , (1 - + // alpha) * dM2/dP2} + dMob_dS[ip][ke] = mobWeights[ke] * m_dMob[seri[ke]][sesri[ke]][sei[ke]][ip][Deriv::dS]; // dM/dP = {alpha * dM1/dS1 , (1 - + // alpha) * dM2/dS2} + } + } + + // pressure gradient depends on all points in the stencil + for( integer ke = 0; ke < 2; ++ke ) + { + dFlux_dP[ip][ke] += dPresGrad_dP[ip][ke]; // dF/dP = { T + dT/dP1 * (P1 - P2) , + // -T + dT/dP2 * (P1 - P2) } + } + + // gravitational head depends only on the two cells connected (same as mean density) + for( integer ke = 0; ke < 2; ++ke ) + { + dFlux_dP[ip][ke] -= dGravHead_dP[ip][ke]; // dF/dP = { T + dT/dP1 * (P1 - P2) - drho/dP1 * T g (z1 - z2) - dT/dP1 * rho g (z1 - + // z2) , + // -T + dT/dP2 * (P1 - P2) - drho/dP2 * T g (z1 - z2) - dT/dP2 * rho g (z1 - + // z2) } + } + + // capillary pressure contribution + if( m_hasCapPressure ) + { + for( integer ke = 0; ke < 2; ++ke ) + { + dFlux_dP[ip][ke] += dCapGrad_dP[ip][ke]; // dF/dP = { T + dT/dP1 * (P1 - P2) - drho/dP1 * T g (z1 - z2) - dT/dP1 * rho g (z1 + // - z2) + dT/dP1 * (-Pc1 + Pc2) , + // -T + dT/dP2 * (P1 - P2) - drho/dP2 * T g (z1 - z2) - dT/dP2 * rho g (z1 + // - z2) + dT/dP2 * (-Pc1 + Pc2) } + + dFlux_dS[ip][ke] += dCapGrad_dS[ip][ke]; // dF/dS = { T * -dPc/dS1 , T * dPc/dS2 } + } + } + + // compute the flux and derivatives using upstream cell mobility + fluxVal[ip] = mobility[ip] * potGrad; // F = M * DPhi + + for( integer ke = 0; ke < 2; ++ke ) + { + dFlux_dP[ip][ke] *= mobility[ip]; // dF/dP = { M [ T + dT/dP1 * (P1 - P2) - drho/dP1 * T g (z1 - z2) - dT/dP1 * rho g (z1 - + // z2) + dT/dP1 * (-Pc1 + Pc2)] , + // M [-T + dT/dP2 * (P1 - P2) - drho/dP2 * T g (z1 - z2) - dT/dP2 * rho g (z1 - + // z2) + dT/dP2 * (-Pc1 + Pc2)] } + + dFlux_dS[ip][ke] *= mobility[ip]; // dF/dS = { M [T * -dPc/dS1] , M [T * dPc/dS2] } + } + + // add contribution from upstream cell mobility derivatives + for( integer ke = 0; ke < 2; ++ke ) + { + + real64 dMob_dP2 = mobility[ip] / density2[ip] * (-dVisc_dP[ip][ke] / viscosity[ip]); + + duT_dP[ke] += dFlux_dP[ip][ke] / density2[ip] + dMob_dP2 * potGrad; + + dFlux_dP[ip][ke] += dMob_dP[ip][ke] * potGrad; // dF/dP = { M [ T + dT/dP1 * (P1 - P2) - drho1/dP * T g (z1 - z2) - dT/dP1 * + // rho g (z1 - z2) + dT/dP1 * (-Pc1 + Pc2)] + dM/dP1 * DPhi , + // M [-T + dT/dP2 * (P1 - P2) - drho2/dP * T g (z1 - z2) - dT/dP2 * + // rho g (z1 - z2) + dT/dP2 * (-Pc1 + Pc2)] + dM/dP2 * DPhi } + dFlux_dS[ip][ke] += dMob_dS[ip][ke] * potGrad; // dF/dS = { M [T * -dPc/dS1] + dM/dS1 * DPhi , M [T * dPc/dS2] + dM/dS2 * DPhi + // } + } + + + uT += fluxVal[ip] / density2[ip]; + + // add contribution from upstream cell mobility derivatives + for( integer ke = 0; ke < 2; ++ke ) + { +// duT_dP[ke] += dFlux_dP[ip][ke] - fluxVal[ip] * dDens_dP2[ip][ke] / density2[ip]; +// // duT_dP[ke] += dFlux_dP[ip][ke]; + +// duT_dP[ke] /= density2[ip]; + + duT_dS[ke] += dFlux_dS[ip][ke] / density2[ip]; + // duT_dS[ke] += dFlux_dS[ip][ke]; + + } + + potGrad_ip[ip] = potGrad; + alpha_ip[ip] = alpha; + + } // loop over phases + + + // this determines whether the local solver is needed becuase of heterogeneous capillary pressure regions + // bool notOnInterface = std::fabs( jFMultiplier[0][0] - jFMultiplier[0][1] ) < 1 && std::fabs( jFMultiplier[1][0] - jFMultiplier[1][1] ) < 1; + bool notOnInterface = !connectorHasInterfaceConditionQ; + if( notOnInterface ) + { + for( integer ip = 0; ip < 2; ++ip ) + { + // populate local flux vector and derivatives + stack.localFlux[k[0]*numEqn + ip] += m_dt * fluxVal[ip]; + stack.localFlux[k[1]*numEqn + ip] -= m_dt * fluxVal[ip]; + + for( integer ke = 0; ke < 2; ++ke ) + { + // pressure + localIndex const localDofIndexPres = k[ke] * numDof; + stack.localFluxJacobian[k[0]*numEqn + ip][localDofIndexPres] += m_dt * dFlux_dP[ip][ke]; + stack.localFluxJacobian[k[1]*numEqn + ip][localDofIndexPres] -= m_dt * dFlux_dP[ip][ke]; + + // saturation + localIndex const localDofIndexSat = k[ke] * numDof + 1; + stack.localFluxJacobian[k[0]*numEqn + ip][localDofIndexSat] += m_dt * dFlux_dS[ip][ke]; + stack.localFluxJacobian[k[1]*numEqn + ip][localDofIndexSat] -= m_dt * dFlux_dS[ip][ke]; + } + + // Customize the kernel with this lambda + kernelOp( k, seri, sesri, sei, connectionIndex, alpha_ip[ip], mobility, potGrad_ip[ip], fluxVal, dFlux_dP, dFlux_dS ); + + } + } + else + { + + + bool converged = 0; + + + // clear working arrays + real64 halfFluxVal[numEqn][2]{}; + // real64 dhalfFlux1_dP[numEqn][2]{}; + // real64 dhalfFlux1_dS[numEqn][2]{}; + // real64 dhalfFlux2_dP[numEqn][2]{}; + // real64 dhalfFlux2_dS[numEqn][2]{}; + // real64 dhalfFlux_duT[numEqn][2]{}; + // real64 dhalfFlux_dpc[numEqn][2]{}; + + + // stdVector< real64 > JFMultipliers = {jFMultiplier[0][0], jFMultiplier[0][1]}; + stdVector< real64 > JFMultipliers = {0.0, 0.0}; + // trappedSats will be extracted from models below and passed to local_solver + stdVector< real64 > trappedSats1 = {0.0, 0.0}; + stdVector< real64 > trappedSats2 = {0.0, 0.0}; + stdVector< fields::cappres::ModeIndexType> modes = {static_cast(0), static_cast(0)}; + stdVector< real64 > transHats = {transHat[0], transHat[1]}; + stdVector< real64 > dTransHats_dP = {dTransHat_dP[0], dTransHat_dP[1]}; + stdVector< real64 > gravCoefHats = {gravCoefHat[0], gravCoefHat[1]}; + stdVector< real64 > gravCoefs = {gravCoef2[0], gravCoef2[1]}; + stdVector< real64 > cellCenterDuTdS = {duT_dP[0], duT_dP[1], duT_dS[0], duT_dS[1]}; + stdVector< real64 > cellCenterDens = {density2[0], density2[1]}; + stdVector< real64 > cellCenterDens_dP = {dDens_dP2[0][0], dDens_dP2[0][1], dDens_dP2[1][0], dDens_dP2[1][1]}; + // std::vector< RelativePermeabilityBase * > relPerms = {std::get< 0 >( m_interfaceConstitutivePairs_temp ), std::get< 0 >( m_interfaceConstitutivePairs_temp )}; + // std::vector< CapillaryPressureBase * > capPressures = {std::get< 1 >( m_interfaceConstitutivePairs_temp ), std::get< 1 >( m_interfaceConstitutivePairs_temp )}; + // std::vector< TwoPhaseImmiscibleFluid * > fluids = {std::get< 2 >( m_interfaceConstitutivePairs_temp ), std::get< 2 >( m_interfaceConstitutivePairs_temp )}; + + // auto const & pairArray = m_interfaceConstitutivePairs[0]; + localIndex const surfaceRegionIndex = m_interfaceRegionByConnector.at(iconn); +auto const & pairArray = m_interfaceConstitutivePairs[surfaceRegionIndex]; + +std::vector< constitutive::RelativePermeabilityBase * > relPerms = { + std::get<0>( pairArray[0] ), + std::get<0>( pairArray[1] ) +}; + +std::vector< constitutive::CapillaryPressureBase * > capPressures = { + std::get<1>( pairArray[0] ), + std::get<1>( pairArray[1] ) +}; + +std::vector< constitutive::TwoPhaseImmiscibleFluid * > fluids = { + std::get<2>( pairArray[0] ), + std::get<2>( pairArray[1] ) +}; + + // Extract historical volume fractions, trapped saturations, and mode from TableCapillaryPressureHysteresis models + // Initialize with sentinel values to indicate they haven't been set + // Use -1.0 as a sentinel value (invalid for saturations which are 0-1) + stdVector< real64 > phaseMaxHistoricalVolFraction1 = {-1.0, -1.0}; + stdVector< real64 > phaseMinHistoricalVolFraction1 = {-1.0, -1.0}; + stdVector< real64 > phaseMaxHistoricalVolFraction2 = {-1.0, -1.0}; + stdVector< real64 > phaseMinHistoricalVolFraction2 = {-1.0, -1.0}; + + // Trapped saturations are available in all capillary pressure models (via base class), + // but we only extract them when using hysteresis models since they're most meaningful there + // Initialize with sentinel values to indicate they haven't been set + stdVector< real64 > trappedSats1_extracted = {-1.0, -1.0}; + stdVector< real64 > trappedSats2_extracted = {-1.0, -1.0}; + + for( integer ke = 0; ke < 2; ++ke ) + { + constitutive::TableCapillaryPressureHysteresis * capPresHyst = + dynamic_cast(capPressures[ke]); + + if( capPresHyst != nullptr ) + { + // Get the mode for this cell + auto const & modeArray = capPresHyst->getField< fields::cappres::mode >().reference(); + if( sei[ke] < static_cast(modeArray.size()) ) + { + modes[ke] = static_cast(modeArray[sei[ke]]); + } + + // Get historical volume fractions for this cell + auto const & maxHistArray = capPresHyst->getField< fields::cappres::phaseMaxHistoricalVolFraction >().reference(); + auto const & minHistArray = capPresHyst->getField< fields::cappres::phaseMinHistoricalVolFraction >().reference(); + + if( sei[ke] < static_cast(maxHistArray.size(0)) && m_numPhases > 0 ) + { + for( integer ip = 0; ip < m_numPhases; ++ip ) + { + if( ke == 0 ) + { + phaseMaxHistoricalVolFraction1[ip] = maxHistArray[sei[ke]][ip]; + phaseMinHistoricalVolFraction1[ip] = minHistArray[sei[ke]][ip]; + } + else + { + phaseMaxHistoricalVolFraction2[ip] = maxHistArray[sei[ke]][ip]; + phaseMinHistoricalVolFraction2[ip] = minHistArray[sei[ke]][ip]; + } + } + } + + // Get trapped volume fractions for this cell + // Note: phaseTrappedVolFraction is available in all capillary pressure models (via base class) + auto const & trappedArray = capPresHyst->getField< fields::cappres::phaseTrappedVolFraction >().reference(); + + if( sei[ke] < static_cast(trappedArray.size(0)) && m_numPhases > 0 ) + { + for( integer ip = 0; ip < m_numPhases; ++ip ) + { + if( ke == 0 ) + { + trappedSats1_extracted[ip] = trappedArray[sei[ke]][0][ip]; // [element][subregion][phase] + } + else + { + trappedSats2_extracted[ip] = trappedArray[sei[ke]][0][ip]; + } + } + } + } + else + { + // For non-hysteresis models, we can still try to get trapped saturations if available + // but they're typically not meaningful for non-hysteresis models + // We'll extract them anyway in case they were set (e.g., for consistency) + auto const & trappedArray = capPressures[ke]->getField< fields::cappres::phaseTrappedVolFraction >().reference(); + + if( sei[ke] < static_cast(trappedArray.size(0)) && m_numPhases > 0 ) + { + for( integer ip = 0; ip < m_numPhases; ++ip ) + { + if( ke == 0 ) + { + trappedSats1_extracted[ip] = trappedArray[sei[ke]][0][ip]; + } + else + { + trappedSats2_extracted[ip] = trappedArray[sei[ke]][0][ip]; + } + } + } + } + } + + stdVector< real64 > phi = {halfFluxVal[0][0], halfFluxVal[0][1]}; + stdVector< real64 > grad_phi_P = {0.0, 0.0, 0.0, 0.0}; + stdVector< real64 > grad_phi_S = {0.0, 0.0, 0.0, 0.0}; + + // // Debug: print if local_solver is being called + // printf( "DEBUG: local_solver called for iconn=%d, connectorHasInterfaceConditionQ=%d\n", + // static_cast(iconn), static_cast(connectorHasInterfaceConditionQ) ); + + // Use extracted trapped saturations if available, otherwise use defaults + if( trappedSats1_extracted[0] >= 0.0 ) + { + trappedSats1 = trappedSats1_extracted; + } + if( trappedSats2_extracted[0] >= 0.0 ) + { + trappedSats2 = trappedSats2_extracted; + } + + local_solver( uT, saturations, pressures, JFMultipliers, trappedSats1, trappedSats2, modes, transHats, dTransHats_dP, gravCoefHats, gravCoefs, + cellCenterDuTdS, cellCenterDens, cellCenterDens_dP, relPerms, capPressures, fluids, phi, grad_phi_P, grad_phi_S, converged, + phaseMaxHistoricalVolFraction1, phaseMinHistoricalVolFraction1, phaseMaxHistoricalVolFraction2, phaseMinHistoricalVolFraction2 ); + + + fluxVal[0] = phi[0]; + fluxVal[1] = phi[1]; + dFlux_dP[0][0] = grad_phi_P[0]; + dFlux_dP[0][1] = grad_phi_P[1]; + dFlux_dP[1][0] = grad_phi_P[2]; + dFlux_dP[1][1] = grad_phi_P[3]; + dFlux_dS[0][0] = grad_phi_S[0]; + dFlux_dS[0][1] = grad_phi_S[1]; + dFlux_dS[1][0] = grad_phi_S[2]; + dFlux_dS[1][1] = grad_phi_S[3]; + + // Global residual and jacobian update: + for( integer ip = 0; ip < m_numPhases; ++ip ) + { + // populate local flux vector and derivatives + stack.localFlux[k[0]*numEqn + ip] += m_dt * fluxVal[ip]; + stack.localFlux[k[1]*numEqn + ip] -= m_dt * fluxVal[ip]; + + for( integer ke = 0; ke < 2; ++ke ) + { + // pressure + localIndex const localDofIndexPres = k[ke] * numDof; + stack.localFluxJacobian[k[0]*numEqn + ip][localDofIndexPres] += m_dt * dFlux_dP[ip][ke]; + stack.localFluxJacobian[k[1]*numEqn + ip][localDofIndexPres] -= m_dt * dFlux_dP[ip][ke]; + + // saturation + localIndex const localDofIndexSat = k[ke] * numDof + 1; + stack.localFluxJacobian[k[0]*numEqn + ip][localDofIndexSat] += m_dt * dFlux_dS[ip][ke]; + stack.localFluxJacobian[k[1]*numEqn + ip][localDofIndexSat] -= m_dt * dFlux_dS[ip][ke]; + } + + // Customize the kernel with this lambda + kernelOp( k, seri, sesri, sei, connectionIndex, alpha_ip[ip], mobility, potGrad_ip[ip], fluxVal, dFlux_dP, dFlux_dS ); + + } // loop over phases + } // end of else for interface conditions + + connectionIndex++; + } + } // loop over connection elements + } + +protected: + + /// Reference to the capillary pressure wrapper + // CAPPRESWRAPPER const m_capPressureWrapper; + // RELPERMWRAPPER const m_relPermWrapper; + string_array const m_interfaceFaceSetNames; + stdVector< std::array< std::tuple< constitutive::RelativePermeabilityBase *, + constitutive::CapillaryPressureBase *, + constitutive::TwoPhaseImmiscibleFluid * >, 2 > > const m_interfaceConstitutivePairs; + unordered_map< localIndex, localIndex > const m_interfaceRegionByConnector; + // std::tuple< constitutive::RelativePermeabilityBase *, + // constitutive::CapillaryPressureBase *, + // constitutive::TwoPhaseImmiscibleFluid * > const m_interfaceConstitutivePairs_temp; + +}; + + + +/****************************************** */ + +/** + * @class FluxComputeKernelFactory + */ +class FluxComputeKernelFactory +{ +public: + + /** + * @brief Create a new kernel and launch + * @tparam POLICY the policy used in the RAJA kernel + * @tparam STENCILWRAPPER the type of the stencil wrapper + * @param[in] rankOffset the offset of my MPI rank + * @param[in] dofKey string to get the element degrees of freedom numbers + * @param[in] solverName name of the solver (to name accessors) + * @param[in] elemManager reference to the element region manager + * @param[in] stencilWrapper reference to the stencil wrapper + * @param[in] dt time step size + * @param[inout] localMatrix the local CRS matrix + * @param[inout] localRhs the local right-hand side vector + */ + template< typename POLICY, typename STENCILWRAPPER > + static void + createAndLaunch( integer const numPhases, + globalIndex const rankOffset, + string const & dofKey, + integer const hasCapPressure, + integer const useTotalMassEquation, + integer const checkPhasePresenceInGravity, + string const & solverName, + ElementRegionManager const & elemManager, + STENCILWRAPPER const & stencilWrapper, + real64 const & dt, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) + { + integer constexpr NUM_EQN = 2; + integer constexpr NUM_DOF = 2; + + ElementRegionManager::ElementViewAccessor< arrayView1d< globalIndex const > > dofNumberAccessor = + elemManager.constructArrayViewAccessor< globalIndex, 1 >( dofKey ); + dofNumberAccessor.setName( solverName + "/accessors/" + dofKey ); + + using kernelType = FluxComputeKernel< NUM_EQN, NUM_DOF, STENCILWRAPPER >; + typename kernelType::ImmiscibleMultiphaseFlowAccessors flowAccessors( elemManager, solverName ); + typename kernelType::MultiphaseFluidAccessors fluidAccessors( elemManager, solverName ); + typename kernelType::CapPressureAccessors capPressureAccessors( elemManager, solverName ); + typename kernelType::PermeabilityAccessors permAccessors( elemManager, solverName ); + + kernelType kernel( numPhases, rankOffset, stencilWrapper, dofNumberAccessor, + flowAccessors, fluidAccessors, capPressureAccessors, permAccessors, + dt, localMatrix, localRhs, hasCapPressure, useTotalMassEquation, + checkPhasePresenceInGravity ); + kernelType::template launch< POLICY >( stencilWrapper.size(), kernel ); + } + + /** + * @brief Create a new kernel and launch + * @tparam POLICY the policy used in the RAJA kernel + * @tparam STENCILWRAPPER the type of the stencil wrapper + * @tparam CAPPRESWRAPPER the type of the capillary pressure wrapper + * @param[in] rankOffset the offset of my MPI rank + * @param[in] dofKey string to get the element degrees of freedom numbers + * @param[in] solverName name of the solver (to name accessors) + * @param[in] elemManager reference to the element region manager + * @param[in] stencilWrapper reference to the stencil wrapper + * @param[in] capPresWrapper reference to the capillary pressure wrapper + * @param[in] dt time step size + * @param[inout] localMatrix the local CRS matrix + * @param[inout] localRhs the local right-hand side vector + */ + // template< typename POLICY, typename STENCILWRAPPER, typename CAPPRESWRAPPER, typename RELPERMWRAPPER > + template< typename POLICY, typename STENCILWRAPPER > + static void + createAndLaunch( integer const numPhases, + globalIndex const rankOffset, + string const & dofKey, + integer const hasCapPressure, + integer const useTotalMassEquation, + integer const checkPhasePresenceInGravity, + string const & solverName, + ElementRegionManager const & elemManager, + STENCILWRAPPER const & stencilWrapper, + // CAPPRESWRAPPER const & capPresWrapper, + // RELPERMWRAPPER const & relPermWrapper, + string_array const & interfaceFaceSetNames, + stdVector< std::array< std::tuple< constitutive::RelativePermeabilityBase *, + constitutive::CapillaryPressureBase *, + constitutive::TwoPhaseImmiscibleFluid * >, 2 > > const & interfaceConstitutivePairs, + unordered_map< localIndex, localIndex > const & interfaceRegionByConnector, + // std::tuple< constitutive::RelativePermeabilityBase *, + // constitutive::CapillaryPressureBase *, + // constitutive::TwoPhaseImmiscibleFluid * > const & interfaceConstitutivePairs_temp, + ElementSubRegionBase const & subRegion, + real64 const & dt, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) + { + integer constexpr NUM_EQN = 2; + integer constexpr NUM_DOF = 2; + localIndex const domainSize = subRegion.size(); + ElementRegionManager::ElementViewAccessor< arrayView1d< globalIndex const > > dofNumberAccessor = + elemManager.constructArrayViewAccessor< globalIndex, 1 >( dofKey ); + dofNumberAccessor.setName( solverName + "/accessors/" + dofKey ); + + // using kernelType = FluxComputeInterfaceConditionKernel< NUM_EQN, NUM_DOF, STENCILWRAPPER, CAPPRESWRAPPER, RELPERMWRAPPER >; + using kernelType = FluxComputeInterfaceConditionKernel< NUM_EQN, NUM_DOF, STENCILWRAPPER >; + typename kernelType::ImmiscibleMultiphaseFlowAccessors flowAccessors( elemManager, solverName ); + typename kernelType::MultiphaseFluidAccessors fluidAccessors( elemManager, solverName ); + typename kernelType::CapPressureAccessors capPressureAccessors( elemManager, solverName ); + typename kernelType::PermeabilityAccessors permAccessors( elemManager, solverName ); + + // kernelType kernel( numPhases, rankOffset, stencilWrapper, capPresWrapper, relPermWrapper, dofNumberAccessor, + // flowAccessors, fluidAccessors, capPressureAccessors, permAccessors, + // dt, localMatrix, localRhs, hasCapPressure, useTotalMassEquation, + // checkPhasePresenceInGravity, interfaceFaceSetNames, interfaceConstitutivePairs, interfaceRegionByConnector, interfaceConstitutivePairs_temp, domainSize ); + kernelType kernel( numPhases, rankOffset, stencilWrapper, dofNumberAccessor, + flowAccessors, fluidAccessors, capPressureAccessors, permAccessors, + dt, localMatrix, localRhs, hasCapPressure, useTotalMassEquation, + checkPhasePresenceInGravity, interfaceFaceSetNames, interfaceConstitutivePairs, interfaceRegionByConnector, domainSize ); kernelType::template launch< POLICY >( stencilWrapper.size(), kernel ); } }; @@ -795,7 +3174,7 @@ class FluxComputeKernelFactory enum class KernelFlags { - TotalMassEquation = 1 << 0, // 1 + TotalMassEquation = 1 << 0, // 1 /// Add more flags like that if needed: // Flag2 = 1 << 1, // 2 @@ -1421,7 +3800,7 @@ class ResidualNormKernelFactory { ResidualNormKernel::launchLinf< POLICY >( subRegion.size(), kernel, residualNorm ); } - else // L2 norm + else // L2 norm { ResidualNormKernel::launchL2< POLICY >( subRegion.size(), kernel, residualNorm, residualNormalizer ); } @@ -1431,9 +3810,9 @@ class ResidualNormKernelFactory -} // namespace immiscible multiphasekernels +} // namespace immiscible multiphasekernels -} // namespace geos +} // namespace geos -#endif //GEOS_PHYSICSSOLVERS_FLUIDFLOW_MULTIPHASEKERNELS_HPP + #endif //GEOS_PHYSICSSOLVERS_FLUIDFLOW_MULTIPHASEKERNELS_HPP diff --git a/src/coreComponents/schema/schema.xsd.other b/src/coreComponents/schema/schema.xsd.other index e77cc9d197a..a3def28f6b5 100644 --- a/src/coreComponents/schema/schema.xsd.other +++ b/src/coreComponents/schema/schema.xsd.other @@ -3267,6 +3267,10 @@ A field can represent a physical variable. (pressure, temperature, global compos + + + + @@ -3276,7 +3280,11 @@ A field can represent a physical variable. (pressure, temperature, global compos +<<<<<<< HEAD +======= + +>>>>>>> Killough-PC_copy diff --git a/src/docs/doxygen/GeosxConfig.hpp b/src/docs/doxygen/GeosxConfig.hpp index 21b976a0d4c..a28bf2ae4ce 100644 --- a/src/docs/doxygen/GeosxConfig.hpp +++ b/src/docs/doxygen/GeosxConfig.hpp @@ -144,7 +144,7 @@ #define adiak_VERSION .. /// Version information for caliper -#define caliper_VERSION 2.8.0 +#define caliper_VERSION 2.4.0 /// Version information for Metis #define metis_VERSION 5.1.0