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