diff --git a/inputFiles/compositionalMultiphaseFlow/soreideWhitson/1D_100cells/1D_benchmark.xml b/inputFiles/compositionalMultiphaseFlow/soreideWhitson/1D_100cells/1D_benchmark.xml
index 89b9cc7dc86..adbf453fa25 100644
--- a/inputFiles/compositionalMultiphaseFlow/soreideWhitson/1D_100cells/1D_benchmark.xml
+++ b/inputFiles/compositionalMultiphaseFlow/soreideWhitson/1D_100cells/1D_benchmark.xml
@@ -1,294 +1,397 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/inputFiles/compositionalMultiphaseFlow/soreideWhitson/1D_100cells/1D_smoke.xml b/inputFiles/compositionalMultiphaseFlow/soreideWhitson/1D_100cells/1D_smoke.xml
index 19a577bde98..322444e025d 100644
--- a/inputFiles/compositionalMultiphaseFlow/soreideWhitson/1D_100cells/1D_smoke.xml
+++ b/inputFiles/compositionalMultiphaseFlow/soreideWhitson/1D_100cells/1D_smoke.xml
@@ -1,294 +1,391 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/inputFiles/compositionalMultiphaseWell/benchmarks/Class09Pb3/class09_pb3_drainageOnly_direct_base.xml b/inputFiles/compositionalMultiphaseWell/benchmarks/Class09Pb3/class09_pb3_drainageOnly_direct_base.xml
index f2c31503e8a..01804189fd8 100644
--- a/inputFiles/compositionalMultiphaseWell/benchmarks/Class09Pb3/class09_pb3_drainageOnly_direct_base.xml
+++ b/inputFiles/compositionalMultiphaseWell/benchmarks/Class09Pb3/class09_pb3_drainageOnly_direct_base.xml
@@ -1,8 +1,6 @@
-
-
-
-
-
+ useSurfaceConditions="1"
+ surfacePressure="101325"
+ surfaceTemperature="288.71"
+ control="totalVolRate"
+ useMass="1">
+
+
+
+
-
+
-
+
+ name="fluidTPFA"/>
@@ -80,13 +84,16 @@
solidModelName="nullSolid"
porosityModelName="rockPorosity"
permeabilityModelName="rockPerm"/>
+
+
+
@@ -94,9 +101,8 @@
-
+ wettingNonWettingRelPermTableNames="{ waterRelativePermeabilityTable, gasRelativePermeabilityTable }"/>
+
-
-
-
+
+
+
+
-
@@ -152,11 +158,13 @@
name="initCO2CompFracTable"
coordinates="{ -3238.2, -2506.13 }"
values="{ 0.000001, 0.000001 }"/>
+
-
@@ -165,37 +173,36 @@
name="waterRelativePermeabilityTable"
coordinateFiles="{ tables/phaseVolumeFraction_water.txt }"
voxelFile="tables/relPerm_water.txt"/>
+
+ voxelFile="tables/relPerm_gas.txt"/>
-
+ voxelFile="tables/capPres_water.txt"/>
-
+ interpolation="linear"/>
-
+ interpolation="linear"/>
-
-
diff --git a/inputFiles/compositionalMultiphaseWell/benchmarks/Class09Pb3/class09_pb3_drainageOnly_iterative_base.xml b/inputFiles/compositionalMultiphaseWell/benchmarks/Class09Pb3/class09_pb3_drainageOnly_iterative_base.xml
index 8e76b08905b..fe5f4272ad2 100644
--- a/inputFiles/compositionalMultiphaseWell/benchmarks/Class09Pb3/class09_pb3_drainageOnly_iterative_base.xml
+++ b/inputFiles/compositionalMultiphaseWell/benchmarks/Class09Pb3/class09_pb3_drainageOnly_iterative_base.xml
@@ -1,10 +1,7 @@
-
-
-
-
-
-
-
-
-
-
+
+
+
+
-
+
+
+
+
-
-
+ control="massRate"
+ useMass="1">
+
+
+
+
-
-
-
+
+ name="fluidTPFA"/>
-
-
-
+
+
+
-
-
-
+ wettingNonWettingRelPermTableNames="{ waterRelativePermeabilityTable, gasRelativePermeabilityTable }"/>
-
-
-
-
-
-
-
-
+
+
+
-
@@ -204,11 +202,13 @@
name="initCO2CompFracTable"
coordinates="{ -3238.2, -2506.13 }"
values="{ 0.000001, 0.000001 }"/>
+
-
@@ -217,43 +217,43 @@
name="waterRelativePermeabilityTable"
coordinateFiles="{ tables/phaseVolumeFraction_water.txt }"
voxelFile="tables/relPerm_water.txt"/>
+
+ voxelFile="tables/relPerm_gas.txt"/>
-
+ voxelFile="tables/capPres_water.txt"/>
-
+ interpolation="linear"/>
-
+ interpolation="linear"/>
-
diff --git a/inputFiles/compositionalMultiphaseWell/benchmarks/Class09Pb3/class09_pb3_hystRelperm_direct_base.xml b/inputFiles/compositionalMultiphaseWell/benchmarks/Class09Pb3/class09_pb3_hystRelperm_direct_base.xml
index c731abcf696..01e328f6cfa 100644
--- a/inputFiles/compositionalMultiphaseWell/benchmarks/Class09Pb3/class09_pb3_hystRelperm_direct_base.xml
+++ b/inputFiles/compositionalMultiphaseWell/benchmarks/Class09Pb3/class09_pb3_hystRelperm_direct_base.xml
@@ -1,8 +1,6 @@
-
-
-
-
-
+ useSurfaceConditions="1"
+ surfacePressure="101325"
+ surfaceTemperature="288.71"
+ control="totalVolRate"
+ useMass="1">
+
+
+
+
-
+
-
+
+ name="fluidTPFA"/>
@@ -80,26 +84,27 @@
solidModelName="nullSolid"
porosityModelName="rockPorosity"
permeabilityModelName="rockPerm"/>
+
+
+
-
-
+
-
-
-
-
-
+
+
+
+
-
@@ -157,59 +160,61 @@
name="initCO2CompFracTable"
coordinates="{ -3238.2, -2506.13 }"
values="{ 0.000001, 0.000001 }"/>
+
+
-
+ voxelFile="tables/capPres_water.txt"/>
-
+ interpolation="linear"/>
-
+ interpolation="linear"/>
-
-
+
+
+
-
-
diff --git a/inputFiles/compositionalMultiphaseWell/benchmarks/Class09Pb3/class09_pb3_hystRelperm_iterative_base.xml b/inputFiles/compositionalMultiphaseWell/benchmarks/Class09Pb3/class09_pb3_hystRelperm_iterative_base.xml
index ee1af0ef88d..b71cf698726 100644
--- a/inputFiles/compositionalMultiphaseWell/benchmarks/Class09Pb3/class09_pb3_hystRelperm_iterative_base.xml
+++ b/inputFiles/compositionalMultiphaseWell/benchmarks/Class09Pb3/class09_pb3_hystRelperm_iterative_base.xml
@@ -1,8 +1,6 @@
-
-
-
-
-
+ control="totalVolRate"
+ useMass="1">
+
+
+
+
-
+
-
+
+ name="fluidTPFA"/>
@@ -84,28 +88,27 @@
solidModelName="nullSolid"
porosityModelName="rockPorosity"
permeabilityModelName="rockPerm"/>
+
+
+
-
-
-
-
+
-
-
+
+
+
+
-
@@ -161,10 +164,12 @@
name="initCO2CompFracTable"
coordinates="{ -3238.2, -2506.13 }"
values="{ 0.000001, 0.000001 }"/>
+
+
+
+ voxelFile="tables/relPerm_gas.txt"/>
+ voxelFile="tables/capPres_water.txt"/>
-
+ interpolation="linear"/>
-
+ interpolation="linear"/>
-
+
+
+
-
-
diff --git a/inputFiles/compositionalMultiphaseWell/benchmarks/Egg/deadOilEgg_base_direct.xml b/inputFiles/compositionalMultiphaseWell/benchmarks/Egg/deadOilEgg_base_direct.xml
index 048e88999d7..40d16d1a47a 100644
--- a/inputFiles/compositionalMultiphaseWell/benchmarks/Egg/deadOilEgg_base_direct.xml
+++ b/inputFiles/compositionalMultiphaseWell/benchmarks/Egg/deadOilEgg_base_direct.xml
@@ -1,7 +1,6 @@
-
-
-
-
+
+
+
+
-
-
-
-
+
+
+
+
-
+
+
+
+
-
+
+
+
+
-
+
+
+
+
-
+
+
+
+
-
+
+
+
+
-
+
+
+
+
-
+
+
+
+
-
+
+
+
+
-
+
+
+
+
-
+ maxCompFractionChange="0.5"
+ useMass="1">
+
+
+
+
-
-
-
-
-
-
-
-
@@ -279,7 +350,6 @@
materialList="{ fluid }"/>
-
-
-
-
-
@@ -373,32 +439,27 @@
-
-
-
+ fieldName="wellElementConnectionRate"/>
+ fieldName="wellElementConnectionRate"/>
+ fieldName="wellElementConnectionRate"/>
+ fieldName="wellElementConnectionRate"/>
-
-
diff --git a/inputFiles/compositionalMultiphaseWell/benchmarks/Egg/deadOilEgg_base_iterative.xml b/inputFiles/compositionalMultiphaseWell/benchmarks/Egg/deadOilEgg_base_iterative.xml
index 615688bef78..3922880cf1a 100644
--- a/inputFiles/compositionalMultiphaseWell/benchmarks/Egg/deadOilEgg_base_iterative.xml
+++ b/inputFiles/compositionalMultiphaseWell/benchmarks/Egg/deadOilEgg_base_iterative.xml
@@ -1,7 +1,6 @@
-
-
-
-
+
+
+
+
-
-
-
-
+
+
+
+
-
+
+
+
+
-
+
+
+
+
-
+
+
+
+
-
+
+
+
+
-
+
+
+
+
-
+
+
+
+
-
+
+
+
+
-
+
+
+
+
-
+
+
+
+
-
+ maxCompFractionChange="0.5"
+ useMass="1">
+
+
+
+
-
-
-
-
-
-
-
-
@@ -281,7 +352,6 @@
materialList="{ fluid }"/>
-
-
-
-
-
@@ -375,32 +441,27 @@
-
-
-
+ fieldName="wellElementConnectionRate"/>
+ fieldName="wellElementonnectionRate"/>
+ fieldName="wellElementConnectionRate"/>
+ fieldName="wellElementConnectionRate"/>
-
-
diff --git a/inputFiles/compositionalMultiphaseWell/black_oil_wells_saturated_3d.xml b/inputFiles/compositionalMultiphaseWell/black_oil_wells_saturated_3d.xml
index 7ebac080cbc..dba3b84b0c5 100644
--- a/inputFiles/compositionalMultiphaseWell/black_oil_wells_saturated_3d.xml
+++ b/inputFiles/compositionalMultiphaseWell/black_oil_wells_saturated_3d.xml
@@ -2,7 +2,6 @@
-
-
+
-
-
-
+
+
+
+
-
+ maxRelativePressureChange="0.1"
+ maxCompFractionChange="0.1"
+ useMass="1">
+
+
+
+
-
-
-
-
+
-
+
-
+
-
@@ -148,12 +157,14 @@
name="region"
cellBlocks="{ * }"
materialList="{ fluid, rock, relperm, cappres }"/>
+
+
+ materialList="{ fluid }"/>
@@ -163,7 +174,7 @@
surfaceDensities="{ 800.907131537, 0.856234902739, 1020.3440 }"
componentMolarWeight="{ 120e-3, 25e-3, 18e-3 }"
tableFiles="{ pvto_bo.txt, pvtg_norv_bo.txt, pvtw_bo.txt }"/>
-
+
-
+
-
-
+
+
+ scale="0.2"/>
+
-
+ coordinates="{ 0.0, 0.05, 0.10, 0.15, 0.20, 0.25, 0.30, 0.35, 0.40, 0.45, 0.5, 0.55, 0.60, 0.65, 0.70, 0.75, 0.80, 0.85, 0.90, 0.95, 1.00 }"
+ values="{ 0, 0.0025, 0.0100, 0.0225, 0.0400, 0.0625, 0.0900, 0.1225, 0.1600, 0.2025, 0.2500, 0.3025, 0.3600, 0.4225, 0.4900, 0.5625, 0.6400, 0.7225, 0.8100, 0.9025, 1.0000 }"/>
+
+ coordinates="{ 0.0, 0.05, 0.10, 0.15, 0.20, 0.25, 0.30, 0.35, 0.40, 0.45, 0.5, 0.55, 0.60, 0.65, 0.70, 0.75, 0.80, 0.85, 0.90, 0.95, 1.00 }"
+ values="{ 0, 0.0025, 0.0100, 0.0225, 0.0400, 0.0625, 0.0900, 0.1225, 0.1600, 0.2025, 0.2500, 0.3025, 0.3600, 0.4225, 0.4900, 0.5625, 0.6400, 0.7225, 0.8100, 0.9025, 1.0000 }"/>
+
+ coordinates="{ 0.0, 0.05, 0.10, 0.15, 0.20, 0.25, 0.30, 0.35, 0.40, 0.45, 0.5, 0.55, 0.60, 0.65, 0.70, 0.75, 0.80, 0.85, 0.90, 0.95, 1.00 }"
+ values="{ 0, 0.0025, 0.0100, 0.0225, 0.0400, 0.0625, 0.0900, 0.1225, 0.1600, 0.2025, 0.2500, 0.3025, 0.3600, 0.4225, 0.4900, 0.5625, 0.6400, 0.7225, 0.8100, 0.9025, 1.0000 }"/>
+
+ coordinates="{ 0.0, 0.05, 0.10, 0.15, 0.20, 0.25, 0.30, 0.35, 0.40, 0.45, 0.5, 0.55, 0.60, 0.65, 0.70, 0.75, 0.80, 0.85, 0.90, 0.95, 1.00 }"
+ values="{ 0, 0.0025, 0.0100, 0.0225, 0.0400, 0.0625, 0.0900, 0.1225, 0.1600, 0.2025, 0.2500, 0.3025, 0.3600, 0.4225, 0.4900, 0.5625, 0.6400, 0.7225, 0.8100, 0.9025, 1.0000 }"/>
+ coordinates="{ 0.0, 0.05, 0.10, 0.15, 0.20, 0.25, 0.30, 0.35, 0.40, 0.45, 0.5, 0.55, 0.60, 0.65, 0.70, 0.75, 0.80, 0.85, 0.90, 0.95, 1.00 }"
+ values="{ 10000, 9025, 8100, 7225, 6400, 5625, 4900, 4225, 3600, 3025, 2500, 2025, 1600, 1225, 900, 625, 400, 225, 100, 25, 0 }"/>
+
-
+ coordinates="{ 0.0, 0.05, 0.10, 0.15, 0.20, 0.25, 0.30, 0.35, 0.40, 0.45, 0.5, 0.55, 0.60, 0.65, 0.70, 0.75, 0.80, 0.85, 0.90, 0.95, 1.00 }"
+ values="{ 0, 50, 200, 450, 800, 1250, 1800, 2450, 3200, 4050, 5000, 6050, 7200, 8450, 9800, 11250, 12800, 14450, 16200, 18050, 20000 }"/>
-
+
-
+
+ fieldName="wellElementConnectionRate"/>
-
+
-
+
-
diff --git a/inputFiles/compositionalMultiphaseWell/black_oil_wells_saturated_3d_stone2.xml b/inputFiles/compositionalMultiphaseWell/black_oil_wells_saturated_3d_stone2.xml
index ce669961545..a285a0d9773 100644
--- a/inputFiles/compositionalMultiphaseWell/black_oil_wells_saturated_3d_stone2.xml
+++ b/inputFiles/compositionalMultiphaseWell/black_oil_wells_saturated_3d_stone2.xml
@@ -2,7 +2,6 @@
-
-
+
-
-
-
+
+
+
+
-
+ maxRelativePressureChange="0.1"
+ maxCompFractionChange="0.1"
+ useMass="1">
+
+
+
+
-
-
-
-
-
-
-
+
+
+
+
+
+
-
-
+
-
+
-
+
-
@@ -147,12 +157,14 @@
name="region"
cellBlocks="{ * }"
materialList="{ fluid, rock, relperm, cappres }"/>
+
+
+ materialList="{ fluid }"/>
@@ -162,7 +174,7 @@
surfaceDensities="{ 800.907131537, 0.856234902739, 1020.3440 }"
componentMolarWeight="{ 120e-3, 25e-3, 18e-3 }"
tableFiles="{ pvto_bo.txt, pvtg_norv_bo.txt, pvtw_bo.txt }"/>
-
+
-
+
-
-
+
+
+ scale="0.2"/>
+
-
+ coordinates="{ 0.0, 0.05, 0.10, 0.15, 0.20, 0.25, 0.30, 0.35, 0.40, 0.45, 0.5, 0.55, 0.60, 0.65, 0.70, 0.75, 0.80, 0.85, 0.90, 0.95, 1.00 }"
+ values="{ 0, 0.0025, 0.0100, 0.0225, 0.0400, 0.0625, 0.0900, 0.1225, 0.1600, 0.2025, 0.2500, 0.3025, 0.3600, 0.4225, 0.4900, 0.5625, 0.6400, 0.7225, 0.8100, 0.9025, 1.0000 }"/>
+
+ coordinates="{ 0.0, 0.05, 0.10, 0.15, 0.20, 0.25, 0.30, 0.35, 0.40, 0.45, 0.5, 0.55, 0.60, 0.65, 0.70, 0.75, 0.80, 0.85, 0.90, 0.95, 1.00 }"
+ values="{ 0, 0.0025, 0.0100, 0.0225, 0.0400, 0.0625, 0.0900, 0.1225, 0.1600, 0.2025, 0.2500, 0.3025, 0.3600, 0.4225, 0.4900, 0.5625, 0.6400, 0.7225, 0.8100, 0.9025, 1.0000 }"/>
+
+ coordinates="{ 0.0, 0.05, 0.10, 0.15, 0.20, 0.25, 0.30, 0.35, 0.40, 0.45, 0.5, 0.55, 0.60, 0.65, 0.70, 0.75, 0.80, 0.85, 0.90, 0.95, 1.00 }"
+ values="{ 0, 0.0025, 0.0100, 0.0225, 0.0400, 0.0625, 0.0900, 0.1225, 0.1600, 0.2025, 0.2500, 0.3025, 0.3600, 0.4225, 0.4900, 0.5625, 0.6400, 0.7225, 0.8100, 0.9025, 1.0000 }"/>
+
+ coordinates="{ 0.0, 0.05, 0.10, 0.15, 0.20, 0.25, 0.30, 0.35, 0.40, 0.45, 0.5, 0.55, 0.60, 0.65, 0.70, 0.75, 0.80, 0.85, 0.90, 0.95, 1.00 }"
+ values="{ 0, 0.0025, 0.0100, 0.0225, 0.0400, 0.0625, 0.0900, 0.1225, 0.1600, 0.2025, 0.2500, 0.3025, 0.3600, 0.4225, 0.4900, 0.5625, 0.6400, 0.7225, 0.8100, 0.9025, 1.0000 }"/>
+ coordinates="{ 0.0, 0.05, 0.10, 0.15, 0.20, 0.25, 0.30, 0.35, 0.40, 0.45, 0.5, 0.55, 0.60, 0.65, 0.70, 0.75, 0.80, 0.85, 0.90, 0.95, 1.00 }"
+ values="{ 10000, 9025, 8100, 7225, 6400, 5625, 4900, 4225, 3600, 3025, 2500, 2025, 1600, 1225, 900, 625, 400, 225, 100, 25, 0 }"/>
+
-
+ coordinates="{ 0.0, 0.05, 0.10, 0.15, 0.20, 0.25, 0.30, 0.35, 0.40, 0.45, 0.5, 0.55, 0.60, 0.65, 0.70, 0.75, 0.80, 0.85, 0.90, 0.95, 1.00 }"
+ values="{ 0, 50, 200, 450, 800, 1250, 1800, 2450, 3200, 4050, 5000, 6050, 7200, 8450, 9800, 11250, 12800, 14450, 16200, 18050, 20000 }"/>
-
+
-
+
+ fieldName="wellElementConnectionRate"/>
-
+
-
+
-
diff --git a/inputFiles/compositionalMultiphaseWell/black_oil_wells_unsaturated_3d.xml b/inputFiles/compositionalMultiphaseWell/black_oil_wells_unsaturated_3d.xml
index 323b9a144c6..d2b7a5cd375 100644
--- a/inputFiles/compositionalMultiphaseWell/black_oil_wells_unsaturated_3d.xml
+++ b/inputFiles/compositionalMultiphaseWell/black_oil_wells_unsaturated_3d.xml
@@ -2,7 +2,6 @@
-
-
+
-
-
-
+
+
+
+
-
+ maxRelativePressureChange="0.1"
+ maxCompFractionChange="0.1"
+ useMass="1">
+
+
+
+
-
-
-
-
-
+
-
-
+
-
+
-
@@ -148,12 +158,14 @@
name="region"
cellBlocks="{ * }"
materialList="{ fluid, rock, relperm }"/>
+
+
+ materialList="{ fluid }"/>
@@ -163,7 +175,7 @@
surfaceDensities="{ 800.907131537, 0.856234902739, 1020.3440 }"
componentMolarWeight="{ 120e-3, 25e-3, 18e-3 }"
tableFiles="{ pvto_bo.txt, pvtg_norv_bo.txt, pvtw_bo.txt }"/>
-
+
-
-
+
+
+ scale="0.1"/>
+
-
+
-
+
+ fieldName="wellElementConnectionRate"/>
-
+ name="vtkOutput"/>
+
-
-
diff --git a/inputFiles/compositionalMultiphaseWell/black_oil_wells_unsaturated_3d_stone2.xml b/inputFiles/compositionalMultiphaseWell/black_oil_wells_unsaturated_3d_stone2.xml
index ea41c8adf9c..297511e60ae 100644
--- a/inputFiles/compositionalMultiphaseWell/black_oil_wells_unsaturated_3d_stone2.xml
+++ b/inputFiles/compositionalMultiphaseWell/black_oil_wells_unsaturated_3d_stone2.xml
@@ -1,260 +1,271 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/inputFiles/compositionalMultiphaseWell/bos.xml b/inputFiles/compositionalMultiphaseWell/bos.xml
new file mode 100644
index 00000000000..75b5a9826ad
--- /dev/null
+++ b/inputFiles/compositionalMultiphaseWell/bos.xml
@@ -0,0 +1,307 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/inputFiles/compositionalMultiphaseWell/compositional_multiphase_wells_1d.xml b/inputFiles/compositionalMultiphaseWell/compositional_multiphase_wells_1d.xml
index bea2c48815d..0869a727917 100644
--- a/inputFiles/compositionalMultiphaseWell/compositional_multiphase_wells_1d.xml
+++ b/inputFiles/compositionalMultiphaseWell/compositional_multiphase_wells_1d.xml
@@ -23,28 +23,38 @@
targetRegions="{ Region1 }"
temperature="297.15"/>
-
-
-
+
+
+
+
-
+ control="totalVolRate">
+
+
+
+
@@ -58,7 +68,6 @@
ny="{ 1 }"
nz="{ 1 }"
cellBlockNames="{ cb1 }">
-
-
+ name="fluidTPFA"/>
@@ -175,7 +182,6 @@
-
-
-
-
-
-
+
+
+
+
-
+
+
+
+
-
-
-
+ control="totalVolRate">
+
+
+
+
@@ -69,7 +81,6 @@
ny="{ 20 }"
nz="{ 1 }"
cellBlockNames="{ cb1 }">
-
-
-
+ name="fluidTPFA"/>
@@ -223,7 +231,6 @@
-
-
-
-
+
-
+
+
+
+
-
+
+
+
+
-
+ control="totalVolRate"
+ maxRelativePressureChange="0.1"
+ maxCompFractionChange="0.1">
+
+
+
+
@@ -78,7 +97,6 @@
ny="{ 20 }"
nz="{ 1 }"
cellBlockNames="{ cb1 }">
-
-
-
+ name="fluidTPFA"/>
@@ -223,7 +238,6 @@
-
-
-
-
+
-
-
+
-
+
+
+
+
-
+
+
+
+
-
+ control="totalVolRate"
+ maxRelativePressureChange="0.1"
+ maxCompFractionChange="0.1">
+
+
+
+
@@ -77,7 +96,6 @@
ny="{ 20 }"
nz="{ 1 }"
cellBlockNames="{ cb1 }">
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/inputFiles/compositionalMultiphaseWell/dome_soreide_whitson_base.xml b/inputFiles/compositionalMultiphaseWell/dome_soreide_whitson_base.xml
index 2e00878f00e..d69956ea1de 100644
--- a/inputFiles/compositionalMultiphaseWell/dome_soreide_whitson_base.xml
+++ b/inputFiles/compositionalMultiphaseWell/dome_soreide_whitson_base.xml
@@ -1,287 +1,348 @@
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/inputFiles/compositionalMultiphaseWell/isothm_mass_inj_table.xml b/inputFiles/compositionalMultiphaseWell/isothm_mass_inj_table.xml
index 14076fab917..9ecff209034 100644
--- a/inputFiles/compositionalMultiphaseWell/isothm_mass_inj_table.xml
+++ b/inputFiles/compositionalMultiphaseWell/isothm_mass_inj_table.xml
@@ -10,7 +10,7 @@
initialDt="1e2"
targetRegions="{ region, injwell }">
-
+ targetRegions="{ region }"/>
-
-
-
+ control="massRate"
+ useMass="1">
+
+
+
+
@@ -83,10 +86,8 @@
-
-
@@ -94,8 +95,6 @@
name="sink"
xMin="{ 89.99, 89.99, -0.01 }"
xMax="{ 101.01, 101.01, 1.01 }"/>
-
-
-
-
@@ -143,25 +143,28 @@
name="region"
cellBlocks="{ cb }"
materialList="{ fluid, rock, relperm }"/>
+
-
+
+
+
@@ -181,11 +184,9 @@
phaseMinVolumeFraction="{ 0.0, 0.0 }"
phaseRelPermExponent="{ 1.5, 1.5 }"
phaseRelPermMaxValue="{ 0.9, 0.9 }"/>
-
-
+
+
+
-
diff --git a/inputFiles/compositionalMultiphaseWell/isothm_vol_inj_table.xml b/inputFiles/compositionalMultiphaseWell/isothm_vol_inj_table.xml
index d39421e36fc..1e191175e19 100644
--- a/inputFiles/compositionalMultiphaseWell/isothm_vol_inj_table.xml
+++ b/inputFiles/compositionalMultiphaseWell/isothm_vol_inj_table.xml
@@ -10,7 +10,7 @@
initialDt="1e2"
targetRegions="{ region, injwell }">
-
+ targetRegions="{ region }"/>
-
-
-
+ control="totalVolRate"
+ useMass="1">
+
+
+
+
@@ -82,10 +86,8 @@
-
-
@@ -93,8 +95,6 @@
name="sink"
xMin="{ 89.99, 89.99, -0.01 }"
xMax="{ 101.01, 101.01, 1.01 }"/>
-
-
-
-
-
@@ -148,25 +150,28 @@
name="region"
cellBlocks="{ cb }"
materialList="{ fluid, rock, relperm }"/>
+
-
+
+
+
@@ -186,11 +191,9 @@
phaseMinVolumeFraction="{ 0.0, 0.0 }"
phaseRelPermExponent="{ 1.5, 1.5 }"
phaseRelPermMaxValue="{ 0.9, 0.9 }"/>
-
-
+
+
+
-
diff --git a/inputFiles/compositionalMultiphaseWell/resvol_constraint.xml b/inputFiles/compositionalMultiphaseWell/resvol_constraint.xml
index 7fe917a2076..134b8a44552 100644
--- a/inputFiles/compositionalMultiphaseWell/resvol_constraint.xml
+++ b/inputFiles/compositionalMultiphaseWell/resvol_constraint.xml
@@ -23,31 +23,42 @@
targetRegions="{ Region1 }"
temperature="297.15"/>
-
-
-
+
+
+
+
-
+ writeCSV="1">
+
+
+
+
@@ -61,7 +72,6 @@
ny="{ 1 }"
nz="{ 1 }"
cellBlockNames="{ cb1 }">
-
-
+
-
-
+
+ name="fluidTPFA"/>
@@ -192,7 +201,6 @@
-
-
-
-
-
-
-
+ useSurfaceConditions="1"
+ surfacePressure="101325"
+ surfaceTemperature="288.71"
+ control="totalVolRate"
+ maxCompFractionChange="0.2"
+ useMass="1">
+
+
+
+
-
-
-
+
-
-
-
+
-
-
-
-
-
-
-
-
@@ -185,42 +178,37 @@
-
+ sources="{ /Tasks/wellPressureCollection }"
+ filename="wellPressureHistory"/>
-
-
-
+ fieldName="pressure"/>
-
+
-
-
-
-
-
+
+
+
+
+
-
diff --git a/inputFiles/compositionalMultiphaseWell/staged_perf_base.xml b/inputFiles/compositionalMultiphaseWell/staged_perf_base.xml
index 302d728e9b4..d6359b2765e 100644
--- a/inputFiles/compositionalMultiphaseWell/staged_perf_base.xml
+++ b/inputFiles/compositionalMultiphaseWell/staged_perf_base.xml
@@ -10,11 +10,11 @@
targetRegions="{ reservoir, wellRegion1 }">
+ directParallel="0"/>
-
-
-
+ control="BHP">
+
+
+
+
-
-
+
+ name="fluidTPFA"/>
@@ -91,7 +93,6 @@
-
@@ -158,34 +159,31 @@
scale="1.0"/>
-
+ name="equil"
+ objectPath="ElementRegions"
+ datumElevation="0"
+ datumPressure="2.214e7"
+ initialPhaseName="water"
+ componentNames="{ co2, water }"
+ componentFractionVsElevationTableNames="{ initCO2CompFracTable, initWaterCompFracTable }"
+ temperatureVsElevationTableName="initTempTable"/>
+ name="initCO2CompFracTable"
+ coordinates="{ -3000.0, 0.0 }"
+ values="{ 0.0, 0.0 }"/>
+ name="initWaterCompFracTable"
+ coordinates="{ -3000.0, 0.0 }"
+ values="{ 1.0, 1.0 }"/>
-
+ name="initTempTable"
+ coordinates="{ -3000.0, 0.0 }"
+ values="{ 368, 288 }"/>
diff --git a/inputFiles/compositionalMultiphaseWell/staircase_co2_wells_3d.xml b/inputFiles/compositionalMultiphaseWell/staircase_co2_wells_3d.xml
index 388b15ca605..db9b88c034e 100644
--- a/inputFiles/compositionalMultiphaseWell/staircase_co2_wells_3d.xml
+++ b/inputFiles/compositionalMultiphaseWell/staircase_co2_wells_3d.xml
@@ -25,36 +25,47 @@
maxCompFractionChange="0.2"
useMass="1"/>
-
-
-
+
+
+
+
-
+ maxCompFractionChange="0.2"
+ useMass="1">
+
+
+
+
-
-
-
+ cellBlockNames="{ cb-0_0_0, cb-1_0_0, cb-0_1_0, cb-1_1_0, cb-0_0_1, cb-1_0_1, cb-0_1_1, cb-1_1_1, cb-0_0_2, cb-1_0_2, cb-0_1_2, cb-1_1_2, cb-0_0_3, cb-1_0_3, cb-0_1_3, cb-1_1_3 }">
-
+ wettingNonWettingRelPermTableNames="{ waterRelativePermeabilityTable, gasRelativePermeabilityTable }"/>
-
@@ -222,35 +226,37 @@
scale="0.96"/>
-
+
+ voxelFile="relPerm_gas.txt"/>
+
+ voxelFile="capPres_water.txt"/>
+
+
-
diff --git a/inputFiles/compositionalMultiphaseWell/staircase_co2_wells_hybrid_3d.xml b/inputFiles/compositionalMultiphaseWell/staircase_co2_wells_hybrid_3d.xml
index b7e30e91dcb..3f398adb118 100644
--- a/inputFiles/compositionalMultiphaseWell/staircase_co2_wells_hybrid_3d.xml
+++ b/inputFiles/compositionalMultiphaseWell/staircase_co2_wells_hybrid_3d.xml
@@ -28,35 +28,48 @@
maxRelativePressureChange="0.2"
useMass="1"/>
-
-
-
+
+
+
+
-
+ maxRelativePressureChange="0.2"
+ maxCompFractionChange="0.2"
+ useMass="1">
+
+
+
+
-
-
-
-
+
diff --git a/inputFiles/poromechanics/PoroElastic_staircase_co2_3d_fim.xml b/inputFiles/poromechanics/PoroElastic_staircase_co2_3d_fim.xml
index 0ca07929f8e..77dfa798145 100644
--- a/inputFiles/poromechanics/PoroElastic_staircase_co2_3d_fim.xml
+++ b/inputFiles/poromechanics/PoroElastic_staircase_co2_3d_fim.xml
@@ -1,13 +1,12 @@
-
-
-
+
+
-
-
+
+ targetRegions="{ channel, barrier }"/>
-
-
-
+
+
+
+
-
-
+ control="totalVolRate"
+ maxCompFractionChange="0.2"
+ useMass="1">
+
+
+
+
-
+
+
-
+
+
-
+
-
-
-
diff --git a/inputFiles/poromechanics/PoroElastic_staircase_co2_3d_sequential.xml b/inputFiles/poromechanics/PoroElastic_staircase_co2_3d_sequential.xml
index e978595a49f..4cebe6d253e 100755
--- a/inputFiles/poromechanics/PoroElastic_staircase_co2_3d_sequential.xml
+++ b/inputFiles/poromechanics/PoroElastic_staircase_co2_3d_sequential.xml
@@ -1,13 +1,12 @@
-
-
-
+
+
-
-
+
+ targetRegions="{ channel, barrier }"/>
-
-
-
+
+
+
+
-
-
+ control="totalVolRate"
+ maxCompFractionChange="0.2"
+ useMass="1">
+
+
+
+
-
+
+
-
+
+
-
+
-
-
diff --git a/inputFiles/poromechanics/PoroElastic_staircase_singlephase_3d_fim.xml b/inputFiles/poromechanics/PoroElastic_staircase_singlephase_3d_fim.xml
index 95f2090ada1..ad32b0c1f53 100644
--- a/inputFiles/poromechanics/PoroElastic_staircase_singlephase_3d_fim.xml
+++ b/inputFiles/poromechanics/PoroElastic_staircase_singlephase_3d_fim.xml
@@ -1,13 +1,12 @@
-
-
-
+
+
-
-
+
+ targetRegions="{ channel, barrier }"/>
-
-
-
+
+
+
+
-
-
+ surfacePressure="101325"
+ control="totalVolRate">
+
+
+
+
-
+ minTime="-1e11"
+ maxTime="1e7">
-
+
+
-
+
+
-
-
+
+
-
diff --git a/inputFiles/poromechanics/PoroElastic_staircase_singlephase_3d_sequential.xml b/inputFiles/poromechanics/PoroElastic_staircase_singlephase_3d_sequential.xml
index 0c952c6934f..838f662c5a9 100755
--- a/inputFiles/poromechanics/PoroElastic_staircase_singlephase_3d_sequential.xml
+++ b/inputFiles/poromechanics/PoroElastic_staircase_singlephase_3d_sequential.xml
@@ -1,13 +1,12 @@
-
-
-
+
+
-
-
+
+ targetRegions="{ channel, barrier }"/>
-
-
-
+
+
+
+
-
-
+ surfacePressure="101325"
+ control="totalVolRate">
+
+
+
+
-
+ minTime="-1e11"
+ maxTime="1e7">
-
+
+
-
+
-
@@ -118,24 +125,25 @@
name="linearElasticityStatistics"
solidSolverName="linearElasticity"
logLevel="1"/>
+
-
-
+
+
-
diff --git a/inputFiles/poromechanics/ReservoirThermoPoroElastic_Circulation_debug.xml b/inputFiles/poromechanics/ReservoirThermoPoroElastic_Circulation_debug.xml
index b2a0d65d59d..feb3d2e9307 100644
--- a/inputFiles/poromechanics/ReservoirThermoPoroElastic_Circulation_debug.xml
+++ b/inputFiles/poromechanics/ReservoirThermoPoroElastic_Circulation_debug.xml
@@ -2,12 +2,12 @@
-
+
-
-
-
-
+
+
+
+
-
-
+ writeCSV="1">
+
+
+
+
+
-
-
+
+
@@ -162,7 +173,6 @@
-
-
+ targetTime="-1e10"/>
-
+
-
-
-
+ poromechanicsSolverName="reservoirPoromechanics"/>
-
diff --git a/inputFiles/poromechanicsFractures/multiphasePoromechanics_FaultModel_well_fim_smoke.xml b/inputFiles/poromechanicsFractures/multiphasePoromechanics_FaultModel_well_fim_smoke.xml
index 0ce10628d75..56de13ffa2b 100644
--- a/inputFiles/poromechanicsFractures/multiphasePoromechanics_FaultModel_well_fim_smoke.xml
+++ b/inputFiles/poromechanicsFractures/multiphasePoromechanics_FaultModel_well_fim_smoke.xml
@@ -1,14 +1,16 @@
+
+
-
+
-
+ maxAllowedResidualNorm="1e+15"/>
+ directParallel="0"/>
+ targetRegions="{ Region, Fault }"
+ discretization="FE1"/>
+ logLevel="3"/>
-
+ targetRegions="{ Region, Fault }"
+ temperature="368.15"/>
-
-
-
+ logLevel="2"
+ useMass="1"
+ writeCSV="1">
+
+
+
+
@@ -88,47 +96,44 @@
numElementsPerSegment="1">
+ distanceFromHead="250"/>
-
+
+
-
-
-
+ target="/Outputs/vtkOutput"/>
+ target="/Solvers/reservoirSolver"/>
+ target="/Outputs/vtkOutput"/>
+ target="/Outputs/restartOutput"/>
diff --git a/inputFiles/poromechanicsFractures/multiphasePoromechanics_FaultModel_well_seq_smoke.xml b/inputFiles/poromechanicsFractures/multiphasePoromechanics_FaultModel_well_seq_smoke.xml
index d0959438756..4a30e02941e 100644
--- a/inputFiles/poromechanicsFractures/multiphasePoromechanics_FaultModel_well_seq_smoke.xml
+++ b/inputFiles/poromechanicsFractures/multiphasePoromechanics_FaultModel_well_seq_smoke.xml
@@ -1,14 +1,16 @@
+
+
-
+
-
+ maxAllowedResidualNorm="1e+15"/>
+ directParallel="0"/>
+ targetRegions="{ Region, Fault }"
+ discretization="FE1"/>
+ newtonMaxIter="20"/>
+ preconditionerType="mgr"/>
-
+ targetRegions="{ Region, Fault }"
+ temperature="368.15"/>
-
-
-
+ logLevel="2"
+ useMass="1"
+ writeCSV="1">
+
+
+
+
@@ -97,47 +105,44 @@
numElementsPerSegment="1">
+ distanceFromHead="250"/>
-
+
+
-
-
-
+ target="/Outputs/vtkOutput"/>
+ target="/Solvers/reservoirSolver"/>
+ target="/Outputs/vtkOutput"/>
+ target="/Outputs/restartOutput"/>
diff --git a/inputFiles/poromechanicsFractures/singlePhasePoromechanics_FaultModel_well_fim_new_smoke.xml b/inputFiles/poromechanicsFractures/singlePhasePoromechanics_FaultModel_well_fim_new_smoke.xml
index 07a6e1521ff..72aad6f89b6 100644
--- a/inputFiles/poromechanicsFractures/singlePhasePoromechanics_FaultModel_well_fim_new_smoke.xml
+++ b/inputFiles/poromechanicsFractures/singlePhasePoromechanics_FaultModel_well_fim_new_smoke.xml
@@ -1,14 +1,16 @@
+
+
-
+
-
+ solverType="direct"/>
-
+ logLevel="1"/>
+ targetRegions="{ Region, Fault }"
+ discretization="FE1"/>
-
+ targetRegions="{ Region, Fault }"/>
-
-
-
+
+
+
+
-
+ control="BHP">
+
+
+
+
-
+
+
-
-
-
+
-
+
-
+ solverType="direct"/>
+ targetRegions="{ Region, Fault }"
+ discretization="FE1"/>
-
+ targetRegions="{ Region, Fault }"/>
-
-
-
+
+
+
+
-
+ control="BHP">
+
+
+
+
-
+
+
-
-
-
+
-
+
-
+ directParallel="1"/>
-
+ logLevel="1"/>
+ targetRegions="{ Region, Fault }"
+ discretization="FE1"/>
-
+ targetRegions="{ Region, Fault }"/>
-
-
-
+
+
+
+
-
+ logLevel="2"
+ writeCSV="1"
+ type="injector">
+
+
+
+
-
+
+
-
-
-
+
-
+
-
+ directParallel="1"/>
+ targetRegions="{ Region, Fault }"
+ discretization="FE1"/>
-
+ targetRegions="{ Region, Fault }"/>
-
-
-
+
+
+
+
-
+ logLevel="2"
+ writeCSV="1"
+ type="injector">
+
+
+
+
-
+
+
-
-
-
+ targetRegions="{ region , wellRegion2 }">
-
+ targetRegions="{ region }"/>
-
-
-
+ control="BHP">
+
+
+
+
@@ -78,13 +82,13 @@
name="region"
cellBlocks="{ * }"
materialList="{ fluid, rock, thermalCond }"/>
+
-
-
-
-
diff --git a/inputFiles/singlePhaseWell/compressible_single_phase_wells_1d.xml b/inputFiles/singlePhaseWell/compressible_single_phase_wells_1d.xml
index 4b5f64d0287..0fe903b3199 100644
--- a/inputFiles/singlePhaseWell/compressible_single_phase_wells_1d.xml
+++ b/inputFiles/singlePhaseWell/compressible_single_phase_wells_1d.xml
@@ -20,25 +20,35 @@
discretization="singlePhaseTPFA"
targetRegions="{ Region1 }"/>
-
-
-
+
+
+
+
-
+ control="totalVolRate">
+
+
+
+
@@ -52,7 +62,6 @@
ny="{ 1 }"
nz="{ 1 }"
cellBlockNames="{ cb1 }">
-
-
-
-
-
+
+
+
+
-
+ control="totalVolRate">
+
+
+
+
@@ -52,7 +62,6 @@
ny="{ 1 }"
nz="{ 1 }"
cellBlockNames="{ cb1 }">
-
+ distanceFromHead="1.45"
+ skinFactor="1"/>
-
-
-
+
-
+
+
+
+
-
+ control="totalVolRate">
+
+
+
+
@@ -53,7 +63,6 @@
ny="{ 1 }"
nz="{ 1 }"
cellBlockNames="{ cb1 }">
-
-
-
-
+
-
+
+
+
+
-
+
+
+
+
-
+ initialPressureCoefficient="0.01"
+ control="totalVolRate">
+
+
+
+
@@ -60,7 +75,6 @@
ny="{ 20 }"
nz="{ 1 }"
cellBlockNames="{ cb1 }">
-
-
-
+ name="singlePhaseTPFA"/>
diff --git a/inputFiles/singlePhaseWell/incompressible_single_phase_wells_hybrid_2d.xml b/inputFiles/singlePhaseWell/incompressible_single_phase_wells_hybrid_2d.xml
index 743a1f7be70..28a95e82f30 100644
--- a/inputFiles/singlePhaseWell/incompressible_single_phase_wells_hybrid_2d.xml
+++ b/inputFiles/singlePhaseWell/incompressible_single_phase_wells_hybrid_2d.xml
@@ -21,32 +21,47 @@
discretization="singlePhaseHybridMimetic"
targetRegions="{ Region1 }"/>
-
-
-
+
+
+
+
-
+
+
+
+
-
+ control="totalVolRate">
+
+
+
+
@@ -60,7 +75,6 @@
ny="{ 20 }"
nz="{ 1 }"
cellBlockNames="{ cb1 }">
-
-
-
-
-
+
-
+
+
+
+
-
+
+
+
+
-
+ initialPressureCoefficient="0.01"
+ control="totalVolRate">
+
+
+
+
@@ -62,7 +76,6 @@
ny="{ 20 }"
nz="{ 1 }"
cellBlockNames="{ cb1 }">
-
-
-
+ name="singlePhaseTPFA"/>
diff --git a/inputFiles/singlePhaseWell/staircase_single_phase_wells_3d.xml b/inputFiles/singlePhaseWell/staircase_single_phase_wells_3d.xml
index 6789ab90542..55da1048afe 100644
--- a/inputFiles/singlePhaseWell/staircase_single_phase_wells_3d.xml
+++ b/inputFiles/singlePhaseWell/staircase_single_phase_wells_3d.xml
@@ -16,29 +16,39 @@
-
-
-
+
+
+
+
-
+ control="totalVolRate">
+
+
+
+
@@ -51,11 +61,7 @@
nx="{ 5, 5 }"
ny="{ 5, 5 }"
nz="{ 3, 3, 3, 3 }"
- cellBlockNames="{ cb-0_0_0, cb-1_0_0, cb-0_1_0, cb-1_1_0,
- cb-0_0_1, cb-1_0_1, cb-0_1_1, cb-1_1_1,
- cb-0_0_2, cb-1_0_2, cb-0_1_2, cb-1_1_2,
- cb-0_0_3, cb-1_0_3, cb-0_1_3, cb-1_1_3 }">
-
+ cellBlockNames="{ cb-0_0_0, cb-1_0_0, cb-0_1_0, cb-1_1_0, cb-0_0_1, cb-1_0_1, cb-0_1_1, cb-1_1_1, cb-0_0_2, cb-1_0_2, cb-0_1_2, cb-1_1_2, cb-0_0_3, cb-1_0_3, cb-0_1_3, cb-1_1_3 }">
-
@@ -172,9 +177,9 @@
diff --git a/inputFiles/singlePhaseWell/staircase_single_phase_wells_hybrid_3d.xml b/inputFiles/singlePhaseWell/staircase_single_phase_wells_hybrid_3d.xml
index 2298a32e483..256250d8cc7 100644
--- a/inputFiles/singlePhaseWell/staircase_single_phase_wells_hybrid_3d.xml
+++ b/inputFiles/singlePhaseWell/staircase_single_phase_wells_hybrid_3d.xml
@@ -21,27 +21,37 @@
discretization="singlePhaseHybridMimetic"
targetRegions="{ Channel }"/>
-
-
-
+
+
+
+
-
+ control="totalVolRate">
+
+
+
+
@@ -54,11 +64,7 @@
nx="{ 5, 5 }"
ny="{ 5, 5 }"
nz="{ 3, 3, 3, 3 }"
- cellBlockNames="{ cb-0_0_0, cb-1_0_0, cb-0_1_0, cb-1_1_0,
- cb-0_0_1, cb-1_0_1, cb-0_1_1, cb-1_1_1,
- cb-0_0_2, cb-1_0_2, cb-0_1_2, cb-1_1_2,
- cb-0_0_3, cb-1_0_3, cb-0_1_3, cb-1_1_3 }">
-
+ cellBlockNames="{ cb-0_0_0, cb-1_0_0, cb-0_1_0, cb-1_1_0, cb-0_0_1, cb-1_0_1, cb-0_1_1, cb-1_1_1, cb-0_0_2, cb-1_0_2, cb-0_1_2, cb-1_1_2, cb-0_0_3, cb-1_0_3, cb-0_1_3, cb-1_1_3 }">
-
::getWaterPhaseIndex() const
return PVTFunctionHelpers::findName( m_phaseNames, expectedWaterPhaseNames, viewKeyStruct::phaseNamesString() );
}
+template< typename PHASE1, typename PHASE2, typename FLASH >
+integer CO2BrineFluid< PHASE1, PHASE2, FLASH >::getPhaseIndex( const std::string & phaseName ) const
+{
+ string const expectedPhaseName[] = { phaseName };
+ return PVTFunctionHelpers::findName( m_phaseNames, expectedPhaseName, viewKeyStruct::phaseNamesString() );
+}
+
template< typename PHASE1, typename PHASE2, typename FLASH >
void CO2BrineFluid< PHASE1, PHASE2, FLASH >::checkTablesParameters( real64 const pressure,
real64 const temperature ) const
diff --git a/src/coreComponents/constitutive/fluid/multifluid/CO2Brine/CO2BrineFluid.hpp b/src/coreComponents/constitutive/fluid/multifluid/CO2Brine/CO2BrineFluid.hpp
index 1b89321acd7..9f7ec5d5860 100644
--- a/src/coreComponents/constitutive/fluid/multifluid/CO2Brine/CO2BrineFluid.hpp
+++ b/src/coreComponents/constitutive/fluid/multifluid/CO2Brine/CO2BrineFluid.hpp
@@ -149,6 +149,8 @@ class CO2BrineFluid : public MultiFluidBase
virtual integer getWaterPhaseIndex() const override final;
+ virtual integer getPhaseIndex( const std::string & phaseName ) const override final;
+
/**
* @copydoc MultiFluidBase::checkTablesParameters( real64 pressure, real64 temperature )
*/
diff --git a/src/coreComponents/constitutive/fluid/multifluid/MultiFluidBase.hpp b/src/coreComponents/constitutive/fluid/multifluid/MultiFluidBase.hpp
index 9396fe6ad3e..c4ffc05a25d 100644
--- a/src/coreComponents/constitutive/fluid/multifluid/MultiFluidBase.hpp
+++ b/src/coreComponents/constitutive/fluid/multifluid/MultiFluidBase.hpp
@@ -105,6 +105,13 @@ class MultiFluidBase : public ConstitutiveBase
*/
virtual integer getWaterPhaseIndex() const = 0;
+ /**
+ * @brief Getter for the phase index
+ * @param phaseName the name of the phase
+ * @return the phase index
+ */
+ virtual integer getPhaseIndex( const std::string & phaseName ) const = 0;
+
/**
* @brief Get the thermal flag.
* @return boolean value indicating whether the model can be used to assemble the energy balance equation or not
diff --git a/src/coreComponents/constitutive/fluid/multifluid/blackOil/BlackOilFluidBase.cpp b/src/coreComponents/constitutive/fluid/multifluid/blackOil/BlackOilFluidBase.cpp
index 098fcefacad..5805746c39b 100644
--- a/src/coreComponents/constitutive/fluid/multifluid/blackOil/BlackOilFluidBase.cpp
+++ b/src/coreComponents/constitutive/fluid/multifluid/blackOil/BlackOilFluidBase.cpp
@@ -162,6 +162,12 @@ integer BlackOilFluidBase::getWaterPhaseIndex() const
return PVTProps::PVTFunctionHelpers::findName( m_phaseNames, expectedWaterPhaseNames, viewKeyStruct::phaseNamesString() );
}
+integer BlackOilFluidBase::getPhaseIndex( const std::string & phaseName ) const
+{
+ string const expectedPhaseName[] = { phaseName };
+ return PVTProps::PVTFunctionHelpers::findName( m_phaseNames, expectedPhaseName, viewKeyStruct::phaseNamesString() );
+}
+
void BlackOilFluidBase::postInputInitialization()
{
m_componentNames = m_phaseNames;
diff --git a/src/coreComponents/constitutive/fluid/multifluid/blackOil/BlackOilFluidBase.hpp b/src/coreComponents/constitutive/fluid/multifluid/blackOil/BlackOilFluidBase.hpp
index 8ec94f07f64..37215b0916f 100644
--- a/src/coreComponents/constitutive/fluid/multifluid/blackOil/BlackOilFluidBase.hpp
+++ b/src/coreComponents/constitutive/fluid/multifluid/blackOil/BlackOilFluidBase.hpp
@@ -129,6 +129,8 @@ class BlackOilFluidBase : public MultiFluidBase
virtual integer getWaterPhaseIndex() const override final;
+ virtual integer getPhaseIndex( const std::string & phaseName ) const override final;
+
virtual void postInputInitialization() override;
virtual void initializePostSubGroups() override;
diff --git a/src/coreComponents/constitutive/fluid/multifluid/compositional/CompositionalMultiphaseFluid.cpp b/src/coreComponents/constitutive/fluid/multifluid/compositional/CompositionalMultiphaseFluid.cpp
index 56c24735d27..e3a86326aa9 100644
--- a/src/coreComponents/constitutive/fluid/multifluid/compositional/CompositionalMultiphaseFluid.cpp
+++ b/src/coreComponents/constitutive/fluid/multifluid/compositional/CompositionalMultiphaseFluid.cpp
@@ -91,6 +91,13 @@ integer CompositionalMultiphaseFluid< FLASH, PHASE1, PHASE2, PHASE3 >::getWaterP
return -1;
}
+template< typename FLASH, typename PHASE1, typename PHASE2, typename PHASE3 >
+integer CompositionalMultiphaseFluid< FLASH, PHASE1, PHASE2, PHASE3 >::getPhaseIndex( const std::string & phaseName ) const
+{
+ integer const phaseIndex = findPhaseIndex( phaseName );
+ return m_phaseOrder.size() > phaseIndex ? m_phaseOrder[phaseIndex] : -1;
+}
+
template< typename FLASH, typename PHASE1, typename PHASE2, typename PHASE3 >
string CompositionalMultiphaseFluid< FLASH, PHASE1, PHASE2, PHASE3 >::catalogName()
{
@@ -285,7 +292,21 @@ array1d< integer > CompositionalMultiphaseFluid< FLASH, PHASE1, PHASE2, PHASE3 >
}
return phaseTypes;
}
+template< typename FLASH, typename PHASE1, typename PHASE2, typename PHASE3 >
+integer CompositionalMultiphaseFluid< FLASH, PHASE1, PHASE2, PHASE3 >::findPhaseIndex( string names ) const
+{
+ auto const nameContainer = stringutilities::tokenize( names, ",", true, false );
+ for( integer ip = 0; ip < numFluidPhases(); ++ip )
+ {
+ std::string const phaseName = stringutilities::toLower( m_phaseNames[ip] );
+ if( std::find( nameContainer.begin(), nameContainer.end(), phaseName ) != nameContainer.end())
+ {
+ return ip;
+ }
+ }
+ return -1;
+}
// Create the fluid models
template< typename FLASH, typename PHASE1, typename PHASE2, typename PHASE3 >
std::unique_ptr< compositional::ModelParameters >
diff --git a/src/coreComponents/constitutive/fluid/multifluid/compositional/CompositionalMultiphaseFluid.hpp b/src/coreComponents/constitutive/fluid/multifluid/compositional/CompositionalMultiphaseFluid.hpp
index bd9faf0ce44..e2d04996ca1 100644
--- a/src/coreComponents/constitutive/fluid/multifluid/compositional/CompositionalMultiphaseFluid.hpp
+++ b/src/coreComponents/constitutive/fluid/multifluid/compositional/CompositionalMultiphaseFluid.hpp
@@ -92,6 +92,8 @@ class CompositionalMultiphaseFluid : public MultiFluidBase
virtual integer getWaterPhaseIndex() const override final;
+ virtual integer getPhaseIndex( const std::string & phaseName ) const override final;
+
struct viewKeyStruct : MultiFluidBase::viewKeyStruct
{
static constexpr char const * componentCriticalPressureString() { return "componentCriticalPressure"; }
@@ -122,6 +124,7 @@ class CompositionalMultiphaseFluid : public MultiFluidBase
array1d< integer > getPhaseTypes() const;
+ integer findPhaseIndex( string names ) const;
static std::unique_ptr< compositional::ModelParameters > createModelParameters();
// Flash model
diff --git a/src/coreComponents/constitutive/fluid/multifluid/compositional/CompositionalMultiphaseFluidPVTPackage.cpp b/src/coreComponents/constitutive/fluid/multifluid/compositional/CompositionalMultiphaseFluidPVTPackage.cpp
index ae3303004b1..102f7a1464b 100644
--- a/src/coreComponents/constitutive/fluid/multifluid/compositional/CompositionalMultiphaseFluidPVTPackage.cpp
+++ b/src/coreComponents/constitutive/fluid/multifluid/compositional/CompositionalMultiphaseFluidPVTPackage.cpp
@@ -77,6 +77,12 @@ integer CompositionalMultiphaseFluidPVTPackage::getWaterPhaseIndex() const
return PVTProps::PVTFunctionHelpers::findName( m_phaseNames, expectedWaterPhaseNames, viewKeyStruct::phaseNamesString() );
}
+integer CompositionalMultiphaseFluidPVTPackage::getPhaseIndex( const std::string & phaseName ) const
+{
+ string const expectedPhaseName[] = { phaseName};
+ return PVTProps::PVTFunctionHelpers::findName( m_phaseNames, expectedPhaseName, viewKeyStruct::phaseNamesString() );
+}
+
void CompositionalMultiphaseFluidPVTPackage::postInputInitialization()
{
MultiFluidBase::postInputInitialization();
diff --git a/src/coreComponents/constitutive/fluid/multifluid/compositional/CompositionalMultiphaseFluidPVTPackage.hpp b/src/coreComponents/constitutive/fluid/multifluid/compositional/CompositionalMultiphaseFluidPVTPackage.hpp
index 6cab7a26e33..08321f21077 100644
--- a/src/coreComponents/constitutive/fluid/multifluid/compositional/CompositionalMultiphaseFluidPVTPackage.hpp
+++ b/src/coreComponents/constitutive/fluid/multifluid/compositional/CompositionalMultiphaseFluidPVTPackage.hpp
@@ -59,6 +59,8 @@ class CompositionalMultiphaseFluidPVTPackage : public MultiFluidBase
virtual integer getWaterPhaseIndex() const override final;
+ virtual integer getPhaseIndex( const std::string & phaseName ) const override final;
+
struct viewKeyStruct : MultiFluidBase::viewKeyStruct
{
static constexpr char const * equationsOfStateString() { return "equationsOfState"; }
diff --git a/src/coreComponents/constitutive/fluid/multifluid/constant/InvariantImmiscibleFluid.cpp b/src/coreComponents/constitutive/fluid/multifluid/constant/InvariantImmiscibleFluid.cpp
index 2083fba1b80..f366b41d568 100644
--- a/src/coreComponents/constitutive/fluid/multifluid/constant/InvariantImmiscibleFluid.cpp
+++ b/src/coreComponents/constitutive/fluid/multifluid/constant/InvariantImmiscibleFluid.cpp
@@ -45,7 +45,16 @@ integer InvariantImmiscibleFluid::getWaterPhaseIndex() const
}
return -1;
}
-
+integer InvariantImmiscibleFluid::getPhaseIndex( const std::string & phaseName ) const
+{
+ // find index of water phase in user ordering
+ for( size_t i=0; i< m_phaseNames.size(); ++i )
+ {
+ if( stringutilities::toLower( m_phaseNames[i] ) == phaseName )
+ return static_cast< integer >(i);
+ }
+ return -1;
+}
void InvariantImmiscibleFluid::postInputInitialization()
{
// check base inputs
diff --git a/src/coreComponents/constitutive/fluid/multifluid/constant/InvariantImmiscibleFluid.hpp b/src/coreComponents/constitutive/fluid/multifluid/constant/InvariantImmiscibleFluid.hpp
index 243198fa9b4..7a9274260c8 100644
--- a/src/coreComponents/constitutive/fluid/multifluid/constant/InvariantImmiscibleFluid.hpp
+++ b/src/coreComponents/constitutive/fluid/multifluid/constant/InvariantImmiscibleFluid.hpp
@@ -26,6 +26,8 @@ class InvariantImmiscibleFluid : public MultiFluidBase
virtual integer getWaterPhaseIndex() const override;
+ virtual integer getPhaseIndex( const std::string & phaseName ) const override final;
+
void checkTablesParameters( real64 pressure, real64 temperature ) const override;
/**
diff --git a/src/coreComponents/constitutive/fluid/multifluid/reactive/ReactiveBrineFluid.cpp b/src/coreComponents/constitutive/fluid/multifluid/reactive/ReactiveBrineFluid.cpp
index 055d48079e6..00b18834edb 100644
--- a/src/coreComponents/constitutive/fluid/multifluid/reactive/ReactiveBrineFluid.cpp
+++ b/src/coreComponents/constitutive/fluid/multifluid/reactive/ReactiveBrineFluid.cpp
@@ -122,7 +122,13 @@ integer ReactiveBrineFluid< PHASE > ::getWaterPhaseIndex() const
// There is only 1 phase
return 0;
}
-
+template< typename PHASE >
+integer ReactiveBrineFluid< PHASE > ::getPhaseIndex( const std::string & phaseName ) const
+{
+ GEOS_UNUSED_VAR( phaseName );
+ // There is only 1 phase
+ return 0;
+}
template< typename PHASE >
void ReactiveBrineFluid< PHASE > ::postInputInitialization()
diff --git a/src/coreComponents/functions/CMakeLists.txt b/src/coreComponents/functions/CMakeLists.txt
index b3869114d73..5e558f37528 100644
--- a/src/coreComponents/functions/CMakeLists.txt
+++ b/src/coreComponents/functions/CMakeLists.txt
@@ -35,6 +35,7 @@ set( functions_sources
FunctionManager.cpp
TableFunction.cpp
MultivariableTableFunction.cpp
+ MultivariableNonuniformTableFunction.cpp
)
if( ENABLE_MATHPRESSO )
diff --git a/src/coreComponents/functions/MultivariableNonuniformTableFunction.cpp b/src/coreComponents/functions/MultivariableNonuniformTableFunction.cpp
new file mode 100644
index 00000000000..ffc5691acb4
--- /dev/null
+++ b/src/coreComponents/functions/MultivariableNonuniformTableFunction.cpp
@@ -0,0 +1,279 @@
+/*
+ * ------------------------------------------------------------------------------------------------------------
+ * 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 MultivariableNonuniformTableFunction.cpp
+ */
+
+#include "MultivariableNonuniformTableFunction.hpp"
+
+#include "common/DataTypes.hpp"
+#include
+
+namespace geos
+{
+
+using namespace dataRepository;
+
+MultivariableNonuniformTableFunction::MultivariableNonuniformTableFunction( const string & name,
+ Group * const parent ):
+ FunctionBase( name, parent )
+{}
+
+void MultivariableNonuniformTableFunction::initializeFunctionFromFile( string const & filename )
+{
+ std::ifstream file( filename.c_str() );
+ GEOS_THROW_IF( !file, catalogName() << " " << getDataContext() << ": could not read input file " << filename, InputError );
+
+ integer numDims, numOps;
+ globalIndex numPointsTotal = 1;
+ real64_array2d axisCoordinates;
+ integer_array axisPoints;
+
+ // 1. Read numDims and numOps
+
+
+ file >> numDims;
+ GEOS_THROW_IF( !file, "Can`t read number of table dimensions", InputError );
+ file >> numOps;
+ GEOS_THROW_IF( !file, "Can`t read number of interpolatored operators", InputError );
+
+ // assume no more than 10 dimensions
+ GEOS_THROW_IF_LT_MSG( numDims, 1, catalogName() << " " << getDataContext() << ": positive integer value expected", InputError );
+ GEOS_THROW_IF_GT_MSG( numDims, 10, catalogName() << " " << getDataContext() << ": maximum 10 dimensions expected", InputError );
+
+ // assume no more than 100 operators
+ GEOS_THROW_IF_LT_MSG( numOps, 1, catalogName() << " " << getDataContext() << ": positive integer value expected", InputError );
+ GEOS_THROW_IF_GT_MSG( numOps, 100, catalogName() << " " << getDataContext() << ": maximum 100 operators expected", InputError );
+
+ axisPoints.resize( numDims );
+
+ // 2. Read axis parameters
+
+ std::vector< std::vector< real64 > > coords;
+ coords.resize( numDims );
+ for( integer i = 0; i < numDims; i++ )
+ {
+ file >> axisPoints[i];
+ GEOS_THROW_IF( !file, catalogName() << " " << getDataContext() << ": can`t read the number of points for axis " + std::to_string( i ), InputError );
+ GEOS_THROW_IF_LE_MSG( axisPoints[i], 1, catalogName() << " " << getDataContext() << ": minimum 2 discretization point per axis are expected", InputError );
+ real64 x;
+ for( integer j=0; j> x;
+ coords[i].push_back( x );
+ }
+ //GEOS_THROW_IF( !file, catalogName() << " " << getDataContext() << ": can`t read maximum value for axis " + std::to_string( i ),
+ // InputError );
+ GEOS_THROW_IF_LT_MSG( coords[i][coords[i].size()-1], coords[i][0], catalogName() << " " << getDataContext() << ": maximum axis value is expected to be larger than minimum", InputError );
+
+ numPointsTotal *= axisPoints[i];
+ }
+ int maxNumCoords=0;
+ for( integer i = 0; i < numDims; i++ )
+ {
+ maxNumCoords = std::max( maxNumCoords, *std::max_element( axisPoints.begin(), axisPoints.end()));
+ }
+ axisCoordinates.resize( numDims, maxNumCoords );
+ for( integer i = 0; i < numDims; i++ )
+ {
+ for( integer j=0; j> m_pointData[i * numOps + j];
+ GEOS_THROW_IF( !file, catalogName() << " " << getDataContext() << ": table file is shorter than expected", InputError );
+ }
+ }
+ real64 value;
+
+ file >> value;
+ GEOS_THROW_IF( file, catalogName() << " " << getDataContext() << ": table file is longer than expected", InputError );
+
+ file.close();
+
+ setTableCoordinates( numDims, numOps, axisCoordinates, axisPoints );
+ initializeFunction();
+}
+
+
+void MultivariableNonuniformTableFunction::setTableCoordinates( integer const numDims,
+ integer const numOps,
+ real64_array2d const & axisCoordinates,
+ integer_array const & axisPoints )
+{
+ m_numDims = numDims;
+ m_numOps = numOps;
+ m_numVerts = 1 << numDims;
+ m_axisCoordinates = axisCoordinates;
+ m_axisPoints = axisPoints;
+
+}
+
+void MultivariableNonuniformTableFunction::setTableValues( real64_array values )
+{
+ m_pointData = std::move( values );
+}
+void MultivariableNonuniformTableFunction::getHypercubePoints( globalIndex const hypercubeIndex, globalIndex_array & hypercubePoints ) const
+{
+ auto remainder = hypercubeIndex;
+ auto pwr = m_numVerts;
+
+ for( auto j = 0; j < m_numVerts; ++j )
+ {
+ hypercubePoints[j] = 0;
+ }
+
+ for( auto i = 0; i < m_numDims; ++i )
+ {
+ if( m_axisHypercubeMults[i] <=0 )
+ std::cout << hypercubeIndex << " " << i << " " << m_axisHypercubeMults[i] << std::endl;
+ integer const axis_idx = remainder / m_axisHypercubeMults[i];
+ remainder = remainder % m_axisHypercubeMults[i];
+
+ pwr /= 2;
+
+ for( auto j = 0; j < m_numVerts; ++j )
+ {
+ auto zero_or_one = (j / pwr) % 2;
+ hypercubePoints[j] += (axis_idx + zero_or_one) * m_axisPointMults[i];
+ }
+ }
+}
+
+
+void MultivariableNonuniformTableFunction::initializeFunction()
+{
+ // check input
+
+#if 0
+ GEOS_THROW_IF_NE_MSG( m_numDims, m_axisCoordinates.size(), catalogName() << " " << getDataContext() <<
+ ": single minimum value is expected for each of " + std::to_string( m_numDims ) + "dimensions",
+ InputError );
+ GEOS_THROW_IF_NE_MSG( m_numDims, m_axisMaximums.size(), catalogName() << " " << getDataContext() <<
+ ": single maxumum value is expected for each of " + std::to_string( m_numDims ) + "dimensions",
+ InputError );
+ GEOS_THROW_IF_NE_MSG( m_numDims, m_axisPoints.size(), catalogName() << " " << getDataContext() <<
+ "single number is expected for each of " + std::to_string( m_numDims ) + "dimensions",
+ InputError );
+#endif
+
+ integer maxDims = *std::max_element( m_axisPoints.begin(), m_axisPoints.end());
+ m_axisSteps.resize( m_numDims, maxDims );
+ m_axisStepInvs.resize( m_numDims, maxDims );
+
+ m_axisPointMults.resize( m_numDims );
+ m_axisHypercubeMults.resize( m_numDims );
+
+ // compute service data
+
+ for( integer dim = 0; dim < m_numDims; dim++ )
+ {
+ if( m_axisPoints[dim] < 2 )
+ {
+ m_axisSteps[dim][0]=0.0;
+ m_axisStepInvs[dim][0]=0.0000000001;
+ }
+ for( integer j=1; j= 0; --dim )
+ {
+ m_axisPointMults[dim] = m_axisPointMults[dim + 1] * m_axisPoints[dim + 1];
+ m_axisHypercubeMults[dim] = m_axisHypercubeMults[dim + 1] * (m_axisPoints[dim + 1] - 1);
+ if( isZero( m_axisHypercubeMults[dim] ) )
+ m_axisHypercubeMults[dim] =0;
+
+ std::cout << "cube " << dim << " " << m_axisPointMults[dim] << " " << m_axisPointMults[dim + 1] << " " << m_axisPoints[dim + 1] << " "< const & set,
+ arrayView1d< real64 > const & result ) const override final
+ {
+ GEOS_UNUSED_VAR( group );
+ GEOS_UNUSED_VAR( time );
+ GEOS_UNUSED_VAR( set );
+ GEOS_UNUSED_VAR( result );
+
+ GEOS_ERROR( "This method is not supported by MultivariableNonuniformTableFunction" );
+
+ };
+
+ /**
+ * @brief Method to evaluate a function (not supported)
+ * @param[in] input a scalar input
+ * @return the function evaluation
+ */
+ virtual real64 evaluate( real64 const * const input ) const override final
+ {
+ GEOS_UNUSED_VAR( input );
+
+ GEOS_ERROR( "This method is not supported by MultivariableNonuniformTableFunction" );
+ return 0;
+ };
+
+ /**
+ * @brief Get the table axes coordinates
+ * @return a reference to an array of table axes coordinates
+ */
+ arrayView2d< real64 const > getAxisCoordinates() const { return m_axisCoordinates.toViewConst(); }
+
+
+ /**
+ * @brief Get the table axes discretization points numbers
+ * @return a reference to an array of table axes discretization points numbers
+ */
+ arrayView1d< integer const > getAxisPoints() const { return m_axisPoints.toViewConst(); }
+
+
+ /**
+ * @brief Get the table axes step sizes
+ * @return a reference to an array of table axes step sizes
+ */
+ arrayView2d< real64 const > getAxisSteps() const { return m_axisSteps.toViewConst(); }
+
+ /**
+ * @brief Get the table axes step sizes inversions
+ * @return a reference to an array of table axes step sizes inversions
+ */
+ arrayView2d< real64 const > getAxisStepInvs() const { return m_axisStepInvs.toViewConst(); }
+
+ /**
+ * @brief Get the table axes hypercube index multiplicators
+ * @return a reference to an array of table axes hypercube index multiplicators
+ */
+ arrayView1d< globalIndex const > getAxisHypercubeMults() const { return m_axisHypercubeMults.toViewConst(); }
+
+ /**
+ * @brief Get the table values stored per-hypercube
+ * @return a reference to an array of table values stored per-hypercube
+ */
+ arrayView1d< real64 const > getHypercubeData() const { return m_hypercubeData.toViewConst(); }
+
+ /**
+ * @brief Get the number of table dimensions
+ * @return the number of table dimensions
+ */
+ integer numDims() const {return m_numDims;};
+
+ /**
+ * @brief Get the number of operators (functions to be interpolated)
+ * @return the number of operators (functions to be interpolated)
+ */
+ integer numOps() const {return m_numOps;};
+
+private:
+
+ /**
+ * @brief Get indexes of all vertices of a hypercube
+ *
+ * @param[in] hypercubeIndex index of a hypercube
+ * @param[out] hypercubePoints array of indexes of hypercube vertexes
+ */
+ void getHypercubePoints( globalIndex const hypercubeIndex, globalIndex_array & hypercubePoints ) const;
+
+ /// Number of table dimensions (inputs)
+ integer m_numDims;
+
+ /// Number of operators (functions to interpolate - ouputs)
+ integer m_numOps;
+
+ /// Number of vertices in each hypercube
+ integer m_numVerts;
+
+ /// Array [numDims,numCoordinates[dim]] axis discretization points in each dimension
+ real64_array2d m_axisCoordinates;
+
+ /// Array [numDims] of axis discretization points numbers
+ integer_array m_axisPoints;
+
+ // inputs : data derived from table coordinates data
+
+
+ /// Array [numDimss,numInt[dim]] of axis interval lengths (nonuniform axis discretization)
+ real64_array2d m_axisSteps;
+
+ /// Array [numDims,numInt[dim]] of inversions of axis interval lengths (nonuniform axis discretization)
+ real64_array2d m_axisStepInvs;
+
+ /// Array [numDims] of point index mult factors for each axis
+ globalIndex_array m_axisPointMults;
+
+ /// Array [numDims] of hypercube index mult factors for each axis
+ globalIndex_array m_axisHypercubeMults;
+
+ // inputs: operator sample data
+
+ /// Main table data stored per point: all operator values for each table coordinate
+ real64_array m_pointData;
+
+ /// Main table data stored per hypercube: all values required for interpolation withing give hypercube are stored contiguously
+ real64_array m_hypercubeData;
+
+};
+
+
+} /* namespace geos */
+
+#endif /* GEOS_FUNCTIONS_MULTIVARIABLENONUNIFORMTABLEFUNCTION_HPP_ */
diff --git a/src/coreComponents/functions/MultivariableNonuniformTableFunctionKernels.hpp b/src/coreComponents/functions/MultivariableNonuniformTableFunctionKernels.hpp
new file mode 100644
index 00000000000..adcb0611893
--- /dev/null
+++ b/src/coreComponents/functions/MultivariableNonuniformTableFunctionKernels.hpp
@@ -0,0 +1,392 @@
+/*
+ * ------------------------------------------------------------------------------------------------------------
+ * 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 MultivariableNonuniformTableFunctionKernels.hpp
+ */
+
+#ifndef GEOS_FUNCTIONS_MULTIVARIABLENONUNIFORMTABLEFUNCTIONKERNELS_HPP_
+#define GEOS_FUNCTIONS_MULTIVARIABLENONUNIFORMTABLEFUNCTIONKERNELS_HPP_
+
+namespace geos
+{
+
+/**
+ * @class MultivariableNonuniformTableFunctionStaticKernel
+ *
+ * A class for multivariable piecewise interpolation with static storage
+ * Functions are interpolated assuming a non uniform discretized space
+ * for independent variables
+ * @tparam NUM_DIMS number of dimensions (inputs)
+ * @tparam NUM_OPS number of interpolated functions (outputs)
+ */
+template< integer NUM_DIMS, integer NUM_OPS >
+class MultivariableNonuniformTableFunctionStaticKernel
+{
+public:
+
+ /// Compile time value for the number of table dimensions (inputs)
+ static constexpr integer numDims = NUM_DIMS;
+
+ /// Compile time value for the number of operators (interpolated functions, outputs)
+ static constexpr integer numOps = NUM_OPS;
+
+ /// Compile time value for the number of hypercube vertices
+ static constexpr integer numVerts = 1 << numDims;
+
+
+ /**
+ * @brief Construct a new Multivariable Table Function Static Kernel object
+ *
+ * @param[in] axisCoordinates discretization points for each axis
+ * @param[in] axisPoints number of discretization points for each axis
+ * @param[in] axisSteps axis interval lengths (nonuniform axis discretization)
+ * @param[in] axisStepInvs inversions of axis interval lengths (nonuniform axis discretization)
+ * @param[in] axisHypercubeMults hypercube index mult factors for each axis
+ * @param[in] hypercubeData table data stored per hypercube
+ */
+ MultivariableNonuniformTableFunctionStaticKernel( arrayView2d< real64 const > const & axisCoordinates,
+ arrayView1d< integer const > const & axisPoints,
+ arrayView2d< real64 const > const & axisSteps,
+ arrayView2d< real64 const > const & axisStepInvs,
+ arrayView1d< globalIndex const > const & axisHypercubeMults,
+ arrayView1d< real64 const > const & hypercubeData ):
+ m_axisCoordinates ( axisCoordinates ),
+ m_axisPoints ( axisPoints ),
+ m_axisSteps ( axisSteps ),
+ m_axisStepInv ( axisStepInvs ),
+ m_axisHypercubeMults ( axisHypercubeMults ),
+ m_hypercubeData ( hypercubeData )
+ {};
+
+/**
+ * @brief interpolate all operators at a given point
+ *
+ * @param[in] coordinates point coordinates
+ * @param[out] values interpolated operator values
+ */
+ template< typename IN_ARRAY, typename OUT_ARRAY >
+ GEOS_HOST_DEVICE
+ void
+ compute( IN_ARRAY const & coordinates,
+ OUT_ARRAY && values ) const
+ {
+ globalIndex hypercubeIndex = 0;
+ real64 axisLows[numDims] = {0.0};
+ real64 axisStepInv[numDims] = {0.0};
+ real64 axisMults[numDims] = {0.0};
+
+ for( int i = 0; i < numDims; ++i )
+ {
+ integer const axisIndex = getAxisIntervalIndexLowMult( coordinates[i],
+ m_axisCoordinates[i],
+ m_axisStepInv[i],
+ m_axisPoints[i],
+ axisLows[i], axisStepInv[i], axisMults[i] );
+ hypercubeIndex += axisIndex * m_axisHypercubeMults[i];
+ }
+
+ interpolatePoint( coordinates,
+ getHypercubeData( hypercubeIndex ),
+ &axisLows[0],
+ &axisStepInv[0],
+ values );
+ }
+
+ /**
+ * @brief interpolate all operators and compute their derivatives at a given point
+ *
+ * @param[in] coordinates point coordinates
+ * @param[out] values interpolated operator values
+ * @param[out] derivatives derivatives of interpolated operators
+ */
+ template< typename IN_ARRAY, typename OUT_ARRAY, typename OUT_2D_ARRAY >
+ GEOS_HOST_DEVICE
+ void
+ compute( IN_ARRAY const & coordinates,
+ OUT_ARRAY && values,
+ OUT_2D_ARRAY && derivatives ) const
+ {
+ globalIndex hypercubeIndex = 0;
+ real64 axisLows[numDims] = {0.0};
+ real64 axisStepInv[numDims] = {0.0};
+ real64 axisMults[numDims] = {0.0};
+
+ for( int i = 0; i < numDims; ++i )
+ {
+ integer const axisIndex = getAxisIntervalIndexLowMult( coordinates[i],
+ m_axisCoordinates[i],
+ m_axisStepInv[i],
+ m_axisPoints[i],
+ axisLows[i], axisStepInv[i], axisMults[i] );
+ std::cout << " axis " << i << " index " << axisIndex << " mul " << m_axisHypercubeMults[i] < axisCoordinates[axisPoints-1] )
+ {
+ axisIntervalIndex = std::max( 0, axisPoints-2 );
+ //axisIntervalIndex= axisPoints - 2;
+ intervalStepInv = axisStepInv[axisIntervalIndex];
+ axisLow = axisCoordinates[axisIntervalIndex];
+ axisMult = (coordinate - axisCoordinates[axisIntervalIndex]) * axisStepInv[axisIntervalIndex];
+ printf( "Interpolation warning: axis coordinate is beyond upper limit ( %lf) with value %lf, extrapolation is applied\n", axisCoordinates[axisPoints-1], coordinate );
+ }
+ else
+ {
+ for( int j=1; j
+ GEOS_HOST_DEVICE
+ inline
+ void
+ interpolatePoint( IN_ARRAY const & axisCoordinates,
+ real64 const * const hypercubeData,
+ real64 const * const axisLows,
+ real64 const * const axisStepInvs,
+ OUT_ARRAY && values ) const
+ {
+ integer pwr = numVerts / 2; // distance between high and low values
+ real64 workspace[numVerts][numOps];
+
+ // copy operator values for all vertices
+ for( integer i = 0; i < numVerts; ++i )
+ {
+ for( integer j = 0; j < numOps; ++j )
+ {
+ workspace[i][j] = hypercubeData[i * numOps + j];
+ }
+ }
+
+ for( integer i = 0; i < numDims; ++i )
+ {
+
+ for( integer j = 0; j < pwr; ++j )
+ {
+ for( integer op = 0; op < numOps; ++op )
+ {
+ // update own derivative
+ workspace[j][op] += (axisCoordinates[i] - axisLows[i]) * (workspace[j + pwr][op] - workspace[j][op]) * axisStepInvs[i];
+ }
+ }
+ pwr /= 2;
+ }
+ for( integer op = 0; op < numOps; ++op )
+ {
+ values[op] = workspace[0][op];
+ }
+ }
+
+
+ /**
+ * @brief interpolate all operators values and derivatives at a given point
+ * The algoritm is based on http://dx.doi.org/10.1090/S0025-5718-1988-0917826-0
+ *
+ * @param[in] axisCoordinates coordinates of a point
+ * @param[in] hypercubeData data of target hypercube
+ * @param[in] axisLows array of left coordinates of target axis intervals
+ * @param[in] axisMults array of weights of right coordinates of target axis intervals
+ * @param[in] axisStepInvs array of inversions of axis steps
+ * @param[out] values interpolated operator values
+ * @param[out] derivatives derivatives of interpolated operators
+ */
+ template< typename IN_ARRAY, typename OUT_ARRAY, typename OUT_2D_ARRAY >
+ GEOS_HOST_DEVICE
+ inline
+ void
+ interpolatePointWithDerivatives( IN_ARRAY const & axisCoordinates,
+ real64 const * const hypercubeData,
+ real64 const * const axisLows,
+ real64 const * const axisMults,
+ real64 const * const axisStepInvs,
+ OUT_ARRAY && values,
+ OUT_2D_ARRAY && derivatives ) const
+ {
+ integer pwr = numVerts / 2; // distance between high and low values
+ real64 workspace[2 * numVerts - 1][numOps];
+
+ // copy operator values for all vertices
+ for( integer i = 0; i < numVerts; ++i )
+ {
+ for( integer j = 0; j < numOps; ++j )
+ {
+ workspace[i][j] = hypercubeData[i * numOps + j];
+ }
+ }
+
+ for( integer i = 0; i < numDims; ++i )
+ {
+
+ for( integer j = 0; j < pwr; ++j )
+ {
+ for( integer op = 0; op < numOps; ++op )
+ {
+ // update own derivative
+ workspace[2 * numVerts - (numVerts >> i) + j][op] = (workspace[j + pwr][op] - workspace[j][op]) * axisStepInvs[i];
+ }
+
+ // update all dependent derivatives
+ for( integer k = 0; k < i; k++ )
+ {
+ for( integer op = 0; op < numOps; ++op )
+ {
+ workspace[2 * numVerts - (numVerts >> k) + j][op] = workspace[2 * numVerts - (numVerts >> k) + j][op] + axisMults[i] *
+ (workspace[2 * numVerts - (numVerts >> k) + j + pwr][op] -
+ workspace[2 * numVerts - (numVerts >> k) + j][op]);
+ }
+ }
+
+ for( integer op = 0; op < numOps; ++op )
+ {
+ // interpolate value
+ workspace[j][op] = workspace[j][op] + (axisCoordinates[i] - axisLows[i]) * workspace[2 * numVerts - (numVerts >> i) + j][op];
+ }
+ }
+ pwr /= 2;
+ }
+ for( integer op = 0; op < numOps; ++op )
+ {
+ values[op] = workspace[0][op];
+ for( integer i = 0; i < numDims; ++i )
+ {
+ derivatives[op][i] = workspace[2 * numVerts - (numVerts >> i)][op];
+ }
+ }
+ }
+
+ // inputs : table discretization data
+
+ /// Array [numDims, numCoords[numDims]] of axis coordinate values for each dim
+ arrayView2d< real64 const > m_axisCoordinates;
+
+ /// Array [numDims] of axis discretization points
+ arrayView1d< integer const > m_axisPoints;
+
+ // inputs : service data derived from table discretization data
+
+ /// Array [numDims] of axis interval lengths v
+ arrayView2d< real64 const > m_axisSteps;
+
+ /// Array [numDims] of inversions of axis interval lengths v
+ arrayView2d< real64 const > m_axisStepInv;
+
+ /// Array [numDims] of hypercube index mult factors for each axis
+ arrayView1d< globalIndex const > m_axisHypercubeMults;
+
+ // inputs: operator sample data
+
+ /// Main table data stored per hypercube: all values required for interpolation withing give hypercube are stored contiguously
+ arrayView1d< real64 const > m_hypercubeData;
+
+ // inputs: where to interpolate
+
+ /// Coordinates in numDims-dimensional space where interpolation is requested
+ arrayView1d< real64 const > m_coordinates;
+
+ // outputs
+
+ /// Interpolated values
+ arrayView1d< real64 > m_values;
+
+ /// /// Interpolated derivatives
+ arrayView1d< real64 > m_derivatives;
+};
+
+
+} /* namespace geos */
+
+#endif /* GEOS_FUNCTIONS_MULTIVARIABLETABLEFUNCTIONKERNELS_HPP_ */
diff --git a/src/coreComponents/functions/TableFunction.hpp b/src/coreComponents/functions/TableFunction.hpp
index 54807bfa0af..277b6fc124c 100644
--- a/src/coreComponents/functions/TableFunction.hpp
+++ b/src/coreComponents/functions/TableFunction.hpp
@@ -58,7 +58,7 @@ class TableFunction : public FunctionBase
};
/// maximum dimensions for the coordinates in the table
- static constexpr integer maxDimensions = 4;
+ static constexpr integer maxDimensions = 5;
/**
* @class KernelWrapper
diff --git a/src/coreComponents/integrationTests/tableFunctionsFileTests/testTableFunctionsOutput.cpp b/src/coreComponents/integrationTests/tableFunctionsFileTests/testTableFunctionsOutput.cpp
index bbb29ff0972..ae5394e2d26 100644
--- a/src/coreComponents/integrationTests/tableFunctionsFileTests/testTableFunctionsOutput.cpp
+++ b/src/coreComponents/integrationTests/tableFunctionsFileTests/testTableFunctionsOutput.cpp
@@ -84,26 +84,31 @@ char const * xmlInput =
targetRegions="{ region }">
-
-
+
-
+ referenceElevation="-0.01"/>
+
+
+
diff --git a/src/coreComponents/integrationTests/wellsTests/CMakeLists.txt b/src/coreComponents/integrationTests/wellsTests/CMakeLists.txt
index 5a30f2fe376..4aad90f9655 100644
--- a/src/coreComponents/integrationTests/wellsTests/CMakeLists.txt
+++ b/src/coreComponents/integrationTests/wellsTests/CMakeLists.txt
@@ -9,6 +9,8 @@ set( dependencyList mainInterface )
if( ENABLE_PVTPackage )
list( APPEND gtest_geosx_tests
testOpenClosePerf.cpp
+ testThermalEstimatorProdWell.cpp
+ testThermalEstimatorInjWell.cpp
testReservoirCompositionalMultiphaseMSWells.cpp
testReservoirThermalSinglePhaseMSWells.cpp
testReservoirThermalSinglePhaseMSWells_RateInj.cpp
diff --git a/src/coreComponents/integrationTests/wellsTests/testIsothermalReservoirCompositionalMultiphaseMSWells.cpp b/src/coreComponents/integrationTests/wellsTests/testIsothermalReservoirCompositionalMultiphaseMSWells.cpp
index da7d3606fb2..d7bf9d12087 100644
--- a/src/coreComponents/integrationTests/wellsTests/testIsothermalReservoirCompositionalMultiphaseMSWells.cpp
+++ b/src/coreComponents/integrationTests/wellsTests/testIsothermalReservoirCompositionalMultiphaseMSWells.cpp
@@ -94,26 +94,31 @@ char const * xmlInput =
targetRegions="{ region }">
-
-
+ surfaceTemperature="300.15">
+
+
+
@@ -300,7 +305,7 @@ void testNumericalJacobian( CompositionalMultiphaseReservoirAndWells< Compositio
real64 const relTol,
LAMBDA && assembleFunction )
{
- CompositionalMultiphaseWell & wellSolver = *solver.wellSolver();
+ WellManager & wellSolver = *solver.wellSolver();
CompositionalMultiphaseFVM & flowSolver = dynamicCast< CompositionalMultiphaseFVM & >( *solver.reservoirSolver() );
localIndex const NC = flowSolver.numFluidComponents();
@@ -463,7 +468,7 @@ void testNumericalJacobian( CompositionalMultiphaseReservoirAndWells< Compositio
wellElemCompDens.move( hostMemorySpace, false );
arrayView1d< real64 > const & connRate =
- subRegion.getField< fields::well::mixtureConnectionRate >();
+ subRegion.getField< fields::well::connectionRate >();
connRate.move( hostMemorySpace, false );
// a) compute all the derivatives wrt to the pressure in WELL elem iwelem
@@ -580,6 +585,23 @@ class CompositionalMultiphaseReservoirSolverTest : public ::testing::Test
solver->getSystemSolution() );
solver->implicitStepSetup( time, dt, domain );
+ WellManager & wellSolver = *solver->wellSolver();
+ wellSolver.forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const & meshBodyName,
+ MeshLevel & meshLevel,
+ string_array const & regionNames )
+ {
+ GEOS_UNUSED_VAR( meshBodyName );
+ ElementRegionManager & elementRegionManager = meshLevel.getElemManager();
+ elementRegionManager.forElementRegions< WellElementRegion >( regionNames,
+ [&]( localIndex const,
+ WellElementRegion & region )
+ {
+ WellElementSubRegion & subRegion = region.getGroup( ElementRegionBase::viewKeyStruct::elementSubRegions() )
+ .getGroup< WellElementSubRegion >( region.getSubRegionName() );
+ WellControls & wellControls = wellSolver.getWellControls( subRegion );
+ wellControls.initializeWell( domain, meshLevel, subRegion, time );
+ } );
+ } );
}
static real64 constexpr time = 0.0;
@@ -641,7 +663,23 @@ TEST_F( CompositionalMultiphaseReservoirSolverTest, jacobianNumericalCheck_Accum
[&] ( CRSMatrixView< real64, globalIndex const > const & localMatrix,
arrayView1d< real64 > const & localRhs )
{
- solver->wellSolver()->assembleAccumulationTerms( time, dt, domain, solver->getDofManager(), localMatrix, localRhs );
+ WellManager & wellSolver = *solver->wellSolver();
+ wellSolver.forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const & meshBodyName,
+ MeshLevel & meshLevel,
+ string_array const & regionNames )
+ {
+ GEOS_UNUSED_VAR( meshBodyName );
+ ElementRegionManager & elementRegionManager = meshLevel.getElemManager();
+ elementRegionManager.forElementRegions< WellElementRegion >( regionNames,
+ [&]( localIndex const,
+ WellElementRegion & region )
+ {
+ WellElementSubRegion & subRegion = region.getGroup( ElementRegionBase::viewKeyStruct::elementSubRegions() )
+ .getGroup< WellElementSubRegion >( region.getSubRegionName() );
+ WellControls & wellControls = wellSolver.getWellControls( subRegion );
+ wellControls.assembleWellAccumulationTerms( time, dt, subRegion, solver->getDofManager(), localMatrix, localRhs );
+ } );
+ } );
} );
}
diff --git a/src/coreComponents/integrationTests/wellsTests/testIsothermalReservoirCompositionalMultiphaseSSWells.cpp b/src/coreComponents/integrationTests/wellsTests/testIsothermalReservoirCompositionalMultiphaseSSWells.cpp
index 277c7a2a38c..0023ec39654 100644
--- a/src/coreComponents/integrationTests/wellsTests/testIsothermalReservoirCompositionalMultiphaseSSWells.cpp
+++ b/src/coreComponents/integrationTests/wellsTests/testIsothermalReservoirCompositionalMultiphaseSSWells.cpp
@@ -94,26 +94,31 @@ char const * xmlInput =
targetRegions="{ region }">
-
-
-
+ surfaceTemperature="300.15">
+
+
+
+
@@ -301,7 +306,7 @@ void testNumericalJacobian( CompositionalMultiphaseReservoirAndWells< Compositio
real64 const relTol,
LAMBDA && assembleFunction )
{
- CompositionalMultiphaseWell & wellSolver = *solver.wellSolver();
+ WellManager & wellSolver = *solver.wellSolver();
CompositionalMultiphaseFVM & flowSolver = dynamicCast< CompositionalMultiphaseFVM & >( *solver.reservoirSolver() );
localIndex const NC = flowSolver.numFluidComponents();
@@ -510,7 +515,7 @@ void testNumericalJacobian( CompositionalMultiphaseReservoirAndWells< Compositio
wellElemCompDens.move( hostMemorySpace, false );
arrayView1d< real64 > const & connRate =
- subRegion.getField< fields::well::mixtureConnectionRate >();
+ subRegion.getField< fields::well::connectionRate >();
connRate.move( hostMemorySpace, false );
// a) compute all the derivatives wrt to the pressure in WELL elem iwelem
@@ -531,7 +536,6 @@ void testNumericalJacobian( CompositionalMultiphaseReservoirAndWells< Compositio
real64 const dP = perturbParameter * ( wellElemPressure[iwelem] + perturbParameter );
wellElemPressure.move( hostMemorySpace, true );
wellElemPressure[iwelem] += dP;
-
// after perturbing, update the pressure-dependent quantities in the well
wellSolver.updateState( domain );
@@ -652,6 +656,23 @@ class CompositionalMultiphaseReservoirSolverTest : public ::testing::Test
solver->getSystemSolution() );
solver->implicitStepSetup( time, dt, domain );
+ WellManager & wellSolver = *solver->wellSolver();
+ wellSolver.forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const & meshBodyName,
+ MeshLevel & meshLevel,
+ string_array const & regionNames )
+ {
+ GEOS_UNUSED_VAR( meshBodyName );
+ ElementRegionManager & elementRegionManager = meshLevel.getElemManager();
+ elementRegionManager.forElementRegions< WellElementRegion >( regionNames,
+ [&]( localIndex const,
+ WellElementRegion & region )
+ {
+ WellElementSubRegion & subRegion = region.getGroup( ElementRegionBase::viewKeyStruct::elementSubRegions() )
+ .getGroup< WellElementSubRegion >( region.getSubRegionName() );
+ WellControls & wellControls = wellSolver.getWellControls( subRegion );
+ wellControls.initializeWell( domain, meshLevel, subRegion, time );
+ } );
+ } );
}
static real64 constexpr time = 0.0;
@@ -699,7 +720,23 @@ TEST_F( CompositionalMultiphaseReservoirSolverTest, jacobianNumericalCheck_Accum
[&] ( CRSMatrixView< real64, globalIndex const > const & localMatrix,
arrayView1d< real64 > const & localRhs )
{
- solver->wellSolver()->assembleAccumulationTerms( time, dt, domain, solver->getDofManager(), localMatrix, localRhs );
+ WellManager & wellSolver = *solver->wellSolver();
+ wellSolver.forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const & meshBodyName,
+ MeshLevel & meshLevel,
+ string_array const & regionNames )
+ {
+ GEOS_UNUSED_VAR( meshBodyName );
+ ElementRegionManager & elementRegionManager = meshLevel.getElemManager();
+ elementRegionManager.forElementRegions< WellElementRegion >( regionNames,
+ [&]( localIndex const,
+ WellElementRegion & region )
+ {
+ WellElementSubRegion & subRegion = region.getGroup( ElementRegionBase::viewKeyStruct::elementSubRegions() )
+ .getGroup< WellElementSubRegion >( region.getSubRegionName() );
+ WellControls & wellControls = wellSolver.getWellControls( subRegion );
+ wellControls.assembleWellAccumulationTerms( time, dt, subRegion, solver->getDofManager(), localMatrix, localRhs );
+ } );
+ } );
} );
}
#endif
diff --git a/src/coreComponents/integrationTests/wellsTests/testOpenClosePerf.cpp b/src/coreComponents/integrationTests/wellsTests/testOpenClosePerf.cpp
index c45c9a21e68..d0826f8b9f4 100644
--- a/src/coreComponents/integrationTests/wellsTests/testOpenClosePerf.cpp
+++ b/src/coreComponents/integrationTests/wellsTests/testOpenClosePerf.cpp
@@ -57,18 +57,24 @@ char const * PreXmlInput =
temperature="297.15"
useMass="0">
-
-
-
+
+
+
+
+
& solv
LAMBDA && perfFunction )
{
- CompositionalMultiphaseWell & wellSolver = *solver.wellSolver();
+ WellManager & wellSolver = *solver.wellSolver();
typedef stdMap< real64, std::vector< int > > map_type;
map_type refVal;
@@ -217,7 +223,7 @@ void testPlugBottomUpPerfCheck( CompositionalMultiphaseReservoirAndWells<> & sol
LAMBDA && perfFunction )
{
- CompositionalMultiphaseWell & wellSolver = *solver.wellSolver();
+ WellManager & wellSolver = *solver.wellSolver();
typedef stdMap< real64, std::vector< int > > map_type;
map_type refVal;
@@ -260,7 +266,7 @@ void testOpenTopDownPerfCheck( CompositionalMultiphaseReservoirAndWells<> & solv
LAMBDA && perfFunction )
{
- CompositionalMultiphaseWell & wellSolver = *solver.wellSolver();
+ WellManager & wellSolver = *solver.wellSolver();
typedef stdMap< real64, std::vector< int > > map_type;
map_type refPerfTable;
@@ -302,7 +308,7 @@ void testOpenBottomUpPerfCheck( CompositionalMultiphaseReservoirAndWells<> & sol
LAMBDA && perfFunction )
{
- CompositionalMultiphaseWell & wellSolver = *solver.wellSolver();
+ WellManager & wellSolver = *solver.wellSolver();
typedef stdMap< real64, std::vector< int > > map_type;
map_type refVal;
@@ -516,8 +522,23 @@ TEST_F( CompositionalMultiphaseReservoirSolverTest, plugTopDownPerfCheck )
testPlugTopDownPerfCheck( *solver, domain,
[&] ( real64 time )
{
- WellSolverBase * wellSolverBase = solver->wellSolver();
- wellSolverBase->setPerforationStatus( time, domain );
+ WellManager & wellSolver = *solver->wellSolver();
+ wellSolver.forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const & meshBodyName,
+ MeshLevel & meshLevel,
+ string_array const & regionNames )
+ {
+ GEOS_UNUSED_VAR( meshBodyName );
+ ElementRegionManager & elementRegionManager = meshLevel.getElemManager();
+ elementRegionManager.forElementRegions< WellElementRegion >( regionNames,
+ [&]( localIndex const,
+ WellElementRegion & region )
+ {
+ WellElementSubRegion & subRegion = region.getGroup( ElementRegionBase::viewKeyStruct::elementSubRegions() )
+ .getGroup< WellElementSubRegion >( region.getSubRegionName() );
+ WellControls & wellControls = wellSolver.getWellControls( subRegion );
+ wellControls.setPerforationStatus( time, subRegion );
+ } );
+ } );
} );
}
@@ -530,8 +551,23 @@ TEST_F( CompositionalMultiphaseReservoirSolverTest, plugBottomUpPerfCheck )
testPlugBottomUpPerfCheck( *solver, domain,
[&] ( real64 time )
{
- WellSolverBase * wellSolverBase = solver->wellSolver();
- wellSolverBase->setPerforationStatus( time, domain );
+ WellManager & wellSolver = *solver->wellSolver();
+ wellSolver.forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const & meshBodyName,
+ MeshLevel & meshLevel,
+ string_array const & regionNames )
+ {
+ GEOS_UNUSED_VAR( meshBodyName );
+ ElementRegionManager & elementRegionManager = meshLevel.getElemManager();
+ elementRegionManager.forElementRegions< WellElementRegion >( regionNames,
+ [&]( localIndex const,
+ WellElementRegion & region )
+ {
+ WellElementSubRegion & subRegion = region.getGroup( ElementRegionBase::viewKeyStruct::elementSubRegions() )
+ .getGroup< WellElementSubRegion >( region.getSubRegionName() );
+ WellControls & wellControls = wellSolver.getWellControls( subRegion );
+ wellControls.setPerforationStatus( time, subRegion );
+ } );
+ } );
} );
}
TEST_F( CompositionalMultiphaseReservoirSolverTest, openTopDownPerfCheck )
@@ -543,8 +579,23 @@ TEST_F( CompositionalMultiphaseReservoirSolverTest, openTopDownPerfCheck )
testOpenTopDownPerfCheck( *solver, domain,
[&] ( real64 time )
{
- WellSolverBase * wellSolverBase = solver->wellSolver();
- wellSolverBase->setPerforationStatus( time, domain );
+ WellManager & wellSolver = *solver->wellSolver();
+ wellSolver.forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const & meshBodyName,
+ MeshLevel & meshLevel,
+ string_array const & regionNames )
+ {
+ GEOS_UNUSED_VAR( meshBodyName );
+ ElementRegionManager & elementRegionManager = meshLevel.getElemManager();
+ elementRegionManager.forElementRegions< WellElementRegion >( regionNames,
+ [&]( localIndex const,
+ WellElementRegion & region )
+ {
+ WellElementSubRegion & subRegion = region.getGroup( ElementRegionBase::viewKeyStruct::elementSubRegions() )
+ .getGroup< WellElementSubRegion >( region.getSubRegionName() );
+ WellControls & wellControls = wellSolver.getWellControls( subRegion );
+ wellControls.setPerforationStatus( time, subRegion );
+ } );
+ } );
} );
}
@@ -557,8 +608,24 @@ TEST_F( CompositionalMultiphaseReservoirSolverTest, openBottomUpPerfCheck )
testOpenBottomUpPerfCheck( *solver, domain,
[&] ( real64 time )
{
- WellSolverBase * wellSolverBase = solver->wellSolver();
- wellSolverBase->setPerforationStatus( time, domain );
+
+ WellManager & wellSolver = *solver->wellSolver();
+ wellSolver.forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const & meshBodyName,
+ MeshLevel & meshLevel,
+ string_array const & regionNames )
+ {
+ GEOS_UNUSED_VAR( meshBodyName );
+ ElementRegionManager & elementRegionManager = meshLevel.getElemManager();
+ elementRegionManager.forElementRegions< WellElementRegion >( regionNames,
+ [&]( localIndex const,
+ WellElementRegion & region )
+ {
+ WellElementSubRegion & subRegion = region.getGroup( ElementRegionBase::viewKeyStruct::elementSubRegions() )
+ .getGroup< WellElementSubRegion >( region.getSubRegionName() );
+ WellControls & wellControls = wellSolver.getWellControls( subRegion );
+ wellControls.setPerforationStatus( time, subRegion );
+ } );
+ } );
} );
}
diff --git a/src/coreComponents/integrationTests/wellsTests/testReservoirCompositionalMultiphaseMSWells.cpp b/src/coreComponents/integrationTests/wellsTests/testReservoirCompositionalMultiphaseMSWells.cpp
index f1ca32e9e81..3b5639a588c 100644
--- a/src/coreComponents/integrationTests/wellsTests/testReservoirCompositionalMultiphaseMSWells.cpp
+++ b/src/coreComponents/integrationTests/wellsTests/testReservoirCompositionalMultiphaseMSWells.cpp
@@ -57,26 +57,38 @@ char const * xmlInput =
temperature="297.15"
useMass="0">
-
-
-
+
+
+
+
+
+
+
+
& solver,
real64 const relTol,
LAMBDA && assembleFunction )
{
- CompositionalMultiphaseWell & wellSolver = *solver.wellSolver();
+ WellManager & wellSolver = *solver.wellSolver();
CompositionalMultiphaseFVM & flowSolver = dynamicCast< CompositionalMultiphaseFVM & >( *solver.reservoirSolver() );
localIndex const NC = flowSolver.numFluidComponents();
@@ -366,7 +378,7 @@ void testNumericalJacobian( CompositionalMultiphaseReservoirAndWells<> & solver,
wellElemCompDens.move( hostMemorySpace, false );
arrayView1d< real64 > const & connRate =
- subRegion.getField< fields::well::mixtureConnectionRate >();
+ subRegion.getField< fields::well::connectionRate >();
connRate.move( hostMemorySpace, false );
// a) compute all the derivatives wrt to the pressure in WELL elem iwelem
@@ -483,6 +495,23 @@ class CompositionalMultiphaseReservoirSolverTest : public ::testing::Test
solver->getSystemSolution() );
solver->implicitStepSetup( time, dt, domain );
+ WellManager & wellSolver = *solver->wellSolver();
+ wellSolver.forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const & meshBodyName,
+ MeshLevel & meshLevel,
+ string_array const & regionNames )
+ {
+ GEOS_UNUSED_VAR( meshBodyName );
+ ElementRegionManager & elementRegionManager = meshLevel.getElemManager();
+ elementRegionManager.forElementRegions< WellElementRegion >( regionNames,
+ [&]( localIndex const,
+ WellElementRegion & region )
+ {
+ WellElementSubRegion & subRegion = region.getGroup( ElementRegionBase::viewKeyStruct::elementSubRegions() )
+ .getGroup< WellElementSubRegion >( region.getSubRegionName() );
+ WellControls & wellControls = wellSolver.getWellControls( subRegion );
+ wellControls.initializeWell( domain, meshLevel, subRegion, time );
+ } );
+ } );
}
static real64 constexpr time = 0.0;
@@ -528,7 +557,23 @@ TEST_F( CompositionalMultiphaseReservoirSolverTest, jacobianNumericalCheck_Flux
[&] ( CRSMatrixView< real64, globalIndex const > const & localMatrix,
arrayView1d< real64 > const & localRhs )
{
- solver->wellSolver()->assembleFluxTerms( time, dt, domain, solver->getDofManager(), localMatrix, localRhs );
+ WellManager & wellSolver = *solver->wellSolver();
+ wellSolver.forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const & meshBodyName,
+ MeshLevel & meshLevel,
+ string_array const & regionNames )
+ {
+ GEOS_UNUSED_VAR( meshBodyName );
+ ElementRegionManager & elementRegionManager = meshLevel.getElemManager();
+ elementRegionManager.forElementRegions< WellElementRegion >( regionNames,
+ [&]( localIndex const,
+ WellElementRegion & region )
+ {
+ WellElementSubRegion & subRegion = region.getGroup( ElementRegionBase::viewKeyStruct::elementSubRegions() )
+ .getGroup< WellElementSubRegion >( region.getSubRegionName() );
+ WellControls & wellControls = wellSolver.getWellControls( subRegion );
+ wellControls.assembleWellFluxTerms( time, dt, subRegion, solver->getDofManager(), localMatrix, localRhs );
+ } );
+ } );
} );
}
@@ -545,7 +590,23 @@ TEST_F( CompositionalMultiphaseReservoirSolverTest, jacobianNumericalCheck_Press
[&] ( CRSMatrixView< real64, globalIndex const > const & localMatrix,
arrayView1d< real64 > const & localRhs )
{
- solver->wellSolver()->assemblePressureRelations( time, dt, domain, solver->getDofManager(), localMatrix, localRhs );
+ WellManager & wellSolver = *solver->wellSolver();
+ wellSolver.forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const & meshBodyName,
+ MeshLevel & meshLevel,
+ string_array const & regionNames )
+ {
+ GEOS_UNUSED_VAR( meshBodyName );
+ ElementRegionManager & elementRegionManager = meshLevel.getElemManager();
+ elementRegionManager.forElementRegions< WellElementRegion >( regionNames,
+ [&]( localIndex const,
+ WellElementRegion & region )
+ {
+ WellElementSubRegion & subRegion = region.getGroup( ElementRegionBase::viewKeyStruct::elementSubRegions() )
+ .getGroup< WellElementSubRegion >( region.getSubRegionName() );
+ WellControls & wellControls = wellSolver.getWellControls( subRegion );
+ wellControls.assembleWellPressureRelations( time, dt, subRegion, solver->getDofManager(), localMatrix, localRhs );
+ } );
+ } );
} );
}
diff --git a/src/coreComponents/integrationTests/wellsTests/testReservoirSinglePhaseMSWells.cpp b/src/coreComponents/integrationTests/wellsTests/testReservoirSinglePhaseMSWells.cpp
index d75ad50a957..5b15ed993e3 100644
--- a/src/coreComponents/integrationTests/wellsTests/testReservoirSinglePhaseMSWells.cpp
+++ b/src/coreComponents/integrationTests/wellsTests/testReservoirSinglePhaseMSWells.cpp
@@ -58,22 +58,35 @@ char const * PreXmlInput =
discretization="singlePhaseTPFA"
targetRegions="{Region1}">
-
-
-
-
+
+
+
+
+
+
+
+
+
+
& solver,
real64 const relTol,
LAMBDA && assembleFunction )
{
- SinglePhaseWell & wellSolver = *solver.wellSolver();
+ WellManager & wellSolver = *solver.wellSolver();
SinglePhaseFVM< SinglePhaseBase > & flowSolver = dynamicCast< SinglePhaseFVM< SinglePhaseBase > & >( *solver.reservoirSolver() );
CRSMatrix< real64, globalIndex > const & jacobian = solver.getLocalMatrix();
@@ -357,6 +370,23 @@ class SinglePhaseReservoirSolverTest : public ::testing::Test
solver->getSystemSolution() );
solver->implicitStepSetup( TIME, DT, domain );
+ WellManager & wellSolver = *solver->wellSolver();
+ wellSolver.forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const & meshBodyName,
+ MeshLevel & meshLevel,
+ string_array const & regionNames )
+ {
+ GEOS_UNUSED_VAR( meshBodyName );
+ ElementRegionManager & elementRegionManager = meshLevel.getElemManager();
+ elementRegionManager.forElementRegions< WellElementRegion >( regionNames,
+ [&]( localIndex const,
+ WellElementRegion & region )
+ {
+ WellElementSubRegion & subRegion = region.getGroup( ElementRegionBase::viewKeyStruct::elementSubRegions() )
+ .getGroup< WellElementSubRegion >( region.getSubRegionName() );
+ WellControls & wellControls = wellSolver.getWellControls( subRegion );
+ wellControls.initializeWell( domain, meshLevel, subRegion, TIME );
+ } );
+ } );
}
void TestAssembleCouplingTerms()
@@ -385,7 +415,23 @@ class SinglePhaseReservoirSolverTest : public ::testing::Test
[&] ( CRSMatrixView< real64, globalIndex const > const & localMatrix,
arrayView1d< real64 > const & localRhs )
{
- solver->wellSolver()->assembleFluxTerms( TIME, DT, domain, solver->getDofManager(), localMatrix, localRhs );
+ WellManager & wellSolver = *solver->wellSolver();
+ wellSolver.forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const & meshBodyName,
+ MeshLevel & meshLevel,
+ string_array const & regionNames )
+ {
+ GEOS_UNUSED_VAR( meshBodyName );
+ ElementRegionManager & elementRegionManager = meshLevel.getElemManager();
+ elementRegionManager.forElementRegions< WellElementRegion >( regionNames,
+ [&]( localIndex const,
+ WellElementRegion & region )
+ {
+ WellElementSubRegion & subRegion = region.getGroup( ElementRegionBase::viewKeyStruct::elementSubRegions() )
+ .getGroup< WellElementSubRegion >( region.getSubRegionName() );
+ WellControls & wellControls = wellSolver.getWellControls( subRegion );
+ wellControls.assembleWellFluxTerms( TIME, DT, subRegion, solver->getDofManager(), localMatrix, localRhs );
+ } );
+ } );
} );
}
@@ -400,7 +446,23 @@ class SinglePhaseReservoirSolverTest : public ::testing::Test
[&] ( CRSMatrixView< real64, globalIndex const > const & localMatrix,
arrayView1d< real64 > const & localRhs )
{
- solver->wellSolver()->assemblePressureRelations( TIME, DT, domain, solver->getDofManager(), localMatrix, localRhs );
+ WellManager & wellSolver = *solver->wellSolver();
+ wellSolver.forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const & meshBodyName,
+ MeshLevel & meshLevel,
+ string_array const & regionNames )
+ {
+ GEOS_UNUSED_VAR( meshBodyName );
+ ElementRegionManager & elementRegionManager = meshLevel.getElemManager();
+ elementRegionManager.forElementRegions< WellElementRegion >( regionNames,
+ [&]( localIndex const,
+ WellElementRegion & region )
+ {
+ WellElementSubRegion & subRegion = region.getGroup( ElementRegionBase::viewKeyStruct::elementSubRegions() )
+ .getGroup< WellElementSubRegion >( region.getSubRegionName() );
+ WellControls & wellControls = wellSolver.getWellControls( subRegion );
+ wellControls.assembleWellPressureRelations( TIME, DT, subRegion, solver->getDofManager(), localMatrix, localRhs );
+ } );
+ } );
} );
}
@@ -415,7 +477,23 @@ class SinglePhaseReservoirSolverTest : public ::testing::Test
[&] ( CRSMatrixView< real64, globalIndex const > const & localMatrix,
arrayView1d< real64 > const & localRhs )
{
- solver->wellSolver()->assembleAccumulationTerms( TIME, DT, domain, solver->getDofManager(), localMatrix, localRhs );
+ WellManager & wellSolver = *solver->wellSolver();
+ wellSolver.forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const & meshBodyName,
+ MeshLevel & meshLevel,
+ string_array const & regionNames )
+ {
+ GEOS_UNUSED_VAR( meshBodyName );
+ ElementRegionManager & elementRegionManager = meshLevel.getElemManager();
+ elementRegionManager.forElementRegions< WellElementRegion >( regionNames,
+ [&]( localIndex const,
+ WellElementRegion & region )
+ {
+ WellElementSubRegion & subRegion = region.getGroup( ElementRegionBase::viewKeyStruct::elementSubRegions() )
+ .getGroup< WellElementSubRegion >( region.getSubRegionName() );
+ WellControls & wellControls = wellSolver.getWellControls( subRegion );
+ wellControls.assembleWellAccumulationTerms( TIME, DT, subRegion, solver->getDofManager(), localMatrix, localRhs );
+ } );
+ } );
} );
}
@@ -497,7 +575,24 @@ TEST_F( SinglePhaseReservoirSolverInternalWellTest, jacobianNumericalCheck_Flux
[&] ( CRSMatrixView< real64, globalIndex const > const & localMatrix,
arrayView1d< real64 > const & localRhs )
{
- solver->wellSolver()->assembleFluxTerms( TIME, DT, domain, solver->getDofManager(), localMatrix, localRhs );
+
+ WellManager & wellSolver = *solver->wellSolver();
+ wellSolver.forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const & meshBodyName,
+ MeshLevel & meshLevel,
+ string_array const & regionNames )
+ {
+ GEOS_UNUSED_VAR( meshBodyName );
+ ElementRegionManager & elementRegionManager = meshLevel.getElemManager();
+ elementRegionManager.forElementRegions< WellElementRegion >( regionNames,
+ [&]( localIndex const,
+ WellElementRegion & region )
+ {
+ WellElementSubRegion & subRegion = region.getGroup( ElementRegionBase::viewKeyStruct::elementSubRegions() )
+ .getGroup< WellElementSubRegion >( region.getSubRegionName() );
+ WellControls & wellControls = wellSolver.getWellControls( subRegion );
+ wellControls.assembleWellFluxTerms( TIME, DT, subRegion, solver->getDofManager(), localMatrix, localRhs );
+ } );
+ } );
} );
}
diff --git a/src/coreComponents/integrationTests/wellsTests/testReservoirThermalSinglePhaseMSWells.cpp b/src/coreComponents/integrationTests/wellsTests/testReservoirThermalSinglePhaseMSWells.cpp
index d10f1da56f6..4a8c5a28a67 100644
--- a/src/coreComponents/integrationTests/wellsTests/testReservoirThermalSinglePhaseMSWells.cpp
+++ b/src/coreComponents/integrationTests/wellsTests/testReservoirThermalSinglePhaseMSWells.cpp
@@ -60,27 +60,40 @@ char const * PreXmlInput =
discretization="singlePhaseTPFA"
targetRegions="{Region1}">
-
-
-
-
+
+
+
+
+
+
+
+
+
+
& solver,
LAMBDA && assembleFunction )
{
GEOS_UNUSED_VAR( testName );
- SinglePhaseWell & wellSolver = *solver.wellSolver();
+ WellManager & wellSolver = *solver.wellSolver();
SinglePhaseFVM< SinglePhaseBase > & flowSolver = dynamicCast< SinglePhaseFVM< SinglePhaseBase > & >( *solver.reservoirSolver() );
CRSMatrix< real64, globalIndex > const & jacobian = solver.getLocalMatrix();
@@ -515,6 +528,23 @@ class SinglePhaseReservoirSolverTest : public ::testing::Test
solver->getSystemSolution() );
solver->implicitStepSetup( TIME, DT, domain );
+ WellManager & wellSolver = *solver->wellSolver();
+ wellSolver.forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const & meshBodyName,
+ MeshLevel & meshLevel,
+ string_array const & regionNames )
+ {
+ GEOS_UNUSED_VAR( meshBodyName );
+ ElementRegionManager & elementRegionManager = meshLevel.getElemManager();
+ elementRegionManager.forElementRegions< WellElementRegion >( regionNames,
+ [&]( localIndex const,
+ WellElementRegion & region )
+ {
+ WellElementSubRegion & subRegion = region.getGroup( ElementRegionBase::viewKeyStruct::elementSubRegions() )
+ .getGroup< WellElementSubRegion >( region.getSubRegionName() );
+ WellControls & wellControls = wellSolver.getWellControls( subRegion );
+ wellControls.initializeWell( domain, meshLevel, subRegion, TIME );
+ } );
+ } );
}
void TestAssembleCouplingTerms()
@@ -543,7 +573,23 @@ class SinglePhaseReservoirSolverTest : public ::testing::Test
[&] ( CRSMatrixView< real64, globalIndex const > const & localMatrix,
arrayView1d< real64 > const & localRhs )
{
- solver->wellSolver()->assembleFluxTerms( TIME, DT, domain, solver->getDofManager(), localMatrix, localRhs );
+ WellManager & wellSolver = *solver->wellSolver();
+ wellSolver.forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const & meshBodyName,
+ MeshLevel & meshLevel,
+ string_array const & regionNames )
+ {
+ GEOS_UNUSED_VAR( meshBodyName );
+ ElementRegionManager & elementRegionManager = meshLevel.getElemManager();
+ elementRegionManager.forElementRegions< WellElementRegion >( regionNames,
+ [&]( localIndex const,
+ WellElementRegion & region )
+ {
+ WellElementSubRegion & subRegion = region.getGroup( ElementRegionBase::viewKeyStruct::elementSubRegions() )
+ .getGroup< WellElementSubRegion >( region.getSubRegionName() );
+ WellControls & wellControls = wellSolver.getWellControls( subRegion );
+ wellControls.assembleWellFluxTerms( TIME, DT, subRegion, solver->getDofManager(), localMatrix, localRhs );
+ } );
+ } );
} );
}
@@ -557,7 +603,23 @@ class SinglePhaseReservoirSolverTest : public ::testing::Test
[&] ( CRSMatrixView< real64, globalIndex const > const & localMatrix,
arrayView1d< real64 > const & localRhs )
{
- solver->wellSolver()->assemblePressureRelations( TIME, DT, domain, solver->getDofManager(), localMatrix, localRhs );
+ WellManager & wellSolver = *solver->wellSolver();
+ wellSolver.forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const & meshBodyName,
+ MeshLevel & meshLevel,
+ string_array const & regionNames )
+ {
+ GEOS_UNUSED_VAR( meshBodyName );
+ ElementRegionManager & elementRegionManager = meshLevel.getElemManager();
+ elementRegionManager.forElementRegions< WellElementRegion >( regionNames,
+ [&]( localIndex const,
+ WellElementRegion & region )
+ {
+ WellElementSubRegion & subRegion = region.getGroup( ElementRegionBase::viewKeyStruct::elementSubRegions() )
+ .getGroup< WellElementSubRegion >( region.getSubRegionName() );
+ WellControls & wellControls = wellSolver.getWellControls( subRegion );
+ wellControls.assembleWellPressureRelations( TIME, DT, subRegion, solver->getDofManager(), localMatrix, localRhs );
+ } );
+ } );
} );
}
@@ -572,7 +634,23 @@ class SinglePhaseReservoirSolverTest : public ::testing::Test
[&] ( CRSMatrixView< real64, globalIndex const > const & localMatrix,
arrayView1d< real64 > const & localRhs )
{
- solver->wellSolver()->assembleAccumulationTerms( TIME, DT, domain, solver->getDofManager(), localMatrix, localRhs );
+ WellManager & wellSolver = *solver->wellSolver();
+ wellSolver.forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const & meshBodyName,
+ MeshLevel & meshLevel,
+ string_array const & regionNames )
+ {
+ GEOS_UNUSED_VAR( meshBodyName );
+ ElementRegionManager & elementRegionManager = meshLevel.getElemManager();
+ elementRegionManager.forElementRegions< WellElementRegion >( regionNames,
+ [&]( localIndex const,
+ WellElementRegion & region )
+ {
+ WellElementSubRegion & subRegion = region.getGroup( ElementRegionBase::viewKeyStruct::elementSubRegions() )
+ .getGroup< WellElementSubRegion >( region.getSubRegionName() );
+ WellControls & wellControls = wellSolver.getWellControls( subRegion );
+ wellControls.assembleWellAccumulationTerms( TIME, DT, subRegion, solver->getDofManager(), localMatrix, localRhs );
+ } );
+ } );
} );
}
@@ -654,7 +732,24 @@ TEST_F( SinglePhaseReservoirSolverInternalWellTest, jacobianNumericalCheck_Flux
[&] ( CRSMatrixView< real64, globalIndex const > const & localMatrix,
arrayView1d< real64 > const & localRhs )
{
- solver->wellSolver()->assembleFluxTerms( TIME, DT, domain, solver->getDofManager(), localMatrix, localRhs );
+
+ WellManager & wellSolver = *solver->wellSolver();
+ wellSolver.forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const & meshBodyName,
+ MeshLevel & meshLevel,
+ string_array const & regionNames )
+ {
+ GEOS_UNUSED_VAR( meshBodyName );
+ ElementRegionManager & elementRegionManager = meshLevel.getElemManager();
+ elementRegionManager.forElementRegions< WellElementRegion >( regionNames,
+ [&]( localIndex const,
+ WellElementRegion & region )
+ {
+ WellElementSubRegion & subRegion = region.getGroup( ElementRegionBase::viewKeyStruct::elementSubRegions() )
+ .getGroup< WellElementSubRegion >( region.getSubRegionName() );
+ WellControls & wellControls = wellSolver.getWellControls( subRegion );
+ wellControls.assembleWellFluxTerms( TIME, DT, subRegion, solver->getDofManager(), localMatrix, localRhs );
+ } );
+ } );
} );
}
diff --git a/src/coreComponents/integrationTests/wellsTests/testReservoirThermalSinglePhaseMSWells_RateInj.cpp b/src/coreComponents/integrationTests/wellsTests/testReservoirThermalSinglePhaseMSWells_RateInj.cpp
index 255b0201692..5f69fb1b97c 100644
--- a/src/coreComponents/integrationTests/wellsTests/testReservoirThermalSinglePhaseMSWells_RateInj.cpp
+++ b/src/coreComponents/integrationTests/wellsTests/testReservoirThermalSinglePhaseMSWells_RateInj.cpp
@@ -73,26 +73,30 @@ char const * XmlInput =
targetRegions="{ region }">
-
-
+
+ referenceElevation="-0.01"/>
+
+
@@ -305,7 +309,7 @@ void testNumericalJacobian( SinglePhaseReservoirAndWells<> & solver,
{
GEOS_UNUSED_VAR( testName );
- SinglePhaseWell & wellSolver = *solver.wellSolver();
+ WellManager & wellSolver = *solver.wellSolver();
SinglePhaseFVM< SinglePhaseBase > & flowSolver = dynamicCast< SinglePhaseFVM< SinglePhaseBase > & >( *solver.reservoirSolver() );
CRSMatrix< real64, globalIndex > const & jacobian = solver.getLocalMatrix();
@@ -597,6 +601,23 @@ class SinglePhaseReservoirSolverTest : public ::testing::Test
solver->getSystemSolution() );
solver->implicitStepSetup( TIME, DT, domain );
+ WellManager & wellSolver = *solver->wellSolver();
+ wellSolver.forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const & meshBodyName,
+ MeshLevel & meshLevel,
+ string_array const & regionNames )
+ {
+ GEOS_UNUSED_VAR( meshBodyName );
+ ElementRegionManager & elementRegionManager = meshLevel.getElemManager();
+ elementRegionManager.forElementRegions< WellElementRegion >( regionNames,
+ [&]( localIndex const,
+ WellElementRegion & region )
+ {
+ WellElementSubRegion & subRegion = region.getGroup( ElementRegionBase::viewKeyStruct::elementSubRegions() )
+ .getGroup< WellElementSubRegion >( region.getSubRegionName() );
+ WellControls & wellControls = wellSolver.getWellControls( subRegion );
+ wellControls.initializeWell( domain, meshLevel, subRegion, TIME );
+ } );
+ } );
}
void TestAssembleCouplingTerms()
@@ -624,7 +645,23 @@ class SinglePhaseReservoirSolverTest : public ::testing::Test
[&] ( CRSMatrixView< real64, globalIndex const > const & localMatrix,
arrayView1d< real64 > const & localRhs )
{
- solver->wellSolver()->assembleFluxTerms( TIME, DT, domain, solver->getDofManager(), localMatrix, localRhs );
+ WellManager & wellSolver = *solver->wellSolver();
+ wellSolver.forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const & meshBodyName,
+ MeshLevel & meshLevel,
+ string_array const & regionNames )
+ {
+ GEOS_UNUSED_VAR( meshBodyName );
+ ElementRegionManager & elementRegionManager = meshLevel.getElemManager();
+ elementRegionManager.forElementRegions< WellElementRegion >( regionNames,
+ [&]( localIndex const,
+ WellElementRegion & region )
+ {
+ WellElementSubRegion & subRegion = region.getGroup( ElementRegionBase::viewKeyStruct::elementSubRegions() )
+ .getGroup< WellElementSubRegion >( region.getSubRegionName() );
+ WellControls & wellControls = wellSolver.getWellControls( subRegion );
+ wellControls.assembleWellFluxTerms( TIME, DT, subRegion, solver->getDofManager(), localMatrix, localRhs );
+ } );
+ } );
} );
}
@@ -638,7 +675,23 @@ class SinglePhaseReservoirSolverTest : public ::testing::Test
[&] ( CRSMatrixView< real64, globalIndex const > const & localMatrix,
arrayView1d< real64 > const & localRhs )
{
- solver->wellSolver()->assemblePressureRelations( TIME, DT, domain, solver->getDofManager(), localMatrix, localRhs );
+ WellManager & wellSolver = *solver->wellSolver();
+ wellSolver.forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const & meshBodyName,
+ MeshLevel & meshLevel,
+ string_array const & regionNames )
+ {
+ GEOS_UNUSED_VAR( meshBodyName );
+ ElementRegionManager & elementRegionManager = meshLevel.getElemManager();
+ elementRegionManager.forElementRegions< WellElementRegion >( regionNames,
+ [&]( localIndex const,
+ WellElementRegion & region )
+ {
+ WellElementSubRegion & subRegion = region.getGroup( ElementRegionBase::viewKeyStruct::elementSubRegions() )
+ .getGroup< WellElementSubRegion >( region.getSubRegionName() );
+ WellControls & wellControls = wellSolver.getWellControls( subRegion );
+ wellControls.assembleWellPressureRelations( TIME, DT, subRegion, solver->getDofManager(), localMatrix, localRhs );
+ } );
+ } );
} );
}
@@ -653,7 +706,23 @@ class SinglePhaseReservoirSolverTest : public ::testing::Test
[&] ( CRSMatrixView< real64, globalIndex const > const & localMatrix,
arrayView1d< real64 > const & localRhs )
{
- solver->wellSolver()->assembleAccumulationTerms( TIME, DT, domain, solver->getDofManager(), localMatrix, localRhs );
+ WellManager & wellSolver = *solver->wellSolver();
+ wellSolver.forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const & meshBodyName,
+ MeshLevel & meshLevel,
+ string_array const & regionNames )
+ {
+ GEOS_UNUSED_VAR( meshBodyName );
+ ElementRegionManager & elementRegionManager = meshLevel.getElemManager();
+ elementRegionManager.forElementRegions< WellElementRegion >( regionNames,
+ [&]( localIndex const,
+ WellElementRegion & region )
+ {
+ WellElementSubRegion & subRegion = region.getGroup( ElementRegionBase::viewKeyStruct::elementSubRegions() )
+ .getGroup< WellElementSubRegion >( region.getSubRegionName() );
+ WellControls & wellControls = wellSolver.getWellControls( subRegion );
+ wellControls.assembleWellAccumulationTerms( TIME, DT, subRegion, solver->getDofManager(), localMatrix, localRhs );
+ } );
+ } );
} );
}
@@ -710,7 +779,24 @@ TEST_F( SinglePhaseReservoirSolverInternalWellTest, jacobianNumericalCheck_Flux
[&] ( CRSMatrixView< real64, globalIndex const > const & localMatrix,
arrayView1d< real64 > const & localRhs )
{
- solver->wellSolver()->assembleFluxTerms( TIME, DT, domain, solver->getDofManager(), localMatrix, localRhs );
+
+ WellManager & wellSolver = *solver->wellSolver();
+ wellSolver.forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const & meshBodyName,
+ MeshLevel & meshLevel,
+ string_array const & regionNames )
+ {
+ GEOS_UNUSED_VAR( meshBodyName );
+ ElementRegionManager & elementRegionManager = meshLevel.getElemManager();
+ elementRegionManager.forElementRegions< WellElementRegion >( regionNames,
+ [&]( localIndex const,
+ WellElementRegion & region )
+ {
+ WellElementSubRegion & subRegion = region.getGroup( ElementRegionBase::viewKeyStruct::elementSubRegions() )
+ .getGroup< WellElementSubRegion >( region.getSubRegionName() );
+ WellControls & wellControls = wellSolver.getWellControls( subRegion );
+ wellControls.assembleWellFluxTerms( TIME, DT, subRegion, solver->getDofManager(), localMatrix, localRhs );
+ } );
+ } );
} );
}
diff --git a/src/coreComponents/integrationTests/wellsTests/testThermalEstimatorInjWell.cpp b/src/coreComponents/integrationTests/wellsTests/testThermalEstimatorInjWell.cpp
new file mode 100644
index 00000000000..12903f077f6
--- /dev/null
+++ b/src/coreComponents/integrationTests/wellsTests/testThermalEstimatorInjWell.cpp
@@ -0,0 +1,1003 @@
+/*
+ * ------------------------------------------------------------------------------------------------------------
+ * 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 "integrationTests/fluidFlowTests/testCompFlowUtils.hpp"
+
+#include "common/DataTypes.hpp"
+#include "mainInterface/initialization.hpp"
+#include "constitutive/fluid/multifluid/MultiFluidBase.hpp"
+#include "mainInterface/ProblemManager.hpp"
+#include "mesh/DomainPartition.hpp"
+#include "mainInterface/GeosxState.hpp"
+#include "mesh/WellElementSubRegion.hpp"
+#include "physicsSolvers/PhysicsSolverManager.hpp"
+#include "physicsSolvers/multiphysics/CompositionalMultiphaseReservoirAndWells.hpp"
+#include "physicsSolvers/fluidFlow/CompositionalMultiphaseFVM.hpp"
+#include "physicsSolvers/fluidFlow/wells/CompositionalMultiphaseWell.hpp"
+#include "physicsSolvers/fluidFlow/wells/CompositionalMultiphaseWellFields.hpp"
+#include "physicsSolvers/fluidFlow/wells/WellSolverBaseFields.hpp"
+#include "physicsSolvers/fluidFlow/wells/kernels/CompositionalMultiphaseWellKernels.hpp"
+
+using namespace geos;
+using namespace geos::dataRepository;
+using namespace geos::constitutive;
+using namespace geos::testing;
+
+CommandLineOptions g_commandLineOptions;
+
+void writeTableToFile( string const & filename, char const * str )
+{
+ std::ofstream os( filename );
+ ASSERT_TRUE( os.is_open() );
+ os << str;
+ os.close();
+}
+
+void removeFile( string const & filename )
+{
+ int const ret = std::remove( filename.c_str() );
+ ASSERT_TRUE( ret == 0 );
+}
+char const * co2flash = "FlashModel CO2Solubility 1e5 7.5e7 5e5 283.15 414.15 10 0\n";
+char const * pvtLiquid = "DensityFun PhillipsBrineDensity 1e5 7.5e7 5e5 283.15 414.15 10 0\n"
+ "ViscosityFun PhillipsBrineViscosity 0\n"
+ "EnthalpyFun BrineEnthalpy 1e5 7.5e7 5e5 283.15 414.15 10 0\n";
+
+char const * pvtGas = "DensityFun SpanWagnerCO2Density 1e5 7.5e7 5e5 283.15 414.15 10\n"
+ "ViscosityFun FenghourCO2Viscosity 1e5 7.5e7 5e5 283.15 414.15 10\n"
+ "EnthalpyFun CO2Enthalpy 1e5 7.5e7 5e5 283.15 414.15 10\n";
+char const * xmlInput =
+ R"xml(
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+)xml";
+
+template< typename T, typename COL_INDEX >
+void printCompareLocalMatrices( CRSMatrixView< T const, COL_INDEX const > const & matrix1,
+ CRSMatrixView< T const, COL_INDEX const > const & matrix2, std::string const & testName )
+{
+ std::ofstream omat1( testName+".csv" );
+
+
+ std::vector< std::vector< double > > fmat1( matrix1.numRows(), std::vector< double >( matrix1.numRows(), 0.0 ));
+ std::vector< std::vector< double > > fmat2( matrix2.numRows(), std::vector< double >( matrix2.numRows(), 0.0 ));
+
+ for( localIndex i = 0; i < matrix1.numRows(); ++i )
+ {
+ arraySlice1d< globalIndex const > indices1 = matrix1.getColumns( i );
+ arraySlice1d< globalIndex const > indices2 = matrix2.getColumns( i );
+ arraySlice1d< double const > values1 = matrix1.getEntries( i );
+ arraySlice1d< double const > values2 = matrix2.getEntries( i );
+ for( integer j=0; j const & rsd1,
+ array1d< real64 > const & rsd2, std::string const & testName )
+{
+ std::ofstream omat1( testName+".csv" );
+
+ for( integer i=0; i
+void testWellEstimatorNumericalJacobian( CompositionalMultiphaseReservoirAndWells< CompositionalMultiphaseBase > & solver,
+ DomainPartition & domain,
+ real64 const perturbParameter,
+ real64 const time_n,
+ real64 const relTol, std::string const & testName,
+ LAMBDA && assembleFunction )
+{
+ CompositionalMultiphaseWell & wellSolver = *solver.wellSolver();
+ CompositionalMultiphaseFVM & flowSolver = dynamicCast< CompositionalMultiphaseFVM & >( *solver.reservoirSolver() );
+
+ localIndex const NC = flowSolver.numFluidComponents();
+
+ CRSMatrix< real64, globalIndex > const & jacobian = wellSolver.getLocalMatrix();
+ array1d< real64 > residual( jacobian.numRows() );
+ DofManager const & dofManager = wellSolver.getDofManager();
+
+ // assemble the analytical residual
+ solver.resetStateToBeginningOfStep( domain );
+
+ residual.zero();
+ jacobian.zero();
+
+ wellSolver.forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &,
+ MeshLevel & mesh,
+ string_array const & regionNames )
+ {
+ mesh.getElemManager().forElementSubRegions< WellElementSubRegion >( regionNames,
+ [&]( localIndex const,
+ WellElementSubRegion & subRegion )
+ {
+ WellControls & wellControls = wellSolver.getWellControls( subRegion );
+ wellControls.setWellState( 1 );
+ wellSolver.initializeWell( domain, mesh, subRegion, time_n );
+ } );
+ } );
+
+ assembleFunction( jacobian.toViewConstSizes(), residual.toView() );
+ residual.move( hostMemorySpace, false );
+
+ // copy the analytical residual
+ array1d< real64 > residualOrig( residual );
+
+ // create the numerical jacobian
+ jacobian.move( hostMemorySpace );
+ CRSMatrix< real64, globalIndex > jacobianFD( jacobian );
+ jacobianFD.zero();
+
+
+ string const wellDofKey = dofManager.getKey( wellSolver.wellElementDofName() );
+
+ // at this point we start assembling the finite-difference block by block
+
+
+ /////////////////////////////////////////////////
+ // Step 2) Compute the terms in J_RW and J_WW //
+ /////////////////////////////////////////////////
+
+ // loop over the wells
+ if( 1 )
+ wellSolver.forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &,
+ MeshLevel & mesh,
+ string_array const & regionNames )
+ {
+ mesh.getElemManager().forElementSubRegions< WellElementSubRegion >( regionNames,
+ [&]( localIndex const,
+ WellElementSubRegion & subRegion )
+ {
+ // get the degrees of freedom, ghosting info and next well elem index
+ arrayView1d< globalIndex const > const & wellElemDofNumber =
+ subRegion.getReference< array1d< globalIndex > >( wellDofKey );
+
+ // get the primary variables on the well elements
+ arrayView1d< real64 > const & wellElemPressure =
+ subRegion.getField< fields::well::pressure >();
+ wellElemPressure.move( hostMemorySpace, false );
+
+ arrayView1d< real64 > const & wellElemTemperature =
+ subRegion.getField< fields::well::temperature >();
+ wellElemTemperature.move( hostMemorySpace, false );
+
+ arrayView2d< real64, compflow::USD_COMP > const & wellElemCompDens =
+ subRegion.getField< fields::well::globalCompDensity >();
+ wellElemCompDens.move( hostMemorySpace, false );
+
+ arrayView1d< real64 > const & connRate =
+ subRegion.getField< fields::well::mixtureConnectionRate >();
+ connRate.move( hostMemorySpace, false );
+
+ // a) compute all the derivatives wrt to the pressure in WELL elem iwelem
+ for( localIndex iwelem = 0; iwelem < subRegion.size(); ++iwelem )
+ {
+
+ real64 wellElemTotalDensity = 0.0;
+ for( localIndex ic = 0; ic < NC; ++ic )
+ {
+ wellElemTotalDensity += wellElemCompDens[iwelem][ic];
+ }
+
+ {
+ solver.resetStateToBeginningOfStep( domain );
+
+ // here is the perturbation in the pressure of the well element
+ real64 const dP = perturbParameter * ( wellElemPressure[iwelem] + perturbParameter );
+ wellElemPressure.move( hostMemorySpace, true );
+ wellElemPressure[iwelem] += dP;
+
+ // after perturbing, update the pressure-dependent quantities in the well
+ wellSolver.updateState( domain );
+
+ residual.zero();
+ jacobian.zero();
+ assembleFunction( jacobian.toViewConstSizes(), residual.toView() );
+
+ fillNumericalJacobian( residual.toViewConst(),
+ residualOrig.toViewConst(),
+ wellElemDofNumber[iwelem] + compositionalMultiphaseWellKernels::ColOffset::DPRES,
+ dP,
+ jacobianFD.toViewConstSizes() );
+ }
+
+ for( localIndex jc = 0; jc < NC; ++jc )
+ {
+ solver.resetStateToBeginningOfStep( domain );
+
+ real64 const dRho = perturbParameter * wellElemTotalDensity;
+ wellElemCompDens.move( hostMemorySpace, true );
+ wellElemCompDens[iwelem][jc] += dRho;
+
+ wellSolver.updateState( domain );
+
+ residual.zero();
+ jacobian.zero();
+ assembleFunction( jacobian.toViewConstSizes(), residual.toView() );
+
+ fillNumericalJacobian( residual.toViewConst(),
+ residualOrig.toViewConst(),
+ wellElemDofNumber[iwelem] + compositionalMultiphaseWellKernels::ColOffset::DCOMP + jc,
+ dRho,
+ jacobianFD.toViewConstSizes() );
+ }
+ {
+ solver.resetStateToBeginningOfStep( domain );
+ residual.zero();
+ jacobian.zero();
+ // here is the perturbation in the temperature of the well element
+ real64 const dT = perturbParameter * ( wellElemTemperature[iwelem] + perturbParameter );
+ wellElemTemperature.move( hostMemorySpace, true );
+ wellElemTemperature[iwelem] += dT;
+
+ // after perturbing, update the pressure-dependent quantities in the well
+ wellSolver.updateState( domain );
+
+
+ assembleFunction( jacobian.toViewConstSizes(), residual.toView() );
+ fillNumericalJacobian( residual.toViewConst(),
+ residualOrig.toViewConst(),
+ wellElemDofNumber[iwelem] + compositionalMultiphaseWellKernels::ColOffset::DCOMP + NC+1,
+ dT,
+ jacobianFD.toViewConstSizes() );
+
+ }
+ }
+
+
+ // b) compute all the derivatives wrt to the connection in WELL elem
+ // iwelem
+ for( localIndex iwelem = 0; iwelem < subRegion.size(); ++iwelem )
+ {
+ {
+ solver.resetStateToBeginningOfStep( domain );
+
+ // here is the perturbation in the rate of the well element
+ real64 const dRate = perturbParameter * ( connRate[iwelem] + perturbParameter );
+ connRate.move( hostMemorySpace, true );
+ connRate[iwelem] += dRate;
+
+ wellSolver.updateState( domain );
+
+ residual.zero();
+ jacobian.zero();
+ assembleFunction( jacobian.toViewConstSizes(), residual.toView() );
+
+ fillNumericalJacobian( residual.toViewConst(),
+ residualOrig.toViewConst(),
+ wellElemDofNumber[iwelem] + compositionalMultiphaseWellKernels::ColOffset::DCOMP + NC,
+ dRate,
+ jacobianFD.toViewConstSizes() );
+ }
+ }
+ } );
+ } );
+
+ // assemble the analytical jacobian
+ solver.resetStateToBeginningOfStep( domain );
+
+ residual.zero();
+ jacobian.zero();
+ assembleFunction( jacobian.toViewConstSizes(), residual.toView() );
+ printCompareLocalMatrices( jacobian.toViewConst(), jacobianFD.toViewConst(), testName );
+ compareLocalMatrices( jacobian.toViewConst(), jacobianFD.toViewConst(), relTol );
+}
+template< typename LAMBDA >
+void testNumericalJacobian( CompositionalMultiphaseReservoirAndWells< CompositionalMultiphaseBase > & solver,
+ DomainPartition & domain,
+ real64 const perturbParameter,
+ real64 const relTol, std::string const & testName,
+ LAMBDA && assembleFunction )
+{
+ CompositionalMultiphaseWell & wellSolver = *solver.wellSolver();
+ CompositionalMultiphaseFVM & flowSolver = dynamicCast< CompositionalMultiphaseFVM & >( *solver.reservoirSolver() );
+
+ localIndex const NC = flowSolver.numFluidComponents();
+
+ CRSMatrix< real64, globalIndex > const & jacobian = solver.getLocalMatrix();
+ array1d< real64 > residual( jacobian.numRows() );
+ DofManager const & dofManager = solver.getDofManager();
+
+ // assemble the analytical residual
+ solver.resetStateToBeginningOfStep( domain );
+
+ residual.zero();
+ jacobian.zero();
+
+ assembleFunction( jacobian.toViewConstSizes(), residual.toView() );
+ residual.move( hostMemorySpace, false );
+
+ // copy the analytical residual
+ array1d< real64 > residualOrig( residual );
+
+ // create the numerical jacobian
+ jacobian.move( hostMemorySpace );
+ CRSMatrix< real64, globalIndex > jacobianFD( jacobian );
+ jacobianFD.zero();
+
+ string const resDofKey = dofManager.getKey( wellSolver.resElementDofName() );
+ string const wellDofKey = dofManager.getKey( wellSolver.wellElementDofName() );
+
+ // at this point we start assembling the finite-difference block by block
+
+ ////////////////////////////////////////////////
+ // Step 1) Compute the terms in J_RR and J_WR //
+ ////////////////////////////////////////////////
+ if( 1 )
+ domain.forMeshBodies( [&] ( MeshBody & meshBody )
+ {
+ meshBody.forMeshLevels( [&] ( MeshLevel & mesh )
+ {
+ ElementRegionManager & elemManager = mesh.getElemManager();
+ for( localIndex er = 0; er < elemManager.numRegions(); ++er )
+ {
+ ElementRegionBase & elemRegion = elemManager.getRegion( er );
+ elemRegion.forElementSubRegionsIndex< CellElementSubRegion >( [&]( localIndex const, CellElementSubRegion & subRegion )
+ {
+ // get the degrees of freedom and ghosting information
+ arrayView1d< globalIndex const > const & dofNumber =
+ subRegion.getReference< array1d< globalIndex > >( resDofKey );
+
+ // get the primary variables on the reservoir elements
+ arrayView1d< real64 > const & pres =
+ subRegion.getField< fields::flow::pressure >();
+ pres.move( hostMemorySpace, false );
+
+ arrayView2d< real64, compflow::USD_COMP > const & compDens =
+ subRegion.getField< fields::flow::globalCompDensity >();
+ compDens.move( hostMemorySpace, false );
+
+ arrayView1d< real64 > const & temp =
+ subRegion.getField< fields::flow::temperature >();
+ temp.move( hostMemorySpace, false );
+
+ // a) compute all the derivatives wrt to the pressure in RESERVOIR elem ei
+ for( localIndex ei = 0; ei < subRegion.size(); ++ei )
+ {
+ real64 totalDensity = 0.0;
+ for( localIndex ic = 0; ic < NC; ++ic )
+ {
+ totalDensity += compDens[ei][ic];
+ }
+
+ {
+ solver.resetStateToBeginningOfStep( domain );
+
+ // here is the perturbation in the pressure of the element
+ real64 const dP = perturbParameter * (pres[ei] + perturbParameter);
+ pres.move( hostMemorySpace, true );
+ pres[ei] += dP;
+
+ // after perturbing, update the pressure-dependent quantities in the reservoir
+ flowSolver.forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &,
+ MeshLevel & mesh2,
+ string_array const & regionNames2 )
+ {
+ mesh2.getElemManager().forElementSubRegions( regionNames2,
+ [&]( localIndex const,
+ ElementSubRegionBase & subRegion2 )
+ {
+ flowSolver.updateFluidState( subRegion2 );
+ } );
+ } );
+
+ wellSolver.updateState( domain );
+
+ residual.zero();
+ jacobian.zero();
+ assembleFunction( jacobian.toViewConstSizes(), residual.toView() );
+
+ fillNumericalJacobian( residual.toViewConst(),
+ residualOrig.toViewConst(),
+ dofNumber[ei],
+ dP,
+ jacobianFD.toViewConstSizes() );
+ }
+
+ for( localIndex jc = 0; jc < NC; ++jc )
+ {
+ solver.resetStateToBeginningOfStep( domain );
+
+ real64 const dRho = perturbParameter * totalDensity;
+ compDens.move( hostMemorySpace, true );
+ compDens[ei][jc] += dRho;
+
+ flowSolver.forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &,
+ MeshLevel & mesh2,
+ string_array const & regionNames2 )
+ {
+ mesh2.getElemManager().forElementSubRegions( regionNames2,
+ [&]( localIndex const,
+ ElementSubRegionBase & subRegion2 )
+ {
+ flowSolver.updateFluidState( subRegion2 );
+ } );
+ } );
+ wellSolver.updateState( domain );
+ residual.zero();
+ jacobian.zero();
+ assembleFunction( jacobian.toViewConstSizes(), residual.toView() );
+
+ fillNumericalJacobian( residual.toViewConst(),
+ residualOrig.toViewConst(),
+ dofNumber[ei] + jc + 1,
+ dRho,
+ jacobianFD.toViewConstSizes() );
+ }
+ {
+ solver.resetStateToBeginningOfStep( domain );
+
+ // here is the perturbation in the pressure of the element
+ real64 const dTemp = perturbParameter * (temp[ei] + perturbParameter);
+ temp.move( hostMemorySpace, true );
+ temp[ei] += dTemp;
+
+ // after perturbing, update the pressure-dependent quantities in the reservoir
+ flowSolver.forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &,
+ MeshLevel & mesh2,
+ string_array const & regionNames2 )
+ {
+ mesh2.getElemManager().forElementSubRegions( regionNames2,
+ [&]( localIndex const,
+ ElementSubRegionBase & subRegion2 )
+ {
+ flowSolver.updateFluidState( subRegion2 );
+ } );
+ } );
+
+ wellSolver.updateState( domain );
+
+ residual.zero();
+ jacobian.zero();
+ assembleFunction( jacobian.toViewConstSizes(), residual.toView() );
+
+ fillNumericalJacobian( residual.toViewConst(),
+ residualOrig.toViewConst(),
+ dofNumber[ei]+NC+1,
+ dTemp,
+ jacobianFD.toViewConstSizes() );
+ }
+ }
+ } );
+ }
+ } );
+
+ } );
+
+ /////////////////////////////////////////////////
+ // Step 2) Compute the terms in J_RW and J_WW //
+ /////////////////////////////////////////////////
+
+ // loop over the wells
+ if( 1 )
+ wellSolver.forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &,
+ MeshLevel & mesh,
+ string_array const & regionNames )
+ {
+ mesh.getElemManager().forElementSubRegions< WellElementSubRegion >( regionNames,
+ [&]( localIndex const,
+ WellElementSubRegion & subRegion )
+ {
+ // get the degrees of freedom, ghosting info and next well elem index
+ arrayView1d< globalIndex const > const & wellElemDofNumber =
+ subRegion.getReference< array1d< globalIndex > >( wellDofKey );
+
+ // get the primary variables on the well elements
+ arrayView1d< real64 > const & wellElemPressure =
+ subRegion.getField< fields::well::pressure >();
+ wellElemPressure.move( hostMemorySpace, false );
+
+ arrayView1d< real64 > const & wellElemTemperature =
+ subRegion.getField< fields::well::temperature >();
+ wellElemTemperature.move( hostMemorySpace, false );
+
+ arrayView2d< real64, compflow::USD_COMP > const & wellElemCompDens =
+ subRegion.getField< fields::well::globalCompDensity >();
+ wellElemCompDens.move( hostMemorySpace, false );
+
+ arrayView1d< real64 > const & connRate =
+ subRegion.getField< fields::well::mixtureConnectionRate >();
+ connRate.move( hostMemorySpace, false );
+
+ // a) compute all the derivatives wrt to the pressure in WELL elem iwelem
+ for( localIndex iwelem = 0; iwelem < subRegion.size(); ++iwelem )
+ {
+
+ real64 wellElemTotalDensity = 0.0;
+ for( localIndex ic = 0; ic < NC; ++ic )
+ {
+ wellElemTotalDensity += wellElemCompDens[iwelem][ic];
+ }
+
+ {
+ solver.resetStateToBeginningOfStep( domain );
+
+ // here is the perturbation in the pressure of the well element
+ real64 const dP = perturbParameter * ( wellElemPressure[iwelem] + perturbParameter );
+ wellElemPressure.move( hostMemorySpace, true );
+ wellElemPressure[iwelem] += dP;
+
+ // after perturbing, update the pressure-dependent quantities in the well
+ wellSolver.updateState( domain );
+
+ residual.zero();
+ jacobian.zero();
+ assembleFunction( jacobian.toViewConstSizes(), residual.toView() );
+
+ fillNumericalJacobian( residual.toViewConst(),
+ residualOrig.toViewConst(),
+ wellElemDofNumber[iwelem] + compositionalMultiphaseWellKernels::ColOffset::DPRES,
+ dP,
+ jacobianFD.toViewConstSizes() );
+ }
+
+ for( localIndex jc = 0; jc < NC; ++jc )
+ {
+ solver.resetStateToBeginningOfStep( domain );
+
+ real64 const dRho = perturbParameter * wellElemTotalDensity;
+ wellElemCompDens.move( hostMemorySpace, true );
+ wellElemCompDens[iwelem][jc] += dRho;
+
+ wellSolver.updateState( domain );
+
+ residual.zero();
+ jacobian.zero();
+ assembleFunction( jacobian.toViewConstSizes(), residual.toView() );
+
+ fillNumericalJacobian( residual.toViewConst(),
+ residualOrig.toViewConst(),
+ wellElemDofNumber[iwelem] + compositionalMultiphaseWellKernels::ColOffset::DCOMP + jc,
+ dRho,
+ jacobianFD.toViewConstSizes() );
+ }
+ {
+ solver.resetStateToBeginningOfStep( domain );
+ residual.zero();
+ jacobian.zero();
+ // here is the perturbation in the temperature of the well element
+ real64 const dT = perturbParameter * ( wellElemTemperature[iwelem] + perturbParameter );
+ wellElemTemperature.move( hostMemorySpace, true );
+ wellElemTemperature[iwelem] += dT;
+
+ // after perturbing, update the pressure-dependent quantities in the well
+ wellSolver.updateState( domain );
+
+
+ assembleFunction( jacobian.toViewConstSizes(), residual.toView() );
+ fillNumericalJacobian( residual.toViewConst(),
+ residualOrig.toViewConst(),
+ wellElemDofNumber[iwelem] + compositionalMultiphaseWellKernels::ColOffset::DCOMP + NC+1,
+ dT,
+ jacobianFD.toViewConstSizes() );
+
+ }
+ }
+
+
+ // b) compute all the derivatives wrt to the connection in WELL elem
+ // iwelem
+ for( localIndex iwelem = 0; iwelem < subRegion.size(); ++iwelem )
+ {
+ {
+ solver.resetStateToBeginningOfStep( domain );
+
+ // here is the perturbation in the rate of the well element
+ real64 const dRate = perturbParameter * ( connRate[iwelem] + perturbParameter );
+ connRate.move( hostMemorySpace, true );
+ connRate[iwelem] += dRate;
+
+ wellSolver.updateState( domain );
+
+ residual.zero();
+ jacobian.zero();
+ assembleFunction( jacobian.toViewConstSizes(), residual.toView() );
+
+ fillNumericalJacobian( residual.toViewConst(),
+ residualOrig.toViewConst(),
+ wellElemDofNumber[iwelem] + compositionalMultiphaseWellKernels::ColOffset::DCOMP + NC,
+ dRate,
+ jacobianFD.toViewConstSizes() );
+ }
+ }
+ } );
+ } );
+
+ // assemble the analytical jacobian
+ solver.resetStateToBeginningOfStep( domain );
+
+ residual.zero();
+ jacobian.zero();
+ assembleFunction( jacobian.toViewConstSizes(), residual.toView() );
+ printCompareLocalMatrices( jacobian.toViewConst(), jacobianFD.toViewConst(), testName );
+ compareLocalMatrices( jacobian.toViewConst(), jacobianFD.toViewConst(), relTol );
+}
+
+class CompositionalMultiphaseReservoirSolverTest : public ::testing::Test
+{
+public:
+
+ CompositionalMultiphaseReservoirSolverTest():
+ state( std::make_unique< CommandLineOptions >( g_commandLineOptions ) )
+ {}
+
+protected:
+
+ void SetUp() override
+ {
+ setupProblemFromXML( state.getProblemManager(), xmlInput );
+ solver = &state.getProblemManager().getPhysicsSolverManager().getGroup< CompositionalMultiphaseReservoirAndWells< CompositionalMultiphaseBase > >( "reservoirSystem" );
+
+ DomainPartition & domain = state.getProblemManager().getDomainPartition();
+
+ solver->setupSystem( domain,
+ solver->getDofManager(),
+ solver->getLocalMatrix(),
+ solver->getSystemRhs(),
+ solver->getSystemSolution() );
+
+ solver->wellSolver()->setupSystem( domain,
+ solver->wellSolver()->getDofManager(),
+ solver->wellSolver()->getLocalMatrix(),
+ solver->wellSolver()->getSystemRhs(),
+ solver->wellSolver()->getSystemSolution() );
+
+ solver->implicitStepSetup( time, dt, domain );
+
+ }
+
+ static real64 constexpr time = 0.0;
+ static real64 constexpr dt = 1e4;
+ static real64 constexpr eps = std::numeric_limits< real64 >::epsilon();
+
+ GeosxState state;
+ CompositionalMultiphaseReservoirAndWells< CompositionalMultiphaseBase > * solver;
+};
+
+real64 constexpr CompositionalMultiphaseReservoirSolverTest::time;
+real64 constexpr CompositionalMultiphaseReservoirSolverTest::dt;
+real64 constexpr CompositionalMultiphaseReservoirSolverTest::eps;
+
+
+TEST_F( CompositionalMultiphaseReservoirSolverTest, jacobianNumericalCheck_Perforation )
+{
+ real64 const perturb = std::sqrt( eps );
+ real64 const tol = 1e-1; // 10% error margin
+
+ DomainPartition & domain = state.getProblemManager().getDomainPartition();
+
+ testWellEstimatorNumericalJacobian( *solver, domain, perturb, time, tol, "WellEstimator",
+ [&] ( CRSMatrixView< real64, globalIndex const > const & localMatrix,
+ arrayView1d< real64 > const & localRhs )
+ {
+
+ DofManager const & dofManager = solver->wellSolver()->getDofManager();
+ solver->wellSolver()->forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &,
+ MeshLevel & mesh,
+ string_array const & regionNames )
+ {
+ ElementRegionManager & elemManager = mesh.getElemManager();
+ elemManager.forElementSubRegions< WellElementSubRegion >( regionNames,
+ [&]( localIndex const,
+ WellElementSubRegion & subRegion )
+ {
+ // call assemble to fill the matrix and the rhs
+ solver->wellSolver()->assembleWellSystem( time,
+ dt,
+ elemManager,
+ subRegion,
+ dofManager,
+ localMatrix,
+ localRhs );
+
+// apply boundary conditions to system
+
+ solver->wellSolver()->applyWellBoundaryConditions( time,
+ dt,
+ elemManager,
+ subRegion,
+ dofManager,
+ localRhs,
+ localMatrix );
+
+ solver->wellSolver()->outputSingleWellDebug( time, dt, 0, 0, 0, mesh, subRegion, dofManager, localMatrix, localRhs );
+ } );
+ } );
+
+
+
+ } );
+}
+
+
+int main( int argc, char * * argv )
+{
+ writeTableToFile( "co2flash.txt", co2flash );
+ writeTableToFile( "pvtliquid.txt", pvtLiquid );
+ writeTableToFile( "pvtgas.txt", pvtGas );
+ ::testing::InitGoogleTest( &argc, argv );
+ g_commandLineOptions = *geos::basicSetup( argc, argv );
+ int const result = RUN_ALL_TESTS();
+ geos::basicCleanup();
+ removeFile( "co2flash.txt" );
+ removeFile( "pvtliquid.txt" );
+ removeFile( "pvtgas.txt" );
+
+ return result;
+}
diff --git a/src/coreComponents/integrationTests/wellsTests/testThermalEstimatorProdWell.cpp b/src/coreComponents/integrationTests/wellsTests/testThermalEstimatorProdWell.cpp
new file mode 100644
index 00000000000..8ef86902c2d
--- /dev/null
+++ b/src/coreComponents/integrationTests/wellsTests/testThermalEstimatorProdWell.cpp
@@ -0,0 +1,1002 @@
+/*
+ * ------------------------------------------------------------------------------------------------------------
+ * 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 "integrationTests/fluidFlowTests/testCompFlowUtils.hpp"
+
+#include "common/DataTypes.hpp"
+#include "mainInterface/initialization.hpp"
+#include "constitutive/fluid/multifluid/MultiFluidBase.hpp"
+#include "mainInterface/ProblemManager.hpp"
+#include "mesh/DomainPartition.hpp"
+#include "mainInterface/GeosxState.hpp"
+#include "mesh/WellElementSubRegion.hpp"
+#include "physicsSolvers/PhysicsSolverManager.hpp"
+#include "physicsSolvers/multiphysics/CompositionalMultiphaseReservoirAndWells.hpp"
+#include "physicsSolvers/fluidFlow/CompositionalMultiphaseFVM.hpp"
+#include "physicsSolvers/fluidFlow/wells/CompositionalMultiphaseWell.hpp"
+#include "physicsSolvers/fluidFlow/wells/CompositionalMultiphaseWellFields.hpp"
+#include "physicsSolvers/fluidFlow/wells/WellSolverBaseFields.hpp"
+#include "physicsSolvers/fluidFlow/wells/kernels/CompositionalMultiphaseWellKernels.hpp"
+
+using namespace geos;
+using namespace geos::dataRepository;
+using namespace geos::constitutive;
+using namespace geos::testing;
+
+CommandLineOptions g_commandLineOptions;
+
+void writeTableToFile( string const & filename, char const * str )
+{
+ std::ofstream os( filename );
+ ASSERT_TRUE( os.is_open() );
+ os << str;
+ os.close();
+}
+
+void removeFile( string const & filename )
+{
+ int const ret = std::remove( filename.c_str() );
+ ASSERT_TRUE( ret == 0 );
+}
+char const * co2flash = "FlashModel CO2Solubility 1e5 7.5e7 5e5 283.15 414.15 10 0\n";
+char const * pvtLiquid = "DensityFun PhillipsBrineDensity 1e5 7.5e7 5e5 283.15 414.15 10 0\n"
+ "ViscosityFun PhillipsBrineViscosity 0\n"
+ "EnthalpyFun BrineEnthalpy 1e5 7.5e7 5e5 283.15 414.15 10 0\n";
+
+char const * pvtGas = "DensityFun SpanWagnerCO2Density 1e5 7.5e7 5e5 283.15 414.15 10\n"
+ "ViscosityFun FenghourCO2Viscosity 1e5 7.5e7 5e5 283.15 414.15 10\n"
+ "EnthalpyFun CO2Enthalpy 1e5 7.5e7 5e5 283.15 414.15 10\n";
+char const * xmlInput =
+ R"xml(
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+)xml";
+
+template< typename T, typename COL_INDEX >
+void printCompareLocalMatrices( CRSMatrixView< T const, COL_INDEX const > const & matrix1,
+ CRSMatrixView< T const, COL_INDEX const > const & matrix2, std::string const & testName )
+{
+ std::ofstream omat1( testName+".csv" );
+
+
+ std::vector< std::vector< double > > fmat1( matrix1.numRows(), std::vector< double >( matrix1.numRows(), 0.0 ));
+ std::vector< std::vector< double > > fmat2( matrix2.numRows(), std::vector< double >( matrix2.numRows(), 0.0 ));
+
+ for( localIndex i = 0; i < matrix1.numRows(); ++i )
+ {
+ arraySlice1d< globalIndex const > indices1 = matrix1.getColumns( i );
+ arraySlice1d< globalIndex const > indices2 = matrix2.getColumns( i );
+ arraySlice1d< double const > values1 = matrix1.getEntries( i );
+ arraySlice1d< double const > values2 = matrix2.getEntries( i );
+ for( integer j=0; j const & rsd1,
+ array1d< real64 > const & rsd2, std::string const & testName )
+{
+ std::ofstream omat1( testName+".csv" );
+
+ for( integer i=0; i
+void testWellEstimatorNumericalJacobian( CompositionalMultiphaseReservoirAndWells< CompositionalMultiphaseBase > & solver,
+ DomainPartition & domain,
+ real64 const perturbParameter,
+ real64 const time_n,
+ real64 const relTol, std::string const & testName,
+ LAMBDA && assembleFunction )
+{
+ CompositionalMultiphaseWell & wellSolver = *solver.wellSolver();
+ CompositionalMultiphaseFVM & flowSolver = dynamicCast< CompositionalMultiphaseFVM & >( *solver.reservoirSolver() );
+
+ localIndex const NC = flowSolver.numFluidComponents();
+
+ CRSMatrix< real64, globalIndex > const & jacobian = wellSolver.getLocalMatrix();
+ array1d< real64 > residual( jacobian.numRows() );
+ DofManager const & dofManager = wellSolver.getDofManager();
+
+ // assemble the analytical residual
+ solver.resetStateToBeginningOfStep( domain );
+
+ residual.zero();
+ jacobian.zero();
+
+ wellSolver.forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &,
+ MeshLevel & mesh,
+ string_array const & regionNames )
+ {
+ mesh.getElemManager().forElementSubRegions< WellElementSubRegion >( regionNames,
+ [&]( localIndex const,
+ WellElementSubRegion & subRegion )
+ {
+ WellControls & wellControls = wellSolver.getWellControls( subRegion );
+ wellControls.setWellState( 1 );
+ wellSolver.initializeWell( domain, mesh, subRegion, time_n );
+ } );
+ } );
+
+ assembleFunction( jacobian.toViewConstSizes(), residual.toView() );
+ residual.move( hostMemorySpace, false );
+
+ // copy the analytical residual
+ array1d< real64 > residualOrig( residual );
+
+ // create the numerical jacobian
+ jacobian.move( hostMemorySpace );
+ CRSMatrix< real64, globalIndex > jacobianFD( jacobian );
+ jacobianFD.zero();
+
+
+ string const wellDofKey = dofManager.getKey( wellSolver.wellElementDofName() );
+
+ // at this point we start assembling the finite-difference block by block
+
+
+ /////////////////////////////////////////////////
+ // Step 2) Compute the terms in J_RW and J_WW //
+ /////////////////////////////////////////////////
+
+ // loop over the wells
+ if( 1 )
+ wellSolver.forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &,
+ MeshLevel & mesh,
+ string_array const & regionNames )
+ {
+ mesh.getElemManager().forElementSubRegions< WellElementSubRegion >( regionNames,
+ [&]( localIndex const,
+ WellElementSubRegion & subRegion )
+ {
+ // get the degrees of freedom, ghosting info and next well elem index
+ arrayView1d< globalIndex const > const & wellElemDofNumber =
+ subRegion.getReference< array1d< globalIndex > >( wellDofKey );
+
+ // get the primary variables on the well elements
+ arrayView1d< real64 > const & wellElemPressure =
+ subRegion.getField< fields::well::pressure >();
+ wellElemPressure.move( hostMemorySpace, false );
+
+ arrayView1d< real64 > const & wellElemTemperature =
+ subRegion.getField< fields::well::temperature >();
+ wellElemTemperature.move( hostMemorySpace, false );
+
+ arrayView2d< real64, compflow::USD_COMP > const & wellElemCompDens =
+ subRegion.getField< fields::well::globalCompDensity >();
+ wellElemCompDens.move( hostMemorySpace, false );
+
+ arrayView1d< real64 > const & connRate =
+ subRegion.getField< fields::well::mixtureConnectionRate >();
+ connRate.move( hostMemorySpace, false );
+
+ // a) compute all the derivatives wrt to the pressure in WELL elem iwelem
+ for( localIndex iwelem = 0; iwelem < subRegion.size(); ++iwelem )
+ {
+
+ real64 wellElemTotalDensity = 0.0;
+ for( localIndex ic = 0; ic < NC; ++ic )
+ {
+ wellElemTotalDensity += wellElemCompDens[iwelem][ic];
+ }
+
+ {
+ solver.resetStateToBeginningOfStep( domain );
+
+ // here is the perturbation in the pressure of the well element
+ real64 const dP = perturbParameter * ( wellElemPressure[iwelem] + perturbParameter );
+ wellElemPressure.move( hostMemorySpace, true );
+ wellElemPressure[iwelem] += dP;
+
+ // after perturbing, update the pressure-dependent quantities in the well
+ wellSolver.updateState( domain );
+
+ residual.zero();
+ jacobian.zero();
+ assembleFunction( jacobian.toViewConstSizes(), residual.toView() );
+
+ fillNumericalJacobian( residual.toViewConst(),
+ residualOrig.toViewConst(),
+ wellElemDofNumber[iwelem] + compositionalMultiphaseWellKernels::ColOffset::DPRES,
+ dP,
+ jacobianFD.toViewConstSizes() );
+ }
+
+ for( localIndex jc = 0; jc < NC; ++jc )
+ {
+ solver.resetStateToBeginningOfStep( domain );
+
+ real64 const dRho = perturbParameter * wellElemTotalDensity;
+ wellElemCompDens.move( hostMemorySpace, true );
+ wellElemCompDens[iwelem][jc] += dRho;
+
+ wellSolver.updateState( domain );
+
+ residual.zero();
+ jacobian.zero();
+ assembleFunction( jacobian.toViewConstSizes(), residual.toView() );
+
+ fillNumericalJacobian( residual.toViewConst(),
+ residualOrig.toViewConst(),
+ wellElemDofNumber[iwelem] + compositionalMultiphaseWellKernels::ColOffset::DCOMP + jc,
+ dRho,
+ jacobianFD.toViewConstSizes() );
+ }
+ {
+ solver.resetStateToBeginningOfStep( domain );
+ residual.zero();
+ jacobian.zero();
+ // here is the perturbation in the temperature of the well element
+ real64 const dT = perturbParameter * ( wellElemTemperature[iwelem] + perturbParameter );
+ wellElemTemperature.move( hostMemorySpace, true );
+ wellElemTemperature[iwelem] += dT;
+
+ // after perturbing, update the pressure-dependent quantities in the well
+ wellSolver.updateState( domain );
+
+
+ assembleFunction( jacobian.toViewConstSizes(), residual.toView() );
+ fillNumericalJacobian( residual.toViewConst(),
+ residualOrig.toViewConst(),
+ wellElemDofNumber[iwelem] + compositionalMultiphaseWellKernels::ColOffset::DCOMP + NC+1,
+ dT,
+ jacobianFD.toViewConstSizes() );
+
+ }
+ }
+
+
+ // b) compute all the derivatives wrt to the connection in WELL elem
+ // iwelem
+ for( localIndex iwelem = 0; iwelem < subRegion.size(); ++iwelem )
+ {
+ {
+ solver.resetStateToBeginningOfStep( domain );
+
+ // here is the perturbation in the rate of the well element
+ real64 const dRate = perturbParameter * ( connRate[iwelem] + perturbParameter );
+ connRate.move( hostMemorySpace, true );
+ connRate[iwelem] += dRate;
+
+ wellSolver.updateState( domain );
+
+ residual.zero();
+ jacobian.zero();
+ assembleFunction( jacobian.toViewConstSizes(), residual.toView() );
+
+ fillNumericalJacobian( residual.toViewConst(),
+ residualOrig.toViewConst(),
+ wellElemDofNumber[iwelem] + compositionalMultiphaseWellKernels::ColOffset::DCOMP + NC,
+ dRate,
+ jacobianFD.toViewConstSizes() );
+ }
+ }
+ } );
+ } );
+
+ // assemble the analytical jacobian
+ solver.resetStateToBeginningOfStep( domain );
+
+ residual.zero();
+ jacobian.zero();
+ assembleFunction( jacobian.toViewConstSizes(), residual.toView() );
+ printCompareLocalMatrices( jacobian.toViewConst(), jacobianFD.toViewConst(), testName );
+ compareLocalMatrices( jacobian.toViewConst(), jacobianFD.toViewConst(), relTol );
+}
+template< typename LAMBDA >
+void testNumericalJacobian( CompositionalMultiphaseReservoirAndWells< CompositionalMultiphaseBase > & solver,
+ DomainPartition & domain,
+ real64 const perturbParameter,
+ real64 const relTol, std::string const & testName,
+ LAMBDA && assembleFunction )
+{
+ CompositionalMultiphaseWell & wellSolver = *solver.wellSolver();
+ CompositionalMultiphaseFVM & flowSolver = dynamicCast< CompositionalMultiphaseFVM & >( *solver.reservoirSolver() );
+
+ localIndex const NC = flowSolver.numFluidComponents();
+
+ CRSMatrix< real64, globalIndex > const & jacobian = solver.getLocalMatrix();
+ array1d< real64 > residual( jacobian.numRows() );
+ DofManager const & dofManager = solver.getDofManager();
+
+ // assemble the analytical residual
+ solver.resetStateToBeginningOfStep( domain );
+
+ residual.zero();
+ jacobian.zero();
+
+ assembleFunction( jacobian.toViewConstSizes(), residual.toView() );
+ residual.move( hostMemorySpace, false );
+
+ // copy the analytical residual
+ array1d< real64 > residualOrig( residual );
+
+ // create the numerical jacobian
+ jacobian.move( hostMemorySpace );
+ CRSMatrix< real64, globalIndex > jacobianFD( jacobian );
+ jacobianFD.zero();
+
+ string const resDofKey = dofManager.getKey( wellSolver.resElementDofName() );
+ string const wellDofKey = dofManager.getKey( wellSolver.wellElementDofName() );
+
+ // at this point we start assembling the finite-difference block by block
+
+ ////////////////////////////////////////////////
+ // Step 1) Compute the terms in J_RR and J_WR //
+ ////////////////////////////////////////////////
+ if( 1 )
+ domain.forMeshBodies( [&] ( MeshBody & meshBody )
+ {
+ meshBody.forMeshLevels( [&] ( MeshLevel & mesh )
+ {
+ ElementRegionManager & elemManager = mesh.getElemManager();
+ for( localIndex er = 0; er < elemManager.numRegions(); ++er )
+ {
+ ElementRegionBase & elemRegion = elemManager.getRegion( er );
+ elemRegion.forElementSubRegionsIndex< CellElementSubRegion >( [&]( localIndex const, CellElementSubRegion & subRegion )
+ {
+ // get the degrees of freedom and ghosting information
+ arrayView1d< globalIndex const > const & dofNumber =
+ subRegion.getReference< array1d< globalIndex > >( resDofKey );
+
+ // get the primary variables on the reservoir elements
+ arrayView1d< real64 > const & pres =
+ subRegion.getField< fields::flow::pressure >();
+ pres.move( hostMemorySpace, false );
+
+ arrayView2d< real64, compflow::USD_COMP > const & compDens =
+ subRegion.getField< fields::flow::globalCompDensity >();
+ compDens.move( hostMemorySpace, false );
+
+ arrayView1d< real64 > const & temp =
+ subRegion.getField< fields::flow::temperature >();
+ temp.move( hostMemorySpace, false );
+
+ // a) compute all the derivatives wrt to the pressure in RESERVOIR elem ei
+ for( localIndex ei = 0; ei < subRegion.size(); ++ei )
+ {
+ real64 totalDensity = 0.0;
+ for( localIndex ic = 0; ic < NC; ++ic )
+ {
+ totalDensity += compDens[ei][ic];
+ }
+
+ {
+ solver.resetStateToBeginningOfStep( domain );
+
+ // here is the perturbation in the pressure of the element
+ real64 const dP = perturbParameter * (pres[ei] + perturbParameter);
+ pres.move( hostMemorySpace, true );
+ pres[ei] += dP;
+
+ // after perturbing, update the pressure-dependent quantities in the reservoir
+ flowSolver.forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &,
+ MeshLevel & mesh2,
+ string_array const & regionNames2 )
+ {
+ mesh2.getElemManager().forElementSubRegions( regionNames2,
+ [&]( localIndex const,
+ ElementSubRegionBase & subRegion2 )
+ {
+ flowSolver.updateFluidState( subRegion2 );
+ } );
+ } );
+
+ wellSolver.updateState( domain );
+
+ residual.zero();
+ jacobian.zero();
+ assembleFunction( jacobian.toViewConstSizes(), residual.toView() );
+
+ fillNumericalJacobian( residual.toViewConst(),
+ residualOrig.toViewConst(),
+ dofNumber[ei],
+ dP,
+ jacobianFD.toViewConstSizes() );
+ }
+
+ for( localIndex jc = 0; jc < NC; ++jc )
+ {
+ solver.resetStateToBeginningOfStep( domain );
+
+ real64 const dRho = perturbParameter * totalDensity;
+ compDens.move( hostMemorySpace, true );
+ compDens[ei][jc] += dRho;
+
+ flowSolver.forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &,
+ MeshLevel & mesh2,
+ string_array const & regionNames2 )
+ {
+ mesh2.getElemManager().forElementSubRegions( regionNames2,
+ [&]( localIndex const,
+ ElementSubRegionBase & subRegion2 )
+ {
+ flowSolver.updateFluidState( subRegion2 );
+ } );
+ } );
+ wellSolver.updateState( domain );
+ residual.zero();
+ jacobian.zero();
+ assembleFunction( jacobian.toViewConstSizes(), residual.toView() );
+
+ fillNumericalJacobian( residual.toViewConst(),
+ residualOrig.toViewConst(),
+ dofNumber[ei] + jc + 1,
+ dRho,
+ jacobianFD.toViewConstSizes() );
+ }
+ {
+ solver.resetStateToBeginningOfStep( domain );
+
+ // here is the perturbation in the pressure of the element
+ real64 const dTemp = perturbParameter * (temp[ei] + perturbParameter);
+ temp.move( hostMemorySpace, true );
+ temp[ei] += dTemp;
+
+ // after perturbing, update the pressure-dependent quantities in the reservoir
+ flowSolver.forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &,
+ MeshLevel & mesh2,
+ string_array const & regionNames2 )
+ {
+ mesh2.getElemManager().forElementSubRegions( regionNames2,
+ [&]( localIndex const,
+ ElementSubRegionBase & subRegion2 )
+ {
+ flowSolver.updateFluidState( subRegion2 );
+ } );
+ } );
+
+ wellSolver.updateState( domain );
+
+ residual.zero();
+ jacobian.zero();
+ assembleFunction( jacobian.toViewConstSizes(), residual.toView() );
+
+ fillNumericalJacobian( residual.toViewConst(),
+ residualOrig.toViewConst(),
+ dofNumber[ei]+NC+1,
+ dTemp,
+ jacobianFD.toViewConstSizes() );
+ }
+ }
+ } );
+ }
+ } );
+
+ } );
+
+ /////////////////////////////////////////////////
+ // Step 2) Compute the terms in J_RW and J_WW //
+ /////////////////////////////////////////////////
+
+ // loop over the wells
+ if( 1 )
+ wellSolver.forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &,
+ MeshLevel & mesh,
+ string_array const & regionNames )
+ {
+ mesh.getElemManager().forElementSubRegions< WellElementSubRegion >( regionNames,
+ [&]( localIndex const,
+ WellElementSubRegion & subRegion )
+ {
+ // get the degrees of freedom, ghosting info and next well elem index
+ arrayView1d< globalIndex const > const & wellElemDofNumber =
+ subRegion.getReference< array1d< globalIndex > >( wellDofKey );
+
+ // get the primary variables on the well elements
+ arrayView1d< real64 > const & wellElemPressure =
+ subRegion.getField< fields::well::pressure >();
+ wellElemPressure.move( hostMemorySpace, false );
+
+ arrayView1d< real64 > const & wellElemTemperature =
+ subRegion.getField< fields::well::temperature >();
+ wellElemTemperature.move( hostMemorySpace, false );
+
+ arrayView2d< real64, compflow::USD_COMP > const & wellElemCompDens =
+ subRegion.getField< fields::well::globalCompDensity >();
+ wellElemCompDens.move( hostMemorySpace, false );
+
+ arrayView1d< real64 > const & connRate =
+ subRegion.getField< fields::well::mixtureConnectionRate >();
+ connRate.move( hostMemorySpace, false );
+
+ // a) compute all the derivatives wrt to the pressure in WELL elem iwelem
+ for( localIndex iwelem = 0; iwelem < subRegion.size(); ++iwelem )
+ {
+
+ real64 wellElemTotalDensity = 0.0;
+ for( localIndex ic = 0; ic < NC; ++ic )
+ {
+ wellElemTotalDensity += wellElemCompDens[iwelem][ic];
+ }
+
+ {
+ solver.resetStateToBeginningOfStep( domain );
+
+ // here is the perturbation in the pressure of the well element
+ real64 const dP = perturbParameter * ( wellElemPressure[iwelem] + perturbParameter );
+ wellElemPressure.move( hostMemorySpace, true );
+ wellElemPressure[iwelem] += dP;
+
+ // after perturbing, update the pressure-dependent quantities in the well
+ wellSolver.updateState( domain );
+
+ residual.zero();
+ jacobian.zero();
+ assembleFunction( jacobian.toViewConstSizes(), residual.toView() );
+
+ fillNumericalJacobian( residual.toViewConst(),
+ residualOrig.toViewConst(),
+ wellElemDofNumber[iwelem] + compositionalMultiphaseWellKernels::ColOffset::DPRES,
+ dP,
+ jacobianFD.toViewConstSizes() );
+ }
+
+ for( localIndex jc = 0; jc < NC; ++jc )
+ {
+ solver.resetStateToBeginningOfStep( domain );
+
+ real64 const dRho = perturbParameter * wellElemTotalDensity;
+ wellElemCompDens.move( hostMemorySpace, true );
+ wellElemCompDens[iwelem][jc] += dRho;
+
+ wellSolver.updateState( domain );
+
+ residual.zero();
+ jacobian.zero();
+ assembleFunction( jacobian.toViewConstSizes(), residual.toView() );
+
+ fillNumericalJacobian( residual.toViewConst(),
+ residualOrig.toViewConst(),
+ wellElemDofNumber[iwelem] + compositionalMultiphaseWellKernels::ColOffset::DCOMP + jc,
+ dRho,
+ jacobianFD.toViewConstSizes() );
+ }
+ {
+ solver.resetStateToBeginningOfStep( domain );
+ residual.zero();
+ jacobian.zero();
+ // here is the perturbation in the temperature of the well element
+ real64 const dT = perturbParameter * ( wellElemTemperature[iwelem] + perturbParameter );
+ wellElemTemperature.move( hostMemorySpace, true );
+ wellElemTemperature[iwelem] += dT;
+
+ // after perturbing, update the pressure-dependent quantities in the well
+ wellSolver.updateState( domain );
+
+
+ assembleFunction( jacobian.toViewConstSizes(), residual.toView() );
+ fillNumericalJacobian( residual.toViewConst(),
+ residualOrig.toViewConst(),
+ wellElemDofNumber[iwelem] + compositionalMultiphaseWellKernels::ColOffset::DCOMP + NC+1,
+ dT,
+ jacobianFD.toViewConstSizes() );
+
+ }
+ }
+
+
+ // b) compute all the derivatives wrt to the connection in WELL elem
+ // iwelem
+ for( localIndex iwelem = 0; iwelem < subRegion.size(); ++iwelem )
+ {
+ {
+ solver.resetStateToBeginningOfStep( domain );
+
+ // here is the perturbation in the rate of the well element
+ real64 const dRate = perturbParameter * ( connRate[iwelem] + perturbParameter );
+ connRate.move( hostMemorySpace, true );
+ connRate[iwelem] += dRate;
+
+ wellSolver.updateState( domain );
+
+ residual.zero();
+ jacobian.zero();
+ assembleFunction( jacobian.toViewConstSizes(), residual.toView() );
+
+ fillNumericalJacobian( residual.toViewConst(),
+ residualOrig.toViewConst(),
+ wellElemDofNumber[iwelem] + compositionalMultiphaseWellKernels::ColOffset::DCOMP + NC,
+ dRate,
+ jacobianFD.toViewConstSizes() );
+ }
+ }
+ } );
+ } );
+
+ // assemble the analytical jacobian
+ solver.resetStateToBeginningOfStep( domain );
+
+ residual.zero();
+ jacobian.zero();
+ assembleFunction( jacobian.toViewConstSizes(), residual.toView() );
+ printCompareLocalMatrices( jacobian.toViewConst(), jacobianFD.toViewConst(), testName );
+ compareLocalMatrices( jacobian.toViewConst(), jacobianFD.toViewConst(), relTol );
+}
+
+class CompositionalMultiphaseReservoirSolverTest : public ::testing::Test
+{
+public:
+
+ CompositionalMultiphaseReservoirSolverTest():
+ state( std::make_unique< CommandLineOptions >( g_commandLineOptions ) )
+ {}
+
+protected:
+
+ void SetUp() override
+ {
+ setupProblemFromXML( state.getProblemManager(), xmlInput );
+ solver = &state.getProblemManager().getPhysicsSolverManager().getGroup< CompositionalMultiphaseReservoirAndWells< CompositionalMultiphaseBase > >( "reservoirSystem" );
+
+ DomainPartition & domain = state.getProblemManager().getDomainPartition();
+
+ solver->setupSystem( domain,
+ solver->getDofManager(),
+ solver->getLocalMatrix(),
+ solver->getSystemRhs(),
+ solver->getSystemSolution() );
+
+ solver->wellSolver()->setupSystem( domain,
+ solver->wellSolver()->getDofManager(),
+ solver->wellSolver()->getLocalMatrix(),
+ solver->wellSolver()->getSystemRhs(),
+ solver->wellSolver()->getSystemSolution() );
+
+ solver->implicitStepSetup( time, dt, domain );
+
+ }
+
+ static real64 constexpr time = 0.0;
+ static real64 constexpr dt = 1e4;
+ static real64 constexpr eps = std::numeric_limits< real64 >::epsilon();
+
+ GeosxState state;
+ CompositionalMultiphaseReservoirAndWells< CompositionalMultiphaseBase > * solver;
+};
+
+real64 constexpr CompositionalMultiphaseReservoirSolverTest::time;
+real64 constexpr CompositionalMultiphaseReservoirSolverTest::dt;
+real64 constexpr CompositionalMultiphaseReservoirSolverTest::eps;
+
+
+TEST_F( CompositionalMultiphaseReservoirSolverTest, jacobianNumericalCheck_Perforation )
+{
+ real64 const perturb = std::sqrt( eps );
+ real64 const tol = 1e-1; // 10% error margin
+
+ DomainPartition & domain = state.getProblemManager().getDomainPartition();
+
+ testWellEstimatorNumericalJacobian( *solver, domain, perturb, time, tol, "WellEstimator",
+ [&] ( CRSMatrixView< real64, globalIndex const > const & localMatrix,
+ arrayView1d< real64 > const & localRhs )
+ {
+
+ DofManager const & dofManager = solver->wellSolver()->getDofManager();
+ solver->wellSolver()->forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &,
+ MeshLevel & mesh,
+ string_array const & regionNames )
+ {
+ ElementRegionManager & elemManager = mesh.getElemManager();
+ elemManager.forElementSubRegions< WellElementSubRegion >( regionNames,
+ [&]( localIndex const,
+ WellElementSubRegion & subRegion )
+ {
+ // call assemble to fill the matrix and the rhs
+ solver->wellSolver()->assembleWellSystem( time,
+ dt,
+ elemManager,
+ subRegion,
+ dofManager,
+ localMatrix,
+ localRhs );
+
+// apply boundary conditions to system
+
+ solver->wellSolver()->applyWellBoundaryConditions( time,
+ dt,
+ elemManager,
+ subRegion,
+ dofManager,
+ localRhs,
+ localMatrix );
+ } );
+ } );
+
+
+
+ } );
+}
+
+
+int main( int argc, char * * argv )
+{
+ writeTableToFile( "co2flash.txt", co2flash );
+ writeTableToFile( "pvtliquid.txt", pvtLiquid );
+ writeTableToFile( "pvtgas.txt", pvtGas );
+ ::testing::InitGoogleTest( &argc, argv );
+ g_commandLineOptions = *geos::basicSetup( argc, argv );
+ int const result = RUN_ALL_TESTS();
+ geos::basicCleanup();
+ removeFile( "co2flash.txt" );
+ removeFile( "pvtliquid.txt" );
+ removeFile( "pvtgas.txt" );
+
+ return result;
+}
diff --git a/src/coreComponents/integrationTests/wellsTests/testThermalReservoirCompositionalMultiphaseMSWells.cpp b/src/coreComponents/integrationTests/wellsTests/testThermalReservoirCompositionalMultiphaseMSWells.cpp
index 98aa57a5c84..4ae6c8f47a6 100644
--- a/src/coreComponents/integrationTests/wellsTests/testThermalReservoirCompositionalMultiphaseMSWells.cpp
+++ b/src/coreComponents/integrationTests/wellsTests/testThermalReservoirCompositionalMultiphaseMSWells.cpp
@@ -96,28 +96,33 @@ char const * xmlInput =
targetRegions="{ region }">
-
-
-
+ surfaceTemperature="300.15">
+
+
+
+
@@ -328,7 +333,7 @@ void testNumericalJacobian( CompositionalMultiphaseReservoirAndWells< Compositio
real64 const relTol, bool diag_check,
LAMBDA && assembleFunction )
{
- CompositionalMultiphaseWell & wellSolver = *solver.wellSolver();
+ WellManager & wellSolver = *solver.wellSolver();
CompositionalMultiphaseFVM & flowSolver = dynamicCast< CompositionalMultiphaseFVM & >( *solver.reservoirSolver() );
localIndex const NC = flowSolver.numFluidComponents();
@@ -537,7 +542,7 @@ void testNumericalJacobian( CompositionalMultiphaseReservoirAndWells< Compositio
wellElemCompDens.move( hostMemorySpace, false );
arrayView1d< real64 > const & connRate =
- subRegion.getField< fields::well::mixtureConnectionRate >();
+ subRegion.getField< fields::well::connectionRate >();
connRate.move( hostMemorySpace, false );
// a) compute all the derivatives wrt to the pressure in WELL elem iwelem
@@ -699,6 +704,24 @@ class CompositionalMultiphaseReservoirSolverTest : public ::testing::Test
solver->getSystemSolution() );
solver->implicitStepSetup( time, dt, domain );
+
+ WellManager & wellSolver = *solver->wellSolver();
+ wellSolver.forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const & meshBodyName,
+ MeshLevel & meshLevel,
+ string_array const & regionNames )
+ {
+ GEOS_UNUSED_VAR( meshBodyName );
+ ElementRegionManager & elementRegionManager = meshLevel.getElemManager();
+ elementRegionManager.forElementRegions< WellElementRegion >( regionNames,
+ [&]( localIndex const,
+ WellElementRegion & region )
+ {
+ WellElementSubRegion & subRegion = region.getGroup( ElementRegionBase::viewKeyStruct::elementSubRegions() )
+ .getGroup< WellElementSubRegion >( region.getSubRegionName() );
+ WellControls & wellControls = wellSolver.getWellControls( subRegion );
+ wellControls.initializeWell( domain, meshLevel, subRegion, time );
+ } );
+ } );
}
static real64 constexpr time = 0.0;
@@ -784,7 +807,23 @@ TEST_F( CompositionalMultiphaseReservoirSolverTest, jacobianNumericalCheck_Accum
[&] ( CRSMatrixView< real64, globalIndex const > const & localMatrix,
arrayView1d< real64 > const & localRhs )
{
- solver->wellSolver()->assembleAccumulationTerms( time, dt, domain, solver->getDofManager(), localMatrix, localRhs );
+ WellManager & wellSolver = *solver->wellSolver();
+ wellSolver.forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const & meshBodyName,
+ MeshLevel & meshLevel,
+ string_array const & regionNames )
+ {
+ GEOS_UNUSED_VAR( meshBodyName );
+ ElementRegionManager & elementRegionManager = meshLevel.getElemManager();
+ elementRegionManager.forElementRegions< WellElementRegion >( regionNames,
+ [&]( localIndex const,
+ WellElementRegion & region )
+ {
+ WellElementSubRegion & subRegion = region.getGroup( ElementRegionBase::viewKeyStruct::elementSubRegions() )
+ .getGroup< WellElementSubRegion >( region.getSubRegionName() );
+ WellControls & wellControls = wellSolver.getWellControls( subRegion );
+ wellControls.assembleWellAccumulationTerms( time, dt, subRegion, solver->getDofManager(), localMatrix, localRhs );
+ } );
+ } );
} );
}
TEST_F( CompositionalMultiphaseReservoirSolverTest, jacobianNumericalCheck_PressureRel )
@@ -798,7 +837,23 @@ TEST_F( CompositionalMultiphaseReservoirSolverTest, jacobianNumericalCheck_Press
[&] ( CRSMatrixView< real64, globalIndex const > const & localMatrix,
arrayView1d< real64 > const & localRhs )
{
- solver->wellSolver()->assemblePressureRelations( time, dt, domain, solver->getDofManager(), localMatrix, localRhs );
+ WellManager & wellSolver = *solver->wellSolver();
+ wellSolver.forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const & meshBodyName,
+ MeshLevel & meshLevel,
+ string_array const & regionNames )
+ {
+ GEOS_UNUSED_VAR( meshBodyName );
+ ElementRegionManager & elementRegionManager = meshLevel.getElemManager();
+ elementRegionManager.forElementRegions< WellElementRegion >( regionNames,
+ [&]( localIndex const,
+ WellElementRegion & region )
+ {
+ WellElementSubRegion & subRegion = region.getGroup( ElementRegionBase::viewKeyStruct::elementSubRegions() )
+ .getGroup< WellElementSubRegion >( region.getSubRegionName() );
+ WellControls & wellControls = wellSolver.getWellControls( subRegion );
+ wellControls.assembleWellPressureRelations( time, dt, subRegion, solver->getDofManager(), localMatrix, localRhs );
+ } );
+ } );
} );
}
#endif
diff --git a/src/coreComponents/integrationTests/wellsTests/testThermalReservoirCompositionalMultiphaseSSWells.cpp b/src/coreComponents/integrationTests/wellsTests/testThermalReservoirCompositionalMultiphaseSSWells.cpp
index 76fa20d4cea..722be4fb77a 100644
--- a/src/coreComponents/integrationTests/wellsTests/testThermalReservoirCompositionalMultiphaseSSWells.cpp
+++ b/src/coreComponents/integrationTests/wellsTests/testThermalReservoirCompositionalMultiphaseSSWells.cpp
@@ -97,28 +97,33 @@ char const * xmlInput =
targetRegions="{ region }">
-
-
-
+ surfaceTemperature="300.15">
+
+
+
+
@@ -329,7 +334,7 @@ void testNumericalJacobian( CompositionalMultiphaseReservoirAndWells< Compositio
real64 const relTol, bool diag_check,
LAMBDA && assembleFunction )
{
- CompositionalMultiphaseWell & wellSolver = *solver.wellSolver();
+ WellManager & wellSolver = *solver.wellSolver();
CompositionalMultiphaseFVM & flowSolver = dynamicCast< CompositionalMultiphaseFVM & >( *solver.reservoirSolver() );
localIndex const NC = flowSolver.numFluidComponents();
@@ -539,7 +544,7 @@ void testNumericalJacobian( CompositionalMultiphaseReservoirAndWells< Compositio
wellElemCompDens.move( hostMemorySpace, false );
arrayView1d< real64 > const & connRate =
- subRegion.getField< fields::well::mixtureConnectionRate >();
+ subRegion.getField< fields::well::connectionRate >();
connRate.move( hostMemorySpace, false );
// a) compute all the derivatives wrt to the pressure in WELL elem iwelem
@@ -688,6 +693,23 @@ class CompositionalMultiphaseReservoirSolverTest : public ::testing::Test
solver->getSystemSolution() );
solver->implicitStepSetup( time, dt, domain );
+ WellManager & wellSolver = *solver->wellSolver();
+ wellSolver.forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const & meshBodyName,
+ MeshLevel & meshLevel,
+ string_array const & regionNames )
+ {
+ GEOS_UNUSED_VAR( meshBodyName );
+ ElementRegionManager & elementRegionManager = meshLevel.getElemManager();
+ elementRegionManager.forElementRegions< WellElementRegion >( regionNames,
+ [&]( localIndex const,
+ WellElementRegion & region )
+ {
+ WellElementSubRegion & subRegion = region.getGroup( ElementRegionBase::viewKeyStruct::elementSubRegions() )
+ .getGroup< WellElementSubRegion >( region.getSubRegionName() );
+ WellControls & wellControls = wellSolver.getWellControls( subRegion );
+ wellControls.initializeWell( domain, meshLevel, subRegion, time );
+ } );
+ } );
}
static real64 constexpr time = 0.0;
@@ -773,7 +795,23 @@ TEST_F( CompositionalMultiphaseReservoirSolverTest, jacobianNumericalCheck_Accum
[&] ( CRSMatrixView< real64, globalIndex const > const & localMatrix,
arrayView1d< real64 > const & localRhs )
{
- solver->wellSolver()->assembleAccumulationTerms( time, dt, domain, solver->getDofManager(), localMatrix, localRhs );
+ WellManager & wellSolver = *solver->wellSolver();
+ wellSolver.forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const & meshBodyName,
+ MeshLevel & meshLevel,
+ string_array const & regionNames )
+ {
+ GEOS_UNUSED_VAR( meshBodyName );
+ ElementRegionManager & elementRegionManager = meshLevel.getElemManager();
+ elementRegionManager.forElementRegions< WellElementRegion >( regionNames,
+ [&]( localIndex const,
+ WellElementRegion & region )
+ {
+ WellElementSubRegion & subRegion = region.getGroup( ElementRegionBase::viewKeyStruct::elementSubRegions() )
+ .getGroup< WellElementSubRegion >( region.getSubRegionName() );
+ WellControls & wellControls = wellSolver.getWellControls( subRegion );
+ wellControls.assembleWellAccumulationTerms( time, dt, subRegion, solver->getDofManager(), localMatrix, localRhs );
+ } );
+ } );
} );
}
TEST_F( CompositionalMultiphaseReservoirSolverTest, jacobianNumericalCheck_PressureRel )
@@ -787,7 +825,23 @@ TEST_F( CompositionalMultiphaseReservoirSolverTest, jacobianNumericalCheck_Press
[&] ( CRSMatrixView< real64, globalIndex const > const & localMatrix,
arrayView1d< real64 > const & localRhs )
{
- solver->wellSolver()->assemblePressureRelations( time, dt, domain, solver->getDofManager(), localMatrix, localRhs );
+ WellManager & wellSolver = *solver->wellSolver();
+ wellSolver.forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const & meshBodyName,
+ MeshLevel & meshLevel,
+ string_array const & regionNames )
+ {
+ GEOS_UNUSED_VAR( meshBodyName );
+ ElementRegionManager & elementRegionManager = meshLevel.getElemManager();
+ elementRegionManager.forElementRegions< WellElementRegion >( regionNames,
+ [&]( localIndex const,
+ WellElementRegion & region )
+ {
+ WellElementSubRegion & subRegion = region.getGroup( ElementRegionBase::viewKeyStruct::elementSubRegions() )
+ .getGroup< WellElementSubRegion >( region.getSubRegionName() );
+ WellControls & wellControls = wellSolver.getWellControls( subRegion );
+ wellControls.assembleWellPressureRelations( time, dt, subRegion, solver->getDofManager(), localMatrix, localRhs );
+ } );
+ } );
} );
}
#endif
diff --git a/src/coreComponents/linearAlgebra/utilities/ComponentMask.hpp b/src/coreComponents/linearAlgebra/utilities/ComponentMask.hpp
index a27d1a37a64..42eab7c568e 100644
--- a/src/coreComponents/linearAlgebra/utilities/ComponentMask.hpp
+++ b/src/coreComponents/linearAlgebra/utilities/ComponentMask.hpp
@@ -92,10 +92,10 @@ class ComponentMask
private:
/// Number of bits in mask storage
- static constexpr int NUM_BITS = internal::roundToNextPowerOfTwo( MAX_COMP );
+ static constexpr int NUM_BITS = geos::internal::roundToNextPowerOfTwo( MAX_COMP );
/// Type used to represent the bit mask
- using mask_t = typename internal::ComponentMaskType< NUM_BITS >::type;
+ using mask_t = typename geos::internal::ComponentMaskType< NUM_BITS >::type;
public:
diff --git a/src/coreComponents/mesh/WellElementRegion.hpp b/src/coreComponents/mesh/WellElementRegion.hpp
index ee3e8be547b..d93a564aa9b 100644
--- a/src/coreComponents/mesh/WellElementRegion.hpp
+++ b/src/coreComponents/mesh/WellElementRegion.hpp
@@ -106,6 +106,12 @@ class WellElementRegion : public ElementRegionBase
*/
void setWellControlsName( string const & name ) { m_wellControlsName = name; }
+ /**
+ * @return name the name of the WellControls object
+ */
+ string const & getWellControlsName() const { return m_wellControlsName; }
+
+
/**
* @brief Get the name of the subRegion.
* @return the name of the subRegion object
diff --git a/src/coreComponents/mesh/WellElementSubRegion.hpp b/src/coreComponents/mesh/WellElementSubRegion.hpp
index 26089a42838..4be7cf48e3b 100644
--- a/src/coreComponents/mesh/WellElementSubRegion.hpp
+++ b/src/coreComponents/mesh/WellElementSubRegion.hpp
@@ -223,6 +223,14 @@ class WellElementSubRegion : public ElementSubRegionBase
m_topRank = rank;
}
+ /**
+ * @brief Get for the MPI rank that owns this well (i.e. the top segment).
+ * @return the rank that owns the top well element
+ */
+ int getTopRank() const
+ {
+ return m_topRank;
+ }
/**
* @brief Check if well is owned by current rank
* @return true if the well is owned by current rank, false otherwise
diff --git a/src/coreComponents/physicsSolvers/SolverStatistics.hpp b/src/coreComponents/physicsSolvers/SolverStatistics.hpp
index 13b2d5c0a90..0cc8ee74282 100644
--- a/src/coreComponents/physicsSolvers/SolverStatistics.hpp
+++ b/src/coreComponents/physicsSolvers/SolverStatistics.hpp
@@ -184,6 +184,11 @@ class IterationsStatistics : public dataRepository::Group
void closeFile()
{ if( m_CSVOutputOpened ) { m_logStream.close(); m_CSVOutputOpened = false; } }
+ /**
+ * @brief Get the number of time steps
+ */
+ integer const & getNumTimeSteps() const { return m_numTimeSteps; }
+
protected:
/// Number of time steps
diff --git a/src/coreComponents/physicsSolvers/fluidFlow/CMakeLists.txt b/src/coreComponents/physicsSolvers/fluidFlow/CMakeLists.txt
index 6eb55e9775a..4d9c7671fff 100644
--- a/src/coreComponents/physicsSolvers/fluidFlow/CMakeLists.txt
+++ b/src/coreComponents/physicsSolvers/fluidFlow/CMakeLists.txt
@@ -129,11 +129,23 @@ set( fluidFlowSolvers_headers
wells/SinglePhaseWellFields.hpp
wells/WellConstants.hpp
wells/WellControls.hpp
- wells/WellSolverBase.hpp
- wells/WellSolverBaseFields.hpp
+ wells/WellConstraintsBase.hpp
+ wells/WellInjectionConstraint.hpp
+ wells/WellProductionConstraint.hpp
+ wells/WellBHPConstraints.hpp
+ wells/WellWHPConstraint.hpp
+ wells/WellVolumeRateConstraint.hpp
+ wells/WellMassRateConstraint.hpp
+ wells/WellPhaseVolumeRateConstraint.hpp
+ wells/WellLiquidRateConstraint.hpp
+ wells/PipeFlowTableFunction.hpp
+ wells/WellManager.hpp
+ wells/WellNewtonSolver.hpp
wells/LogLevelsInfo.hpp
wells/kernels/SinglePhaseWellKernels.hpp
wells/kernels/CompositionalMultiphaseWellKernels.hpp
+ wells/kernels/CompositionalMultiphaseWellConstraintKernels.hpp
+ wells/kernels/SinglePhaseWellConstraintKernels.hpp
proppantTransport/ProppantTransport.hpp
proppantTransport/ProppantTransportFields.hpp
proppantTransport/ProppantTransportKernels.hpp )
@@ -165,7 +177,18 @@ set( fluidFlowSolvers_sources
wells/SinglePhaseWell.cpp
wells/kernels/SinglePhaseWellKernels.cpp
wells/WellControls.cpp
- wells/WellSolverBase.cpp
+ wells/WellConstraintsBase.cpp
+ wells/WellInjectionConstraint.cpp
+ wells/WellProductionConstraint.cpp
+ wells/WellBHPConstraints.cpp
+ wells/WellWHPConstraint.cpp
+ wells/WellVolumeRateConstraint.cpp
+ wells/WellMassRateConstraint.cpp
+ wells/WellPhaseVolumeRateConstraint.cpp
+ wells/WellLiquidRateConstraint.cpp
+ wells/PipeFlowTableFunction.cpp
+ wells/WellManager.cpp
+ wells/WellNewtonSolver.cpp
proppantTransport/ProppantTransport.cpp
proppantTransport/ProppantTransportKernels.cpp )
diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/CompositionalMultiphaseWell.cpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/CompositionalMultiphaseWell.cpp
index 9f18e9741da..11e8c056637 100644
--- a/src/coreComponents/physicsSolvers/fluidFlow/wells/CompositionalMultiphaseWell.cpp
+++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/CompositionalMultiphaseWell.cpp
@@ -36,7 +36,7 @@
#include "physicsSolvers/fluidFlow/wells/WellSolverBaseFields.hpp"
#include "physicsSolvers/fluidFlow/wells/WellFields.hpp"
#include "physicsSolvers/fluidFlow/wells/CompositionalMultiphaseWellFields.hpp"
-#include "physicsSolvers/fluidFlow/wells/WellControls.hpp"
+
#include "physicsSolvers/fluidFlow/wells/kernels/CompositionalMultiphaseWellKernels.hpp"
#include "physicsSolvers/fluidFlow/wells/kernels/ThermalCompositionalMultiphaseWellKernels.hpp"
#include "physicsSolvers/fluidFlow/wells/kernels/PerforationFluxKernels.hpp"
@@ -50,6 +50,17 @@
#include "physicsSolvers/fluidFlow/kernels/compositional/FluidUpdateKernel.hpp"
#include "physicsSolvers/fluidFlow/CompositionalMultiphaseStatistics.hpp"
+#include "physicsSolvers/fluidFlow/wells/WellBHPConstraints.hpp"
+#include "physicsSolvers/fluidFlow/wells/WellWHPConstraint.hpp"
+#include "physicsSolvers/fluidFlow/wells/PipeFlowTableFunction.hpp"
+#include "physicsSolvers/fluidFlow/wells/WellVolumeRateConstraint.hpp"
+#include "physicsSolvers/fluidFlow/wells/WellPhaseVolumeRateConstraint.hpp"
+#include "physicsSolvers/fluidFlow/wells/WellMassRateConstraint.hpp"
+#include "physicsSolvers/fluidFlow/wells/WellLiquidRateConstraint.hpp"
+#include "physicsSolvers/fluidFlow/wells/kernels/CompositionalMultiphaseWellConstraintKernels.hpp"
+#include "physicsSolvers/multiphysics/CoupledReservoirAndWellKernels.hpp"
+
+#include "events/EventManager.hpp"
#if defined( __INTEL_COMPILER )
#pragma GCC optimize "O0"
#endif
@@ -64,15 +75,14 @@ using namespace fields;
CompositionalMultiphaseWell::CompositionalMultiphaseWell( const string & name,
Group * const parent )
:
- WellSolverBase( name, parent ),
+ WellControls( name, parent ),
m_useMass( false ),
m_useTotalMassEquation( 1 ),
m_maxCompFracChange( 1.0 ),
m_maxRelativePresChange( 0.2 ),
m_maxAbsolutePresChange( -1 ), // disabled by default
m_minScalingFactor( 0.01 ),
- m_allowCompDensChopping( 1 ),
- m_targetPhaseIndex( -1 )
+ m_allowCompDensChopping( 1 )
{
this->registerWrapper( viewKeyStruct::useMassFlagString(), &m_useMass ).
setApplyDefaultValue( 0 ).
@@ -123,7 +133,7 @@ CompositionalMultiphaseWell::CompositionalMultiphaseWell( const string & name,
void CompositionalMultiphaseWell::postInputInitialization()
{
- WellSolverBase::postInputInitialization();
+ WellControls::postInputInitialization();
GEOS_ERROR_IF_GT_MSG( m_maxCompFracChange, 1.0,
"The maximum absolute change in component fraction must smaller or equal to 1.0",
@@ -135,29 +145,24 @@ void CompositionalMultiphaseWell::postInputInitialization()
GEOS_ERROR_IF_LE_MSG( m_maxRelativeCompDensChange, 0.0,
"The maximum relative change in component density must be larger than 0.0",
getWrapperDataContext( viewKeyStruct::maxRelativeCompDensChangeString() ) );
+
+}
+void CompositionalMultiphaseWell::setConstitutiveNames( ElementSubRegionBase & subRegion ) const
+{
+ setConstitutiveName< MultiFluidBase >( subRegion, viewKeyStruct::fluidNamesString(), "multiphase fluid" );
}
-void CompositionalMultiphaseWell::registerDataOnMesh( Group & meshBodies )
+void CompositionalMultiphaseWell::registerWellDataOnMesh( WellElementSubRegion & subRegion )
{
- WellSolverBase::registerDataOnMesh( meshBodies );
+
DomainPartition const & domain = this->getGroupByPath< DomainPartition >( "/Problem/domain" );
ConstitutiveManager const & cm = domain.getConstitutiveManager();
-
- forDiscretizationOnMeshTargets( meshBodies, [&]( string const &,
- MeshLevel & mesh,
- string_array const & regionNames )
+ setConstitutiveNames ( subRegion );
+ if( m_referenceFluidModelName.empty() )
{
- mesh.getElemManager().forElementSubRegions( regionNames,
- [&]( localIndex const,
- ElementSubRegionBase & subRegion )
- {
- if( m_referenceFluidModelName.empty() )
- {
- m_referenceFluidModelName = getConstitutiveName< MultiFluidBase >( subRegion );
- }
- } );
- } );
+ m_referenceFluidModelName = getConstitutiveName< MultiFluidBase >( subRegion );
+ }
// 1. Set key dimensions of the problem
// Empty check needed to avoid errors when running in schema generation mode.
@@ -172,131 +177,120 @@ void CompositionalMultiphaseWell::registerDataOnMesh( Group & meshBodies )
// 1 pressure + NC compositions + temp if thermal
m_numDofPerResElement = isThermal() ? m_numComponents + 2 : m_numComponents + 1;
- // loop over the wells
- forDiscretizationOnMeshTargets( meshBodies, [&] ( string const &,
- MeshLevel & mesh,
- string_array const & regionNames )
- {
- ElementRegionManager & elemManager = mesh.getElemManager();
+ WellControls::registerWellDataOnMesh( subRegion );
- elemManager.forElementSubRegions< WellElementSubRegion >( regionNames,
- [&]( localIndex const,
- WellElementSubRegion & subRegion )
- {
- string const & fluidName = getConstitutiveName< MultiFluidBase >( subRegion );
- MultiFluidBase const & fluid = subRegion.getConstitutiveModel< MultiFluidBase >( fluidName );
- // 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< well::globalCompDensity >( getName() ).
- reference().resizeDimension< 1 >( m_numComponents );
- subRegion.registerField< well::globalCompDensity_n >( getName() ).
- reference().resizeDimension< 1 >( m_numComponents );
+ string const & fluidName = getConstitutiveName< MultiFluidBase >( subRegion );
+ MultiFluidBase const & fluid = subRegion.getConstitutiveModel< MultiFluidBase >( fluidName );
- subRegion.registerField< well::mixtureConnectionRate >( getName() );
- subRegion.registerField< well::mixtureConnectionRate_n >( getName() );
+ // 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< well::pressure >( getName() );
+ subRegion.registerField< well::pressure_n >( getName() );
- subRegion.registerField< well::globalCompFraction >( getName() ).
- setDimLabels( 1, fluid.componentNames() ).
- reference().resizeDimension< 1 >( m_numComponents );
- subRegion.registerField< well::dGlobalCompFraction_dGlobalCompDensity >( getName() ).
- reference().resizeDimension< 1, 2 >( m_numComponents, m_numComponents );
+ subRegion.registerField< well::temperature >( getName() );
+ if( isThermal() )
+ {
+ subRegion.registerField< well::temperature_n >( getName() );
+ }
- subRegion.registerField< well::phaseVolumeFraction >( getName() ).
- setDimLabels( 1, fluid.phaseNames() ).
- reference().resizeDimension< 1 >( m_numPhases );
- subRegion.registerField< well::dPhaseVolumeFraction >( getName() ).
- reference().resizeDimension< 1, 2 >( m_numPhases, m_numComponents + 2 ); // dP, dT, dC
+ subRegion.registerField< well::gravityCoefficient >( getName() );
- subRegion.registerField< well::totalMassDensity >( getName() );
- subRegion.registerField< well::dTotalMassDensity >( getName() ).
- reference().resizeDimension< 1 >( m_numComponents +2 ); // dP, dT, dC
- subRegion.registerField< well::phaseVolumeFraction_n >( getName() ).
- reference().resizeDimension< 1 >( m_numPhases );
+ subRegion.registerField< well::globalCompDensity >( getName() ).
+ reference().resizeDimension< 1 >( m_numComponents );
+ subRegion.registerField< well::globalCompDensity_n >( getName() ).
+ reference().resizeDimension< 1 >( m_numComponents );
- subRegion.registerField< well::pressureScalingFactor >( getName() );
- subRegion.registerField< well::temperatureScalingFactor >( getName() );
- subRegion.registerField< well::globalCompDensityScalingFactor >( getName() );
+ subRegion.registerField< well::connectionRate >( getName() );
+ subRegion.registerField< well::connectionRate_n >( getName() );
- PerforationData & perforationData = *subRegion.getPerforationData();
- perforationData.registerField< well::compPerforationRate >( getName() ).
- reference().resizeDimension< 1 >( m_numComponents );
+ subRegion.registerField< well::globalCompFraction >( getName() ).
+ setDimLabels( 1, fluid.componentNames() ).
+ reference().resizeDimension< 1 >( m_numComponents );
+ subRegion.registerField< well::dGlobalCompFraction_dGlobalCompDensity >( getName() ).
+ reference().resizeDimension< 1, 2 >( m_numComponents, m_numComponents );
- perforationData.registerField< well::dCompPerforationRate >( getName() ).
- reference().resizeDimension< 1, 2, 3 >( 2, m_numComponents, m_numComponents+ 2 );
- if( fluid.isThermal() )
- {
- perforationData.registerField< well::energyPerforationFlux >( getName() );
- perforationData.registerField< well::dEnergyPerforationFlux >( getName() ).
- reference().resizeDimension< 1, 2 >( 2, m_numComponents+2 );
- }
+ subRegion.registerField< well::phaseVolumeFraction >( getName() ).
+ setDimLabels( 1, fluid.phaseNames() ).
+ reference().resizeDimension< 1 >( m_numPhases );
+ subRegion.registerField< well::dPhaseVolumeFraction >( getName() ).
+ reference().resizeDimension< 1, 2 >( m_numPhases, m_numComponents + 2 ); // dP, dT, dC
- WellControls & wellControls = getWellControls( subRegion );
- wellControls.registerWrapper< real64 >( viewKeyStruct::currentBHPString() );
+ subRegion.registerField< well::totalMassDensity >( getName() );
+ subRegion.registerField< well::dTotalMassDensity >( getName() ).
+ reference().resizeDimension< 1 >( m_numComponents +2 ); // dP, dT, dC
- wellControls.registerWrapper< array1d< real64 > >( viewKeyStruct::dCurrentBHPString() ).
- setSizedFromParent( 0 ).
- reference().resizeDimension< 0 >( m_numComponents + 2 ); // dP, dT, dC
+ subRegion.registerField< well::phaseVolumeFraction_n >( getName() ).
+ reference().resizeDimension< 1 >( m_numPhases );
- wellControls.registerWrapper< array1d< real64 > >( viewKeyStruct::currentPhaseVolRateString() ).
- setSizedFromParent( 0 ).
- reference().resizeDimension< 0 >( m_numPhases );
+ subRegion.registerField< well::pressureScalingFactor >( getName() );
+ subRegion.registerField< well::temperatureScalingFactor >( getName() );
+ subRegion.registerField< well::globalCompDensityScalingFactor >( getName() );
- wellControls.registerWrapper< array2d< real64 > >( viewKeyStruct::dCurrentPhaseVolRateString() ).
- setSizedFromParent( 0 ).
- reference().resizeDimension< 0, 1 >( m_numPhases, m_numComponents + 3 ); // dP, dT, dC, dQ
+ PerforationData & perforationData = *subRegion.getPerforationData();
- wellControls.registerWrapper< real64 >( viewKeyStruct::massDensityString() );
+ perforationData.registerField< well::gravityCoefficient >( getName() );
+ perforationData.registerField< well::compPerforationRate >( getName() ).
+ reference().resizeDimension< 1 >( m_numComponents );
- wellControls.registerWrapper< real64 >( viewKeyStruct::currentTotalVolRateString() );
- wellControls.registerWrapper< array1d< real64 > >( viewKeyStruct::dCurrentTotalVolRateString() ).
- setSizedFromParent( 0 ).
- reference().resizeDimension< 0 >( m_numComponents + 3 ); // dP, dT, dC dQ
+ perforationData.registerField< well::dCompPerforationRate >( getName() ).
+ reference().resizeDimension< 1, 2, 3 >( 2, m_numComponents, m_numComponents+ 2 );
+ if( fluid.isThermal() )
+ {
+ perforationData.registerField< well::energyPerforationFlux >( getName() );
+ perforationData.registerField< well::dEnergyPerforationFlux >( getName() ).
+ reference().resizeDimension< 1, 2 >( 2, m_numComponents+2 );
+ }
- wellControls.registerWrapper< real64 >( viewKeyStruct::massDensityString() );
+ registerWrapper< real64 >( viewKeyStruct::currentBHPString() );
- wellControls.registerWrapper< real64 >( viewKeyStruct::currentMassRateString() );
+ registerWrapper< array1d< real64 > >( viewKeyStruct::currentPhaseVolRateString() ).
+ setSizedFromParent( 0 ).
+ reference().resizeDimension< 0 >( m_numPhases );
- // write rates output header
- // the rank that owns the reference well element is responsible
- if( m_writeCSV > 0 && subRegion.isLocallyOwned() )
- {
- string const fileName = GEOS_FMT( "{}/{}.csv", m_ratesOutputDir, wellControls.getName() );
- string const massUnit = m_useMass ? "kg" : "mol";
- integer const useSurfaceConditions = wellControls.useSurfaceConditions();
- string const conditionKey = useSurfaceConditions ? "surface" : "reservoir";
- string const unitKey = useSurfaceConditions ? "s" : "r";
- integer const numPhase = m_numPhases;
- integer const numComp = m_numComponents;
- // format: time,bhp,total_rate,total_vol_rate,phase0_vol_rate,phase1_vol_rate,...
- makeDirsForPath( m_ratesOutputDir );
- GEOS_LOG( GEOS_FMT( "{}: Rates CSV generated at {}", getName(), fileName ) );
- std::ofstream outputFile( fileName );
- outputFile << "Time [s],dt[s],BHP [Pa],Total rate [" << massUnit << "/s],Total " << conditionKey << " volumetric rate [" << unitKey << "m3/s]";
- for( integer ip = 0; ip < numPhase; ++ip )
- {
- outputFile << ",Phase" << ip << " " << conditionKey << " volumetric rate [" << unitKey << "m3/s]";
- }
- for( integer ic = 0; ic < numComp; ++ic )
- {
- outputFile << ",Component" << ic << " rate [" << massUnit << "/s]";
- }
- outputFile << std::endl;
- outputFile.close();
- }
- } );
- } );
+ registerWrapper< real64 >( viewKeyStruct::massDensityString() );
+
+ registerWrapper< real64 >( viewKeyStruct::currentTotalVolRateString() );
+
+ registerWrapper< real64 >( viewKeyStruct::massDensityString() );
+
+ registerWrapper< real64 >( viewKeyStruct::currentMassRateString() );
+
+ // write rates output header
+ // the rank that owns the reference well element is responsible
+ if( m_writeCSV > 0 && subRegion.isLocallyOwned() )
+ {
+ string const fileName = GEOS_FMT( "{}/{}.csv", m_ratesOutputDir, getName() );
+ string const massUnit = m_useMass ? "kg" : "mol";
+ integer const useSurfaceConditions = this->useSurfaceConditions();
+ string const conditionKey = useSurfaceConditions ? "surface" : "reservoir";
+ string const unitKey = useSurfaceConditions ? "s" : "r";
+ integer const numPhase = m_numPhases;
+ integer const numComp = m_numComponents;
+ // format: time,bhp,total_rate,total_vol_rate,phase0_vol_rate,phase1_vol_rate,...
+ makeDirsForPath( m_ratesOutputDir );
+ GEOS_LOG( GEOS_FMT( "{}: Rates CSV generated at {}", getName(), fileName ) );
+ std::ofstream outputFile( fileName );
+ outputFile << "Time [s],dt[s],BHP [Pa],Total rate [" << massUnit << "/s],Total " << conditionKey << " volumetric rate [" << unitKey << "m3/s]";
+ for( integer ip = 0; ip < numPhase; ++ip )
+ {
+ outputFile << ",Phase" << ip << " " << conditionKey << " volumetric rate [" << unitKey << "m3/s]";
+ }
+ for( integer ic = 0; ic < numComp; ++ic )
+ {
+ outputFile << ",Component" << ic << " rate [" << massUnit << "/s]";
+ }
+ outputFile << std::endl;
+ outputFile.close();
+ }
-}
-void CompositionalMultiphaseWell::setConstitutiveNames( ElementSubRegionBase & subRegion ) const
-{
- setConstitutiveName< MultiFluidBase >( subRegion, viewKeyStruct::fluidNamesString(), "multiphase fluid" );
}
+
namespace
{
@@ -340,15 +334,17 @@ void compareMulticomponentModels( MODEL1_TYPE const & lhs, MODEL2_TYPE const & r
* @brief Checks if the WellControls parameters are within the fluid tables ranges
* @param fluid the fluid to check
*/
-void CompositionalMultiphaseWell::validateWellControlsForFluid( WellControls const & wellControls,
- MultiFluidBase const & fluid ) const
+void CompositionalMultiphaseWell::validateFluidModel(
+ constitutive::MultiFluidBase const & fluid, constitutive::MultiFluidBase const & referenceFluid ) const
{
- if( wellControls.useSurfaceConditions() )
+ compareMultiphaseModels( fluid, referenceFluid );
+ compareMulticomponentModels( fluid, referenceFluid );
+ if( useSurfaceConditions() )
{
try
{
- real64 const & surfaceTemp = wellControls.getSurfaceTemperature();
- real64 const & surfacePres = wellControls.getSurfacePressure();
+ real64 const & surfaceTemp = getSurfaceTemperature();
+ real64 const & surfacePres = getSurfacePressure();
fluid.checkTablesParameters( surfacePres, surfaceTemp );
} catch( SimulationError const & ex )
{
@@ -361,95 +357,33 @@ void CompositionalMultiphaseWell::validateWellControlsForFluid( WellControls con
}
}
-void CompositionalMultiphaseWell::validateConstitutiveModels( DomainPartition const & domain ) const
-{
- GEOS_MARK_FUNCTION;
-
- ConstitutiveManager const & cm = domain.getConstitutiveManager();
- CompositionalMultiphaseBase const & flowSolver = getParent().getGroup< CompositionalMultiphaseBase >( getFlowSolverName() );
- string const referenceFluidName = flowSolver.referenceFluidModelName();
- MultiFluidBase const & referenceFluid = cm.getConstitutiveRelation< MultiFluidBase >( m_referenceFluidModelName );
-
- forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &,
- MeshLevel const & mesh,
- string_array const & regionNames )
- {
-
- mesh.getElemManager().forElementSubRegions< WellElementSubRegion >( regionNames, [&]( localIndex const,
- WellElementSubRegion const & subRegion )
- {
- string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString() );
- MultiFluidBase const & fluid = getConstitutiveModel< MultiFluidBase >( subRegion, fluidName );
- compareMultiphaseModels( fluid, referenceFluid );
- compareMulticomponentModels( fluid, referenceFluid );
-
- WellControls const & wellControls = getWellControls( subRegion );
- validateWellControlsForFluid( wellControls, fluid );
- } );
-
- } );
-}
-
-void CompositionalMultiphaseWell::validateInjectionStreams( WellElementSubRegion const & subRegion ) const
-{
- WellControls const & wellControls = getWellControls( subRegion );
-
- // check well injection stream for injectors
- if( wellControls.isInjector())
- {
- arrayView1d< real64 const > const & injectionStream = wellControls.getInjectionStream();
-
- integer const streamSize = injectionStream.size();
- GEOS_THROW_IF( ( streamSize == 0 ),
- "Injection stream not specified for well ",
- InputError, wellControls.getDataContext() );
- GEOS_THROW_IF( ( streamSize != m_numComponents ),
- "Injection stream for well should have " << m_numComponents << " components.",
- InputError, wellControls.getDataContext() );
-
- real64 compFracSum = 0;
- for( integer ic = 0; ic < m_numComponents; ++ic )
- {
- real64 const compFrac = injectionStream[ic];
- GEOS_THROW_IF( ( compFrac < 0.0 ) || ( compFrac > 1.0 ),
- "Invalid injection stream for well ",
- InputError, wellControls.getDataContext() );
- compFracSum += compFrac;
- }
- GEOS_THROW_IF( ( compFracSum < 1.0 - std::numeric_limits< real64 >::epsilon() ) ||
- ( compFracSum > 1.0 + std::numeric_limits< real64 >::epsilon() ),
- "Invalid injection stream for well ",
- InputError, wellControls.getDataContext() );
- }
-}
-
void CompositionalMultiphaseWell::validateWellConstraints( real64 const & time_n,
real64 const & GEOS_UNUSED_PARAM( dt ),
WellElementSubRegion const & subRegion )
{
- WellControls & wellControls = getWellControls( subRegion );
- if( !wellControls.useSurfaceConditions() )
+ GEOS_UNUSED_VAR( time_n );
+
+ if( !useSurfaceConditions() )
{
- bool const useSeg =wellControls.referenceReservoirRegion().empty();
+ bool const useSeg = getReferenceReservoirRegion().empty();
GEOS_WARNING_IF( useSeg,
"WellControls " <( getFlowSolverName() );
+ string const regionName = getReferenceReservoirRegion();
+ CompositionalMultiphaseBase const & flowSolver = getParent().getParent().getGroup< CompositionalMultiphaseBase >( getFlowSolverName() );
string_array const & targetRegionsNames = flowSolver.getTargetRegionNames();
auto const pos = std::find( targetRegionsNames.begin(), targetRegionsNames.end(), regionName );
GEOS_ERROR_IF( pos == targetRegionsNames.end(),
- GEOS_FMT( "Region {} is not a target of the reservoir solver and cannot be used for referenceReservoirRegion in WellControl {}.",
- regionName, wellControls.getName() ),
- getDataContext() );
+ GEOS_FMT( "{}: Region {} is not a target of the reservoir solver and cannot be used for referenceReservoirRegion in WellControl {}.",
+ getDataContext(), regionName, getName() ) );
}
@@ -457,125 +391,73 @@ void CompositionalMultiphaseWell::validateWellConstraints( real64 const & time_n
string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString());
MultiFluidBase const & fluid = subRegion.getConstitutiveModel< MultiFluidBase >( fluidName );
- WellControls::Control const currentControl = wellControls.getControl();
- real64 const & targetTotalRate = wellControls.getTargetTotalRate( time_n );
- real64 const & targetPhaseRate = wellControls.getTargetPhaseRate( time_n );
- real64 const & targetMassRate = wellControls.getTargetMassRate( time_n );
-
- GEOS_THROW_IF( wellControls.isInjector() && currentControl == WellControls::Control::PHASEVOLRATE,
- "Phase rate control is not available for injectors",
- InputError, wellControls.getDataContext() );
- GEOS_THROW_IF( wellControls.isProducer() && currentControl == WellControls::Control::TOTALVOLRATE,
- "Total rate control is not available for producers",
- InputError, wellControls.getDataContext() );
-
- GEOS_THROW_IF( wellControls.isInjector() && targetTotalRate < 0.0,
- "Target total rate cannot be negative for injectors",
- InputError, wellControls.getDataContext() );
- GEOS_THROW_IF( wellControls.isInjector() && !isZero( targetPhaseRate ),
- "Target phase rate cannot be used for injectors",
- InputError, wellControls.getDataContext() );
- GEOS_THROW_IF( wellControls.isProducer() && !isZero( targetTotalRate ),
- "Target total rate cannot be used for producers",
- InputError, wellControls.getDataContext() );
- GEOS_THROW_IF( wellControls.isProducer() && !isZero( targetMassRate ),
- "Target mass rate cannot be used for producers",
- InputError, wellControls.getDataContext() );
- GEOS_THROW_IF( !m_useMass && !isZero( targetMassRate ),
- "Target mass rate cannot with useMass=0",
- InputError, wellControls.getDataContext() );
-
- // The user always provides positive rates, but these rates are later multiplied by -1 internally for producers
- GEOS_THROW_IF( wellControls.isProducer() && targetPhaseRate > 0.0,
- "Target phase rate cannot be negative for producers",
- InputError, wellControls.getDataContext() );
- GEOS_THROW_IF( wellControls.isProducer() && !isZero( targetTotalRate ),
- "Target total rate cannot be used for producers",
- InputError, wellControls.getDataContext() );
-
- // Find target phase index for phase rate constraint
- for( integer ip = 0; ip < fluid.numFluidPhases(); ++ip )
- {
- if( fluid.phaseNames()[ip] == wellControls.getTargetPhaseName() )
- {
- m_targetPhaseIndex = ip;
- }
- }
- GEOS_THROW_IF( wellControls.isProducer() && m_targetPhaseIndex == -1,
- "Phase " << wellControls.getTargetPhaseName() << " not found",
- InputError, wellControls.getDataContext() );
+ forSubGroups< InjectionConstraint< PhaseVolumeRateConstraint >, ProductionConstraint< PhaseVolumeRateConstraint > >( [&]( auto & constraint )
+ {
+ constraint.validatePhaseType( fluid );
+ } );
+
+
+
}
void CompositionalMultiphaseWell::initializePostSubGroups()
{
- WellSolverBase::initializePostSubGroups();
+ WellControls::initializePostSubGroups();
- DomainPartition & domain = this->getGroupByPath< DomainPartition >( "/Problem/domain" );
-
- validateConstitutiveModels( domain );
-
- forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &,
- MeshLevel & mesh,
- string_array const & regionNames )
- {
- mesh.getElemManager().forElementSubRegions< WellElementSubRegion >( regionNames, [&]( localIndex const,
- WellElementSubRegion & subRegion )
- {
- validateInjectionStreams( subRegion );
- } );
- } );
}
void CompositionalMultiphaseWell::initializePostInitialConditionsPreSubGroups()
{
- WellSolverBase::initializePostInitialConditionsPreSubGroups();
- createSeparator();
+ WellControls::initializePostInitialConditionsPreSubGroups();
+
}
-void CompositionalMultiphaseWell::postRestartInitialization()
+void CompositionalMultiphaseWell::initializeWellPostInitialConditionsPreSubGroups( WellElementSubRegion & subRegion )
{
- DomainPartition & domain = this->getGroupByPath< DomainPartition >( "/Problem/domain" );
- forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &,
- MeshLevel & mesh,
- string_array const & regionNames )
- {
- // loop over the wells
- mesh.getElemManager().forElementSubRegions< WellElementSubRegion >( regionNames, [&]( localIndex const,
- WellElementSubRegion & subRegion )
- {
- // setup fluid separator
- WellControls & wellControls = getWellControls( subRegion );
- constitutive::MultiFluidBase & fluidSeparator = wellControls.getMultiFluidSeparator();
- fluidSeparator.allocateConstitutiveData( wellControls, 1 );
- fluidSeparator.resize( 1 );
- } );
- } );
-}
+ // set gravity coefficient
+ setGravCoef( subRegion, getParent().getParent().getReference< R1Tensor >( PhysicsSolverManager::viewKeyStruct::gravityVectorString() ));
+
+ // setup fluid model
+ string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString() );
+ constitutive::MultiFluidBase & fluid = subRegion.getConstitutiveModel< constitutive::MultiFluidBase >( fluidName );
+ fluid.setMassFlag( m_useMass );
+ createSeparator( subRegion );
+
+ // Wellhead pressure constraints
+ if( hasMinimumWHPConstraint())
+ {
+ createMinBHPConstraintForWHP();
+ createMaxLiquidConstraintForWHP();
+ }
-void CompositionalMultiphaseWell::createSeparator()
+}
+void CompositionalMultiphaseWell::postRestartInitialization( )
{
- DomainPartition & domain = this->getGroupByPath< DomainPartition >( "/Problem/domain" );
- forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &,
- MeshLevel & mesh,
- string_array const & regionNames )
+ // setup fluid separator
+ constitutive::MultiFluidBase & fluidSeparator = getMultiFluidSeparator();
+ fluidSeparator.allocateConstitutiveData( *this, 1 );
+ fluidSeparator.resize( 1 );
+ // Wellhead pressure constraints
+ if( hasMinimumWHPConstraint())
{
+ createMinBHPConstraintForWHP();
+ createMaxLiquidConstraintForWHP();
+ }
+}
+
+void CompositionalMultiphaseWell::createSeparator( WellElementSubRegion & subRegion )
+{
+
+ string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString() );
+ MultiFluidBase & fluid = subRegion.getConstitutiveModel< MultiFluidBase >( fluidName );
+ fluid.setMassFlag( m_useMass );
+ // setup fluid separator
+ string const fluidSeparatorName = getName() + "Separator";
+ std::unique_ptr< constitutive::ConstitutiveBase > fluidSeparatorPtr = fluid.deliverClone( fluidSeparatorName, this );
+ fluidSeparatorPtr->allocateConstitutiveData( *this, 1 );
+ fluidSeparatorPtr->resize( 1 );
+ setFluidSeparator( std::move( fluidSeparatorPtr ));
- // loop over the wells
- mesh.getElemManager().forElementSubRegions< WellElementSubRegion >( regionNames, [&]( localIndex const,
- WellElementSubRegion & subRegion )
- {
- string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString() );
- MultiFluidBase & fluid = subRegion.getConstitutiveModel< MultiFluidBase >( fluidName );
- fluid.setMassFlag( m_useMass );
- // setup fluid separator
- WellControls & wellControls = getWellControls( subRegion );
- string const fluidSeparatorName = wellControls.getName() + "Separator";
- std::unique_ptr< constitutive::ConstitutiveBase > fluidSeparatorPtr = fluid.deliverClone( fluidSeparatorName, &wellControls );
- fluidSeparatorPtr->allocateConstitutiveData( wellControls, 1 );
- fluidSeparatorPtr->resize( 1 );
- wellControls.setFluidSeparator( std::move( fluidSeparatorPtr ));
- } );
- } );
}
void CompositionalMultiphaseWell::updateGlobalComponentFraction( WellElementSubRegion & subRegion ) const
{
@@ -597,70 +479,41 @@ void CompositionalMultiphaseWell::updateBHPForConstraint( WellElementSubRegion &
{
return;
}
- using Deriv = constitutive::multifluid::DerivativeOffset;
- integer const numComp = m_numComponents;
localIndex const iwelemRef = subRegion.getTopWellElementIndex();
- string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString() );
- MultiFluidBase const & fluid = subRegion.getConstitutiveModel< MultiFluidBase >( fluidName );
- integer const isThermal = fluid.isThermal();
// subRegion data
-
arrayView1d< real64 const > const & pres = subRegion.getField< well::pressure >();
-
arrayView1d< real64 > const & totalMassDens = subRegion.getField< well::totalMassDensity >();
- arrayView2d< real64, compflow::USD_FLUID_DC > const & dTotalMassDens = subRegion.getField< well::dTotalMassDensity >();
-
arrayView1d< real64 const > const wellElemGravCoef = subRegion.getField< well::gravityCoefficient >();
// control data
- WellControls & wellControls = getWellControls( subRegion );
- string const wellControlsName = wellControls.getName();
- real64 const & refGravCoef = wellControls.getReferenceGravityCoef();
+ string const wellControlsName = getName();
+ real64 const & refGravCoef = getReferenceGravityCoef();
real64 & currentBHP =
- wellControls.getReference< real64 >( CompositionalMultiphaseWell::viewKeyStruct::currentBHPString() );
- arrayView1d< real64 > const & dCurrentBHP =
- wellControls.getReference< array1d< real64 > >( CompositionalMultiphaseWell::viewKeyStruct::dCurrentBHPString() );
-
- geos::internal::kernelLaunchSelectorCompThermSwitch( numComp, isThermal, [&] ( auto NC, auto ISTHERMAL )
+ getReference< real64 >( CompositionalMultiphaseWell::viewKeyStruct::currentBHPString() );
+ // bring everything back to host, capture the scalars by reference
+ forAll< serialPolicy >( 1, [ pres,
+ totalMassDens,
+ wellElemGravCoef,
+ ¤tBHP,
+ &iwelemRef,
+ &refGravCoef] ( localIndex const )
{
- integer constexpr IS_THERMAL = ISTHERMAL();
- GEOS_UNUSED_VAR( NC );
- // bring everything back to host, capture the scalars by reference
- forAll< serialPolicy >( 1, [&numComp,
- pres,
- totalMassDens,
- dTotalMassDens,
- wellElemGravCoef,
- ¤tBHP,
- &dCurrentBHP,
- &iwelemRef,
- &refGravCoef] ( localIndex const )
- {
- real64 const diffGravCoef = refGravCoef - wellElemGravCoef[iwelemRef];
- currentBHP = pres[iwelemRef] + totalMassDens[iwelemRef] * diffGravCoef;
- dCurrentBHP[Deriv::dP] = 1 + dTotalMassDens[iwelemRef][Deriv::dP] * diffGravCoef;
- for( integer ic = 0; ic < numComp; ++ic )
- {
- dCurrentBHP[Deriv::dC+ic] = dTotalMassDens[iwelemRef][Deriv::dC+ic] * diffGravCoef;
- }
- if constexpr ( IS_THERMAL )
- {
- dCurrentBHP[Deriv::dT] = dTotalMassDens[iwelemRef][Deriv::dT] * diffGravCoef;
- }
- } );
+ real64 const diffGravCoef = refGravCoef - wellElemGravCoef[iwelemRef];
+ currentBHP = pres[iwelemRef] + totalMassDens[iwelemRef] * diffGravCoef;
} );
+
GEOS_LOG_LEVEL_BY_RANK( logInfo::BoundaryConditions,
GEOS_FMT( "{}: BHP (at the specified reference elevation) = {} Pa",
wellControlsName, currentBHP ) );
}
-void CompositionalMultiphaseWell::updateVolRatesForConstraint( ElementRegionManager const & elemManager, WellElementSubRegion const & subRegion )
+void CompositionalMultiphaseWell::updateVolRatesForConstraint( WellElementSubRegion const & subRegion )
{
GEOS_MARK_FUNCTION;
@@ -670,252 +523,201 @@ void CompositionalMultiphaseWell::updateVolRatesForConstraint( ElementRegionMana
return;
}
- integer constexpr maxNumComp = constitutive::MultiFluidBase::MAX_NUM_COMPONENTS;
- integer const numComp = m_numComponents;
+
integer const numPhase = m_numPhases;
localIndex const iwelemRef = subRegion.getTopWellElementIndex();
- WellControls & wellControls = getWellControls( subRegion );
-
// subRegion data
- arrayView1d< real64 const > const & pres = subRegion.getField< well::pressure >();
- arrayView1d< real64 const > const & temp = subRegion.getField< well::temperature >();
- arrayView1d< real64 const > const & connRate = subRegion.getField< well::mixtureConnectionRate >();
-
- arrayView2d< real64 const, compflow::USD_COMP > const & compFrac = subRegion.getField< well::globalCompFraction >();
- arrayView3d< real64 const, compflow::USD_COMP_DC > const & dCompFrac_dCompDens = subRegion.getField< well::dGlobalCompFraction_dGlobalCompDensity >();
+ arrayView1d< real64 const > const & connRate = subRegion.getField< well::connectionRate >();
// fluid data
- constitutive::MultiFluidBase & fluidSeparator = wellControls.getMultiFluidSeparator();
- integer isThermal = fluidSeparator.isThermal();
- arrayView3d< real64 const, constitutive::multifluid::USD_PHASE > const & phaseFrac = fluidSeparator.phaseFraction();
- arrayView4d< real64 const, constitutive::multifluid::USD_PHASE_DC > const & dPhaseFrac = fluidSeparator.dPhaseFraction();
+ constitutive::MultiFluidBase & fluidSeparator = getMultiFluidSeparator();
+ arrayView3d< real64 const, constitutive::multifluid::USD_PHASE > const & phaseFrac = fluidSeparator.phaseFraction();
arrayView2d< real64 const, constitutive::multifluid::USD_FLUID > const & totalDens = fluidSeparator.totalDensity();
- arrayView3d< real64 const, constitutive::multifluid::USD_FLUID_DC > const & dTotalDens = fluidSeparator.dTotalDensity();
-
arrayView3d< real64 const, constitutive::multifluid::USD_PHASE > const & phaseDens = fluidSeparator.phaseDensity();
- arrayView4d< real64 const, constitutive::multifluid::USD_PHASE_DC > const & dPhaseDens = fluidSeparator.dPhaseDensity();
// control data
- string const wellControlsName = wellControls.getName();
- bool const logSurfaceCondition = isLogLevelActive< logInfo::BoundaryConditions >( wellControls.getLogLevel());
- string const massUnit = m_useMass ? "kg" : "mol";
+ string const wellControlsName = getName();
- integer const useSurfaceConditions = wellControls.useSurfaceConditions();
- real64 flashPressure;
- real64 flashTemperature;
- if( useSurfaceConditions )
- {
- // use surface conditions
- flashPressure = wellControls.getSurfacePressure();
- flashTemperature = wellControls.getSurfaceTemperature();
- }
- else
+ arrayView1d< real64 > const & currentPhaseVolRate =
+ getReference< array1d< real64 > >( viewKeyStruct::currentPhaseVolRateString() );
+
+ real64 & currentTotalVolRate =
+ getReference< real64 >( viewKeyStruct::currentTotalVolRateString() );
+
+ real64 & currentMassRate =
+ getReference< real64 >( viewKeyStruct::currentMassRateString() );
+
+ real64 & massDensity =
+ getReference< real64 >( viewKeyStruct::massDensityString() );
+
+ // bring everything back to host, capture the scalars by reference
+ forAll< serialPolicy >( 1, [&numPhase,
+ connRate,
+ totalDens,
+ phaseDens,
+ phaseFrac,
+ ¤tTotalVolRate,
+ currentPhaseVolRate,
+ ¤tMassRate,
+ &iwelemRef,
+ &massDensity] ( localIndex const )
{
- if( !wellControls.referenceReservoirRegion().empty() )
- {
- ElementRegionBase const & region = elemManager.getRegion( wellControls.referenceReservoirRegion());
- GEOS_ERROR_IF ( !region.hasWrapper( CompositionalMultiphaseStatistics::regionStatisticsName() ),
- GEOS_FMT( "WellControl {} referenceReservoirRegion field requires CompositionalMultiphaseStatistics to be configured for region {} ",
- wellControls.getName(), wellControls.referenceReservoirRegion() ),
- getDataContext() );
+ // Step 1: update the total volume rate
- CompositionalMultiphaseStatistics::RegionStatistics const & stats = region.getReference< CompositionalMultiphaseStatistics::RegionStatistics >(
- CompositionalMultiphaseStatistics::regionStatisticsName() );
- wellControls.setRegionAveragePressure( stats.averagePressure );
- wellControls.setRegionAverageTemperature( stats.averageTemperature );
- GEOS_ERROR_IF( stats.averagePressure <= 0.0,
- GEOS_FMT( "No region average quantities computed. WellControl {} referenceReservoirRegion field requires CompositionalMultiphaseStatistics to be configured for region {} ",
- wellControls.getName(), wellControls.referenceReservoirRegion() ),
- getDataContext());
- }
- // If flashPressure is not set by region the value is defaulted to -1 and indicates to use top segment conditions
- flashPressure = wellControls.getRegionAveragePressure();
- if( flashPressure < 0.0 )
- {
- // region name not set, use segment conditions
- flashPressure = pres[iwelemRef];
- flashTemperature = temp[iwelemRef];
- }
- else
+ real64 const currentTotalRate = connRate[iwelemRef];
+ // Assumes useMass is true
+ currentMassRate = currentTotalRate;
+ // Step 1.1: compute the inverse of the total density and derivatives
+ massDensity = totalDens[iwelemRef][0];
+ real64 const totalDensInv = 1.0 / totalDens[iwelemRef][0];
+
+ // Step 1.2: divide the total mass/molar rate by the total density to get the total volumetric rate
+ currentTotalVolRate = currentTotalRate * totalDensInv;
+
+ // Step 2: update the phase volume rate
+ for( integer ip = 0; ip < numPhase; ++ip )
{
- // use reservoir region averages
- flashTemperature = wellControls.getRegionAverageTemperature();
+ // Step 2.1: compute the inverse of the (phase density * phase fraction) and derivatives
+
+ // skip the rest of this function if phase ip is absent
+ bool const phaseExists = (phaseFrac[iwelemRef][0][ip] > 0);
+ if( !phaseExists )
+ {
+ continue;
+ }
+
+ real64 const phaseDensInv = 1.0 / phaseDens[iwelemRef][0][ip];
+ real64 const phaseFracTimesPhaseDensInv = phaseFrac[iwelemRef][0][ip] * phaseDensInv;
+
+ // Step 2.2: divide the total mass/molar rate by the (phase density * phase fraction) to get the phase volumetric rate
+ currentPhaseVolRate[ip] = currentTotalRate * phaseFracTimesPhaseDensInv;
}
+ } );
+
+}
+
+void CompositionalMultiphaseWell::calculateReferenceElementRates( WellElementSubRegion & subRegion )
+{
+ GEOS_MARK_FUNCTION;
+
+ // the rank that owns the reference well element is responsible for the calculations below.
+ if( !subRegion.isLocallyOwned() )
+ {
+ return;
}
+
+ integer const numPhase = m_numPhases;
+ localIndex const iwelemRef = subRegion.getTopWellElementIndex();
+
+
+
+ // subRegion data
+ arrayView1d< real64 const > const & connRate = subRegion.getField< fields::well::connectionRate >();
+
+ // fluid data
+ constitutive::MultiFluidBase & fluidSeparator = getMultiFluidSeparator();
+ arrayView3d< real64 const, constitutive::multifluid::USD_PHASE > const & phaseFrac = fluidSeparator.phaseFraction();
+ arrayView2d< real64 const, constitutive::multifluid::USD_FLUID > const & totalDens = fluidSeparator.totalDensity();
+ arrayView3d< real64 const, constitutive::multifluid::USD_PHASE > const & phaseDens = fluidSeparator.phaseDensity();
+
+ // control data
+ string const wellControlsName = getName();
+ bool const logSurfaceCondition = isLogLevelActive< logInfo::BoundaryConditions >( getLogLevel());
+ string const massUnit = m_useMass ? "kg" : "mol";
+
+ integer const useSurfCond = useSurfaceConditions();
+
arrayView1d< real64 > const & currentPhaseVolRate =
- wellControls.getReference< array1d< real64 > >( CompositionalMultiphaseWell::viewKeyStruct::currentPhaseVolRateString() );
- arrayView2d< real64 > const & dCurrentPhaseVolRate =
- wellControls.getReference< array2d< real64 > >( CompositionalMultiphaseWell::viewKeyStruct::dCurrentPhaseVolRateString() );
+ getReference< array1d< real64 > >( CompositionalMultiphaseWell::viewKeyStruct::currentPhaseVolRateString() );
real64 & currentTotalVolRate =
- wellControls.getReference< real64 >( CompositionalMultiphaseWell::viewKeyStruct::currentTotalVolRateString() );
+ getReference< real64 >( CompositionalMultiphaseWell::viewKeyStruct::currentTotalVolRateString() );
real64 & currentMassRate =
- wellControls.getReference< real64 >( CompositionalMultiphaseWell::viewKeyStruct::currentMassRateString() );
-
- arrayView1d< real64 > const & dCurrentTotalVolRate =
- wellControls.getReference< array1d< real64 > >( CompositionalMultiphaseWell::viewKeyStruct::dCurrentTotalVolRateString() );
+ getReference< real64 >( CompositionalMultiphaseWell::viewKeyStruct::currentMassRateString() );
real64 & massDensity =
- wellControls.getReference< real64 >( CompositionalMultiphaseWell::viewKeyStruct::massDensityString() );
+ getReference< real64 >( CompositionalMultiphaseWell::viewKeyStruct::massDensityString() );
+
constitutive::constitutiveUpdatePassThru( fluidSeparator, [&] ( auto & castedFluidSeparator )
{
// typename TYPEOFREF( castedFluid ) ::KernelWrapper fluidWrapper = castedFluid.createKernelWrapper();
typename TYPEOFREF( castedFluidSeparator ) ::KernelWrapper fluidSeparatorWrapper = castedFluidSeparator.createKernelWrapper();
- geos::internal::kernelLaunchSelectorCompThermSwitch( numComp, isThermal, [&] ( auto NC, auto ISTHERMAL )
+
+ // bring everything back to host, capture the scalars by reference
+ forAll< serialPolicy >( 1, [fluidSeparatorWrapper,
+ &numPhase,
+ connRate,
+ totalDens,
+ phaseDens,
+ phaseFrac,
+ logSurfaceCondition,
+ &useSurfCond,
+ ¤tTotalVolRate,
+ currentPhaseVolRate,
+ ¤tMassRate,
+ &iwelemRef,
+ &wellControlsName,
+ &massUnit,
+ &massDensity] ( localIndex const )
{
- integer constexpr NUM_COMP = NC();
- integer constexpr IS_THERMAL = ISTHERMAL();
- using COFFSET_WJ = compositionalMultiphaseWellKernels::ColOffset_WellJac< NUM_COMP, IS_THERMAL >;
- // bring everything back to host, capture the scalars by reference
- forAll< serialPolicy >( 1, [&numComp,
- &numPhase,
- fluidSeparatorWrapper,
- pres,
- temp,
- compFrac,
- dCompFrac_dCompDens,
- connRate,
- totalDens,
- dTotalDens,
- phaseDens,
- dPhaseDens,
- phaseFrac,
- dPhaseFrac,
- logSurfaceCondition,
- &useSurfaceConditions,
- &flashPressure,
- &flashTemperature,
- ¤tTotalVolRate,
- dCurrentTotalVolRate,
- currentPhaseVolRate,
- dCurrentPhaseVolRate,
- ¤tMassRate,
- &iwelemRef,
- &wellControlsName,
- &massUnit,
- &massDensity] ( localIndex const )
- {
- GEOS_UNUSED_VAR( massUnit );
- using Deriv = constitutive::multifluid::DerivativeOffset;
- stackArray1d< real64, maxNumComp > work( numComp );
- // Step 1: evaluate the phase and total density in the reference element
-
- // We need to evaluate the density as follows:
- // - Surface conditions: using the surface pressure provided by the user
- // - Segment conditions: using the pressure in the top element
- // - Reservoir conditions: using the average region pressure
- if( useSurfaceConditions )
- {
- // we need to compute the surface density
- fluidSeparatorWrapper.update( iwelemRef, 0, flashPressure, flashTemperature, compFrac[iwelemRef] );
- if( logSurfaceCondition )
- {
- GEOS_LOG_RANK( GEOS_FMT( "{}: surface density computed with P_surface = {} Pa and T_surface = {} K",
- wellControlsName, flashPressure, flashTemperature ) );
- }
-#ifdef GEOS_USE_HIP
- GEOS_UNUSED_VAR( wellControlsName );
-#endif
+ GEOS_UNUSED_VAR( massUnit );
- }
- else
- {
- fluidSeparatorWrapper.update( iwelemRef, 0, flashPressure, flashTemperature, compFrac[iwelemRef] );
- }
- // Step 2: update the total volume rate
- real64 const currentTotalRate = connRate[iwelemRef];
- // Assumes useMass is true
- currentMassRate = currentTotalRate;
- // Step 2.1: compute the inverse of the total density and derivatives
- massDensity = totalDens[iwelemRef][0];
- real64 const totalDensInv = 1.0 / totalDens[iwelemRef][0];
+ // Step 2: update the total volume rate
- stackArray1d< real64, maxNumComp > dTotalDensInv_dCompDens( numComp );
- for( integer ic = 0; ic < numComp; ++ic )
- {
- dTotalDensInv_dCompDens[ic] = -dTotalDens[iwelemRef][0][Deriv::dC+ic] * totalDensInv * totalDensInv;
- }
- applyChainRuleInPlace( numComp, dCompFrac_dCompDens[iwelemRef], dTotalDensInv_dCompDens, work.data() );
-
- // Step 2.2: divide the total mass/molar rate by the total density to get the total volumetric rate
- currentTotalVolRate = currentTotalRate * totalDensInv;
- // Compute derivatives dP dT
- real64 const dTotalDensInv_dPres = -dTotalDens[iwelemRef][0][Deriv::dP] * totalDensInv * totalDensInv;
- dCurrentTotalVolRate[COFFSET_WJ::dP] = ( useSurfaceConditions == 0 ) * currentTotalRate * dTotalDensInv_dPres;
- if constexpr ( IS_THERMAL )
- {
- dCurrentTotalVolRate[COFFSET_WJ::dT] = ( useSurfaceConditions == 0 ) * currentTotalRate * -dTotalDens[iwelemRef][0][Deriv::dT] * totalDensInv * totalDensInv;
- }
+ real64 const currentTotalRate = connRate[iwelemRef];
+ // Assumes useMass is true
+ currentMassRate = currentTotalRate;
+ // Step 2.1: compute the inverse of the total density
+ massDensity = totalDens[iwelemRef][0];
+ real64 const totalDensInv = 1.0 / totalDens[iwelemRef][0];
- if( logSurfaceCondition && useSurfaceConditions )
- {
- GEOS_LOG_RANK( GEOS_FMT( "{}: total fluid density at surface conditions = {} {}/sm3, total rate = {} {}/s, total surface volumetric rate = {} sm3/s",
- wellControlsName, totalDens[iwelemRef][0], massUnit, connRate[iwelemRef], massUnit, currentTotalVolRate ) );
- }
- dCurrentTotalVolRate[COFFSET_WJ::dQ] = totalDensInv;
- for( integer ic = 0; ic < numComp; ++ic )
- {
- dCurrentTotalVolRate[COFFSET_WJ::dC+ic] = currentTotalRate * dTotalDensInv_dCompDens[ic];
- }
+ // Step 2.2: divide the total mass/molar rate by the total density to get the total volumetric rate
+ currentTotalVolRate = currentTotalRate * totalDensInv;
- // Step 3: update the phase volume rate
- for( integer ip = 0; ip < numPhase; ++ip )
- {
- // Step 3.1: compute the inverse of the (phase density * phase fraction) and derivatives
+ if( logSurfaceCondition && useSurfCond )
+ {
+ GEOS_LOG_RANK( GEOS_FMT( "{}: total fluid density at surface conditions = {} {}/sm3, total rate = {} {}/s, total surface volumetric rate = {} sm3/s",
+ wellControlsName, totalDens[iwelemRef][0], massUnit, connRate[iwelemRef], massUnit, currentTotalVolRate ) );
+ }
- // skip the rest of this function if phase ip is absent
- bool const phaseExists = (phaseFrac[iwelemRef][0][ip] > 0);
- if( !phaseExists )
- {
- continue;
- }
+ // Step 3: update the phase volume rate
+ for( integer ip = 0; ip < numPhase; ++ip )
+ {
- real64 const phaseDensInv = 1.0 / phaseDens[iwelemRef][0][ip];
- real64 const phaseFracTimesPhaseDensInv = phaseFrac[iwelemRef][0][ip] * phaseDensInv;
- real64 const dPhaseFracTimesPhaseDensInv_dPres = dPhaseFrac[iwelemRef][0][ip][Deriv::dP] * phaseDensInv
- - dPhaseDens[iwelemRef][0][ip][Deriv::dP] * phaseFracTimesPhaseDensInv * phaseDensInv;
+ // Step 3.1: compute the inverse of the (phase density * phase fraction)
+ // skip the rest of this function if phase ip is absent
+ bool const phaseExists = (phaseFrac[iwelemRef][0][ip] > 0);
+ if( !phaseExists )
+ {
+ continue;
+ }
- // Step 3.2: divide the total mass/molar rate by the (phase density * phase fraction) to get the phase volumetric rate
- currentPhaseVolRate[ip] = currentTotalRate * phaseFracTimesPhaseDensInv;
- dCurrentPhaseVolRate[ip][COFFSET_WJ::dP] = ( useSurfaceConditions == 0 ) * currentTotalRate * dPhaseFracTimesPhaseDensInv_dPres;
- dCurrentPhaseVolRate[ip][COFFSET_WJ::dQ] = phaseFracTimesPhaseDensInv;
- if constexpr (IS_THERMAL )
- {
- real64 const dPhaseFracTimesPhaseDensInv_dTemp = dPhaseFrac[iwelemRef][0][ip][Deriv::dT] * phaseDensInv
- - dPhaseDens[iwelemRef][0][ip][Deriv::dT] * phaseFracTimesPhaseDensInv * phaseDensInv;
- dCurrentPhaseVolRate[ip][COFFSET_WJ::dT] = ( useSurfaceConditions == 0 ) * currentTotalRate * dPhaseFracTimesPhaseDensInv_dTemp;
- }
+ real64 const phaseDensInv = 1.0 / phaseDens[iwelemRef][0][ip];
+ real64 const phaseFracTimesPhaseDensInv = phaseFrac[iwelemRef][0][ip] * phaseDensInv;
- for( integer ic = 0; ic < numComp; ++ic )
- {
- dCurrentPhaseVolRate[ip][COFFSET_WJ::dC+ic] = -phaseFracTimesPhaseDensInv * dPhaseDens[iwelemRef][0][ip][Deriv::dC+ic] * phaseDensInv;
- dCurrentPhaseVolRate[ip][COFFSET_WJ::dC+ic] += dPhaseFrac[iwelemRef][0][ip][Deriv::dC+ic] * phaseDensInv;
- dCurrentPhaseVolRate[ip][COFFSET_WJ::dC+ic] *= currentTotalRate;
- }
- applyChainRuleInPlace( numComp, dCompFrac_dCompDens[iwelemRef], &dCurrentPhaseVolRate[ip][COFFSET_WJ::dC], work.data() );
+ // Step 3.2: divide the total mass/molar rate by the (phase density * phase fraction) to get the phase volumetric rate
+ currentPhaseVolRate[ip] = currentTotalRate * phaseFracTimesPhaseDensInv;
- if( logSurfaceCondition && useSurfaceConditions )
- {
- GEOS_LOG_RANK( GEOS_FMT( "{}: density of phase {} at surface conditions = {} {}/sm3, phase surface volumetric rate = {} sm3/s",
- wellControlsName, ip, phaseDens[iwelemRef][0][ip], massUnit, currentPhaseVolRate[ip] ) );
- }
+ if( logSurfaceCondition && useSurfCond )
+ {
+ GEOS_LOG_RANK( GEOS_FMT( "{}: density of phase {} at surface conditions = {} {}/sm3, phase surface volumetric rate = {} sm3/s",
+ wellControlsName, ip, phaseDens[iwelemRef][0][ip], massUnit, currentPhaseVolRate[ip] ) );
}
- } );
+ }
} );
} );
}
-
void CompositionalMultiphaseWell::updateFluidModel( WellElementSubRegion & subRegion )
{
GEOS_MARK_FUNCTION;
@@ -942,19 +744,129 @@ void CompositionalMultiphaseWell::updateFluidModel( WellElementSubRegion & subRe
}
-real64 CompositionalMultiphaseWell::updatePhaseVolumeFraction( WellElementSubRegion & subRegion ) const
+void CompositionalMultiphaseWell::updateSeparator( ElementRegionManager const & elemManager, WellElementSubRegion & subRegion )
{
GEOS_MARK_FUNCTION;
- string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString() );
- MultiFluidBase & fluid = subRegion.getConstitutiveModel< MultiFluidBase >( fluidName );
+ // the rank that owns the reference well element is responsible for the calculations below.
+ if( !subRegion.isLocallyOwned() )
+ {
+ return;
+ }
- real64 maxDeltaPhaseVolFrac =
- m_isThermal ?
- thermalCompositionalMultiphaseBaseKernels::
- PhaseVolumeFractionKernelFactory::
- createAndLaunch< parallelDevicePolicy<> >( m_numComponents,
- m_numPhases,
+ localIndex const iwelemRef = subRegion.getTopWellElementIndex();
+
+ // subRegion data
+ arrayView1d< real64 const > const & pres = subRegion.getField< fields::well::pressure >();
+ arrayView1d< real64 const > const & temp = subRegion.getField< fields::well::temperature >();
+ arrayView2d< real64 const, compflow::USD_COMP > const & compFrac = subRegion.getField< fields::well::globalCompFraction >();
+
+
+ // fluid data
+ constitutive::MultiFluidBase & fluidSeparator = getMultiFluidSeparator();
+
+ // control data
+
+ string const wellControlsName = getName();
+ bool const logSurfaceCondition = isLogLevelActive< logInfo::BoundaryConditions >( getLogLevel());
+ string const massUnit = m_useMass ? "kg" : "mol";
+
+ integer const useSurfaceCond = useSurfaceConditions();
+ real64 flashPressure;
+ real64 flashTemperature;
+ if( useSurfaceCond )
+ {
+ // use surface conditions
+ flashPressure = getSurfacePressure();
+ flashTemperature = getSurfaceTemperature();
+ }
+ else
+ {
+ if( !getReferenceReservoirRegion().empty() )
+ {
+ ElementRegionBase const & region = elemManager.getRegion( getReferenceReservoirRegion() );
+ GEOS_ERROR_IF ( !region.hasWrapper( CompositionalMultiphaseStatistics::regionStatisticsName()),
+ GEOS_FMT( "{}: WellControl {} referenceReservoirRegion field requires CompositionalMultiphaseStatistics to be configured for region {} ",
+ getDataContext(), getName(), getReferenceReservoirRegion() ) );
+
+ CompositionalMultiphaseStatistics::RegionStatistics const & stats = region.getReference< CompositionalMultiphaseStatistics::RegionStatistics >(
+ CompositionalMultiphaseStatistics::regionStatisticsName() );
+ GEOS_ERROR_IF( stats.averagePressure <= 0.0,
+ GEOS_FMT(
+ "{}: No region average quantities computed. WellControl {} referenceReservoirRegion field requires CompositionalMultiphaseStatistics to be configured for region {} ",
+ getDataContext(), getName(), getReferenceReservoirRegion() ));
+ setRegionAveragePressure( stats.averagePressure );
+ setRegionAverageTemperature( stats.averageTemperature );
+ }
+ // If flashPressure is not set by region the value is defaulted to -1 and indicates to use top segment conditions
+ flashPressure = getRegionAveragePressure();
+ if( flashPressure < 0.0 )
+ {
+ // region name not set, use segment conditions
+ flashPressure = pres[iwelemRef];
+ flashTemperature = temp[iwelemRef];
+ }
+ else
+ {
+ // use reservoir region averages
+ flashTemperature = getRegionAverageTemperature();
+ }
+ }
+
+ constitutive::constitutiveUpdatePassThru( fluidSeparator, [&] ( auto & castedFluidSeparator )
+ {
+ // typename TYPEOFREF( castedFluid ) ::KernelWrapper fluidWrapper = castedFluid.createKernelWrapper();
+ typename TYPEOFREF( castedFluidSeparator ) ::KernelWrapper fluidSeparatorWrapper = castedFluidSeparator.createKernelWrapper();
+ // bring everything back to host, capture the scalars by reference
+ forAll< serialPolicy >( 1, [fluidSeparatorWrapper,
+ wellControlsName,
+ useSurfaceCond,
+ flashPressure,
+ flashTemperature,
+ logSurfaceCondition,
+ iwelemRef,
+ pres,
+ temp,
+ compFrac] ( localIndex const )
+ {
+ // - Surface conditions: using the surface pressure provided by the user
+ // - Reservoir conditions: using the pressure in the top element
+ if( useSurfaceCond )
+ {
+ // we need to compute the surface density
+ //fluidWrapper.update( iwelemRef, 0, surfacePres, surfaceTemp, compFrac[iwelemRef] );
+ fluidSeparatorWrapper.update( iwelemRef, 0, flashPressure, flashTemperature, compFrac[iwelemRef] );
+ if( logSurfaceCondition )
+ {
+ GEOS_LOG_RANK( GEOS_FMT( "{}: surface density computed with P_surface = {} Pa and T_surface = {} K",
+ wellControlsName, flashPressure, flashTemperature ) );
+ }
+#ifdef GEOS_USE_HIP
+ GEOS_UNUSED_VAR( wellControlsName );
+#endif
+ }
+ else
+ {
+ fluidSeparatorWrapper.update( iwelemRef, 0, flashPressure, flashTemperature, compFrac[iwelemRef] );
+ }
+ } );
+ } );
+}
+
+
+real64 CompositionalMultiphaseWell::updatePhaseVolumeFraction( WellElementSubRegion & subRegion ) const
+{
+ GEOS_MARK_FUNCTION;
+
+ string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString() );
+ MultiFluidBase & fluid = subRegion.getConstitutiveModel< MultiFluidBase >( fluidName );
+
+ real64 maxDeltaPhaseVolFrac =
+ m_isThermal ?
+ thermalCompositionalMultiphaseBaseKernels::
+ PhaseVolumeFractionKernelFactory::
+ createAndLaunch< parallelDevicePolicy<> >( m_numComponents,
+ m_numPhases,
subRegion,
fluid )
: isothermalCompositionalMultiphaseBaseKernels::
@@ -990,185 +902,280 @@ void CompositionalMultiphaseWell::updateTotalMassDensity( WellElementSubRegion &
}
-void CompositionalMultiphaseWell::updateState( DomainPartition & domain )
+real64 CompositionalMultiphaseWell::updateWellState( ElementRegionManager const & elemManager, WellElementSubRegion & subRegion )
{
GEOS_MARK_FUNCTION;
- real64 maxPhaseVolFrac = 0.0;
- forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &,
- MeshLevel & mesh,
- string_array const & regionNames )
- {
- ElementRegionManager & elemManager = mesh.getElemManager();
- elemManager.forElementSubRegions< WellElementSubRegion >( regionNames, [&]( localIndex const,
- WellElementSubRegion & subRegion )
- {
- WellControls & wellControls = getWellControls( subRegion );
- if( wellControls.getWellStatus() == WellControls::Status::OPEN )
- {
- real64 const maxRegionPhaseVolFrac = updateSubRegionState( elemManager, subRegion );
- maxPhaseVolFrac = LvArray::math::max( maxRegionPhaseVolFrac, maxPhaseVolFrac );
- }
- } );
- } );
- maxPhaseVolFrac = MpiWrapper::max( maxPhaseVolFrac );
-
- GEOS_LOG_LEVEL_RANK_0( logInfo::Solution,
- GEOS_FMT( " {}: Max well phase volume fraction change = {}",
- getName(), fmt::format( "{:.{}f}", maxPhaseVolFrac, 4 ) ) );
+ real64 maxPhaseVolFrac = updateSubRegionState( elemManager, subRegion );
+ return maxPhaseVolFrac;
}
real64 CompositionalMultiphaseWell::updateSubRegionState( ElementRegionManager const & elemManager, WellElementSubRegion & subRegion )
{
- // update properties
- updateGlobalComponentFraction( subRegion );
+ real64 maxPhaseVolChange=0.0;
+
+ if( getWellState())
+ {
+
+ // update properties
+ updateGlobalComponentFraction( subRegion );
+
+ // update densities, phase fractions, phase volume fractions
+
+ updateFluidModel( subRegion ); // Calculate fluid properties
+
+ updateSeparator( elemManager, subRegion ); // Calculate fluid properties at control conditions
+
+ updateVolRatesForConstraint( subRegion ); // remove tjb ??
+
+ maxPhaseVolChange = updatePhaseVolumeFraction( subRegion );
+ updateTotalMassDensity( subRegion );
+
+ // Calculate the reference element rates
+ calculateReferenceElementRates( subRegion );
- // update volumetric rates for the well constraints
- // note: this must be called before updateFluidModel
- updateVolRatesForConstraint( elemManager, subRegion );
+ // update the current BHP
+ updateBHPForConstraint( subRegion );
- // update densities, phase fractions, phase volume fractions
+ // Broad case the updated well state to other ranks
+ real64 & currentBHP =
+ getReference< real64 >( CompositionalMultiphaseWell::viewKeyStruct::currentBHPString() );
+ array1d< real64 > currentPhaseVolRate =
+ getReference< array1d< real64 > >( CompositionalMultiphaseWell::viewKeyStruct::currentPhaseVolRateString() );
+ real64 & currentTotalVolRate =
+ getReference< real64 >( CompositionalMultiphaseWell::viewKeyStruct::currentTotalVolRateString() );
+ real64 & currentMassRate =
+ getReference< real64 >( CompositionalMultiphaseWell::viewKeyStruct::currentMassRateString() );
+ integer topRank = subRegion.getTopRank();
+ MpiWrapper::broadcast( currentBHP, topRank );
+ MpiWrapper::bcast( currentPhaseVolRate.data(), LvArray::integerConversion< int >( currentPhaseVolRate.size() ), topRank );
+ MpiWrapper::broadcast( currentTotalVolRate, topRank );
+ MpiWrapper::broadcast( currentMassRate, topRank );
+ if( !subRegion.isLocallyOwned() )
+ {
+ getReference< array1d< real64 > >(
+ CompositionalMultiphaseWell::viewKeyStruct::currentPhaseVolRateString() ) =currentPhaseVolRate;
+
+ }
+
+ WellConstraintBase * constraint = getCurrentConstraint();
+ if( constraint != nullptr )
+ {
+ constraint->setBHP ( getReference< real64 >( CompositionalMultiphaseWell::viewKeyStruct::currentBHPString() ));
+ constraint->setPhaseVolumeRates ( getReference< array1d< real64 > >(
+ CompositionalMultiphaseWell::viewKeyStruct::currentPhaseVolRateString() ) );
+ constraint->setTotalVolumeRate ( getReference< real64 >(
+ CompositionalMultiphaseWell::viewKeyStruct::currentTotalVolRateString() ));
+ constraint->setMassRate( getReference< real64 >( CompositionalMultiphaseWell::viewKeyStruct::currentMassRateString() ));
+ }
- updateFluidModel( subRegion ); // Calculate fluid properties;
- real64 maxPhaseVolChange = updatePhaseVolumeFraction( subRegion );
- updateTotalMassDensity( subRegion );
- // update the current BHP pressure
- updateBHPForConstraint( subRegion );
+ }
return maxPhaseVolChange;
}
-void CompositionalMultiphaseWell::initializeWells( DomainPartition & domain, real64 const & time_n )
+void CompositionalMultiphaseWell::initializeWell( DomainPartition & domain, MeshLevel & mesh, WellElementSubRegion & subRegion, real64 const & time_n )
{
GEOS_MARK_FUNCTION;
-
+ GEOS_UNUSED_VAR( domain );
integer const numComp = m_numComponents;
integer const numPhase = m_numPhases;
- // TODO: change the way we access the flowSolver here
- CompositionalMultiphaseBase const & flowSolver = getParent().getGroup< CompositionalMultiphaseBase >( getFlowSolverName() );
- // loop over the wells
- forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &,
- MeshLevel & mesh,
- string_array const & regionNames )
- {
+ // TODO: change the way we access the flowSolver here
+ ElementRegionManager const & elemManager = mesh.getElemManager();
- ElementRegionManager & elemManager = mesh.getElemManager();
- compositionalMultiphaseWellKernels::PresTempCompFracInitializationKernel::CompFlowAccessors
- resCompFlowAccessors( mesh.getElemManager(), flowSolver.getName() );
- compositionalMultiphaseWellKernels::PresTempCompFracInitializationKernel::MultiFluidAccessors
- resMultiFluidAccessors( mesh.getElemManager(), flowSolver.getName() );
+ compositionalMultiphaseWellKernels::PresTempCompFracInitializationKernel::CompFlowAccessors
+ resCompFlowAccessors( elemManager, getFlowSolverName() );
+ compositionalMultiphaseWellKernels::PresTempCompFracInitializationKernel::MultiFluidAccessors
+ resMultiFluidAccessors( elemManager, getFlowSolverName() );
- elemManager.forElementSubRegions< WellElementSubRegion >( regionNames,
- [&]( localIndex const,
- WellElementSubRegion & subRegion )
- {
- WellControls & wellControls = getWellControls( subRegion );
- PerforationData const & perforationData = *subRegion.getPerforationData();
- arrayView2d< real64 const > const compPerfRate = perforationData.getField< fields::well::compPerforationRate >();
+ PerforationData const & perforationData = *subRegion.getPerforationData();
+ arrayView2d< real64 const > const compPerfRate = perforationData.getField< fields::well::compPerforationRate >();
- bool const hasNonZeroRate = MpiWrapper::max< integer >( hasNonZero( compPerfRate ));
+ bool const hasNonZeroRate = MpiWrapper::max< integer >( hasNonZero( compPerfRate ));
- if( wellControls.isWellOpen() && !hasNonZeroRate )
+ if( time_n <= 0.0 || ( isWellOpen( ) && !hasNonZeroRate ) )
+ {
+ setWellState( true );
+ if( getCurrentConstraint() == nullptr )
+ {
+ // tjb needed for backward compatibility. and these 2 lists must be consistent
+ ConstraintTypeId inputControl = ConstraintTypeId( getInputControl());
+ if( isProducer() )
{
- // get well primary variables on well elements
- arrayView1d< real64 > const & wellElemPressure = subRegion.getField< well::pressure >();
- arrayView1d< real64 > const & wellElemTemp = subRegion.getField< well::temperature >();
- arrayView2d< real64, compflow::USD_COMP > const & wellElemCompDens = subRegion.getField< well::globalCompDensity >();
- arrayView1d< real64 > const & connRate = subRegion.getField< well::mixtureConnectionRate >();
-
- // get the info stored on well elements
- arrayView2d< real64, compflow::USD_COMP > const & wellElemCompFrac = subRegion.getField< well::globalCompFraction >();
- arrayView1d< real64 const > const & wellElemGravCoef = subRegion.getField< well::gravityCoefficient >();
-
- // get the element region, subregion, index
- arrayView1d< localIndex const > const resElementRegion = perforationData.getField< perforation::reservoirElementRegion >();
- arrayView1d< localIndex const > const resElementSubRegion = perforationData.getField< perforation::reservoirElementSubRegion >();
- arrayView1d< localIndex const > const resElementIndex = perforationData.getField< perforation::reservoirElementIndex >();
-
- arrayView1d< real64 const > const & perfGravCoef = perforationData.getField< fields::well::gravityCoefficient >();
- arrayView1d< integer const > const & perfStatus = perforationData.getField< fields::perforation::perforationStatus >();
-
- // 1) Loop over all perforations to compute an average mixture density and component fraction
- // 2) Initialize the reference pressure
- // 3) Estimate the pressures in the well elements using the average density
- compositionalMultiphaseWellKernels::
- PresTempCompFracInitializationKernel::
- launch( perforationData.size(),
- subRegion.size(),
- numComp,
- numPhase,
- wellControls,
- 0.0, // initialization done at t = 0
- resCompFlowAccessors.get( flow::pressure{} ),
- resCompFlowAccessors.get( flow::temperature{} ),
- resCompFlowAccessors.get( flow::globalCompDensity{} ),
- resCompFlowAccessors.get( flow::phaseVolumeFraction{} ),
- resMultiFluidAccessors.get( fields::multifluid::phaseMassDensity{} ),
- resElementRegion,
- resElementSubRegion,
- resElementIndex,
- perfGravCoef,
- perfStatus,
- wellElemGravCoef,
- wellElemPressure,
- wellElemTemp,
- wellElemCompFrac );
-
- // get well secondary variables on well elements
- string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString() );
- MultiFluidBase & fluid = getConstitutiveModel< MultiFluidBase >( subRegion, fluidName );
- arrayView3d< real64 const, constitutive::multifluid::USD_PHASE > const & wellElemPhaseDens = fluid.phaseDensity();
- arrayView2d< real64 const, constitutive::multifluid::USD_FLUID > const & wellElemTotalDens = fluid.totalDensity();
-
- // 4) Back calculate component densities
- constitutive::constitutiveUpdatePassThru( fluid, [&] ( auto & castedFluid )
+ forSubGroups< MinimumBHPConstraint, ProductionConstraint< VolumeRateConstraint >, ProductionConstraint< MassRateConstraint >,
+ ProductionConstraint< PhaseVolumeRateConstraint > >( [&]( auto & constraint )
{
- typename TYPEOFREF( castedFluid ) ::KernelWrapper fluidWrapper = castedFluid.createKernelWrapper();
-
- thermalCompositionalMultiphaseBaseKernels::
- FluidUpdateKernel::
- launch< serialPolicy >( subRegion.size(),
- fluidWrapper,
- wellElemPressure,
- wellElemTemp,
- wellElemCompFrac );
+ if( constraint.getControl() == inputControl && constraint.isConstraintActive())
+ {
+ setCurrentConstraint( &constraint );
+ setControl( static_cast< WellControls::Control >(inputControl) ); // tjb old
+ }
} );
+ }
+ else
+ {
- compositionalMultiphaseWellKernels::
- CompDensInitializationKernel::launch( subRegion.size(),
- numComp,
- wellElemCompFrac,
- wellElemTotalDens,
- wellElemCompDens );
-
- // 5) Recompute the pressure-dependent properties
- updateSubRegionState( elemManager, subRegion );
-
- // 6) Estimate the well rates
- // TODO: initialize rates using perforation rates
- compositionalMultiphaseWellKernels::
- RateInitializationKernel::
- launch( subRegion.size(),
- m_targetPhaseIndex,
- wellControls,
- time_n, // initialization done at time_n
- wellElemPhaseDens,
- wellElemTotalDens,
- connRate );
+ forSubGroups< MaximumBHPConstraint, InjectionConstraint< VolumeRateConstraint >, InjectionConstraint< MassRateConstraint >,
+ InjectionConstraint< PhaseVolumeRateConstraint > >( [&]( auto & constraint )
+ {
+ if( constraint.getControl() == inputControl && constraint.isConstraintActive())
+ {
+ setCurrentConstraint( &constraint );
+ setControl( static_cast< WellControls::Control >(inputControl) ); // tjb old
+ }
+ } );
}
+ }
+ // get well primary variables on well elements
+ arrayView1d< real64 > const & wellElemPressure = subRegion.getField< well::pressure >();
+ arrayView1d< real64 > const & wellElemTemp = subRegion.getField< well::temperature >();
+ arrayView2d< real64, compflow::USD_COMP > const & wellElemCompDens = subRegion.getField< well::globalCompDensity >();
+ arrayView1d< real64 > const & connRate = subRegion.getField< well::connectionRate >();
+
+ // get the info stored on well elements
+ arrayView2d< real64, compflow::USD_COMP > const & wellElemCompFrac = subRegion.getField< well::globalCompFraction >();
+ arrayView1d< real64 const > const & wellElemGravCoef = subRegion.getField< well::gravityCoefficient >();
+
+ // get the element region, subregion, index
+ arrayView1d< localIndex const > const resElementRegion = perforationData.getField< perforation::reservoirElementRegion >();
+ arrayView1d< localIndex const > const resElementSubRegion = perforationData.getField< perforation::reservoirElementSubRegion >();
+ arrayView1d< localIndex const > const resElementIndex = perforationData.getField< perforation::reservoirElementIndex >();
+
+ arrayView1d< real64 const > const & perfGravCoef = perforationData.getField< fields::well::gravityCoefficient >();
+ arrayView1d< integer const > const & perfStatus = perforationData.getField< fields::perforation::perforationStatus >();
+
+ // 1) Loop over all perforations to compute an average mixture density and component fraction
+ // 2) Initialize the reference pressure
+ // 3) Estimate the pressures in the well elements using the average density
+ compositionalMultiphaseWellKernels::
+ PresTempCompFracInitializationKernel::
+ launch( perforationData.size(),
+ subRegion.size(),
+ numComp,
+ numPhase,
+ *this,
+ 0.0, // initialization done at t = 0
+ resCompFlowAccessors.get( flow::pressure{} ),
+ resCompFlowAccessors.get( flow::temperature{} ),
+ resCompFlowAccessors.get( flow::globalCompDensity{} ),
+ resCompFlowAccessors.get( flow::phaseVolumeFraction{} ),
+ resMultiFluidAccessors.get( fields::multifluid::phaseMassDensity{} ),
+ resElementRegion,
+ resElementSubRegion,
+ resElementIndex,
+ perfGravCoef,
+ perfStatus,
+ wellElemGravCoef,
+ wellElemPressure,
+ wellElemTemp,
+ wellElemCompFrac );
+
+ // get well secondary variables on well elements
+ string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString() );
+ MultiFluidBase & fluid = getConstitutiveModel< MultiFluidBase >( subRegion, fluidName );
+ arrayView3d< real64 const, constitutive::multifluid::USD_PHASE > const & wellElemPhaseDens = fluid.phaseDensity();
+ arrayView2d< real64 const, constitutive::multifluid::USD_FLUID > const & wellElemTotalDens = fluid.totalDensity();
+
+ // 4) Back calculate component densities
+ constitutive::constitutiveUpdatePassThru( fluid, [&] ( auto & castedFluid )
+ {
+ typename TYPEOFREF( castedFluid ) ::KernelWrapper fluidWrapper = castedFluid.createKernelWrapper();
+
+ thermalCompositionalMultiphaseBaseKernels::
+ FluidUpdateKernel::
+ launch< serialPolicy >( subRegion.size(),
+ fluidWrapper,
+ wellElemPressure,
+ wellElemTemp,
+ wellElemCompFrac );
} );
- } );
+ compositionalMultiphaseWellKernels::
+ CompDensInitializationKernel::launch( subRegion.size(),
+ numComp,
+ wellElemCompFrac,
+ wellElemTotalDens,
+ wellElemCompDens );
+
+ // 5) Recompute the pressure-dependent properties
+
+ updateSubRegionState( elemManager, subRegion );
+
+ // 6) Estimate the well rates
+ // TODO: initialize rates using perforation rates
+ compositionalMultiphaseWellKernels::
+ RateInitializationKernel::
+ launch( subRegion.size(),
+ *this,
+ time_n, // initialization done at time_n
+ wellElemPhaseDens,
+ wellElemTotalDens,
+ connRate );
+
+ updateVolRatesForConstraint( subRegion );
+ // Since this is a well manager class the rates need to be pushed into the WellControls class, which represnets the well
+ WellConstraintBase * constraint = getCurrentConstraint();
+ constraint->setBHP ( getReference< real64 >( CompositionalMultiphaseWell::viewKeyStruct::currentBHPString() ));
+ constraint->setPhaseVolumeRates ( getReference< array1d< real64 > >(
+ CompositionalMultiphaseWell::viewKeyStruct::currentPhaseVolRateString() ) );
+ constraint->setTotalVolumeRate ( getReference< real64 >(
+ CompositionalMultiphaseWell::viewKeyStruct::currentTotalVolRateString() ));
+ constraint->setMassRate( getReference< real64 >( CompositionalMultiphaseWell::viewKeyStruct::currentMassRateString() ));
+ // 7) Copy well / fluid dofs to "prop"_n variables
+ saveState( subRegion );
+ }
+ else if( !hasNonZeroRate )
+ {
+ setWellState( false );
+ }
+ else
+ {
+ setWellState( true );
+ // setup if restart
+ if( getCurrentConstraint() == nullptr )
+ {
+ if( isProducer() )
+ {
+ forSubGroups< MinimumBHPConstraint, ProductionConstraint< VolumeRateConstraint >, ProductionConstraint< MassRateConstraint >,
+ ProductionConstraint< PhaseVolumeRateConstraint > >( [&](
+ auto
+ & constraint )
+ {
+ if( ConstraintTypeId( getControl()) == constraint.getControl() && constraint.isConstraintActive() )
+ {
+ setCurrentConstraint( &constraint );
+ }
+ } );
+ }
+ else
+ {
+ forSubGroups< MaximumBHPConstraint, InjectionConstraint< VolumeRateConstraint >, InjectionConstraint< MassRateConstraint >, InjectionConstraint< PhaseVolumeRateConstraint > >( [&](
+ auto
+ &
+ constraint )
+ {
+ if( ConstraintTypeId( getControl()) == constraint.getControl() && constraint.isConstraintActive() )
+ {
+ setCurrentConstraint( &constraint );
+ }
+ } );
+ }
+ updateSubRegionState( elemManager, subRegion );
+ }
+
+ }
}
-void CompositionalMultiphaseWell::assembleFluxTerms( real64 const & time,
- real64 const & dt,
- DomainPartition & domain,
- DofManager const & dofManager,
- CRSMatrixView< real64, globalIndex const > const & localMatrix,
- arrayView1d< real64 > const & localRhs )
+
+
+void CompositionalMultiphaseWell::assembleWellFluxTerms( real64 const & time,
+ real64 const & dt,
+ WellElementSubRegion const & subRegion,
+ DofManager const & dofManager,
+ CRSMatrixView< real64, globalIndex const > const & localMatrix,
+ arrayView1d< real64 > const & localRhs )
{
GEOS_MARK_FUNCTION;
GEOS_UNUSED_VAR( time );
@@ -1178,62 +1185,55 @@ void CompositionalMultiphaseWell::assembleFluxTerms( real64 const & time,
kernelFlags.set( isothermalCompositionalMultiphaseBaseKernels::KernelFlags::TotalMassEquation );
string const wellDofKey = dofManager.getKey( wellElementDofName());
- forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &,
- MeshLevel & mesh,
- string_array const & regionNames )
+
+
+ if( isWellOpen( ) && !m_keepVariablesConstantDuringInitStep )
{
- mesh.getElemManager().forElementSubRegions< WellElementSubRegion >( regionNames,
- [&]( localIndex const,
- WellElementSubRegion & subRegion )
+ string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString());
+ MultiFluidBase const & fluid = getConstitutiveModel< MultiFluidBase >( subRegion, fluidName );
+ int numComponents = fluid.numFluidComponents();
+
+ if( isThermal() )
{
- WellControls const & well_controls = getWellControls( subRegion );
- if( well_controls.getWellStatus() == WellControls::Status::OPEN && !m_keepVariablesConstantDuringInitStep )
- {
- string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString());
- MultiFluidBase const & fluid = getConstitutiveModel< MultiFluidBase >( subRegion, fluidName );
- int const numComponents = fluid.numFluidComponents();
+ thermalCompositionalMultiphaseWellKernels::
+ FaceBasedAssemblyKernelFactory::
+ createAndLaunch< parallelDevicePolicy<> >( numComponents,
+ dt,
+ dofManager.rankOffset(),
+ kernelFlags,
+ wellDofKey,
+ *this,
+ subRegion,
+ fluid,
+ localMatrix,
+ localRhs );
+ }
+ else
+ {
+ compositionalMultiphaseWellKernels::
+ FaceBasedAssemblyKernelFactory::
+ createAndLaunch< parallelDevicePolicy<> >( numComponents,
+ dt,
+ dofManager.rankOffset(),
+ kernelFlags,
+ wellDofKey,
+ *this,
+ subRegion,
+ localMatrix,
+ localRhs );
+ }
+ }
- if( isThermal() )
- {
- thermalCompositionalMultiphaseWellKernels::
- FaceBasedAssemblyKernelFactory::
- createAndLaunch< parallelDevicePolicy<> >( numComponents,
- dt,
- dofManager.rankOffset(),
- kernelFlags,
- wellDofKey,
- well_controls,
- subRegion,
- fluid,
- localMatrix,
- localRhs );
- }
- else
- {
- compositionalMultiphaseWellKernels::
- FaceBasedAssemblyKernelFactory::
- createAndLaunch< parallelDevicePolicy<> >( numComponents,
- dt,
- dofManager.rankOffset(),
- kernelFlags,
- wellDofKey,
- well_controls,
- subRegion,
- localMatrix,
- localRhs );
- }
- }
- } );
- } );
}
-void CompositionalMultiphaseWell::assembleAccumulationTerms( real64 const & time,
- real64 const & dt,
- DomainPartition & domain,
- DofManager const & dofManager,
- CRSMatrixView< real64, globalIndex const > const & localMatrix,
- arrayView1d< real64 > const & localRhs )
+
+void CompositionalMultiphaseWell::assembleWellAccumulationTerms( real64 const & time,
+ real64 const & dt,
+ WellElementSubRegion & subRegion,
+ DofManager const & dofManager,
+ CRSMatrixView< real64, globalIndex const > const & localMatrix,
+ arrayView1d< real64 > const & localRhs )
{
GEOS_MARK_FUNCTION;
GEOS_UNUSED_VAR( time );
@@ -1244,123 +1244,126 @@ void CompositionalMultiphaseWell::assembleAccumulationTerms( real64 const & time
kernelFlags.set( isothermalCompositionalMultiphaseBaseKernels::KernelFlags::TotalMassEquation );
string const wellDofKey = dofManager.getKey( wellElementDofName() );
- forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &,
- MeshLevel & mesh,
- string_array const & regionNames )
+
+ string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString());
+ MultiFluidBase const & fluid = getConstitutiveModel< MultiFluidBase >( subRegion, fluidName );
+ integer const numPhases = fluid.numFluidPhases();
+ integer const numComponents = fluid.numFluidComponents();
+
+ if( getWellStatus() == WellControls::Status::OPEN && !m_keepVariablesConstantDuringInitStep )
{
- mesh.getElemManager().forElementSubRegions< WellElementSubRegion >( regionNames,
- [&]( localIndex const,
- WellElementSubRegion & subRegion )
+ if( isThermal() )
+ {
+
+ thermalCompositionalMultiphaseWellKernels::
+ ElementBasedAssemblyKernelFactory::
+ createAndLaunch< parallelDevicePolicy<> >( numComponents,
+ numPhases,
+ thermalEffectsEnabled(),
+ isProducer(),
+ dofManager.rankOffset(),
+ kernelFlags,
+ wellDofKey,
+ subRegion,
+ fluid,
+ localMatrix,
+ localRhs );
+ }
+ else
+ {
+ compositionalMultiphaseWellKernels::
+ ElementBasedAssemblyKernelFactory::
+ createAndLaunch< parallelDevicePolicy<> >( numComponents,
+ numPhases,
+ thermalEffectsEnabled(),
+ isProducer(),
+ dofManager.rankOffset(),
+ kernelFlags,
+ wellDofKey,
+ subRegion,
+ fluid,
+ localMatrix,
+ localRhs );
+ }
+ // get the degrees of freedom and ghosting info
+ arrayView1d< globalIndex const > const & wellElemDofNumber =
+ subRegion.getReference< array1d< globalIndex > >( wellDofKey );
+ arrayView1d< integer const > const wellElemGhostRank = subRegion.ghostRank();
+ arrayView1d< integer const > const elemStatus = subRegion.getLocalWellElementStatus();
+ arrayView1d< real64 > const mixConnRate = subRegion.getField< fields::well::connectionRate >();
+ localIndex rank_offset = dofManager.rankOffset();
+ forAll< parallelDevicePolicy<> >( subRegion.size(), [=] GEOS_HOST_DEVICE ( localIndex const ei )
{
- string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString());
- MultiFluidBase const & fluid = getConstitutiveModel< MultiFluidBase >( subRegion, fluidName );
- integer const numPhases = fluid.numFluidPhases();
- integer const numComponents = fluid.numFluidComponents();
- WellControls const & wellControls = getWellControls( subRegion );
- if( wellControls.getWellStatus() == WellControls::Status::OPEN && !m_keepVariablesConstantDuringInitStep )
+ if( wellElemGhostRank[ei] < 0 )
{
- if( isThermal() )
+ if( elemStatus[ei]==WellElementSubRegion::WellElemStatus::CLOSED )
{
+ mixConnRate[ei] = 0.0;
+ globalIndex const dofIndex = wellElemDofNumber[ei];
+ localIndex const localRow = dofIndex - rank_offset;
- thermalCompositionalMultiphaseWellKernels::
- ElementBasedAssemblyKernelFactory::
- createAndLaunch< parallelDevicePolicy<> >( numComponents,
- numPhases,
- wellControls.isProducer(),
- dofManager.rankOffset(),
- kernelFlags,
- wellDofKey,
- subRegion,
- fluid,
- localMatrix,
- localRhs );
- }
- else
- {
- compositionalMultiphaseWellKernels::
- ElementBasedAssemblyKernelFactory::
- createAndLaunch< parallelDevicePolicy<> >( numComponents,
- numPhases,
- wellControls.isProducer(),
- dofManager.rankOffset(),
- kernelFlags,
- wellDofKey,
- subRegion,
- fluid,
- localMatrix,
- localRhs );
- }
- // get the degrees of freedom and ghosting info
- arrayView1d< globalIndex const > const & wellElemDofNumber =
- subRegion.getReference< array1d< globalIndex > >( wellDofKey );
- arrayView1d< integer const > const wellElemGhostRank = subRegion.ghostRank();
- arrayView1d< integer const > const elemStatus = subRegion.getLocalWellElementStatus();
- arrayView1d< real64 > const mixConnRate = subRegion.getField< fields::well::mixtureConnectionRate >();
- localIndex rank_offset = dofManager.rankOffset();
- forAll< parallelDevicePolicy<> >( subRegion.size(), [=] GEOS_HOST_DEVICE ( localIndex const ei )
- {
- if( wellElemGhostRank[ei] < 0 )
+ real64 const unity = 1.0;
+ for( integer i=0; i < m_numDofPerWellElement; i++ )
{
- if( elemStatus[ei]==WellElementSubRegion::WellElemStatus::CLOSED )
- {
- mixConnRate[ei] = 0.0;
- globalIndex const dofIndex = wellElemDofNumber[ei];
- localIndex const localRow = dofIndex - rank_offset;
-
- real64 const unity = 1.0;
- for( integer i=0; i < m_numDofPerWellElement; i++ )
- {
- globalIndex const rindex = localRow+i;
- globalIndex const cindex =dofIndex + i;
- localMatrix.template addToRow< serialAtomic >( rindex,
- &cindex,
- &unity,
- 1 );
- localRhs[rindex] = 0.0;
- }
- }
+ globalIndex const rindex = localRow+i;
+ globalIndex const cindex =dofIndex + i;
+ localMatrix.template addToRow< serialAtomic >( rindex,
+ &cindex,
+ &unity,
+ 1 );
+ localRhs[rindex] = 0.0;
}
- } );
+ }
}
- else
+ } );
+
+ }
+ else
+ {
+ arrayView1d< globalIndex const > const & wellElemDofNumber =
+ subRegion.getReference< array1d< globalIndex > >( wellDofKey );
+ arrayView1d< integer const > const wellElemGhostRank = subRegion.ghostRank();
+
+ arrayView1d< real64 > mixConnRate = subRegion.getField< fields::well::connectionRate >();
+ localIndex rank_offset = dofManager.rankOffset();
+ forAll< parallelDevicePolicy<> >( subRegion.size(), [=] GEOS_HOST_DEVICE ( localIndex const ei )
+ {
+ if( wellElemGhostRank[ei] < 0 )
{
- // get the degrees of freedom and ghosting info
- arrayView1d< globalIndex const > const & wellElemDofNumber =
- subRegion.getReference< array1d< globalIndex > >( wellDofKey );
- arrayView1d< integer const > const & wellElemGhostRank = subRegion.ghostRank();
- localIndex rank_offset = dofManager.rankOffset();
- forAll< parallelDevicePolicy<> >( subRegion.size(), [=] GEOS_HOST_DEVICE ( localIndex const ei )
+ mixConnRate[ei] = 0.0;
+ globalIndex const dofIndex = wellElemDofNumber[ei];
+ localIndex const localRow = dofIndex - rank_offset;
+
+ real64 const unity = 1.0;
+ for( integer i=0; i < m_numDofPerWellElement; i++ )
{
- if( wellElemGhostRank[ei] < 0 )
- {
- globalIndex const dofIndex = wellElemDofNumber[ei];
- localIndex const localRow = dofIndex - rank_offset;
-
- real64 unity = 1.0;
- for( integer i=0; i < m_numDofPerWellElement; i++ )
- {
- globalIndex const rindex = localRow+i;
- globalIndex const cindex =dofIndex + i;
- localMatrix.template addToRow< serialAtomic >( rindex,
- &cindex,
- &unity,
- 1 );
- localRhs[rindex] = 0.0;
- }
- }
- } );
+ globalIndex const rindex = localRow+i;
+ globalIndex const cindex =dofIndex + i;
+ localMatrix.template addToRow< serialAtomic >( rindex,
+ &cindex,
+ &unity,
+ 1 );
+ localRhs[rindex] = 0.0;
+ }
}
- } ); // forElementSubRegions
- } ); // forDiscretizationOnMeshTargets
+ } );
+ // zero out current state constraint quantities
+ getReference< real64 >( CompositionalMultiphaseWell::viewKeyStruct::currentBHPString() )=0.0;
+ getReference< array1d< real64 > >(
+ CompositionalMultiphaseWell::viewKeyStruct::currentPhaseVolRateString() ).zero();
+ getReference< real64 >(
+ CompositionalMultiphaseWell::viewKeyStruct::currentTotalVolRateString() )=0.0;
+ getReference< real64 >( CompositionalMultiphaseWell::viewKeyStruct::currentMassRateString() )=0.0;
+ }
}
-
-real64
-CompositionalMultiphaseWell::calculateResidualNorm( real64 const & time_n,
- real64 const & dt,
- DomainPartition const & domain,
- DofManager const & dofManager,
- arrayView1d< real64 const > const & localRhs )
+array1d< real64 >
+CompositionalMultiphaseWell::calculateLocalWellResidualNorm( real64 const & time_n,
+ real64 const & dt,
+ NonlinearSolverParameters const & nonlinearSolverParameters,
+ WellElementSubRegion const & subRegion,
+ DofManager const & dofManager,
+ arrayView1d< real64 const > const & localRhs )
{
GEOS_MARK_FUNCTION;
@@ -1371,195 +1374,227 @@ CompositionalMultiphaseWell::calculateResidualNorm( real64 const & time_n,
globalIndex const rankOffset = dofManager.rankOffset();
string const wellDofKey = dofManager.getKey( wellElementDofName() );
- forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &,
- MeshLevel const & mesh,
- string_array const & regionNames )
- {
-
- ElementRegionManager const & elemManager = mesh.getElemManager();
- elemManager.forElementSubRegions< WellElementSubRegion >( regionNames,
- [&]( localIndex const,
- WellElementSubRegion const & subRegion )
- {
-
-
- string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString() );
- MultiFluidBase const & fluid = subRegion.getConstitutiveModel< MultiFluidBase >( fluidName );
+ string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString() );
+ MultiFluidBase const & fluid = subRegion.getConstitutiveModel< MultiFluidBase >( fluidName );
- WellControls const & wellControls = getWellControls( subRegion );
- // step 1: compute the norm in the subRegion
+ // step 1: compute the norm in the subRegion
- if( !wellControls.isWellOpen() )
- {
- for( integer i = 0; i < numNorm; ++i )
- {
- localResidualNorm[i] = 0.0;
- localResidualNormalizer[i] = m_nonlinearSolverParameters.m_minNormalizer;
- }
- }
- else if( isThermal() )
- {
- real64 subRegionResidualNorm[2]{};
-
- thermalCompositionalMultiphaseWellKernels::ResidualNormKernelFactory::
- createAndLaunch< parallelDevicePolicy<> >( m_numComponents,
- m_targetPhaseIndex,
- rankOffset,
- wellDofKey,
- localRhs,
- subRegion,
- fluid,
- wellControls,
- time_n,
- dt,
- m_nonlinearSolverParameters.m_minNormalizer,
- subRegionResidualNorm );
- // step 2: reduction across meshBodies/regions/subRegions
-
- for( integer i=0; i localResidualNorm[i] )
- {
- localResidualNorm[i] = subRegionResidualNorm[i];
- }
- }
+ if( !isWellOpen() )
+ {
+ for( integer i = 0; i < numNorm; ++i )
+ {
+ localResidualNorm[i] = 0.0;
+ localResidualNormalizer[i] = nonlinearSolverParameters.m_minNormalizer;
+ }
+ }
+ else if( isThermal() )
+ {
+ real64 subRegionResidualNorm[2]{};
- }
- else
+ thermalCompositionalMultiphaseWellKernels::ResidualNormKernelFactory::
+ createAndLaunch< parallelDevicePolicy<> >( m_numComponents,
+ rankOffset,
+ wellDofKey,
+ localRhs,
+ subRegion,
+ fluid,
+ *this,
+ time_n,
+ dt,
+ nonlinearSolverParameters.m_minNormalizer,
+ subRegionResidualNorm );
+ // step 2: reduction across meshBodies/regions/subRegions
+
+ for( integer i=0; i localResidualNorm[i] )
{
- real64 subRegionResidualNorm[1]{};
- compositionalMultiphaseWellKernels::ResidualNormKernelFactory::
- createAndLaunch< parallelDevicePolicy<> >( m_numComponents,
- numDofPerWellElement(),
- m_targetPhaseIndex,
- rankOffset,
- wellDofKey,
- localRhs,
- subRegion,
- fluid,
- wellControls,
- time_n,
- dt,
- m_nonlinearSolverParameters.m_minNormalizer,
- subRegionResidualNorm );
-
-
-
- // step 2: reduction across meshBodies/regions/subRegions
-
- if( subRegionResidualNorm[0] > localResidualNorm[0] )
- {
- localResidualNorm[0] = subRegionResidualNorm[0];
- }
+ localResidualNorm[i] = subRegionResidualNorm[i];
}
- } );
- } );
-
- // step 3: second reduction across MPI ranks
- real64 resNorm=localResidualNorm[0];
- if( isThermal() )
- {
- real64 globalResidualNorm[2]{};
- globalResidualNorm[0] = MpiWrapper::max( localResidualNorm[0] );
- globalResidualNorm[1] = MpiWrapper::max( localResidualNorm[1] );
- resNorm = sqrt( globalResidualNorm[0] * globalResidualNorm[0] + globalResidualNorm[1] * globalResidualNorm[1] );
-
- GEOS_LOG_LEVEL_RANK_0_NLR( logInfo::ResidualNorm, GEOS_FMT( " ( R{} ) = ( {:4.2e} ) ( Renergy ) = ( {:4.2e} )",
- coupledSolverAttributePrefix(), globalResidualNorm[0], globalResidualNorm[1] ));
+ }
- getConvergenceStats().setResidualValue( GEOS_FMT( "R{}", coupledSolverAttributePrefix()), globalResidualNorm[0] );
- getConvergenceStats().setResidualValue( "Renergy", globalResidualNorm[1] );
}
else
{
- resNorm = MpiWrapper::max( resNorm );
+ real64 subRegionResidualNorm[1]{};
+ compositionalMultiphaseWellKernels::ResidualNormKernelFactory::
+ createAndLaunch< parallelDevicePolicy<> >( m_numComponents,
+ m_numDofPerWellElement,
+ rankOffset,
+ wellDofKey,
+ localRhs,
+ subRegion,
+ fluid,
+ *this,
+ time_n,
+ dt,
+ nonlinearSolverParameters.m_minNormalizer,
+ subRegionResidualNorm );
+
+
+
+ // step 2: reduction across meshBodies/regions/subRegions
- GEOS_LOG_LEVEL_RANK_0_NLR( logInfo::ResidualNorm, GEOS_FMT( " ( R{} ) = ( {:4.2e} )",
- coupledSolverAttributePrefix(), resNorm ));
- getConvergenceStats().setResidualValue( GEOS_FMT( "R{}", coupledSolverAttributePrefix()), resNorm );
+ if( subRegionResidualNorm[0] > localResidualNorm[0] )
+ {
+ localResidualNorm[0] = subRegionResidualNorm[0];
+ }
}
- return resNorm;
+
+ return localResidualNorm;
+
}
real64
-CompositionalMultiphaseWell::scalingForSystemSolution( DomainPartition & domain,
- DofManager const & dofManager,
- arrayView1d< real64 const > const & localSolution )
+CompositionalMultiphaseWell::calculateWellResidualNorm( real64 const & time_n,
+ real64 const & dt,
+ NonlinearSolverParameters const & nonlinearSolverParameters,
+ WellElementSubRegion const & subRegion,
+ DofManager const & dofManager,
+ arrayView1d< real64 const > const & localRhs )
{
GEOS_MARK_FUNCTION;
+ integer numNorm = 1; // mass balance
+ array1d< real64 > localResidualNorm;
+ array1d< real64 > localResidualNormalizer;
+
+ if( isThermal() )
+ {
+ numNorm = 2; // mass balance and energy balance
+ }
+ localResidualNorm.resize( numNorm );
+ localResidualNormalizer.resize( numNorm );
+
+ //globalIndex const rankOffset = dofManager.rankOffset();
string const wellDofKey = dofManager.getKey( wellElementDofName() );
- real64 scalingFactor = 1.0;
- real64 maxDeltaPres = 0.0, maxDeltaCompDens = 0.0, maxDeltaTemp = 0.0;
- real64 minPresScalingFactor = 1.0, minCompDensScalingFactor = 1.0, minTempScalingFactor = 1.0;
- forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &,
- MeshLevel & mesh,
- string_array const & regionNames )
+
+ //string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString() );
+ //MultiFluidBase const & fluid = subRegion.getConstitutiveModel< MultiFluidBase >( fluidName );
+
+ if( isWellOpen( ) )
{
- mesh.getElemManager().forElementSubRegions( regionNames,
- [&]( localIndex const,
- ElementSubRegionBase & subRegion )
+ localResidualNorm = calculateLocalWellResidualNorm( time_n,
+ dt,
+ nonlinearSolverParameters,
+ subRegion,
+ dofManager,
+ localRhs );
+
+ }
+ else
+ {
+ for( integer i=0; i const pressure = subRegion.getField< well::pressure >();
- arrayView1d< real64 const > const temperature = subRegion.getField< well::temperature >();
- arrayView2d< real64 const, compflow::USD_COMP > const compDens = subRegion.getField< well::globalCompDensity >();
- arrayView1d< real64 > pressureScalingFactor = subRegion.getField< well::pressureScalingFactor >();
- arrayView1d< real64 > temperatureScalingFactor = subRegion.getField< well::temperatureScalingFactor >();
- arrayView1d< real64 > compDensScalingFactor = subRegion.getField< well::globalCompDensityScalingFactor >();
- const integer temperatureOffset = m_numComponents+2;
- auto const subRegionData =
- m_isThermal
+ localResidualNorm[i] = 0.0;
+ }
+
+ }
+ // step 3: second reduction across MPI ranks
+ real64 resNorm=localResidualNorm[0];
+ if( isThermal() )
+ {
+ real64 globalResidualNorm[2]{};
+ globalResidualNorm[0] = MpiWrapper::max( localResidualNorm[0] );
+ globalResidualNorm[1] = MpiWrapper::max( localResidualNorm[1] );
+ resNorm=sqrt( globalResidualNorm[0] * globalResidualNorm[0] + globalResidualNorm[1] * globalResidualNorm[1] );
+
+ GEOS_LOG_LEVEL_RANK_0( logInfo::ResidualNorm, GEOS_FMT( " ( R{} ) = ( {:4.2e} ) ( Renergy ) = ( {:4.2e} )",
+ coupledSolverAttributePrefix(), globalResidualNorm[0], globalResidualNorm[1] ));
+
+ }
+ else
+ {
+ resNorm= MpiWrapper::max( resNorm );
+
+ GEOS_LOG_LEVEL_RANK_0( logInfo::ResidualNorm, GEOS_FMT( " ( R{} ) = ( {:4.2e} )",
+ coupledSolverAttributePrefix(), resNorm ));
+ }
+ return resNorm;
+}
+
+
+real64
+CompositionalMultiphaseWell::scalingForLocalSystemSolution( WellElementSubRegion & subRegion,
+ DofManager const & dofManager,
+ real64 & maxDeltaPres,
+ real64 & maxDeltaCompDens,
+ real64 & maxDeltaTemp,
+ real64 & minPresScalingFactor,
+ real64 & minCompDensScalingFactor,
+ real64 & minTempScalingFactor,
+ arrayView1d< real64 const > const & localSolution )
+{
+ GEOS_MARK_FUNCTION;
+
+ string const wellDofKey = dofManager.getKey( wellElementDofName() );
+
+ real64 scalingFactor = 1.0;
+ maxDeltaPres = 0.0;
+ maxDeltaCompDens = 0.0;
+ maxDeltaTemp = 0.0;
+ minPresScalingFactor = 1.0;
+ minCompDensScalingFactor = 1.0;
+ minTempScalingFactor = 1.0;
+
+ arrayView1d< real64 const > const pressure = subRegion.getField< fields::well::pressure >();
+ arrayView1d< real64 const > const temperature = subRegion.getField< fields::well::temperature >();
+ arrayView2d< real64 const, compflow::USD_COMP > const compDens = subRegion.getField< fields::well::globalCompDensity >();
+ arrayView1d< real64 > pressureScalingFactor = subRegion.getField< fields::well::pressureScalingFactor >();
+ arrayView1d< real64 > temperatureScalingFactor = subRegion.getField< fields::well::temperatureScalingFactor >();
+ arrayView1d< real64 > compDensScalingFactor = subRegion.getField< fields::well::globalCompDensityScalingFactor >();
+ const integer temperatureOffset = m_numComponents+2;
+ auto const subRegionData =
+ m_isThermal
? thermalCompositionalMultiphaseBaseKernels::
- SolutionScalingKernelFactory::
- createAndLaunch< parallelDevicePolicy<> >( m_maxRelativePresChange,
- m_maxAbsolutePresChange,
- m_maxRelativeTempChange,
- m_maxCompFracChange,
- m_maxRelativeCompDensChange,
- pressure,
- temperature,
- compDens,
- pressureScalingFactor,
- compDensScalingFactor,
- temperatureScalingFactor,
- dofManager.rankOffset(),
- m_numComponents,
- wellDofKey,
- subRegion,
- localSolution,
- temperatureOffset )
+ SolutionScalingKernelFactory::
+ createAndLaunch< parallelDevicePolicy<> >( m_maxRelativePresChange,
+ m_maxAbsolutePresChange,
+ m_maxRelativeTempChange,
+ m_maxCompFracChange,
+ m_maxRelativeCompDensChange,
+ pressure,
+ temperature,
+ compDens,
+ pressureScalingFactor,
+ compDensScalingFactor,
+ temperatureScalingFactor,
+ dofManager.rankOffset(),
+ m_numComponents,
+ wellDofKey,
+ subRegion,
+ localSolution,
+ temperatureOffset )
: isothermalCompositionalMultiphaseBaseKernels::
- SolutionScalingKernelFactory::
- createAndLaunch< parallelDevicePolicy<> >( m_maxRelativePresChange,
- m_maxAbsolutePresChange,
- m_maxCompFracChange,
- m_maxRelativeCompDensChange,
- pressure,
- compDens,
- pressureScalingFactor,
- compDensScalingFactor,
- dofManager.rankOffset(),
- m_numComponents,
- wellDofKey,
- subRegion,
- localSolution );
-
-
- scalingFactor = std::min( subRegionData.localMinVal, scalingFactor );
-
- maxDeltaPres = std::max( maxDeltaPres, subRegionData.localMaxDeltaPres );
- maxDeltaCompDens = std::max( maxDeltaCompDens, subRegionData.localMaxDeltaCompDens );
- maxDeltaTemp = std::max( maxDeltaTemp, subRegionData.localMaxDeltaTemp );
- minPresScalingFactor = std::min( minPresScalingFactor, subRegionData.localMinPresScalingFactor );
- minCompDensScalingFactor = std::min( minCompDensScalingFactor, subRegionData.localMinCompDensScalingFactor );
- minTempScalingFactor = std::min( minTempScalingFactor, subRegionData.localMinTempScalingFactor );
- } );
- } );
+ SolutionScalingKernelFactory::
+ createAndLaunch< parallelDevicePolicy<> >( m_maxRelativePresChange,
+ m_maxAbsolutePresChange,
+ m_maxCompFracChange,
+ m_maxRelativeCompDensChange,
+ pressure,
+ compDens,
+ pressureScalingFactor,
+ compDensScalingFactor,
+ dofManager.rankOffset(),
+ m_numComponents,
+ wellDofKey,
+ subRegion,
+ localSolution );
+
+
+ scalingFactor = std::min( subRegionData.localMinVal, scalingFactor );
+
+ maxDeltaPres = std::max( maxDeltaPres, subRegionData.localMaxDeltaPres );
+ maxDeltaCompDens = std::max( maxDeltaCompDens, subRegionData.localMaxDeltaCompDens );
+ maxDeltaTemp = std::max( maxDeltaTemp, subRegionData.localMaxDeltaTemp );
+ minPresScalingFactor = std::min( minPresScalingFactor, subRegionData.localMinPresScalingFactor );
+ minCompDensScalingFactor = std::min( minCompDensScalingFactor, subRegionData.localMinCompDensScalingFactor );
+ minTempScalingFactor = std::min( minTempScalingFactor, subRegionData.localMinTempScalingFactor );
scalingFactor = MpiWrapper::min( scalingFactor );
maxDeltaPres = MpiWrapper::max( maxDeltaPres );
@@ -1584,7 +1619,6 @@ CompositionalMultiphaseWell::scalingForSystemSolution( DomainPartition & domain,
getName(), GEOS_FMT( "{:.{}f}", maxDeltaTemp, 3 ) ) );
}
-
GEOS_LOG_LEVEL_RANK_0( logInfo::Solution,
GEOS_FMT( " {}: Min well pressure scaling factor: {}",
getName(), minPresScalingFactor ) );
@@ -1598,94 +1632,144 @@ CompositionalMultiphaseWell::scalingForSystemSolution( DomainPartition & domain,
getName(), minTempScalingFactor ) );
}
+ return LvArray::math::max( scalingFactor, m_minScalingFactor );
+
+}
+real64
+CompositionalMultiphaseWell::scalingForWellSystemSolution( WellElementSubRegion & subRegion,
+ DofManager const & dofManager,
+ arrayView1d< real64 const > const & localSolution )
+{
+ GEOS_MARK_FUNCTION;
+
+ string const wellDofKey = dofManager.getKey( wellElementDofName() );
+
+ real64 maxDeltaPres = 0.0, maxDeltaCompDens = 0.0, maxDeltaTemp = 0.0;
+ real64 minPresScalingFactor = 1.0, minCompDensScalingFactor = 1.0, minTempScalingFactor = 1.0;
+
+ real64 scalingFactor =scalingForLocalSystemSolution( subRegion,
+ dofManager,
+ maxDeltaPres,
+ maxDeltaCompDens,
+ maxDeltaTemp,
+ minPresScalingFactor,
+ minCompDensScalingFactor,
+ minTempScalingFactor,
+ localSolution );
+ string const massUnit = m_useMass ? "kg/m3" : "mol/m3";
+ GEOS_LOG_LEVEL_RANK_0( logInfo::Solution,
+ GEOS_FMT( " {}: Max well pressure change: {} Pa (before scaling)",
+ getName(), GEOS_FMT( "{:.{}f}", maxDeltaPres, 3 ) ) );
+ GEOS_LOG_LEVEL_RANK_0( logInfo::Solution,
+ GEOS_FMT( " {}: Max well component density change: {} {} (before scaling)",
+ getName(), GEOS_FMT( "{:.{}f}", maxDeltaCompDens, 3 ), massUnit ) );
+
+ if( m_isThermal )
+ {
+ maxDeltaTemp = MpiWrapper::max( maxDeltaTemp );
+ minTempScalingFactor = MpiWrapper::min( minTempScalingFactor );
+ GEOS_LOG_LEVEL_RANK_0( logInfo::Solution,
+ GEOS_FMT( " {}: Max well temperature change: {} K (before scaling)",
+ getName(), GEOS_FMT( "{:.{}f}", maxDeltaTemp, 3 ) ) );
+ }
+
+ GEOS_LOG_LEVEL_RANK_0( logInfo::Solution,
+ GEOS_FMT( " {}: Min well pressure scaling factor: {}",
+ getName(), minPresScalingFactor ) );
+ GEOS_LOG_LEVEL_RANK_0( logInfo::Solution,
+ GEOS_FMT( " {}: Min well component density scaling factor: {}",
+ getName(), minCompDensScalingFactor ) );
+ if( m_isThermal )
+ {
+ GEOS_LOG_LEVEL_RANK_0( logInfo::Solution,
+ GEOS_FMT( " {}: Min well temperature scaling factor: {}",
+ getName(), minTempScalingFactor ) );
+ }
return LvArray::math::max( scalingFactor, m_minScalingFactor );
}
+
+
bool
-CompositionalMultiphaseWell::checkSystemSolution( DomainPartition & domain,
- DofManager const & dofManager,
- arrayView1d< real64 const > const & localSolution,
- real64 const scalingFactor )
+CompositionalMultiphaseWell::checkWellSystemSolution( WellElementSubRegion & subRegion,
+ DofManager const & dofManager,
+ arrayView1d< real64 const > const & localSolution,
+ real64 const scalingFactor )
{
GEOS_MARK_FUNCTION;
string const wellDofKey = dofManager.getKey( wellElementDofName() );
integer localCheck = 1;
+
+
real64 minPres = 0.0, minDens = 0.0, minTotalDens = 0.0;
integer numNegPres = 0, numNegDens = 0, numNegTotalDens = 0;
- forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &,
- MeshLevel & mesh,
- string_array const & regionNames )
- {
- mesh.getElemManager().forElementSubRegions( regionNames,
- [&]( localIndex const,
- ElementSubRegionBase & subRegion )
- {
- //integer const m_allowCompDensChopping(true);
- integer const m_allowNegativePressure( false );
- compositionalMultiphaseUtilities::ScalingType const m_scalingType( compositionalMultiphaseUtilities::ScalingType::Global );
- arrayView1d< real64 const > const pressure =
- subRegion.getField< well::pressure >();
- arrayView1d< real64 const > const temperature =
- subRegion.getField< well::temperature >();
- arrayView2d< real64 const, compflow::USD_COMP > const compDens =
- subRegion.getField< well::globalCompDensity >();
- arrayView1d< real64 > pressureScalingFactor = subRegion.getField< well::pressureScalingFactor >();
- arrayView1d< real64 > temperatureScalingFactor = subRegion.getField< well::temperatureScalingFactor >();
- arrayView1d< real64 > compDensScalingFactor = subRegion.getField< well::globalCompDensityScalingFactor >();
-
- // check that pressure and component densities are non-negative
- // for thermal, check that temperature is above 273.15 K
- const integer temperatureOffset = m_numComponents+2;
- auto const subRegionData =
- m_isThermal
+ const std::string wellName = subRegion.getName();
+
+ //integer const m_allowCompDensChopping(true);
+ integer const m_allowNegativePressure( false );
+ compositionalMultiphaseUtilities::ScalingType const m_scalingType( compositionalMultiphaseUtilities::ScalingType::Global );
+ arrayView1d< real64 const > const pressure =
+ subRegion.getField< fields::well::pressure >();
+ arrayView1d< real64 const > const temperature =
+ subRegion.getField< fields::well::temperature >();
+ arrayView2d< real64 const, compflow::USD_COMP > const compDens =
+ subRegion.getField< fields::well::globalCompDensity >();
+ arrayView1d< real64 > pressureScalingFactor = subRegion.getField< fields::well::pressureScalingFactor >();
+ arrayView1d< real64 > temperatureScalingFactor = subRegion.getField< fields::well::temperatureScalingFactor >();
+ arrayView1d< real64 > compDensScalingFactor = subRegion.getField< fields::well::globalCompDensityScalingFactor >();
+
+ // check that pressure and component densities are non-negative
+ // for thermal, check that temperature is above 273.15 K
+ const integer temperatureOffset = m_numComponents+2;
+ auto const subRegionData =
+ m_isThermal
? thermalCompositionalMultiphaseBaseKernels::
- SolutionCheckKernelFactory::
- createAndLaunch< parallelDevicePolicy<> >( m_allowCompDensChopping,
- m_allowNegativePressure,
- m_scalingType,
- scalingFactor,
- pressure,
- temperature,
- compDens,
- pressureScalingFactor,
- temperatureScalingFactor,
- compDensScalingFactor,
- dofManager.rankOffset(),
- m_numComponents,
- wellDofKey,
- subRegion,
- localSolution,
- temperatureOffset )
+ SolutionCheckKernelFactory::
+ createAndLaunch< parallelDevicePolicy<> >( m_allowCompDensChopping,
+ m_allowNegativePressure,
+ m_scalingType,
+ scalingFactor,
+ pressure,
+ temperature,
+ compDens,
+ pressureScalingFactor,
+ temperatureScalingFactor,
+ compDensScalingFactor,
+ dofManager.rankOffset(),
+ m_numComponents,
+ wellDofKey,
+ subRegion,
+ localSolution,
+ temperatureOffset )
: isothermalCompositionalMultiphaseBaseKernels::
- SolutionCheckKernelFactory::
- createAndLaunch< parallelDevicePolicy<> >( m_allowCompDensChopping,
- m_allowNegativePressure,
- m_scalingType,
- scalingFactor,
- pressure,
- compDens,
- pressureScalingFactor,
- compDensScalingFactor,
- dofManager.rankOffset(),
- m_numComponents,
- wellDofKey,
- subRegion,
- localSolution );
-
- localCheck = std::min( localCheck, subRegionData.localMinVal );
-
- minPres = std::min( minPres, subRegionData.localMinPres );
- minDens = std::min( minDens, subRegionData.localMinDens );
- minTotalDens = std::min( minTotalDens, subRegionData.localMinTotalDens );
- numNegPres += subRegionData.localNumNegPressures;
- numNegDens += subRegionData.localNumNegDens;
- numNegTotalDens += subRegionData.localNumNegTotalDens;
- } );
- } );
+ SolutionCheckKernelFactory::
+ createAndLaunch< parallelDevicePolicy<> >( m_allowCompDensChopping,
+ m_allowNegativePressure,
+ m_scalingType,
+ scalingFactor,
+ pressure,
+ compDens,
+ pressureScalingFactor,
+ compDensScalingFactor,
+ dofManager.rankOffset(),
+ m_numComponents,
+ wellDofKey,
+ subRegion,
+ localSolution );
+
+ localCheck = std::min( localCheck, subRegionData.localMinVal );
+
+ minPres = std::min( minPres, subRegionData.localMinPres );
+ minDens = std::min( minDens, subRegionData.localMinDens );
+ minTotalDens = std::min( minTotalDens, subRegionData.localMinTotalDens );
+ numNegPres += subRegionData.localNumNegPressures;
+ numNegDens += subRegionData.localNumNegDens;
+ numNegTotalDens += subRegionData.localNumNegTotalDens;
+
minPres = MpiWrapper::min( minPres );
minDens = MpiWrapper::min( minDens );
@@ -1697,103 +1781,176 @@ CompositionalMultiphaseWell::checkSystemSolution( DomainPartition & domain,
if( numNegPres > 0 )
GEOS_LOG_LEVEL_RANK_0( logInfo::Solution,
GEOS_FMT( " {}: Number of negative well pressure values: {}, minimum value: {} Pa",
- getName(), numNegPres, fmt::format( "{:.{}f}", minPres, 3 ) ) );
+ subRegion.getName(), numNegPres, fmt::format( "{:.{}f}", minPres, 3 ) ) );
string const massUnit = m_useMass ? "kg/m3" : "mol/m3";
if( numNegDens > 0 )
GEOS_LOG_LEVEL_RANK_0( logInfo::Solution,
GEOS_FMT( " {}: Number of negative well component density values: {}, minimum value: {} {} ",
- getName(), numNegDens, fmt::format( "{:.{}f}", minDens, 3 ), massUnit ) );
+ subRegion.getName(), numNegDens, fmt::format( "{:.{}f}", minDens, 3 ), massUnit ) );
if( minTotalDens > 0 )
GEOS_LOG_LEVEL_RANK_0( logInfo::Solution,
GEOS_FMT( " {}: Number of negative total well density values: {}, minimum value: {} {} ",
- getName(), minTotalDens, fmt::format( "{:.{}f}", minDens, 3 ), massUnit ) );
+ subRegion.getName(), minTotalDens, fmt::format( "{:.{}f}", minDens, 3 ), massUnit ) );
+
+
return MpiWrapper::min( localCheck );
}
-void CompositionalMultiphaseWell::computePerforationRates( real64 const & time_n,
- real64 const & GEOS_UNUSED_PARAM( dt ),
- DomainPartition & domain )
+void CompositionalMultiphaseWell::computeWellPerforationRates( real64 const & time_n,
+ real64 const & GEOS_UNUSED_PARAM( dt ),
+ ElementRegionManager & elemManager,
+ WellElementSubRegion & subRegion )
{
GEOS_MARK_FUNCTION;
GEOS_UNUSED_VAR( time_n );
- forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &,
- MeshLevel & mesh,
- string_array const & regionNames )
+ //CompositionalMultiphaseBase const & flowSolver = getParent().getParent().getGroup< CompositionalMultiphaseBase >( getFlowSolverName() );
+
+ PerforationData * const perforationData = subRegion.getPerforationData();
+
+ if( isWellOpen() && !m_keepVariablesConstantDuringInitStep )
{
- // TODO: change the way we access the flowSolver here
- CompositionalMultiphaseBase const & flowSolver = getParent().getGroup< CompositionalMultiphaseBase >( getFlowSolverName() );
- ElementRegionManager & elemManager = mesh.getElemManager();
+ string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString() );
+ MultiFluidBase const & fluid = getConstitutiveModel< MultiFluidBase >( subRegion, fluidName );
+ bool isThermal = fluid.isThermal();
- elemManager.forElementSubRegions< WellElementSubRegion >( regionNames, [&]( localIndex const,
- WellElementSubRegion & subRegion )
+ if( isThermal )
{
- PerforationData * const perforationData = subRegion.getPerforationData();
- WellControls const & wellControls = getWellControls( subRegion );
- if( wellControls.getWellStatus() == WellControls::Status::OPEN && !m_keepVariablesConstantDuringInitStep )
- {
- string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString() );
- MultiFluidBase const & fluid = getConstitutiveModel< MultiFluidBase >( subRegion, fluidName );
- bool const isThermal = fluid.isThermal();
-
- if( isThermal )
- {
- thermalPerforationFluxKernels::
- PerforationFluxKernelFactory::
- createAndLaunch< parallelDevicePolicy<> >( m_numComponents,
- m_numPhases,
- flowSolver.getName(),
- perforationData,
- subRegion,
- fluid,
- elemManager,
- wellControls.isInjector(),
- wellControls.isCrossflowEnabled() );
- }
- else
- {
- isothermalPerforationFluxKernels::
- PerforationFluxKernelFactory::
- createAndLaunch< parallelDevicePolicy<> >( m_numComponents,
- m_numPhases,
- flowSolver.getName(),
- perforationData,
- subRegion,
- elemManager,
- wellControls.isInjector(),
- wellControls.isCrossflowEnabled() );
- }
- }
- else
+ thermalPerforationFluxKernels::
+ PerforationFluxKernelFactory::
+ createAndLaunch< parallelDevicePolicy<> >( m_numComponents,
+ m_numPhases,
+ getFlowSolverName(),
+ perforationData,
+ subRegion,
+ fluid,
+ elemManager,
+ isInjector(),
+ isCrossflowEnabled());
+ }
+ else
+ {
+ isothermalPerforationFluxKernels::
+ PerforationFluxKernelFactory::
+ createAndLaunch< parallelDevicePolicy<> >( m_numComponents,
+ m_numPhases,
+ getFlowSolverName(),
+ perforationData,
+ subRegion,
+ elemManager,
+ isInjector(),
+ isCrossflowEnabled() );
+ }
+ }
+ else
+ {
+ // Zero completion flow rate
+ arrayView2d< real64 > const compPerfRate = perforationData->getField< fields::well::compPerforationRate >();
+ for( integer iperf=0; iperfsize(); iperf++ )
+ {
+ for( integer ic = 0; ic < m_numComponents; ++ic )
{
- // Zero completion flow rate
- arrayView2d< real64 > const compPerfRate = perforationData->getField< well::compPerforationRate >();
- for( integer iperf=0; iperfsize(); iperf++ )
- {
- for( integer ic = 0; ic < m_numComponents; ++ic )
- {
- compPerfRate[iperf][ic] = 0.0;
- }
- }
+ compPerfRate[iperf][ic] = 0.0;
}
- } );
+ }
+ }
- } );
}
-
void
-CompositionalMultiphaseWell::applySystemSolution( DofManager const & dofManager,
- arrayView1d< real64 const > const & localSolution,
- real64 const scalingFactor,
- real64 const dt,
- DomainPartition & domain )
+CompositionalMultiphaseWell::applyWellBoundaryConditions( real64 const time_n,
+ real64 const dt,
+ ElementRegionManager & elemManager,
+ WellElementSubRegion & subRegion,
+ DofManager const & dofManager,
+ arrayView1d< real64 > const & localRhs,
+ CRSMatrixView< real64, globalIndex const > const & localMatrix )
{
+ GEOS_UNUSED_VAR( elemManager );
+ GEOS_UNUSED_VAR( time_n );
+
+ using namespace compositionalMultiphaseUtilities;
+
+ BitFlags< isothermalCompositionalMultiphaseBaseKernels::KernelFlags > kernelFlags;
+ if( useTotalMassEquation() )
+ kernelFlags.set( isothermalCompositionalMultiphaseBaseKernels::KernelFlags::TotalMassEquation );
+
+ integer const numComps = numFluidComponents();
+
+ globalIndex const rankOffset = dofManager.rankOffset();
+
+ string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString() );
+ MultiFluidBase const & fluid = subRegion.getConstitutiveModel< MultiFluidBase >( fluidName );
+
+ // if the well is shut, we neglect reservoir-well flow that may occur despite the zero rate
+ // therefore, we do not want to compute perforation rates and we simply assume they are zero
+
+ //bool const detectCrossflow =
+ // ( isInjector() ) && isCrossflowEnabled() &&
+ // getLogLevel() >= 1; // since detect crossflow requires communication, we detect it only if the logLevel is sufficiently high
+
+ if( !isWellOpen( ) )
+ {
+ return;
+ }
+
+ PerforationData const * const perforationData = subRegion.getPerforationData();
+
+ // get the degrees of freedom
+ string const wellDofKey = dofManager.getKey( wellElementDofName() );
+
+ if( isThermal ( ) )
+ {
+ coupledReservoirAndWellKernels::
+ ThermalCompositionalMultiPhaseWellFluxKernelFactory::
+ createAndLaunch< parallelDevicePolicy<> >( numComps,
+ *this,
+ isProducer(),
+ dt,
+ rankOffset,
+ wellDofKey,
+ subRegion,
+ perforationData,
+ fluid,
+ kernelFlags,
+ localRhs,
+ localMatrix );
+ }
+ else
+ {
+ coupledReservoirAndWellKernels::
+ IsothermalCompositionalMultiPhaseWellFluxKernelFactory::
+ createAndLaunch< parallelDevicePolicy<> >( numComps,
+ dt,
+ rankOffset,
+ wellDofKey,
+ subRegion,
+ perforationData,
+ fluid,
+ localRhs,
+ localMatrix,
+ kernelFlags );
+ }
+
+
+}
+
+void
+CompositionalMultiphaseWell::applyWellSystemSolution( DofManager const & dofManager,
+ arrayView1d< real64 const > const & localSolution,
+ real64 const scalingFactor,
+ real64 const dt,
+ DomainPartition & domain,
+ MeshLevel & mesh,
+ WellElementSubRegion & subRegion )
+{
+
+ GEOS_UNUSED_VAR( domain );
DofManager::CompMask pressureMask( m_numDofPerWellElement, 0, 1 );
DofManager::CompMask componentMask( m_numDofPerWellElement, 1, numFluidComponents()+1 );
DofManager::CompMask connRateMask( m_numDofPerWellElement, numFluidComponents()+1, numFluidComponents()+2 );
@@ -1801,513 +1958,991 @@ CompositionalMultiphaseWell::applySystemSolution( DofManager const & dofManager,
// update all the fields using the global damping coefficients
dofManager.addVectorToField( localSolution,
wellElementDofName(),
- well::pressure::key(),
+ fields::well::pressure::key(),
scalingFactor,
pressureMask );
dofManager.addVectorToField( localSolution,
wellElementDofName(),
- well::globalCompDensity::key(),
+ fields::well::globalCompDensity::key(),
scalingFactor,
componentMask );
dofManager.addVectorToField( localSolution,
wellElementDofName(),
- well::mixtureConnectionRate::key(),
+ fields::well::connectionRate::key(),
scalingFactor,
connRateMask );
if( isThermal() )
{
DofManager::CompMask temperatureMask( m_numDofPerWellElement, numFluidComponents()+2, numFluidComponents()+3 );
-
dofManager.addVectorToField( localSolution,
wellElementDofName(),
- well::temperature::key(),
+ fields::well::temperature::key(),
scalingFactor,
temperatureMask );
}
+
// if component density chopping is allowed, some component densities may be negative after the update
// these negative component densities are set to zero in this function
if( m_allowCompDensChopping )
{
- chopNegativeDensities( domain );
+ chopNegativeDensities( subRegion );
}
- forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &,
- MeshLevel & mesh,
- string_array const & regionNames )
+ // synchronize
+ FieldIdentifiers fieldsToBeSync;
+ if( isThermal() )
{
- // synchronize
- FieldIdentifiers fieldsToBeSync;
- if( isThermal() )
- {
- fieldsToBeSync.addElementFields( { well::pressure::key(),
- well::globalCompDensity::key(),
- well::mixtureConnectionRate::key(),
- well::temperature::key() },
- regionNames );
- }
- else
- {
- fieldsToBeSync.addElementFields( { well::pressure::key(),
- well::globalCompDensity::key(),
- well::mixtureConnectionRate::key() },
- regionNames );
- }
- CommunicationTools::getInstance().synchronizeFields( fieldsToBeSync,
- mesh,
- domain.getNeighbors(),
- true );
- } );
-
+ fieldsToBeSync.addElementFields( { fields::well::pressure::key(),
+ fields::well::globalCompDensity::key(),
+ fields::well::connectionRate::key(),
+ fields::well::temperature::key() },
+ getTargetRegionNames() );
+ }
+ else
+ {
+ fieldsToBeSync.addElementFields( { fields::well::pressure::key(),
+ fields::well::globalCompDensity::key(),
+ fields::well::connectionRate::key() },
+ getTargetRegionNames() );
+ }
+ CommunicationTools::getInstance().synchronizeFields( fieldsToBeSync,
+ mesh,
+ domain.getNeighbors(),
+ true );
}
-void CompositionalMultiphaseWell::chopNegativeDensities( DomainPartition & domain )
+
+
+void CompositionalMultiphaseWell::chopNegativeDensities( WellElementSubRegion & subRegion )
{
integer const numComp = m_numComponents;
- forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &,
- MeshLevel & mesh,
- string_array const & regionNames )
- {
- ElementRegionManager & elemManager = mesh.getElemManager();
+ arrayView1d< integer const > const & wellElemGhostRank = subRegion.ghostRank();
- elemManager.forElementSubRegions< WellElementSubRegion >( regionNames,
- [&]( localIndex const,
- WellElementSubRegion & subRegion )
- {
- arrayView1d< integer const > const & wellElemGhostRank = subRegion.ghostRank();
+ arrayView2d< real64, compflow::USD_COMP > const & wellElemCompDens =
+ subRegion.getField< fields::well::globalCompDensity >();
- arrayView2d< real64, compflow::USD_COMP > const & wellElemCompDens =
- subRegion.getField< well::globalCompDensity >();
-
- forAll< parallelDevicePolicy<> >( subRegion.size(), [=] GEOS_HOST_DEVICE ( localIndex const iwelem )
+ forAll< parallelDevicePolicy<> >( subRegion.size(), [=] GEOS_HOST_DEVICE ( localIndex const iwelem )
+ {
+ if( wellElemGhostRank[iwelem] < 0 )
+ {
+ for( integer ic = 0; ic < numComp; ++ic )
{
- if( wellElemGhostRank[iwelem] < 0 )
+ // we allowed for some densities to be slightly negative in CheckSystemSolution
+ // if the new density is negative, chop back to zero
+ if( wellElemCompDens[iwelem][ic] < 0 )
{
- for( integer ic = 0; ic < numComp; ++ic )
- {
- // we allowed for some densities to be slightly negative in CheckSystemSolution
- // if the new density is negative, chop back to zero
- if( wellElemCompDens[iwelem][ic] < 0 )
- {
- wellElemCompDens[iwelem][ic] = 0;
- }
- }
+ wellElemCompDens[iwelem][ic] = 0.0;
}
- } );
- } );
-
+ }
+ }
} );
+
}
-void CompositionalMultiphaseWell::resetStateToBeginningOfStep( DomainPartition & domain )
+void CompositionalMultiphaseWell::resetStateToBeginningOfStep( ElementRegionManager const & elemManager, WellElementSubRegion & subRegion )
{
- forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &,
- MeshLevel & mesh,
- string_array const & regionNames )
+
+ // get a reference to the primary variables on well elements
+ arrayView1d< real64 > const & wellElemPressure =
+ subRegion.getField< well::pressure >();
+ arrayView1d< real64 const > const & wellElemPressure_n =
+ subRegion.getField< well::pressure_n >();
+ wellElemPressure.setValues< parallelDevicePolicy<> >( wellElemPressure_n );
+
+ if( isThermal() )
{
+ // get a reference to the primary variables on well elements
+ arrayView1d< real64 > const & wellElemTemperature =
+ subRegion.getField< well::temperature >();
+ arrayView1d< real64 const > const & wellElemTemperature_n =
+ subRegion.getField< well::temperature_n >();
+ wellElemTemperature.setValues< parallelDevicePolicy<> >( wellElemTemperature_n );
+ }
+ arrayView2d< real64, compflow::USD_COMP > const & wellElemGlobalCompDensity =
+ subRegion.getField< well::globalCompDensity >();
+ arrayView2d< real64 const, compflow::USD_COMP > const & wellElemGlobalCompDensity_n =
+ subRegion.getField< well::globalCompDensity_n >();
+ wellElemGlobalCompDensity.setValues< parallelDevicePolicy<> >( wellElemGlobalCompDensity_n );
- ElementRegionManager & elemManager = mesh.getElemManager();
+ arrayView1d< real64 > const & connRate =
+ subRegion.getField< well::connectionRate >();
+ arrayView1d< real64 const > const & connRate_n =
+ subRegion.getField< well::connectionRate_n >();
+ connRate.setValues< parallelDevicePolicy<> >( connRate_n );
- elemManager.forElementSubRegions< WellElementSubRegion >( regionNames,
- [&]( localIndex const,
- WellElementSubRegion & subRegion )
- {
- // get a reference to the primary variables on well elements
- arrayView1d< real64 > const & wellElemPressure =
- subRegion.getField< well::pressure >();
- arrayView1d< real64 const > const & wellElemPressure_n =
- subRegion.getField< well::pressure_n >();
- wellElemPressure.setValues< parallelDevicePolicy<> >( wellElemPressure_n );
- if( isThermal() )
+ if( isWellOpen( ) )
+ {
+ updateSubRegionState( elemManager, subRegion );
+ }
+
+}
+
+void CompositionalMultiphaseWell::assembleWellConstraintTerms( real64 const & time_n,
+ real64 const & GEOS_UNUSED_PARAM( dt ),
+ WellElementSubRegion const & subRegion,
+ DofManager const & dofManager,
+ CRSMatrixView< real64, globalIndex const > const & localMatrix,
+ arrayView1d< real64 > const & localRhs )
+{
+ GEOS_MARK_FUNCTION;
+
+
+ // the rank that owns the reference well element is responsible for the calculations below.
+
+ if( !subRegion.isLocallyOwned() || !( getWellStatus() == WellControls::Status::OPEN ))
+ {
+ return;
+ }
+
+ if( isProducer() )
+ {
+ forSubGroups< MinimumBHPConstraint, ProductionConstraint< PhaseVolumeRateConstraint >, ProductionConstraint< MassRateConstraint >, ProductionConstraint< VolumeRateConstraint >,
+ ProductionConstraint< LiquidRateConstraint >
+ >( [&]( auto & constraint )
+ {
+ // Need to use name since there could be multiple constraints of the same type
+ if( constraint.getName() == getCurrentConstraint()->getName())
{
- // get a reference to the primary variables on well elements
- arrayView1d< real64 > const & wellElemTemperature =
- subRegion.getField< well::temperature >();
- arrayView1d< real64 const > const & wellElemTemperature_n =
- subRegion.getField< well::temperature_n >();
- wellElemTemperature.setValues< parallelDevicePolicy<> >( wellElemTemperature_n );
+ std::cout << "Assembling constraint: " << constraint.getName() << std::endl;
+ // found limiting constraint
+ constitutive::MultiFluidBase & fluidSeparator = getMultiFluidSeparator();
+ integer isThermal = fluidSeparator.isThermal();
+ integer const numComp = fluidSeparator.numFluidComponents();
+ geos::internal::kernelLaunchSelectorCompThermSwitch( numComp, isThermal, [&] ( auto NC, auto ISTHERMAL )
+ {
+ integer constexpr NUM_COMP = NC();
+ integer constexpr IS_THERMAL = ISTHERMAL();
+
+ wellConstraintKernels::ConstraintHelper< NUM_COMP, IS_THERMAL >::assembleConstraintEquation( time_n,
+ *this,
+ constraint,
+ subRegion,
+ dofManager.getKey( wellElementDofName() ),
+ dofManager.rankOffset(),
+ localMatrix,
+ localRhs );
+ } );
}
- arrayView2d< real64, compflow::USD_COMP > const & wellElemGlobalCompDensity =
- subRegion.getField< well::globalCompDensity >();
- arrayView2d< real64 const, compflow::USD_COMP > const & wellElemGlobalCompDensity_n =
- subRegion.getField< well::globalCompDensity_n >();
- wellElemGlobalCompDensity.setValues< parallelDevicePolicy<> >( wellElemGlobalCompDensity_n );
-
- arrayView1d< real64 > const & connRate =
- subRegion.getField< well::mixtureConnectionRate >();
- arrayView1d< real64 const > const & connRate_n =
- subRegion.getField< well::mixtureConnectionRate_n >();
- connRate.setValues< parallelDevicePolicy<> >( connRate_n );
- WellControls & wellControls = getWellControls( subRegion );
-
- if( wellControls.isWellOpen( ) )
+ } );
+ }
+ else
+ {
+ forSubGroups< MaximumBHPConstraint, InjectionConstraint< PhaseVolumeRateConstraint >, InjectionConstraint< MassRateConstraint >,
+ InjectionConstraint< VolumeRateConstraint >,
+ InjectionConstraint< LiquidRateConstraint >
+ >( [&]( auto & constraint )
+ {
+ if( constraint.getName() == getCurrentConstraint()->getName())
{
- updateSubRegionState( elemManager, subRegion );
+ // found limiting constraint
+ constitutive::MultiFluidBase & fluidSeparator = getMultiFluidSeparator();
+ integer isThermal = fluidSeparator.isThermal();
+ integer const numComp = fluidSeparator.numFluidComponents();
+ geos::internal::kernelLaunchSelectorCompThermSwitch( numComp, isThermal, [&] ( auto NC, auto ISTHERMAL )
+ {
+ integer constexpr NUM_COMP = NC();
+ integer constexpr IS_THERMAL = ISTHERMAL();
+
+ wellConstraintKernels::ConstraintHelper< NUM_COMP, IS_THERMAL >::assembleConstraintEquation( time_n,
+ *this,
+ constraint,
+ subRegion,
+ dofManager.getKey( wellElementDofName() ),
+ dofManager.rankOffset(),
+ localMatrix,
+ localRhs );
+ } );
}
} );
- } );
+ }
}
-void CompositionalMultiphaseWell::assemblePressureRelations( real64 const & time_n,
- real64 const & GEOS_UNUSED_PARAM( dt ),
- DomainPartition const & domain,
- DofManager const & dofManager,
- CRSMatrixView< real64, globalIndex const > const & localMatrix,
- arrayView1d< real64 > const & localRhs )
+void CompositionalMultiphaseWell::assembleWellPressureRelations( real64 const & GEOS_UNUSED_PARAM( time_n ),
+ real64 const & GEOS_UNUSED_PARAM( dt ),
+ WellElementSubRegion const & subRegion,
+ DofManager const & dofManager,
+ CRSMatrixView< real64, globalIndex const > const & localMatrix,
+ arrayView1d< real64 > const & localRhs )
{
GEOS_MARK_FUNCTION;
- forDiscretizationOnMeshTargets ( domain.getMeshBodies(), [&] ( string const &,
- MeshLevel const & mesh,
- string_array const & regionNames )
+ if( isWellOpen( ) && !m_keepVariablesConstantDuringInitStep )
{
+ string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString() );
+ MultiFluidBase const & fluid = getConstitutiveModel< MultiFluidBase >( subRegion, fluidName );
+ bool const isThermal = fluid.isThermal();
+ // get the degrees of freedom, depth info, next welem index
+ string const wellDofKey = dofManager.getKey( wellElementDofName() );
+ arrayView1d< globalIndex const > const & wellElemDofNumber =
+ subRegion.getReference< array1d< globalIndex > >( wellDofKey );
+ arrayView1d< real64 const > const & wellElemGravCoef =
+ subRegion.getField< well::gravityCoefficient >();
+ arrayView1d< localIndex const > const & nextWellElemIndex =
+ subRegion.getReference< array1d< localIndex > >( WellElementSubRegion::viewKeyStruct::nextWellElementIndexString() );
+
+ // get primary variables on well elements
+ arrayView1d< real64 const > const & wellElemPres =
+ subRegion.getField< well::pressure >();
+
+ // get total mass density on well elements (for potential calculations)
+ arrayView1d< real64 const > const & wellElemTotalMassDens =
+ subRegion.getField< well::totalMassDensity >();
+ arrayView2d< real64 const, compflow::USD_FLUID_DC > const & dWellElemTotalMassDens =
+ subRegion.getField< well::dTotalMassDensity >();
+
+ // segment status
+ arrayView1d< integer const > const elemStatus =subRegion.getLocalWellElementStatus();
+
+ bool controlHasSwitched = false;
+ isothermalCompositionalMultiphaseBaseKernels::
+ KernelLaunchSelectorCompTherm< compositionalMultiphaseWellKernels::PressureRelationKernel >
+ ( numFluidComponents(),
+ isThermal,
+ subRegion.size(),
+ dofManager.rankOffset(),
+ elemStatus,
+ wellElemDofNumber,
+ wellElemGravCoef,
+ nextWellElemIndex,
+ wellElemPres,
+ wellElemTotalMassDens,
+ dWellElemTotalMassDens,
+ controlHasSwitched,
+ localMatrix,
+ localRhs );
- ElementRegionManager const & elemManager = mesh.getElemManager();
+ }
- elemManager.forElementSubRegions< WellElementSubRegion >( regionNames,
- [&]( localIndex const,
- WellElementSubRegion const & subRegion )
- {
+}
- WellControls & wellControls = getWellControls( subRegion );
- if( wellControls.isWellOpen( ) && !m_keepVariablesConstantDuringInitStep )
- {
- string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString() );
- MultiFluidBase const & fluid = getConstitutiveModel< MultiFluidBase >( subRegion, fluidName );
- bool const isThermal = fluid.isThermal();
- // get the degrees of freedom, depth info, next welem index
- string const wellDofKey = dofManager.getKey( wellElementDofName() );
- arrayView1d< globalIndex const > const & wellElemDofNumber =
- subRegion.getReference< array1d< globalIndex > >( wellDofKey );
- arrayView1d< real64 const > const & wellElemGravCoef =
- subRegion.getField< well::gravityCoefficient >();
- arrayView1d< localIndex const > const & nextWellElemIndex =
- subRegion.getReference< array1d< localIndex > >( WellElementSubRegion::viewKeyStruct::nextWellElementIndexString() );
-
- // get primary variables on well elements
- arrayView1d< real64 const > const & wellElemPres =
- subRegion.getField< well::pressure >();
-
- // get total mass density on well elements (for potential calculations)
- arrayView1d< real64 const > const & wellElemTotalMassDens =
- subRegion.getField< well::totalMassDensity >();
- arrayView2d< real64 const, compflow::USD_FLUID_DC > const & dWellElemTotalMassDens =
- subRegion.getField< well::dTotalMassDensity >();
-
- // segment status
- arrayView1d< integer const > const elemStatus =subRegion.getLocalWellElementStatus();
-
- bool controlHasSwitched = false;
- isothermalCompositionalMultiphaseBaseKernels::
- KernelLaunchSelectorCompTherm< compositionalMultiphaseWellKernels::PressureRelationKernel >
- ( numFluidComponents(),
- isThermal,
- subRegion.size(),
- dofManager.rankOffset(),
- subRegion.isLocallyOwned(),
- subRegion.getTopWellElementIndex(),
- m_targetPhaseIndex,
- wellControls,
- time_n, // controls evaluated with BHP/rate of the beginning of step
- elemStatus,
- wellElemDofNumber,
- wellElemGravCoef,
- nextWellElemIndex,
- wellElemPres,
- wellElemTotalMassDens,
- dWellElemTotalMassDens,
- controlHasSwitched,
- localMatrix,
- localRhs );
-
- if( controlHasSwitched )
- {
- // TODO: move the switch logic into wellControls
- // TODO: implement a more general switch when more then two constraints per well type are allowed
- if( wellControls.getControl() == WellControls::Control::BHP )
- {
- if( wellControls.isProducer() )
- {
- wellControls.switchToPhaseRateControl( wellControls.getTargetPhaseRate( time_n ) );
- GEOS_LOG_LEVEL_RANK_0( logInfo::WellControl,
- GEOS_FMT( "Control switch for well {} from BHP constraint to phase volumetric rate constraint", subRegion.getName() ) );
- }
- else if( wellControls.getInputControl() == WellControls::Control::MASSRATE )
- {
- wellControls.switchToMassRateControl( wellControls.getTargetMassRate( time_n ) );
- GEOS_LOG_LEVEL_RANK_0( logInfo::WellControl,
- GEOS_FMT( "Control switch for well {} from BHP constraint to mass rate constraint", subRegion.getName()) );
- }
- else
- {
- wellControls.switchToTotalRateControl( wellControls.getTargetTotalRate( time_n ) );
- GEOS_LOG_LEVEL_RANK_0( logInfo::WellControl,
- GEOS_FMT( "Control switch for well {} from BHP constraint to total volumetric rate constraint", subRegion.getName()) );
- }
- }
- else
- {
- wellControls.switchToBHPControl( wellControls.getTargetBHP( time_n ) );
- GEOS_LOG_LEVEL_RANK_0( logInfo::WellControl,
- GEOS_FMT( "Control switch for well {} from rate constraint to BHP constraint", subRegion.getName() ) );
- }
- }
+void CompositionalMultiphaseWell::saveState( WellElementSubRegion & subRegion )
+{
- // If a well is opened and then timestep is cut resulting in the well being shut, if the well is opened
- // the well initialization code requires control type to by synced
- integer owner = -1;
- // Only subregion owner evaluates well control and control changes need to be broadcast to all ranks
- if( subRegion.isLocallyOwned() )
- {
- owner = MpiWrapper::commRank( MPI_COMM_GEOS );
- }
- owner = MpiWrapper::max( owner );
- WellControls::Control wellControl = wellControls.getControl();
- MpiWrapper::broadcast( wellControl, owner );
- wellControls.setControl( wellControl );
- }
+ // get a reference to the primary variables on well elements
+ arrayView1d< real64 const > const & wellElemPressure =
+ subRegion.getField< fields::well::pressure >();
+ arrayView2d< real64 const, compflow::USD_COMP > const & wellElemGlobalCompDensity =
+ subRegion.getField< fields::well::globalCompDensity >();
+ arrayView1d< real64 const > const & wellElemTemperature =
+ subRegion.getField< fields::well::temperature >();
+
+ arrayView1d< real64 > const & wellElemPressure_n =
+ subRegion.getField< fields::well::pressure_n >();
+ wellElemPressure_n.setValues< parallelDevicePolicy<> >( wellElemPressure );
+
+ if( isThermal() )
+ {
+
+ arrayView1d< real64 > const & wellElemTemperature_n =
+ subRegion.getField< fields::well::temperature_n >();
+ wellElemTemperature_n.setValues< parallelDevicePolicy<> >( wellElemTemperature );
+ }
+
+ arrayView2d< real64, compflow::USD_COMP > const & wellElemGlobalCompDensity_n =
+ subRegion.getField< fields::well::globalCompDensity_n >();
+ wellElemGlobalCompDensity_n.setValues< parallelDevicePolicy<> >( wellElemGlobalCompDensity );
+
+ arrayView1d< real64 const > const & connRate =
+ subRegion.getField< fields::well::connectionRate >();
+ arrayView1d< real64 > const & connRate_n =
+ subRegion.getField< fields::well::connectionRate_n >();
+ connRate_n.setValues< parallelDevicePolicy<> >( connRate );
+
+ arrayView2d< real64 const, compflow::USD_PHASE > const wellElemPhaseVolFrac =
+ subRegion.getField< fields::well::phaseVolumeFraction >();
+ arrayView2d< real64, compflow::USD_PHASE > const wellElemPhaseVolFrac_n =
+ subRegion.getField< fields::well::phaseVolumeFraction_n >();
+ wellElemPhaseVolFrac_n.setValues< parallelDevicePolicy<> >( wellElemPhaseVolFrac );
+
+ string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString() );
+ MultiFluidBase const & fluid = getConstitutiveModel< MultiFluidBase >( subRegion, fluidName );
+ fluid.saveConvergedState();
+
- } );
- } );
}
void CompositionalMultiphaseWell::implicitStepSetup( real64 const & time_n,
real64 const & dt,
- DomainPartition & domain )
+ ElementRegionManager & elemManager,
+ WellElementSubRegion & subRegion )
{
- WellSolverBase::implicitStepSetup( time_n, dt, domain );
+ WellControls::implicitStepSetup( time_n, dt, elemManager, subRegion );
- forDiscretizationOnMeshTargets ( domain.getMeshBodies(), [&] ( string const &,
- MeshLevel & mesh,
- string_array const & regionNames )
+ if( isWellOpen() )
{
+ // get a reference to the primary variables on well elements
+ arrayView1d< real64 const > const & wellElemPressure =
+ subRegion.getField< fields::well::pressure >();
+ arrayView2d< real64 const, compflow::USD_COMP > const & wellElemGlobalCompDensity =
+ subRegion.getField< fields::well::globalCompDensity >();
+ arrayView1d< real64 const > const & wellElemTemperature =
+ subRegion.getField< fields::well::temperature >();
+
+ arrayView1d< real64 > const & wellElemPressure_n =
+ subRegion.getField< fields::well::pressure_n >();
+ wellElemPressure_n.setValues< parallelDevicePolicy<> >( wellElemPressure );
- ElementRegionManager & elemManager = mesh.getElemManager();
-
- elemManager.forElementSubRegions< WellElementSubRegion >( regionNames,
- [&]( localIndex const,
- WellElementSubRegion & subRegion )
+ if( isThermal() )
{
+ arrayView1d< real64 > const & wellElemTemperature_n =
+ subRegion.getField< fields::well::temperature_n >();
+ wellElemTemperature_n.setValues< parallelDevicePolicy<> >( wellElemTemperature );
+ }
+
+ arrayView2d< real64, compflow::USD_COMP > const & wellElemGlobalCompDensity_n =
+ subRegion.getField< fields::well::globalCompDensity_n >();
+ wellElemGlobalCompDensity_n.setValues< parallelDevicePolicy<> >( wellElemGlobalCompDensity );
+
+ arrayView1d< real64 const > const & connRate =
+ subRegion.getField< fields::well::connectionRate >();
+ arrayView1d< real64 > const & connRate_n =
+ subRegion.getField< fields::well::connectionRate_n >();
+ connRate_n.setValues< parallelDevicePolicy<> >( connRate );
+
+ arrayView2d< real64 const, compflow::USD_PHASE > const wellElemPhaseVolFrac =
+ subRegion.getField< fields::well::phaseVolumeFraction >();
+ arrayView2d< real64, compflow::USD_PHASE > const wellElemPhaseVolFrac_n =
+ subRegion.getField< fields::well::phaseVolumeFraction_n >();
+ wellElemPhaseVolFrac_n.setValues< parallelDevicePolicy<> >( wellElemPhaseVolFrac );
- WellControls & wellControls = getWellControls( subRegion );
- if( wellControls.isWellOpen() )
+ string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString() );
+ MultiFluidBase const & fluid = getConstitutiveModel< MultiFluidBase >( subRegion, fluidName );
+ fluid.saveConvergedState();
+
+ validateWellConstraints( time_n, dt, subRegion );
+
+ updateSubRegionState( elemManager, subRegion );
+ }
+
+}
+
+void CompositionalMultiphaseWell::implicitStepComplete( real64 const & time_n,
+ real64 const & dt,
+ WellElementSubRegion const & subRegion )
+{
+ printRates( time_n, dt, subRegion );
+}
+
+void CompositionalMultiphaseWell::printRates( real64 const & time_n,
+ real64 const & dt,
+ WellElementSubRegion const & subRegion )
+{
+
+ integer const numPhase = m_numPhases;
+ integer const numComp = m_numComponents;
+ integer const numPerf = subRegion.getPerforationData()->size();
+
+
+
+ stdVector< double > compRate( numComp, 0.0 );
+ if( m_writeCSV > 0 && isWellOpen( ) )
+ {
+ arrayView2d< real64 const > const & compPerfRate = subRegion.getPerforationData()->getField< fields::well::compPerforationRate >();
+
+ // bring everything back to host, capture the scalars by reference
+ forAll< serialPolicy >( 1, [&numComp,
+ &numPerf,
+ compPerfRate,
+ &compRate] ( localIndex const )
+ {
+ for( integer ic = 0; ic < numComp; ++ic )
{
- // get a reference to the primary variables on well elements
- arrayView1d< real64 const > const & wellElemPressure =
- subRegion.getField< fields::well::pressure >();
- arrayView2d< real64 const, compflow::USD_COMP > const & wellElemGlobalCompDensity =
- subRegion.getField< fields::well::globalCompDensity >();
- arrayView1d< real64 const > const & wellElemTemperature =
- subRegion.getField< fields::well::temperature >();
-
- arrayView1d< real64 > const & wellElemPressure_n =
- subRegion.getField< fields::well::pressure_n >();
- wellElemPressure_n.setValues< parallelDevicePolicy<> >( wellElemPressure );
-
- if( isThermal() )
+ for( integer iperf = 0; iperf < numPerf; iperf++ )
{
- arrayView1d< real64 > const & wellElemTemperature_n =
- subRegion.getField< fields::well::temperature_n >();
- wellElemTemperature_n.setValues< parallelDevicePolicy<> >( wellElemTemperature );
+ compRate[ic] += compPerfRate[iperf][ic];
}
+ }
+ } );
+ for( integer ic = 0; ic < numComp; ++ic )
+ {
+ compRate[ic] = MpiWrapper::sum( compRate[ic] );
+ }
+ }
- arrayView2d< real64, compflow::USD_COMP > const & wellElemGlobalCompDensity_n =
- subRegion.getField< fields::well::globalCompDensity_n >();
- wellElemGlobalCompDensity_n.setValues< parallelDevicePolicy<> >( wellElemGlobalCompDensity );
+ // the rank that owns the reference well element is responsible for the calculations below.
+ if( !subRegion.isLocallyOwned() )
+ {
+ return;
+ }
- arrayView1d< real64 const > const & connRate =
- subRegion.getField< fields::well::mixtureConnectionRate >();
- arrayView1d< real64 > const & connRate_n =
- subRegion.getField< fields::well::mixtureConnectionRate_n >();
- connRate_n.setValues< parallelDevicePolicy<> >( connRate );
+ string const wellControlsName = getName();
- arrayView2d< real64 const, compflow::USD_PHASE > const wellElemPhaseVolFrac =
- subRegion.getField< fields::well::phaseVolumeFraction >();
- arrayView2d< real64, compflow::USD_PHASE > const wellElemPhaseVolFrac_n =
- subRegion.getField< fields::well::phaseVolumeFraction_n >();
- wellElemPhaseVolFrac_n.setValues< parallelDevicePolicy<> >( wellElemPhaseVolFrac );
+ // format: time,total_rate,total_vol_rate,phase0_vol_rate,phase1_vol_rate,...
+ std::ofstream outputFile;
+ if( m_writeCSV > 0 )
+ {
+ outputFile.open( m_ratesOutputDir + "/" + wellControlsName + ".csv", std::ios_base::app );
+ outputFile << time_n << "," << dt;
+ }
+
+ if( getWellStatus() == WellControls::Status::CLOSED )
+ {
+ GEOS_LOG( GEOS_FMT( "{}: well is shut", wellControlsName ) );
+ if( outputFile.is_open())
+ {
+ // print all zeros in the rates file
+ outputFile << ",0.0,0.0,0.0";
+ for( integer ip = 0; ip < numPhase; ++ip )
+ {
+ outputFile << ",0.0";
+ }
+ for( integer ic = 0; ic < numComp; ++ic )
+ {
+ outputFile << ",0.0";
+ }
+ outputFile << std::endl;
+ outputFile.close();
+ }
+ return;
+ }
- string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString() );
- MultiFluidBase const & fluid = getConstitutiveModel< MultiFluidBase >( subRegion, fluidName );
- fluid.saveConvergedState();
+ localIndex const iwelemRef = subRegion.getTopWellElementIndex();
+ string const massUnit = m_useMass ? "kg" : "mol";
- validateWellConstraints( time_n, dt, subRegion );
+ // subRegion data
- updateSubRegionState( elemManager, subRegion );
+ arrayView1d< real64 const > const & connRate =
+ subRegion.getField< well::connectionRate >();
+
+ integer const useSurfaceCond = useSurfaceConditions();
+
+ real64 const & currentBHP =
+ getReference< real64 >( CompositionalMultiphaseWell::viewKeyStruct::currentBHPString() );
+ arrayView1d< real64 const > const & currentPhaseVolRate =
+ getReference< array1d< real64 > >( CompositionalMultiphaseWell::viewKeyStruct::currentPhaseVolRateString() );
+ real64 const & currentTotalVolRate =
+ getReference< real64 >( CompositionalMultiphaseWell::viewKeyStruct::currentTotalVolRateString() );
+
+ // bring everything back to host, capture the scalars by reference
+ forAll< serialPolicy >( 1, [&numPhase,
+ &numComp,
+ &useSurfaceCond,
+ ¤tBHP,
+ connRate,
+ ¤tTotalVolRate,
+ currentPhaseVolRate,
+ &compRate,
+ &iwelemRef,
+ &wellControlsName,
+ &massUnit,
+ &outputFile] ( localIndex const )
+ {
+ string const conditionKey = useSurfaceCond ? "surface" : "reservoir";
+ string const unitKey = useSurfaceCond ? "s" : "r";
+
+ real64 const currentTotalRate = connRate[iwelemRef];
+ GEOS_LOG( GEOS_FMT( "{}: BHP (at the specified reference elevation): {} Pa",
+ wellControlsName, currentBHP ) );
+ GEOS_LOG( GEOS_FMT( "{}: Total rate: {} {}/s; total {} volumetric rate: {} {}m3/s",
+ wellControlsName, currentTotalRate, massUnit, conditionKey, currentTotalVolRate, unitKey ) );
+ for( integer ip = 0; ip < numPhase; ++ip )
+ GEOS_LOG( GEOS_FMT( "{}: Phase {} {} volumetric rate: {} {}m3/s",
+ wellControlsName, ip, conditionKey, currentPhaseVolRate[ip], unitKey ) );
+ if( outputFile.is_open())
+ {
+ outputFile << "," << currentBHP;
+ outputFile << "," << currentTotalRate << "," << currentTotalVolRate;
+ for( integer ip = 0; ip < numPhase; ++ip )
+ {
+ outputFile << "," << currentPhaseVolRate[ip];
}
- } )
- ;
+ for( integer ic = 0; ic < numComp; ++ic )
+ {
+ outputFile << "," << compRate[ic];
+ }
+ outputFile << std::endl;
+ outputFile.close();
+ }
} );
+
}
-void CompositionalMultiphaseWell::implicitStepComplete( real64 const & time_n,
- real64 const & dt,
- DomainPartition & domain )
+bool CompositionalMultiphaseWell::solveWHPConstraint( real64 const & time_n,
+ real64 const & dt,
+ integer const cycleNumber,
+ integer const coupledIterationNumber,
+ DomainPartition & domain,
+ MeshLevel & mesh,
+ ElementRegionManager & elemManager,
+ WellElementSubRegion & subRegion )
{
- WellSolverBase::implicitStepComplete( time_n, dt, domain );
+ bool whpLimiting = false;
- if( getLogLevel() > 0 )
+ MinimumWHPConstraint * whpConstraint = getMinWHPConstraint();
+ if( whpConstraint == nullptr || !whpConstraint->isConstraintActive() )
+ return whpLimiting;
+
+ real64 & currentBHP = getReference< real64 >( CompositionalMultiphaseWell::viewKeyStruct::currentBHPString() );
+ array1d< real64 > & currentPhaseVolRate =
+ getReference< array1d< real64 > >( CompositionalMultiphaseWell::viewKeyStruct::currentPhaseVolRateString() );
+ real64 & currentTotalVolRate =
+ getReference< real64 >( CompositionalMultiphaseWell::viewKeyStruct::currentTotalVolRateString() );
+
+ real64 currentBHP_local = currentBHP;
+ array1d< real64 > currentPhaseVolRate_local = currentPhaseVolRate;
+ real64 currentTotalVolRate_local = currentTotalVolRate;
+ // Turn off BHP for WHP constraint if active, will be reset if WHP is limiting
+ MinimumBHPConstraint * bhpConstraint= getMinimumBHPConstraintForWHP();
+ bhpConstraint->setConstraintActive( false );
+ real64 constraintWHP = whpConstraint->getConstraintValue( time_n );
+ real64 currentWHP = constraintWHP;
+ integer owner = -1;
+
+ // Get the flow table function
+ FunctionManager & functionManager = FunctionManager::getInstance();
+ const PipeFlowTableFunction & m_flowTable = functionManager.getGroup< PipeFlowTableFunction const >( whpConstraint->getFlowTableName());
+ //m_flowTable.writeTable();
+ integer flowTableSolveState;
+
+ // this will be deleted with next merge
+ if( subRegion.isLocallyOwned() )
+ {
+ owner = MpiWrapper::commRank( MPI_COMM_GEOS );
+ }
+ owner = MpiWrapper::max( owner );
+
+ MpiWrapper::broadcast( currentWHP,
+ owner );
+
+ // get current WHP from flow table
+ m_flowTable.calculateWHP( getName(), currentBHP, currentPhaseVolRate, currentWHP, flowTableSolveState );
+ getReference< real64 >( viewKeyStruct::currentWHPString() ) = currentWHP;
+ //getMinBHPConstraint()->setConstraintActive( true );
+
+ std::cout << getName() << " " < * liqConstraint= getMaxLiquidConstraintForWHP();
+ setCurrentConstraint( liqConstraint );
+ liqConstraint->setConstraintActive( true );
+
+ setControl( static_cast< WellControls::Control >(liqConstraint->getControl()) ); // tjb old
+ WellControls::Control wellControl = getControl();
+ MpiWrapper::broadcast( wellControl, owner );
+ setControl( wellControl );
+
+ // lower bracker IPR solve
+ liqConstraint->setConstraintValue( -ql0 );
+ m_wellNewtonSolver.solveNonlinearSystem( *this, time_n,
+ dt,
+ cycleNumber,
+ domain,
+ mesh,
+ elemManager,
+ subRegion );
+ real64 iprBHP0 = currentBHP;
+ std::cout << time_n << " IPRBracketSolve0 " << getName() << " whp " << currentWHP << " BHP " << " " << currentBHP << " " << liqConstraint->bottomHolePressure() << " " << -ql0 <<
+ " " << liqConstraint->phaseVolumeRates() << " " << liqConstraint->totalVolumeRate() << " " <<
+ liqConstraint->massRate() << std::endl;
+ // upper bracket IPR solve
+ liqConstraint->setConstraintValue( -ql1 );
+ m_wellNewtonSolver.solveNonlinearSystem( *this, time_n,
+ dt,
+ cycleNumber,
+ domain,
+ mesh,
+ elemManager,
+ subRegion );
+ real64 iprBHP1 = currentBHP;
+ std::cout << time_n << " IPRBracketSolve1 " << getName() << " whp " << currentWHP << " BHP " << currentBHP << " " << liqConstraint->bottomHolePressure() << " " << -ql1 << " " <<
+ liqConstraint->phaseVolumeRates() << " " << liqConstraint->totalVolumeRate() << " " <<
+ liqConstraint->massRate() << std::endl;
+ liqConstraint->setConstraintActive( false );
+ real64 dP_dQ_ipr = ( iprBHP1 - iprBHP0 ) / ( -ql1 - (-ql0) );
+ if( dP_dQ_ipr > dP_dQ_table )
+ {
+ std::cout << time_n << " " << getName() << " WHP 0 constraint stability dP/dQ table " << dP_dQ_table << " dP/dQ ipr " << dP_dQ_ipr << " " << currentWHP << " " <<
+ (currentWHP < constraintWHP) << std::endl;
+ dP_dQ_table = dP_dQ_ipr;
+ whpLimiting = currentWHP < constraintWHP;
+ }
+ else
+ {
+ // set so well operates at minwhp
+ currentWHP = constraintWHP;
+ std::cout << time_n << " " << getName() << " WHP 1 constraint stability dP/dQ table " << dP_dQ_table << " dP/dQ ipr " << dP_dQ_ipr << " " << currentWHP << " " <<
+ (currentWHP < constraintWHP) << std::endl;
+
+ whpLimiting = true;
+ }
+ currentBHP = currentBHP_local;
+ currentPhaseVolRate = currentPhaseVolRate_local;
+ currentTotalVolRate = currentTotalVolRate_local;
+ }
+ else
+ {
+ // currentWHP is stable value
+ whpLimiting = currentWHP < constraintWHP;
+ std::cout << time_n << " " << getName() << " WHP 2 constraint stability dP/dQ table " << dP_dQ_table << " " << currentWHP << " " << (currentWHP < constraintWHP) << std::endl;
+
+
+ }
+
+ }
+ else
+ {
+ // no stab check
+ whpLimiting = currentWHP < constraintWHP;
+ }
+
+ if( whpLimiting )
{
- printRates( time_n, dt, domain );
+
+ std::cout << getName() << " WHP constraint violated " << currentWHP << " < " << constraintWHP << " bhp " << currentBHP << " phase rates " << currentPhaseVolRate << " total vol " <<
+ currentTotalVolRate <<
+ std::endl;
+ // WHP is limiting set WHP to constraint value
+ currentWHP = constraintWHP;
+
+
+
+ // sets. tjb cleanup
+ ProductionConstraint< LiquidRateConstraint > * liqConstraint= getMaxLiquidConstraintForWHP();
+ setCurrentConstraint( liqConstraint );
+ liqConstraint->setConstraintActive( true );
+ setControl( static_cast< WellControls::Control >(liqConstraint->getControl()) ); // tjb old
+ WellControls::Control wellControl = getControl();
+ MpiWrapper::broadcast( wellControl, owner );
+ setControl( wellControl );
+ std::ofstream of;
+ of.open( "fl.csv" );
+ of << "liq ,bhp ,tablebhp"<< std::endl;
+ // Liquid constraint is used to find intersection of IPR and VLP
+ const array1d< real64 > & liquidRates = m_flowTable.getRates();
+ std::cout << liquidRates << std::endl;
+ integer numRates = liquidRates.size();
+ real64 liqRate0;
+ real64 bhp0;
+ real64 tableBHP0;
+
+ liqRate0= liquidRates[0];
+ for( integer i=0; i < 1; ++i ) // numRates; ++i )
+ {
+ of << liquidRates[i] << ",";
+ liqRate0 = liquidRates[i];
+ liqConstraint->setConstraintValue( liqRate0 );
+
+ m_wellNewtonSolver.solveNonlinearSystem( *this, time_n,
+ dt,
+ cycleNumber,
+ domain,
+ mesh,
+ elemManager,
+ subRegion );
+ bhp0 = currentBHP;
+ flowTableSolveState=1;
+ m_flowTable.calculateBHP( currentPhaseVolRate, currentWHP, tableBHP0,
+ flowTableSolveState );
+ std::cout << getName() << " Solve at liquid rate [0] " << liqRate0 << " whp " << constraintWHP << " bhp " << bhp0 << " table bhp " << tableBHP0<< " phase rates " <<
+ currentPhaseVolRate <<
+ " total vol " << currentTotalVolRate << std::endl;
+ of << bhp0 << "," << tableBHP0 << std::endl;
+ }
+ of.close();
+ bool cSolve=false;
+ integer currentRateIndex=numRates;
+ while( !cSolve && currentRateIndex > 0 )
+ {
+ currentRateIndex--;
+ liqConstraint->setConstraintValue( liquidRates[currentRateIndex] );
+ cSolve = m_wellNewtonSolver.solveNonlinearSystem( *this, time_n,
+ dt,
+ cycleNumber,
+ domain,
+ mesh,
+ elemManager,
+ subRegion );
+
+ }
+ if( !cSolve )
+ {
+ throw("ft solve ");
+ }
+ real64 bhp1 = currentBHP;
+ real64 liqRate1 = liquidRates[currentRateIndex];
+ real64 tableBHP1;
+ m_flowTable.calculateBHP( currentPhaseVolRate, currentWHP, tableBHP1,
+ flowTableSolveState );
+
+ std::cout << time_n <(bhpConstraint->getControl()) );
+
+ integer const maxIters=100;
+ real64 const tol = 1;
+ integer iter = 0;
+
+ of.open( "fls_"+getName() + "_"+std::to_string( time_n )+"_"+std::to_string( dt )+"_"+std::to_string( coupledIterationNumber )+".csv" );
+ of << " error,wellbhp,tablebhp,whp,orate,grate,wrate " << std::endl;
+ bhpConstraint->setConstraintActive( true );
+ while( iter < maxIters && std::abs( tableBHP1 - bhp1 ) > tol )
+ {
+ // update whp
+ bhp1=bhp1+0.50*(tableBHP1-bhp1);
+
+ bhpConstraint->setConstraintValue( bhp1 );
+ std::cout << "SolveLinear system " << iter << std::endl;
+ m_wellNewtonSolver.solveNonlinearSystem( *this, time_n,
+ dt,
+ cycleNumber,
+ domain,
+ mesh,
+ elemManager,
+ subRegion );
+ //bhp1 = currentBHP; // current(s) were updated in solveNonlinearSystem
+
+ m_flowTable.calculateBHP( currentPhaseVolRate, currentWHP, tableBHP1,
+ flowTableSolveState );
+ of << std::abs( bhp1-tableBHP1 ) << ","<setConstraintValue( bhp1 );
+
+ ++iter;
+ }
+ of.close();
+ if( false )
+ {
+ getReference< real64 >( CompositionalMultiphaseWell::viewKeyStruct::currentWHPString() ) = currentWHP;
+ // Use liquid constraint to find intersection of IPR and VLP
+ setControl( static_cast< WellControls::Control >(bhpConstraint->getControl()) ); // tjb old
+ setCurrentConstraint( bhpConstraint );
+ setControl( static_cast< WellControls::Control >(bhpConstraint->getControl()) ); // tjb old
+ setCurrentConstraint( bhpConstraint );
+ getMinBHPConstraint()->setConstraintActive( false );
+ bhpConstraint->setBHP ( getReference< real64 >( CompositionalMultiphaseWell::viewKeyStruct::currentBHPString() ));
+ bhpConstraint->setPhaseVolumeRates ( getReference< array1d< real64 > >(
+ CompositionalMultiphaseWell::viewKeyStruct::currentPhaseVolRateString() ) );
+ bhpConstraint->setTotalVolumeRate ( getReference< real64 >(
+ CompositionalMultiphaseWell::viewKeyStruct::currentTotalVolRateString() ));
+ bhpConstraint->setMassRate( getReference< real64 >( CompositionalMultiphaseWell::viewKeyStruct::currentMassRateString() ));
+
+ std::cout << bhpConstraint->getName() << " " << bhpConstraint->bottomHolePressure() << " " << bhpConstraint->phaseVolumeRates() << " " << bhpConstraint->totalVolumeRate() << " " <<
+ bhpConstraint->massRate() <<
+ std::endl;
+ GEOS_LOG_RANK_IF ( getLogLevel() > 4 && subRegion.isLocallyOwned(),
+ " Well " << subRegion.getName() << " New Limiting Constraint " << bhpConstraint->getName() << " active " << bhpConstraint->isConstraintActive() <<
+ " value " << bhpConstraint->getConstraintValue( time_n ) );
+ std::cout << " WHP limiting solved in " << iter << " iters " << std::endl;
+ std::cout << " Final WHP " << currentWHP << " BHP " << currentBHP << " phase rates " << currentPhaseVolRate << " total vol " << currentTotalVolRate << std::endl;
+ }
+ else
+ {
+ bhpConstraint->setConstraintActive( false );
+ liqConstraint->setConstraintValue( -currentPhaseVolRate[0]- currentPhaseVolRate[2] );
+ liqConstraint->setConstraintActive( true );
+ setCurrentConstraint( liqConstraint );
+ setControl( static_cast< WellControls::Control >(liqConstraint->getControl()) ); // tjb old
+ liqConstraint->setBHP ( getReference< real64 >( CompositionalMultiphaseWell::viewKeyStruct::currentBHPString() ));
+ liqConstraint->setPhaseVolumeRates ( getReference< array1d< real64 > >(
+ CompositionalMultiphaseWell::viewKeyStruct::currentPhaseVolRateString() ) );
+ liqConstraint->setTotalVolumeRate ( getReference< real64 >(
+ CompositionalMultiphaseWell::viewKeyStruct::currentTotalVolRateString() ));
+ liqConstraint->setMassRate( getReference< real64 >( CompositionalMultiphaseWell::viewKeyStruct::currentMassRateString() ));
+ std::cout << getName() << " WHPConstraint " << liqConstraint->getName() << " limiting " << whpLimiting << " " << liqConstraint->bottomHolePressure() << " " <<
+ liqConstraint->phaseVolumeRates() << " " << liqConstraint->totalVolumeRate() << " " <<
+ liqConstraint->massRate() <<
+ std::endl;
+
+ }
+
}
+ return whpLimiting;
}
-void CompositionalMultiphaseWell::printRates( real64 const & time_n,
- real64 const & dt,
- DomainPartition & domain )
+void CompositionalMultiphaseWell::outputSingleWellDebug( real64 const time,
+ real64 const dt,
+ integer current_newton_iteration,
+ MeshLevel & mesh,
+ WellElementSubRegion & subRegion,
+ DofManager const & dofManager,
+ CRSMatrixView< real64, globalIndex const > const & localMatrix,
+ arrayView1d< const real64 > const & localRhs )
{
- forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &,
- MeshLevel & mesh,
- string_array const & regionNames )
+ GEOS_UNUSED_VAR( time );
+ GEOS_UNUSED_VAR( dofManager );
+ GEOS_UNUSED_VAR( localMatrix );
+ GEOS_UNUSED_VAR( localRhs );
+
+ integer num_timestep_cuts =0;
+ if( m_writeSegDebug > 1 )
{
+ CompositionalMultiphaseBase const & flowSolver = getParent().getGroup< CompositionalMultiphaseBase >( getFlowSolverName() );
+ auto solver_names = getParent().getSubGroupsNames();
+//integer n = solver_names.size();
+// Bit of a hack, cases with > 3 solvers we need to find the base solver for wells
+// Assume that solver definition order follows coupledreswell, res, and then well
+//std::string coupled_solver_name = solver_names[n-3];
+
+//GeosxState & gs = getGlobalState();
+
+//CompositionalMultiphaseReservoirAndWells< CompositionalMultiphaseBase > * solver =
+// &(gs.getProblemManager().getPhysicsSolverManager().getGroup< geos::CompositionalMultiphaseReservoirAndWells<
+// geos::CompositionalMultiphaseBase > >( coupled_solver_name ));
+
+ EventManager const & event = getGroupByPath< EventManager >( "/Problem/Events" );
+ // real64 const & ctime = event.getReference< real64 >( EventManager::viewKeyStruct::timeString() );
+//real64 const dt = event.getReference< real64 >( EventManager::viewKeyStruct::dtString() );
+ integer const & cycle = event.getReference< integer >( EventManager::viewKeyStruct::cycleString() );
+ integer const & subevent = event.getReference< integer >( EventManager::viewKeyStruct::currentSubEventString() );
- ElementRegionManager & elemManager = mesh.getElemManager();
- elemManager.forElementSubRegions< WellElementSubRegion >( regionNames,
- [&]( localIndex const,
- WellElementSubRegion & subRegion )
+// std::cout << "tjbtime1 " << ctime << " " << m_globalNumTimeSteps << " " << dt << " " << cycle << " " << subevent
+// << " " << m_numTimeStepCuts << " " << m_currentNewtonIteration << std::endl;
+ if( true ) // need to fix for restarts cycle >= m_writeSegDebug )
{
- integer const numPhase = m_numPhases;
- integer const numComp = m_numComponents;
- integer const numPerf = subRegion.getPerforationData()->size();
+//SolverStatistics & solver_stat = solver->getSolverStatistics();
+//integer num_timesteps = solver_stat.getReference< integer >( SolverStatistics::viewKeyStruct::numTimeStepsString());
+//integer current_newton_iteration = solver_stat.getReference< integer >(
+// SolverStatistics::viewKeyStruct::numCurrentNonlinearIterationsString());
+//integer num_timestep_cuts = solver_stat.getReference< integer >( SolverStatistics::viewKeyStruct::numTimeStepCutsString());
+//std::cout << "tjbtime2 " << ctime << " " << m_globalNumTimeSteps << " " << dt << " " << cycle << " " << subevent
+//<< " " << m_numTimeStepCuts << " " << m_currentNewtonIteration << std::endl;
+ string & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString() );
+ fluidName = getConstitutiveName< MultiFluidBase >( subRegion );
- // control data
- WellControls const & wellControls = getWellControls( subRegion );
- stdVector< double > compRate( numComp, 0.0 );
- if( m_writeCSV > 0 && wellControls.isWellOpen( ) )
- {
- arrayView2d< real64 > const compPerfRate = subRegion.getPerforationData()->getField< fields::well::compPerforationRate >();
- // bring everything back to host, capture the scalars by reference
- forAll< serialPolicy >( 1, [&numComp,
- &numPerf,
- compPerfRate,
- &compRate] ( localIndex const )
- {
- for( integer ic = 0; ic < numComp; ++ic )
- {
- for( integer iperf = 0; iperf < numPerf; iperf++ )
- {
- compRate[ic] += compPerfRate[iperf][ic];
- }
- }
- } );
- for( integer ic = 0; ic < numComp; ++ic )
- {
- compRate[ic] = MpiWrapper::sum( compRate[ic] );
- }
- }
+ MultiFluidBase const & fluid = subRegion.getConstitutiveModel< MultiFluidBase >( fluidName );
+ PerforationData & perforationData = *subRegion.getPerforationData();
+ using CompFlowAccessors =
+ StencilAccessors< fields::flow::pressure,
+ fields::flow::temperature,
+ fields::flow::phaseVolumeFraction,
- // the rank that owns the reference well element is responsible for the calculations below.
- if( !subRegion.isLocallyOwned() )
+
+
+ fields::flow::dPhaseVolumeFraction,
+ fields::flow::globalCompDensity,
+ fields::flow::dGlobalCompFraction_dGlobalCompDensity >;
+ string const flowSolverName = flowSolver.getName();
+ CompFlowAccessors compFlowAccessors( mesh.getElemManager(), flowSolver.getName() );
+
+ using MultiFluidAccessors =
+ StencilMaterialAccessors< MultiFluidBase,
+ fields::multifluid::phaseEnthalpy,
+ fields::multifluid::phaseDensity,
+ fields::multifluid::phaseViscosity,
+ fields::multifluid::dPhaseDensity,
+ fields::multifluid::phaseViscosity,
+ fields::multifluid::phaseInternalEnergy,
+ fields::multifluid::dPhaseViscosity,
+ fields::multifluid::phaseCompFraction,
+ fields::multifluid::dPhaseCompFraction >;
+ MultiFluidAccessors multiFluidAccessors( mesh.getElemManager(), flowSolver.getName() );
+
+ using RelPermAccessors =
+ StencilMaterialAccessors< RelativePermeabilityBase,
+ fields::relperm::phaseRelPerm,
+ fields::relperm::dPhaseRelPerm_dPhaseVolFraction >;
+
+ RelPermAccessors relPermAccessors( mesh.getElemManager(), flowSolver.getName() );
+
+
+ string const srn = subRegion.getName();
+
+ std::vector< string > cp_der {"dP", "dT"};
+ for( integer i=0; i());
+ if( isThermal() )
{
- return;
+ m_wellPropWriter[srn].registerSegProp( "Temperature", subRegion.getField< fields::well::temperature >());
}
+ m_wellPropWriter[srn].registerSegComponentProp( "ComponentDensity", subRegion.getField< fields::well::globalCompDensity >());
- string const wellControlsName = wellControls.getName();
+ m_wellPropWriter[srn].registerSegProp( "TotalRate", subRegion.getField< fields::well::connectionRate >());
- // format: time,total_rate,total_vol_rate,phase0_vol_rate,phase1_vol_rate,...
- std::ofstream outputFile;
- if( m_writeCSV > 0 )
+ m_wellPropWriter[srn].registerSegProp( "MassDensity", subRegion.getField< fields::well::totalMassDensity >());
+
+ m_wellPropWriter[srn].registerSegComponentProp( "CompFraction", subRegion.getField< fields::well::globalCompFraction >());
+
+ m_wellPropWriter[srn].registerSegPhasePropf( "PhaseDensity", fluid.phaseMassDensity());
+ m_wellPropWriter[srn].registerSegPhasePropf( "PhaseViscosity", fluid.phaseViscosity());
+ m_wellPropWriter[srn].registerSegPhasePropDerf( "dPhaseDensity", cp_der, fluid.dPhaseMassDensity());
+ m_wellPropWriter[srn].registerSegPhaseProp( "PhaseVolumeFraction", subRegion.getField< fields::well::phaseVolumeFraction >());
+ m_wellPropWriter[srn].registerSegPhasePropDer( "dPhaseVolume", cp_der, subRegion.getField< fields::well::dPhaseVolumeFraction >());
+ if( isThermal() )
{
- outputFile.open( m_ratesOutputDir + "/" + wellControlsName + ".csv", std::ios_base::app );
- outputFile << time_n << "," << dt;
+ m_wellPropWriter[srn].registerSegPhasePropf( "InternalEnergy", fluid.phaseInternalEnergy());
+ m_wellPropWriter[srn].registerSegPhasePropDerf( "dPhaseEnthalpy", cp_der, fluid.dPhaseEnthalpy());
+
+ m_wellPropWriter[srn].registerSegPhasePropf( "PhaseEnthalpy", fluid.phaseEnthalpy());
+ m_wellPropWriter[srn].registerSegPhasePropDerf( "dPhaseInternalEnergy", cp_der, fluid.dPhaseInternalEnergy());
}
+ m_wellPropWriter[srn].registerSegPhaseComponentPropf( "PhaseCompFrac", fluid.phaseCompFraction());
- if( wellControls.getWellStatus() == WellControls::Status::CLOSED )
+// Perforation properties
+ m_wellPropWriter[srn].registerPerf2dProp( {"X", "Y", "Z"}, perforationData.getLocation());
+ m_wellPropWriter[srn].registerPerf1dProp( {"Trans"}, perforationData.getWellTransmissibility());
+ m_wellPropWriter[srn].registerPerfResProp( "Pressure", compFlowAccessors.get( fields::flow::pressure {} ));
+ if( isThermal() )
{
- GEOS_LOG( GEOS_FMT( "{}: well is shut", wellControlsName ) );
- if( outputFile.is_open())
- {
- // print all zeros in the rates file
- outputFile << ",0.0,0.0,0.0";
- for( integer ip = 0; ip < numPhase; ++ip )
- {
- outputFile << ",0.0";
- }
- for( integer ic = 0; ic < numComp; ++ic )
- {
- outputFile << ",0.0";
- }
- outputFile << std::endl;
- outputFile.close();
- }
- return;
+ m_wellPropWriter[srn].registerPerfResProp( "Temperature", compFlowAccessors.get( fields::flow::temperature{} ));
+ m_wellPropWriter[srn].registerPerfResPhasePropf( "PhaseEnthalpy", multiFluidAccessors.get( fields::multifluid::phaseEnthalpy{} ));
+ m_wellPropWriter[srn].registerPerfResPhasePropf( "PhaseInternalEnergy", multiFluidAccessors.get( fields::multifluid::phaseInternalEnergy{} ));
}
+ m_wellPropWriter[srn].registerPerfComponentProp( "CompPerfRate", perforationData.getField< fields::well::compPerforationRate >());
+ //m_wellPropWriter[srn].registerPerfResComponentProp( "ComponentDensity", compFlowAccessors.get( fields::flow::globalCompDensity{} ));
+ m_wellPropWriter[srn].registerPerfResPhaseComponentProp( "PhaseCompFrac", multiFluidAccessors.get( fields::multifluid::phaseCompFraction{} ));
+ m_wellPropWriter[srn].registerPerfResPhaseProp( "PhaseVolFrac", compFlowAccessors.get( fields::flow::phaseVolumeFraction{} ));
+ m_wellPropWriter[srn].registerPerfResPhasePropf( "Viscosity", multiFluidAccessors.get( fields::multifluid::phaseViscosity{} ));
+ m_wellPropWriter[srn].registerPerfResPhasePropf( "RelPerm", relPermAccessors.get( fields::relperm::phaseRelPerm{} ));
+
+ m_wellPropWriter[srn].write( m_numTimesteps, dt, cycle, subevent, m_numTimesteps,
+ current_newton_iteration,
+ num_timestep_cuts );
+ }
+
+ }
+
- localIndex const iwelemRef = subRegion.getTopWellElementIndex();
- string const massUnit = m_useMass ? "kg" : "mol";
-
- // subRegion data
-
- arrayView1d< real64 const > const & connRate =
- subRegion.getField< well::mixtureConnectionRate >();
-
- integer const useSurfaceConditions = wellControls.useSurfaceConditions();
-
- real64 const & currentBHP =
- wellControls.getReference< real64 >( CompositionalMultiphaseWell::viewKeyStruct::currentBHPString() );
- arrayView1d< real64 const > const & currentPhaseVolRate =
- wellControls.getReference< array1d< real64 > >( CompositionalMultiphaseWell::viewKeyStruct::currentPhaseVolRateString() );
- real64 const & currentTotalVolRate =
- wellControls.getReference< real64 >( CompositionalMultiphaseWell::viewKeyStruct::currentTotalVolRateString() );
-
- // bring everything back to host, capture the scalars by reference
- forAll< serialPolicy >( 1, [&numPhase,
- &numComp,
- &useSurfaceConditions,
- ¤tBHP,
- connRate,
- ¤tTotalVolRate,
- currentPhaseVolRate,
- &compRate,
- &iwelemRef,
- &wellControlsName,
- &massUnit,
- &outputFile] ( localIndex const )
- {
- string const conditionKey = useSurfaceConditions ? "surface" : "reservoir";
- string const unitKey = useSurfaceConditions ? "s" : "r";
-
- real64 const currentTotalRate = connRate[iwelemRef];
- GEOS_LOG( GEOS_FMT( "{}: BHP (at the specified reference elevation): {} Pa",
- wellControlsName, currentBHP ) );
- GEOS_LOG( GEOS_FMT( "{}: Total rate: {} {}/s; total {} volumetric rate: {} {}m3/s",
- wellControlsName, currentTotalRate, massUnit, conditionKey, currentTotalVolRate, unitKey ) );
- for( integer ip = 0; ip < numPhase; ++ip )
- GEOS_LOG( GEOS_FMT( "{}: Phase {} {} volumetric rate: {} {}m3/s",
- wellControlsName, ip, conditionKey, currentPhaseVolRate[ip], unitKey ) );
- if( outputFile.is_open())
- {
- outputFile << "," << currentBHP;
- outputFile << "," << currentTotalRate << "," << currentTotalVolRate;
- for( integer ip = 0; ip < numPhase; ++ip )
- {
- outputFile << "," << currentPhaseVolRate[ip];
- }
- for( integer ic = 0; ic < numComp; ++ic )
- {
- outputFile << "," << compRate[ic];
- }
- outputFile << std::endl;
- outputFile.close();
- }
- } );
- } );
- } );
}
-REGISTER_CATALOG_ENTRY( PhysicsSolverBase, CompositionalMultiphaseWell, string const &, Group * const )
-} // namespace geos
+
+
+} // namespace geos
diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/CompositionalMultiphaseWell.hpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/CompositionalMultiphaseWell.hpp
index cb8ed9f1a6f..b24744c3f65 100644
--- a/src/coreComponents/physicsSolvers/fluidFlow/wells/CompositionalMultiphaseWell.hpp
+++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/CompositionalMultiphaseWell.hpp
@@ -22,9 +22,11 @@
#include "constitutive/fluid/multifluid/Layouts.hpp"
#include "constitutive/relativePermeability/Layouts.hpp"
-#include "physicsSolvers/fluidFlow/wells/WellSolverBase.hpp"
+
#include "physicsSolvers/fluidFlow/CompositionalMultiphaseBase.hpp"
+#include "physicsSolvers/fluidFlow/wells/WellConstraintsBase.hpp"
+#include "physicsSolvers/fluidFlow/wells/WellControls.hpp"
namespace geos
{
@@ -39,7 +41,7 @@ class MultiFluidBase;
*
* A compositional multiphase well solver
*/
-class CompositionalMultiphaseWell : public WellSolverBase
+class CompositionalMultiphaseWell : public WellControls
{
public:
@@ -57,8 +59,8 @@ class CompositionalMultiphaseWell : public WellSolverBase
/// deleted copy constructor
CompositionalMultiphaseWell( CompositionalMultiphaseWell const & ) = delete;
- /// default move constructor
- CompositionalMultiphaseWell( CompositionalMultiphaseWell && ) = default;
+ /// deleted move constructor
+ CompositionalMultiphaseWell( CompositionalMultiphaseWell && ) = delete;
/// deleted assignment operator
CompositionalMultiphaseWell & operator=( CompositionalMultiphaseWell const & ) = delete;
@@ -71,64 +73,168 @@ class CompositionalMultiphaseWell : public WellSolverBase
*/
virtual ~CompositionalMultiphaseWell() override = default;
+
+ virtual void registerWellDataOnMesh( WellElementSubRegion & subRegion ) override;
/**
- * @brief name of the node manager in the object catalog
- * @return string that contains the catalog name to generate a new NodeManager object through the object catalog.
+ * @defgroup WellManager Interface Functions
+ *
+ * These functions provide the primary interface that is required for derived classes
+ * The "Well" versions apply to individual well subRegions, whereas the others apply to all wells
*/
- static string catalogName() { return "CompositionalMultiphaseWell"; }
+ /**@{*/
/**
- * @copydoc PhysicsSolverBase::getCatalogName()
+ * * @brief Initialize well for the beginning of a simulation or restart
+ * @param domain the domain
+ * @param mesh the mesh level
+ * @param subRegion the well subRegion
*/
- string getCatalogName() const override { return catalogName(); }
+ virtual void initializeWell( DomainPartition & domain, MeshLevel & mesh, WellElementSubRegion & subRegion, real64 const & time_n ) override;
+
+ virtual void initializeWellPostInitialConditionsPreSubGroups( WellElementSubRegion & subRegion ) override;
- virtual void registerDataOnMesh( Group & meshBodies ) override;
+ virtual bool isCompositional() const override { return true; }
+
+
+ /**
+ * @copydoc WellControls::assembleWellAccumulationTerms()
+ */
+ virtual void assembleWellAccumulationTerms( real64 const & time,
+ real64 const & dt,
+ WellElementSubRegion & subRegion,
+ DofManager const & dofManager,
+ CRSMatrixView< real64, globalIndex const > const & localMatrix,
+ arrayView1d< real64 > const & localRhs ) override;
+ /**
+ * @copydoc WellControls::assembleWellPressureRelations()
+ */
+ virtual void assembleWellPressureRelations( real64 const & time_n,
+ real64 const & dt,
+ WellElementSubRegion const & subRegion,
+ DofManager const & dofManager,
+ CRSMatrixView< real64, globalIndex const > const & localMatrix,
+ arrayView1d< real64 > const & localRhs ) override;
+ /**
+ * @copydoc WellControls::assembleWellConstraintTerms()
+ */
+ virtual void assembleWellConstraintTerms( real64 const & time_n,
+ real64 const & dt,
+ WellElementSubRegion const & subRegion,
+ DofManager const & dofManager,
+ CRSMatrixView< real64, globalIndex const > const & localMatrix,
+ arrayView1d< real64 > const & localRhs ) override;
+ /**
+ * @copydoc WellControls::computeWellPerforationRates()
+ */
+ virtual void computeWellPerforationRates( real64 const & time_n,
+ real64 const & GEOS_UNUSED_PARAM( dt ),
+ ElementRegionManager & elemManager,
+ WellElementSubRegion & subRegion ) override;
/**
- * @defgroup Solver Interface Functions
+ * @copydoc WellControls::assembleFluxTerms()
+ */
+ virtual void assembleWellFluxTerms( real64 const & time,
+ real64 const & dt,
+ WellElementSubRegion const & subRegion,
+ DofManager const & dofManager,
+ CRSMatrixView< real64, globalIndex const > const & localMatrix,
+ arrayView1d< real64 > const & localRhs ) override;
+ /**@}*/
+ /**
+ * @defgroup Well Interface Functions - required by WellManager and WellNewtonSolver
*
* These functions provide the primary interface that is required for derived classes
+ * The "Well" versions apply to individual well subRegions
*/
/**@{*/
+ /**
+ * @copydoc WellControls::calculateResidualNorm()
+ */
+
+ virtual array1d< real64 >
+ calculateLocalWellResidualNorm( real64 const & time_n,
+ real64 const & dt,
+ NonlinearSolverParameters const & nonlinearSolverParameters,
+ WellElementSubRegion const & subRegion,
+ DofManager const & dofManager,
+ arrayView1d< real64 const > const & localRhs ) override;
- virtual real64
- calculateResidualNorm( real64 const & time_n,
- real64 const & dt,
- DomainPartition const & domain,
- DofManager const & dofManager,
- arrayView1d< real64 const > const & localRhs ) override;
virtual real64
- scalingForSystemSolution( DomainPartition & domain,
- DofManager const & dofManager,
- arrayView1d< real64 const > const & localSolution ) override;
+ calculateWellResidualNorm( real64 const & time_n,
+ real64 const & dt,
+ NonlinearSolverParameters const & nonlinearSolverParameters,
+ WellElementSubRegion const & subRegion,
+ DofManager const & dofManager,
+ arrayView1d< real64 const > const & localRhs ) override;
- virtual bool
- checkSystemSolution( DomainPartition & domain,
- DofManager const & dofManager,
- arrayView1d< real64 const > const & localSolution,
- real64 const scalingFactor ) override;
+ /**
+ * @copydoc WellControls::scalingForSystemSolution()
+ */
+ real64 scalingForLocalSystemSolution ( WellElementSubRegion & subRegion,
+ DofManager const & dofManager,
+ real64 & maxDeltaPres,
+ real64 & maxDeltaCompDens,
+ real64 & maxDeltaTemp,
+ real64 & minPresScalingFactor,
+ real64 & minCompDensScalingFactor,
+ real64 & minTempScalingFactor,
+ arrayView1d< real64 const > const & localSolution );
+
+ virtual real64 scalingForWellSystemSolution( WellElementSubRegion & subRegion,
+ DofManager const & dofManager,
+ arrayView1d< real64 const > const & localSolution ) override;
- virtual void
- applySystemSolution( DofManager const & dofManager,
- arrayView1d< real64 const > const & localSolution,
- real64 const scalingFactor,
- real64 const dt,
- DomainPartition & domain ) override;
+ /**
+ * @copydoc WellControls::checkSystemSolution()
+ */
+ virtual bool
+ checkWellSystemSolution( WellElementSubRegion & subRegion,
+ DofManager const & dofManager,
+ arrayView1d< real64 const > const & localSolution,
+ real64 const scalingFactor ) override;
- virtual void
- resetStateToBeginningOfStep( DomainPartition & domain ) override;
+ /**
+ * @copydoc WellControls::applyWellSystemSolution()
+ */
virtual void
- implicitStepSetup( real64 const & time,
- real64 const & dt,
- DomainPartition & domain ) override;
+ applyWellSystemSolution( DofManager const & dofManager,
+ arrayView1d< real64 const > const & localSolution,
+ real64 const scalingFactor,
+ real64 const dt,
+ DomainPartition & domain,
+ MeshLevel & mesh,
+ WellElementSubRegion & subRegion ) override;
+
+ virtual void applyWellBoundaryConditions( real64 const time_n,
+ real64 const dt,
+ ElementRegionManager & elemManager,
+ WellElementSubRegion & subRegion,
+ DofManager const & dofManager,
+ arrayView1d< real64 > const & localRhs,
+ CRSMatrixView< real64, globalIndex const > const & localMatrix ) override;
+
+
+ virtual void resetStateToBeginningOfStep( ElementRegionManager const & elemManager, WellElementSubRegion & subRegion ) override;
+
+ virtual void implicitStepSetup( real64 const & time_n,
+ real64 const & GEOS_UNUSED_PARAM( dt ),
+ ElementRegionManager & elemManager,
+ WellElementSubRegion & subRegion ) override;
virtual void
implicitStepComplete( real64 const & time,
real64 const & dt,
- DomainPartition & domain ) override;
+ WellElementSubRegion const & subRegion ) override;
+
+ virtual void printRates( real64 const & time_n,
+ real64 const & dt,
+ WellElementSubRegion const & subRegion ) override;
+
+ virtual real64 updateSubRegionState( ElementRegionManager const & elemManager, WellElementSubRegion & subRegion ) override;
/**@}*/
@@ -140,10 +246,9 @@ class CompositionalMultiphaseWell : public WellSolverBase
/**
* @brief Recompute the volumetric rates that are used in the well constraints
- * @param elemManager the well region manager containing the well
* @param subRegion the well subregion containing all the primary and dependent fields
*/
- void updateVolRatesForConstraint( ElementRegionManager const & elemManager, WellElementSubRegion const & subRegion );
+ void updateVolRatesForConstraint( WellElementSubRegion const & subRegion );
/**
* @brief Recompute the current BHP pressure
@@ -159,6 +264,22 @@ class CompositionalMultiphaseWell : public WellSolverBase
*/
void updateFluidModel( WellElementSubRegion & subRegion );
+ /**
+ * @brief Update well separator using current values of pressure and composition at the reference
+ * element
+ * @param elemManager the element region manager
+
+ */
+ void updateSeparator( ElementRegionManager const & elemManager, WellElementSubRegion & subRegion );
+
+ /**
+ * @brief Calculate well rates at reference element
+ * @param subRegion the well subregion containing all the primary and dependent fields
+ * @param targetIndex the targetIndex of the subRegion
+ */
+
+ void calculateReferenceElementRates( WellElementSubRegion & subRegion );
+
/**
* @brief Recompute phase volume fractions (saturations) from constitutive and primary variables
* @param subRegion the well subregion containing all the primary and dependent fields
@@ -172,20 +293,12 @@ class CompositionalMultiphaseWell : public WellSolverBase
*/
void updateTotalMassDensity( WellElementSubRegion & subRegion ) const;
- /**
- * @brief Recompute the perforation rates for all the wells
- * @param domain the domain containing the mesh and fields
- */
- virtual void computePerforationRates( real64 const & time_n,
- real64 const & dt, DomainPartition & domain ) override;
-
/**
* @brief Recompute all dependent quantities from primary variables (including constitutive models)
* @param subRegion the well subregion containing all the primary and dependent fields
*/
- virtual void updateState( DomainPartition & domain ) override;
+ virtual real64 updateWellState( ElementRegionManager const & elemManager, WellElementSubRegion & subRegion ) override;
- virtual real64 updateSubRegionState( ElementRegionManager const & elemManager, WellElementSubRegion & subRegion ) override;
virtual string wellElementDofName() const override { return viewKeyStruct::dofFieldString(); }
@@ -197,61 +310,16 @@ class CompositionalMultiphaseWell : public WellSolverBase
integer useTotalMassEquation() const { return m_useTotalMassEquation; }
- /**
- * @brief assembles the flux terms for all connections between well elements
- * @param time_n previous time value
- * @param dt time step
- * @param domain the physical domain object
- * @param dofManager degree-of-freedom manager associated with the linear system
- * @param matrix the system matrix
- * @param rhs the system right-hand side vector
- */
-
- virtual void assembleFluxTerms( real64 const & time_n,
- real64 const & dt,
- DomainPartition & domain,
- DofManager const & dofManager,
- CRSMatrixView< real64, globalIndex const > const & localMatrix,
- arrayView1d< real64 > const & localRhs )override;
- /**
- * @brief assembles the accumulation term for all the well elements
- * @param domain the physical domain object
- * @param dofManager degree-of-freedom manager associated with the linear system
- * @param matrix the system matrix
- * @param rhs the system right-hand side vector
- */
- virtual void assembleAccumulationTerms( real64 const & time_n,
- real64 const & dt,
- DomainPartition & domain,
- DofManager const & dofManager,
- CRSMatrixView< real64, globalIndex const > const & localMatrix,
- arrayView1d< real64 > const & localRhs ) override;
-
- /**
- * @brief assembles the pressure relations at all connections between well elements except at the well head
- * @param time_n time at the beginning of the time step
- * @param dt the time step size
- * @param domain the physical domain object
- * @param dofManager degree-of-freedom manager associated with the linear system
- * @param matrix the system matrix
- * @param rhs the system right-hand side vector
- */
- virtual void assemblePressureRelations( real64 const & time_n,
- real64 const & dt,
- DomainPartition const & domain,
- DofManager const & dofManager,
- CRSMatrixView< real64, globalIndex const > const & localMatrix,
- arrayView1d< real64 > const & localRhs ) override;
-
/**
* @brief Sets all the negative component densities (if any) to zero.
- * @param domain the physical domain object
+ * @param subRegion the well subregion containing all the primary and dependent fields
*/
- void chopNegativeDensities( DomainPartition & domain );
+ void chopNegativeDensities( WellElementSubRegion & subRegion );
+
- struct viewKeyStruct : WellSolverBase::viewKeyStruct
+ struct viewKeyStruct : WellControls::viewKeyStruct
{
- static constexpr char const * dofFieldString() { return "compositionalWellVars"; }
+ static constexpr char const * dofFieldString() { return "wellVars"; }
// inputs
@@ -271,38 +339,17 @@ class CompositionalMultiphaseWell : public WellSolverBase
static constexpr char const * allowLocalCompDensChoppingString() { return CompositionalMultiphaseBase::viewKeyStruct::allowLocalCompDensChoppingString(); }
- // control data (not registered on the mesh)
-
- static constexpr char const * massDensityString() { return "massDensity";}
-
- static constexpr char const * currentBHPString() { return "currentBHP"; }
- static constexpr char const * dCurrentBHPString() { return "dCurrentBHP"; }
-
- static constexpr char const * dCurrentBHP_dPresString() { return "dCurrentBHP_dPres"; }
- static constexpr char const * dCurrentBHP_dCompDensString() { return "dCurrentBHP_dCompDens"; }
-
- static constexpr char const * currentPhaseVolRateString() { return "currentPhaseVolumetricRate"; }
- static constexpr char const * dCurrentPhaseVolRateString() { return "dCurrentPhaseVolumetricRate"; }
-
-
- static constexpr char const * dCurrentPhaseVolRate_dPresString() { return "dCurrentPhaseVolumetricRate_dPres"; }
-
- static constexpr char const * dCurrentPhaseVolRate_dCompDensString() { return "dCurrentPhaseVolumetricRate_dCompDens"; }
- static constexpr char const * dCurrentPhaseVolRate_dRateString() { return "dCurrentPhaseVolumetricRate_dRate"; }
-
- static constexpr char const * currentTotalVolRateString() { return "currentTotalVolumetricRate"; }
- static constexpr char const * dCurrentTotalVolRateString() { return "dCurrentTotalVolumetricRate"; }
-
- static constexpr char const * currentMassRateString() { return "currentMassRate"; }
-
- static constexpr char const * dCurrentTotalVolRate_dPresString() { return "dCurrentTotalVolumetricRate_dPres"; }
-
- static constexpr char const * dCurrentTotalVolRate_dCompDensString() { return "dCurrentTotalVolumetricRate_dCompDens"; }
-
- static constexpr char const * dCurrentTotalVolRate_dRateString() { return "dCurrentTotalVolumetricRate_dRate"; }
} viewKeysCompMultiphaseWell;
+ virtual void outputSingleWellDebug( real64 const time,
+ real64 const dt,
+ integer current_newton_iteration,
+ MeshLevel & mesh,
+ WellElementSubRegion & subRegion,
+ DofManager const & dofManager,
+ CRSMatrixView< real64, globalIndex const > const & GEOS_UNUSED_PARAM( localMatrix ),
+ arrayView1d< const real64 > const & GEOS_UNUSED_PARAM( localRhs ) ) override;
protected:
@@ -312,30 +359,19 @@ class CompositionalMultiphaseWell : public WellSolverBase
virtual void initializePostInitialConditionsPreSubGroups() override;
- virtual void postRestartInitialization() override final;
- /*
- * @brief Utility function that checks the consistency of the constitutive models
- * @param[in] domain the domain partition
+ void saveState( WellElementSubRegion & subRegion );
+ virtual void postRestartInitialization( ) override;
+
+ /**
+ * @brief Checks fluild model compatibility and validity
+ * @param[in] fluid the fluid to check
+ * @param[in] referenceFluid the reference fluid model
* @detail
* This function will produce an error if one of the well constitutive models
* is incompatible with the corresponding models in reservoir
* regions connected to that particular well.
*/
- void validateConstitutiveModels( DomainPartition const & domain ) const;
-
- /**
- * @brief Checks if the WellControls parameters are within the fluid tables ranges
- * @param fluid the fluid to check
- */
- void validateWellControlsForFluid( WellControls const & wellControls,
- constitutive::MultiFluidBase const & fluid ) const;
-
- /**
- * @brief Checks injection streams for validity (compositions sum to one)
- * @param subRegion the well subRegion
- */
- void validateInjectionStreams( WellElementSubRegion const & subRegion ) const;
-
+ void validateFluidModel( constitutive::MultiFluidBase const & fluid, constitutive::MultiFluidBase const & referenceFluid )const;
/**
* @brief Make sure that the well constraints are compatible
* @param time_n the time at the beginning of the time step
@@ -350,24 +386,23 @@ class CompositionalMultiphaseWell : public WellSolverBase
/**
* @brief Create well separator
*/
- void createSeparator();
+ virtual void createSeparator( WellElementSubRegion & subRegion ) override;
- void printRates( real64 const & time_n,
- real64 const & dt,
- DomainPartition & domain ) override;
-private:
- /**
- * @brief Initialize all the primary and secondary variables in all the wells
- * @param domain the domain containing the well manager to access individual wells
- */
- void initializeWells( DomainPartition & domain, real64 const & time_n ) override;
+ virtual bool solveWHPConstraint( real64 const & time_n,
+ real64 const & dt,
+ integer const cycleNumber,
+ integer const coupledIterationNumber,
+ DomainPartition & domain,
+ MeshLevel & mesh,
+ ElementRegionManager & elemManager,
+ WellElementSubRegion & subRegion )override;
+private:
virtual void setConstitutiveNames( ElementSubRegionBase & subRegion ) const override;
-
/// flag indicating whether mass or molar formulation should be used
integer m_useMass;
@@ -395,11 +430,6 @@ class CompositionalMultiphaseWell : public WellSolverBase
/// flag indicating whether local (cell-wise) chopping of negative compositions is allowed
integer m_allowCompDensChopping;
- /// index of the target phase, used to impose the phase rate constraint
- localIndex m_targetPhaseIndex;
-
-
-
};
} // namespace geos
diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/CompositionalMultiphaseWellFields.hpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/CompositionalMultiphaseWellFields.hpp
index 3ca423cd7ac..4abd716ffc4 100644
--- a/src/coreComponents/physicsSolvers/fluidFlow/wells/CompositionalMultiphaseWellFields.hpp
+++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/CompositionalMultiphaseWellFields.hpp
@@ -58,21 +58,7 @@ DECLARE_FIELD( globalCompDensity_n,
WRITE_AND_READ,
"Global component density at the previous converged time step" );
-DECLARE_FIELD( mixtureConnectionRate,
- "wellElementMixtureConnectionRate",
- array1d< real64 >,
- 0,
- LEVEL_0,
- WRITE_AND_READ,
- "Mixture connection rate" );
-DECLARE_FIELD( mixtureConnectionRate_n,
- "wellElementMixtureConnectionRate_n",
- array1d< real64 >,
- 0,
- NOPLOT,
- WRITE_AND_READ,
- "Mixture connection rate at the previous converged time step" );
DECLARE_FIELD( globalCompFraction,
"globalCompFraction",
diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/PipeFlowTableFunction.cpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/PipeFlowTableFunction.cpp
new file mode 100644
index 00000000000..7c2bab802c2
--- /dev/null
+++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/PipeFlowTableFunction.cpp
@@ -0,0 +1,700 @@
+/*
+ * ------------------------------------------------------------------------------------------------------------
+ * 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 PipeFlowTableFunction.cpp
+ */
+
+#include "PipeFlowTableFunction.hpp"
+#include "functions/FunctionManager.hpp"
+#include "functions/MultivariableNonuniformTableFunctionKernels.hpp"
+#include "common/DataTypes.hpp"
+#include
+#include
+#include
+
+namespace geos
+{
+
+using namespace dataRepository;
+
+PipeFlowTableFunction::PipeFlowTableFunction( const string & name,
+ Group * const parent ):
+ MultivariableNonuniformTableFunction( name, parent ),
+ m_tableFunction( nullptr )
+{
+ registerWrapper( viewKeyStruct::tableName(), &m_tableName ).
+ setInputFlag( InputFlags::REQUIRED ).
+ setSizedFromParent( 0 ).
+ setDescription( "Flow table name. Table is associated with a Min/MaxWHPConstraint with the constraints flowTableName field" );
+
+ registerWrapper( viewKeyStruct::rateType(), &m_rateType ).
+ setInputFlag( InputFlags::REQUIRED ).
+ setSizedFromParent( 0 ).
+ setDescription( "Type of rate entered in the rates array. Valid entires are ..." );
+
+ registerWrapper( viewKeyStruct::rateArray(), &m_rate ).
+ setInputFlag( InputFlags::REQUIRED ).
+ setSizedFromParent( 0 ).
+ setDescription( "Array of rates" );
+
+ registerWrapper( viewKeyStruct::waterFractionType(), &m_waterFractionType ).
+ setInputFlag( InputFlags::OPTIONAL ).
+ setSizedFromParent( 0 ).
+ setDescription( "Type of water fraction entered in the wfr array. Valid entires are ..." );
+
+ registerWrapper( viewKeyStruct::waterFractionArray(), &m_wfr ).
+ setInputFlag( InputFlags::OPTIONAL ).
+ setSizedFromParent( 0 ).
+ setDescription( "Array of water fractions " );
+
+ registerWrapper( viewKeyStruct::gasFractionType(), &m_gasFractionType ).
+ setInputFlag( InputFlags::OPTIONAL ).
+ setSizedFromParent( 0 ).
+ setDescription( "Type of gas fraction entered in the wfr array. Valid entires are ..." );
+
+ registerWrapper( viewKeyStruct::gasFractionArray(), &m_gfr ).
+ setInputFlag( InputFlags::OPTIONAL ).
+ setSizedFromParent( 0 ).
+ setDescription( "Array of gas fractions " );
+
+ registerWrapper( viewKeyStruct::wellHeadPressureArray(), &m_whp ).
+ setInputFlag( InputFlags::REQUIRED ).
+ setSizedFromParent( 0 ).
+ setDescription( "Array of well head pressures " );
+
+ registerWrapper( viewKeyStruct::gasLiftArray(), &m_gasLift ).
+ setInputFlag( InputFlags::OPTIONAL ).
+ setSizedFromParent( 0 ).
+ setDescription( "Array of well gas lift rates " );
+
+ registerWrapper( viewKeyStruct::bottomHolePressureArray(), &m_bhp ).
+ setInputFlag( InputFlags::REQUIRED ).
+ setSizedFromParent( 0 ).
+ setDescription( "Array of bottom hole pressures representing the dependent variable for the table function.\n"
+ "The quantities must be entered such that the rate index increases first, followed by whp, waterFraction, and finally gasFraction.\n"
+ " For example bhp[1]= f(rate[1],whp[1],,waterFraction[1],gasFraction[1]) , bhp[2]= f(rate[2],whp[1],waterFraction[1],gasFraction[1])" );
+
+}
+
+void PipeFlowTableFunction::postInputInitialization()
+{
+ // Validate independent table values are not decreasing
+ auto checkNotDecreasing = []( const auto & arr, const std::string & name )
+ {
+ for( auto i = 1; i < arr.size(); ++i )
+ {
+ if( arr[i] < arr[i-1] )
+ {
+ GEOS_ERROR( name << " array values must be non-decreasing, but arr[" << i << "] < arr[" << i-1 << "]" );
+ }
+ }
+ };
+ std::cout << " rate " << m_rate.size() << " whp " << m_whp.size() << " wfr " << m_wfr.size() << " gfr " << m_gfr.size() <( *functionManager->createChild( MultivariableNonuniformTableFunction::catalogName(), getTableName() ) ));
+ m_tableFunction0 = &dynamicCast< TableFunction & >( *functionManager->createChild( "TableFunction", "table_b" ) );
+
+ // find max number if independent vars
+ int maxVar = std::max( m_rate.size(), m_whp.size());
+ maxVar = std::max( maxVar, m_wfr.size());
+ maxVar = std::max( maxVar, m_gfr.size());
+ maxVar = std::max( maxVar, m_gasLift.size());
+ // Copy inputs into formats needed by table function
+
+ integer_array axisPoints( nDims );
+
+ axisPoints[0] = m_rate.size();
+ axisPoints[1] = m_whp.size();
+ axisPoints[2] = m_wfr.size();
+ axisPoints[3] = m_gfr.size();
+ axisPoints[4] = m_gasLift.size();
+
+ integer_array axisPointsr( nDims );
+ for( int i=0; i >axisCoordinates;
+ axisCoordinates.resize( nDims );
+ array2d< real64 > axisCoord ( nDims, maxVar );
+ axisCoordinates[0].resize( m_rate.size());
+ for( int i=0; i< m_rate.size(); i++ )
+ {
+ axisCoordinates[0][i] = m_rate[i];
+ axisCoord( 4, i ) = m_rate[i];
+ }
+ axisCoordinates[1].resize( m_whp.size());
+ for( int i=0; isetTableCoordinates( axisCoordinates, { units::Dimensionless } );
+ m_tableFunction0->setTableValues( m_bhp, units::Dimensionless );
+ m_tableFunction0->setInterpolationMethod( TableFunction::InterpolationType::Linear );
+ //m_tableFunction0.setInputVarNames( inputVarNames );
+ m_tableFunction0->reInitializeFunction();
+ writeTable();
+}
+integer
+PipeFlowTableFunction::getRateBracket( real64 const & rate, integer & b0, integer & b1 ) const
+{
+ integer nRate = m_rate.size();
+ integer stat=0;
+ if( rate < m_rate[0] )
+ {
+ b0=0;
+ b1=1;
+ stat = 0;
+ }
+ else if( rate > m_rate[nRate-1] )
+ {
+ b0=std::max( 0, nRate-2 );
+ b1=nRate-1;
+ stat= 2;
+ }
+ else
+ {
+ integer ifnd=0;
+ for( integer j=1; j const & phaseRates, real64 const & whp, real64 & ql0, real64 & ql1, real64 & bhp0, real64 & bhp1 ) const
+{
+ // Calculate ratios that are assumed fixed for table lookup
+ real64 lrate = -( phaseRates[0] + phaseRates[2] );
+ real64 wfr = -phaseRates[2]/lrate;
+ real64 gor = 0;
+
+ if( !isZero( phaseRates[0] ))
+ gor = phaseRates[1]/( phaseRates[0] );
+
+ integer b0, b1;
+ integer bStat = getRateBracket( lrate, b0, b1 );
+ GEOS_UNUSED_VAR( bStat );
+ // Get bhp at lower bracket rates
+ ql0 = -m_rate[b0];
+ phaseRates[0] = ql0*(1-wfr);
+ phaseRates[1] = phaseRates[0]*gor;
+ phaseRates[2] = ql0* wfr;
+ integer solveStat=1;
+ calculateBHP( phaseRates, whp, bhp0, solveStat );
+
+ // Get bhp at upper bracket rates
+ ql1 = -m_rate[b1];
+ phaseRates[0] = ql1*(1-wfr);
+ phaseRates[1] = phaseRates[0]*gor;
+ phaseRates[2] = ql1* wfr;
+ solveStat=1;
+ calculateBHP( phaseRates, whp, bhp1, solveStat );
+
+ real64 dP_dQ = ( bhp1 - bhp0 ) / ( m_rate[b1] - m_rate[b0] );
+ return dP_dQ;
+}
+void PipeFlowTableFunction::calculateBHP( array1d< real64 > const & phaseRates, real64 const & whp, real64 & bhp, integer & solveStat ) const
+{
+
+ MultivariableNonuniformTableFunctionStaticKernel< 5, 1 > kernel( getAxisCoordinates(),
+ getAxisPoints(),
+ getAxisSteps(),
+ getAxisStepInvs(),
+ getAxisHypercubeMults(),
+ getHypercubeData()
+ );
+
+ TableFunction::KernelWrapper kernelWrapper = m_tableFunction0->createKernelWrapper();
+ // Assume success
+ // liq(oil)=0 vap = 1 wat = 2
+ real64 const gasLift=0.0;
+ real64 const m_sign=-1.0;
+ real64 liq = (phaseRates[0] + phaseRates[2]);
+ //for( int i = 0; i < phaseRates.size(); ++i )
+ //{
+ // totalVolumeRate += phaseRates[i];
+// }
+
+ std::cout << bhp << " " << phaseRates << " " << whp << std::endl;
+
+ real64 wct = 0;
+ if( phaseRates[0]*m_sign > 0 )
+ {
+ wct = phaseRates[2]/(phaseRates[0]+phaseRates[2]);
+ }
+#if 0
+ if( wct < m_wfr[0] )
+ {
+ wct = m_wfr[0] + 0.00000001;
+ }
+ else if( wct > m_wfr[ m_wfr.size() -1 ] )
+ {
+ wct = m_wfr[ m_wfr.size() -1 ]- +0.00000001;
+ }
+#endif
+ real64 gor = 0;
+ if( phaseRates[1]*m_sign > 0 )
+ gor = phaseRates[1]/(phaseRates[0] );
+#if 0
+ if( gor < m_gfr[0] )
+ {
+ gor = m_gfr[0]+ 0.00000001;
+ }
+ else if( gor > m_gfr[ m_gfr.size() -1 ] )
+ {
+ gor = m_gfr[ m_gfr.size() -1 ]- 0.00000001;
+ }
+#endif
+ //gor = m_gfr[0];
+ array1d< real64 > table_coords( 5 );
+
+ array2d< real64 > table_derv( 1, 5 );
+ table_coords[0]=liq*m_sign; // gas oil ratio
+ table_coords[1]=whp; // well head pressure
+ table_coords[2]=wct; // water cut
+ table_coords[3]=gor; // well head pressure
+ table_coords[4]=gasLift; // gas lift rate
+ array1d< real64 > table_coordsr( 5 );
+
+ for( integer i=0; i<5; i++ )
+ {
+ table_coordsr[5-i-1]=table_coords[i];
+ }
+ if( solveStat == 0 )
+ {
+ std::cout << "PipeFlowTableFunction::calculateBHP input coords = " << table_coordsr << std::endl;
+
+ array1d< real64 > table_bhp( 1 );
+ kernel.compute( table_coordsr, table_bhp, table_derv );
+ real64 bhpbt = table_bhp[0];
+ // std::cout << " PipeFlowTableFunction::calculateBHP initial bhp = " << bhp << " bhp calc " << table_bhp[0] << " " << table_coordsr
+ // < table_bhp( 1 );
+ real64 derivatives[5]{};
+ table_bhp[0] = kernelWrapper.compute( table_coords, derivatives );
+ bhp = table_bhp[0];
+ std::cout << "PipeFlowTableFunction::calculateBHP 1 output bhp = " << bhp << " " << " liq = " << liq << " whp " << whp << " wct = " << wct << " gor = " << gor << " " << derivatives[1] <<
+ std::endl;
+ }
+ if( whp >m_whp[m_whp.size()-1] )
+ solveStat=2;
+ else if( whp < m_whp[0] )
+ solveStat=0;
+ else
+ solveStat=1;
+
+}
+
+void PipeFlowTableFunction::calculateWHP( const std::string & wellName, real64 const & bhp, array1d< real64 > const & phaseRates, real64 & whp, integer & solveStat ) const
+{
+
+ MultivariableNonuniformTableFunctionStaticKernel< 5, 1 > kernel( getAxisCoordinates(),
+ getAxisPoints(),
+ getAxisSteps(),
+ getAxisStepInvs(),
+ getAxisHypercubeMults(),
+ getHypercubeData()
+ );
+ TableFunction::KernelWrapper kernelWrapper = m_tableFunction0->createKernelWrapper();
+ //solveStat = 0; // Assume success
+ // liq(oil)=0 vap = 1 wat = 2
+ //real64 totalVolumeRate = 0.0;
+ //for( int i = 0; i < phaseRates.size(); ++i )
+ //{
+ // totalVolumeRate += phaseRates[i];
+// }
+ real64 const gasLift=0.0;
+ std::cout << bhp << " " << phaseRates << " " << whp << std::endl;
+ real64 const m_sign=-1.0;
+ real64 liq = (phaseRates[0] + phaseRates[2]);
+ //for( int i = 0; i < phaseRates.size(); ++i )
+ //{
+ // totalVolumeRate += phaseRates[i];
+// }
+ std::cout << bhp << " " << phaseRates << " " << whp << std::endl;
+
+ real64 wct = 0;
+ if( phaseRates[0]*m_sign > 0 )
+ {
+ wct = phaseRates[2]/(phaseRates[0]+phaseRates[2]);
+ }
+#if 0
+ if( wct < m_wfr[0] )
+ {
+ wct = m_wfr[0] + 0.00000001;
+ }
+ else if( wct > m_wfr[ m_wfr.size() -1 ] )
+ {
+ wct = m_wfr[ m_wfr.size() -1 ]- +0.00000001;
+ }
+#endif
+ real64 gor = 0;
+ if( phaseRates[1]*m_sign > 0 )
+ gor = phaseRates[1]/(phaseRates[0] );
+#if 0
+ if( gor < m_gfr[0] )
+ {
+ gor = m_gfr[0]+ 0.00000001;
+ }
+ else if( gor > m_gfr[ m_gfr.size() -1 ] )
+ {
+ gor = m_gfr[ m_gfr.size() -1 ]- 0.00000001;
+ }
+#endif
+ std::cout << wellName << " PipeFlowTableFunction::calculateWHP bhp " << bhp << " liq " << liq << " wct " << wct << " gor " << gor << std::endl;
+#if 1
+ array1d< real64 > table_coords( 5 );
+ real64 derivatives[5]{};
+ array1d< real64 > table_bhp( 1 );
+ array2d< real64 > table_derv( 1, 5 );
+ table_coords[4]=liq*m_sign; // gas oil ratio
+ table_coords[2]=wct; // water cut
+ table_coords[1]=gor; // well head pressure
+ table_coords[0]=gasLift; // gas lift rat
+
+ table_coords[3]=whp; // well head pressure
+ kernel.compute( table_coords, table_bhp, table_derv );
+ std::cout << " PipeFlowTableFunction::calculateWHP initial whp = " << whp << " bhp calc " << table_bhp[0] << " " << table_coords < kernel( getAxisCoordinates(),
+ getAxisPoints(),
+ getAxisSteps(),
+ getAxisStepInvs(),
+ getAxisHypercubeMults(),
+ getHypercubeData()
+ );
+
+ TableFunction::KernelWrapper kernelWrapper = m_tableFunction0->createKernelWrapper();
+
+ of << "rate,wellHeadPressure,waterFraction,gasFraction,bottomHolePressure" << std::endl;
+ //array1d< real64 > table_coords( 5 );
+ real64 table_coords[5]{};
+ real64 derivatives[5]{};
+ table_coords[4] = 0.0; //gas lift
+ for( integer i=0; i < m_gfr.size(); ++i )
+ {
+ table_coords[3]=m_gfr[i]; // well head pressure
+ // gas oil ratio
+ for( real64 j=m_wfr[0]; j < m_wfr[m_wfr.size()-1]; j=j+0.01 )
+ {
+ table_coords[2]=j; // water cut
+ for( real64 k=m_whp[0]; k < m_whp[m_whp.size()-1]; k=k+10e5 )
+ {
+ table_coords[1]=k;
+ for( integer l=0; l < m_rate.size(); ++l )
+ {
+ table_coords[0]=m_rate[l]; // liquid rate
+
+ // array2d< real64 > derivs( 1, 5 );
+ //kernel.compute( table_coords, table_bhp, derivs );
+ real64 table_bhp = kernelWrapper.compute( table_coords, derivatives );
+ of << table_coords[0] << "," << table_coords[1] << "," << table_coords[2] << "," << table_coords[3] << "," << table_bhp << std::endl;
+ }
+ }
+ }
+ }
+ of.close();
+
+
+}
+
+REGISTER_CATALOG_ENTRY( FunctionBase, PipeFlowTableFunction, string const &, Group * const )
+
+} // end of namespace geos
diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/PipeFlowTableFunction.hpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/PipeFlowTableFunction.hpp
new file mode 100644
index 00000000000..4aa8aa4def0
--- /dev/null
+++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/PipeFlowTableFunction.hpp
@@ -0,0 +1,213 @@
+/*
+ * ------------------------------------------------------------------------------------------------------------
+ * 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 PipeFlowTableFunction.hpp
+ */
+
+#ifndef GEOS_FUNCTIONS_PIPEFLOWTABLEFUNCTION_HPP_
+#define GEOS_FUNCTIONS_PIPEFLOWTABLEFUNCTION_HPP_
+#include "functions/TableFunction.hpp"
+#include "functions/MultivariableNonuniformTableFunction.hpp"
+
+#include "common/format/EnumStrings.hpp"
+#include "LvArray/src/tensorOps.hpp"
+
+namespace geos
+{
+
+/**
+ * @class PipeFlowTableFunction
+ *
+ * An interface class for pipeflow table function (function with multiple inputs and outputs) with uniform discretization
+ */
+
+class PipeFlowTableFunction : public MultivariableNonuniformTableFunction
+{
+public:
+
+ /**
+ * @brief The constructor
+ * @param[in] name the name of this object manager
+ * @param[in] parent the parent Group
+ */
+ PipeFlowTableFunction( const string & name,
+ Group * const parent );
+
+ /**
+ * @brief The catalog name interface
+ * @return name of the PipeFlowTableFunction in the FunctionBase catalog
+ */
+ static string catalogName() { return "PipeFlowTableFunction"; }
+
+ /**
+ * @name Getters / Setters
+ */
+ ///@{
+
+ /**
+ * @brief Get type of rate array
+ * @return name of type
+ */
+ const string & getTableName() const { return m_tableName; }
+
+ /**
+ * @brief Get type of rate array
+ * @return name of type
+ */
+ const string & getRateType() const { return m_rateType; }
+
+ /**
+ * @brief Get phases associated with rate type
+ * @return array of phases
+ */
+ const string_array & getRatePhases() const { return m_ratePhases; }
+
+ /**
+ * @brief Get rates table coordinates
+ * @return array of phases
+ */
+ const array1d< real64 > & getRates() const { return m_rate; }
+
+ /**
+ * @brief Get type of water fraction array
+ * @return name of type
+ */
+ const string & getWaterFractionType() const { return m_waterFractionType; }
+
+ /**
+ * @brief Get type of gas fraction array
+ * @return name of type
+ */
+ const string & getGasFractionType() const { return m_gasFractionType; }
+
+ /**
+ * @brief Get type of gas fraction array
+ * @param[in] rate The flow rate to bracket
+ * @param[out] b0 The lower bracket index
+ * @param[out] b1 The upper bracket index
+ * @return 0 if rate is below table min, 2 if rate is above table max, else 1
+ */
+ integer getRateBracket( real64 const & rate, integer & b0, integer & b1 ) const;
+
+ /**
+ * @brief Get dQdP for bracket containing rate
+ * @param[in] phaseRates array of phase rates, indexing according to phase ordering in fluid model
+ * @param[in] whp well head pressure
+ * @param[out] ql0 liquid rate at lower bracket
+ * @param[out] ql1 liquid rate at upper bracket
+ * @param[out] bhp0 bottom hole pressure at lower bracket
+ * @param[out] bhp1 bottom hole pressure at upper bracket
+ * @return dQdP value
+ */
+
+ real64 calculatedPdQ( array1d< real64 > const & phaseRates, real64 const & whp, real64 & ql0, real64 & ql1, real64 & bhp0, real64 & bhp1 ) const;
+
+ ///@}
+
+ /**
+ * @brief Struct to serve as a container for variable strings and keys.
+ * @struct viewKeyStruct
+ */
+ struct viewKeyStruct
+ {
+ /// @return String key type of table name
+ static constexpr char const *tableName() { return "name"; }
+
+ /// @return String key type of flow rate associated with the "rate" array
+ static constexpr char const *rateType() { return "rateType"; }
+ /// @return String key for "rate" array
+ static constexpr char const *rateArray() { return "rate"; }
+
+
+ /// @return String key for "whp" array
+ static constexpr char const *wellHeadPressureArray() { return "wellHeadPressure"; }
+
+ /// @return String key type of water fraction associated with the "wfr" array
+ static constexpr char const *waterFractionType() { return "waterFractionType"; }
+ /// @return String key for "wfr" array
+ static constexpr char const *waterFractionArray() { return "wfr"; }
+
+ /// @return String key type of gass fraction associated with the "gfr" array
+ static constexpr char const *gasFractionType() { return "gasFractionType"; }
+ /// @return String key for "wfr" array
+ static constexpr char const *gasFractionArray() { return "gfr"; }
+
+ /// @return String key type of gas lift rate associated with the "gasLift" array
+ static constexpr char const *gasLiftType() { return "gasLiftType"; }
+ /// @return String key for "gasLift" array
+ static constexpr char const *gasLiftArray() { return "gasLift"; }
+
+ /// @return String key for "bhp" array
+ static constexpr char const *bottomHolePressureArray() { return "bottomHolePressure"; }
+ }
+ /// ViewKey struct for the Perforation class
+ viewKeysPipeFlowTableFunction;
+
+ void calculateBHP( array1d< real64 > const & phaseRates, real64 const & whp, real64 & bhp, integer & solveStat ) const;
+
+ void calculateWHP( const std::string & wellName, real64 const & bhp, array1d< real64 > const & phaseRates, real64 & whp, integer & solveStat ) const;
+
+ void writeTable() const;
+
+ virtual void postInputInitialization() override;
+
+protected:
+
+ /**
+ * @brief Initialize the table function after setting table coordinates and values
+ */
+ virtual void initializeFunction() override;
+ //void initializeFunction1() ;
+private:
+
+ /// Name of the flow table
+ string m_tableName;
+
+ /// Rate
+ string m_rateType;
+ string_array m_ratePhases;
+ array1d< real64 > m_rate;
+
+
+ /// Well head pressure
+ string m_pressureType;
+ array1d< real64 > m_whp;
+
+ /// Water fraction
+ string m_waterFractionType;
+ array1d< real64 > m_wfr;
+
+ /// gas fraction
+ string m_gasFractionType;
+ array1d< real64 > m_gfr;
+
+ /// gas lift rate
+ string m_gasLiftType;
+ array1d< real64 > m_gasLift;
+
+ /// bottom hole pressure
+ array1d< real64 > m_bhp;
+
+ // Table function for evaluating values and derivatives
+ MultivariableNonuniformTableFunction * m_tableFunction;
+ TableFunction * m_tableFunction0;
+
+};
+
+
+} /* namespace geos */
+
+#endif /* GEOS_FUNCTIONS_PipeFlowTableFunction_HPP_ */
diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/SinglePhaseWell.cpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/SinglePhaseWell.cpp
index 6414693bf22..642e874764b 100644
--- a/src/coreComponents/physicsSolvers/fluidFlow/wells/SinglePhaseWell.cpp
+++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/SinglePhaseWell.cpp
@@ -37,14 +37,14 @@
#include "physicsSolvers/fluidFlow/wells/WellFields.hpp"
#include "physicsSolvers/fluidFlow/wells/WellSolverBaseFields.hpp"
#include "physicsSolvers/fluidFlow/wells/SinglePhaseWellFields.hpp"
-#include "physicsSolvers/fluidFlow/wells/WellControls.hpp"
#include "physicsSolvers/fluidFlow/wells/kernels/SinglePhaseWellKernels.hpp"
#include "physicsSolvers/fluidFlow/wells/kernels/ThermalSinglePhaseWellKernels.hpp"
#include "physicsSolvers/fluidFlow/wells/kernels/SinglePhasePerforationFluxKernels.hpp"
#include "physicsSolvers/fluidFlow/kernels/singlePhase/FluidUpdateKernel.hpp"
#include "physicsSolvers/fluidFlow/kernels/singlePhase/SolutionCheckKernel.hpp"
#include "physicsSolvers/fluidFlow/SinglePhaseStatistics.hpp"
-
+#include "physicsSolvers/fluidFlow/wells/kernels/SinglePhaseWellConstraintKernels.hpp"
+#include "events/EventManager.hpp"
namespace geos
{
@@ -55,7 +55,7 @@ using namespace singlePhaseWellKernels;
SinglePhaseWell::SinglePhaseWell( const string & name,
Group * const parent ):
- WellSolverBase( name, parent )
+ WellControls( name, parent )
{
m_numDofPerWellElement = 2;
m_numDofPerResElement = 1;
@@ -68,64 +68,57 @@ SinglePhaseWell::SinglePhaseWell( const string & name,
setDescription( "Flag indicating if negative pressure is allowed" );
}
-void SinglePhaseWell::registerDataOnMesh( Group & meshBodies )
+void SinglePhaseWell::registerWellDataOnMesh( WellElementSubRegion & subRegion )
{
- WellSolverBase::registerDataOnMesh( meshBodies );
+ WellControls::registerDataOnMesh( subRegion );
+ setConstitutiveNames ( subRegion );
- // loop over the wells
- forDiscretizationOnMeshTargets( meshBodies, [&] ( string const &,
- MeshLevel & mesh,
- string_array const & regionNames )
+ if( m_referenceFluidModelName.empty() )
{
+ m_referenceFluidModelName = getConstitutiveName< SingleFluidBase >( subRegion );
+ }
+ subRegion.registerField< well::pressure >( getName() );
+ subRegion.registerField< well::pressure_n >( getName() );
- ElementRegionManager & elemManager = mesh.getElemManager();
-
- elemManager.forElementSubRegions< WellElementSubRegion >( regionNames,
- [&]( localIndex const,
- WellElementSubRegion & subRegion )
- {
- subRegion.registerField< well::connectionRate_n >( getName() );
- subRegion.registerField< well::connectionRate >( getName() );
-
- PerforationData & perforationData = *subRegion.getPerforationData();
- perforationData.registerField< well::perforationRate >( getName() );
- perforationData.registerField< well::dPerforationRate >( getName() ).
- reference().resizeDimension< 1, 2 >( 2, 2 );
- if( isThermal() )
- {
- perforationData.registerField< well::energyPerforationFlux >( getName() );
- perforationData.registerField< well::dEnergyPerforationFlux >( getName() ).
- reference().resizeDimension< 1, 2 >( 2, 2 );
- }
-
- WellControls & wellControls = getWellControls( subRegion );
- wellControls.registerWrapper< real64 >( viewKeyStruct::currentBHPString() );
+ subRegion.registerField< well::temperature >( getName() );
+ if( isThermal() )
+ {
+ subRegion.registerField< well::temperature_n >( getName() );
+ }
+ subRegion.registerField< well::connectionRate_n >( getName() );
+ subRegion.registerField< well::connectionRate >( getName() );
+ subRegion.registerField< well::gravityCoefficient >( getName() );
+ PerforationData & perforationData = *subRegion.getPerforationData();
+ perforationData.registerField< well::gravityCoefficient >( getName() );
+
+ perforationData.registerField< well::perforationRate >( getName() );
+ perforationData.registerField< well::dPerforationRate >( getName() ).
+ reference().resizeDimension< 1, 2 >( 2, 2 );
+ if( isThermal() )
+ {
+ perforationData.registerField< well::energyPerforationFlux >( getName() );
+ perforationData.registerField< well::dEnergyPerforationFlux >( getName() ).
+ reference().resizeDimension< 1, 2 >( 2, 2 );
+ perforationData.registerField< well::gravityCoefficient >( getName() );
+ }
- wellControls.registerWrapper< array1d< real64 > >( viewKeyStruct::dCurrentBHPString() ).
- setSizedFromParent( 0 ).
- reference().resizeDimension< 0 >( 2 + isThermal() ); // dP, dT , dQ
+ registerWrapper< real64 >( viewKeyStruct::currentBHPString() );
+ registerWrapper< real64 >( viewKeyStruct::currentVolRateString() );
- wellControls.registerWrapper< real64 >( viewKeyStruct::currentVolRateString() );
- wellControls.registerWrapper< array1d< real64 > >( viewKeyStruct::dCurrentVolRateString() ).
- setSizedFromParent( 0 ).
- reference().resizeDimension< 0 >( 2 + isThermal() ); // dP, dT, dQ
+ // write rates output header
+ if( m_writeCSV > 0 && subRegion.isLocallyOwned())
+ {
+ string const fileName = GEOS_FMT( "{}/{}.csv", m_ratesOutputDir, getName() );
+ string const conditionKey = useSurfaceConditions() ? "surface" : "reservoir";
+ string const unitKey = useSurfaceConditions() ? "s" : "r";
+ // format: time,bhp,total_rate,total_vol_rate
+ makeDirsForPath( m_ratesOutputDir );
+ GEOS_LOG( GEOS_FMT( "{}: Rates CSV generated at {}", getName(), fileName ) );
+ std::ofstream outputFile( fileName );
+ outputFile << "Time [s],BHP [Pa],Total rate [kg/s],Total " << conditionKey << " volumetric rate ["< 0 && subRegion.isLocallyOwned())
- {
- string const fileName = GEOS_FMT( "{}/{}.csv", m_ratesOutputDir, wellControls.getName() );
- integer const useSurfaceConditions = wellControls.useSurfaceConditions();
- string const conditionKey = useSurfaceConditions ? "surface" : "reservoir";
- string const unitKey = useSurfaceConditions ? "s" : "r";
- // format: time,bhp,total_rate,total_vol_rate
- makeDirsForPath( m_ratesOutputDir );
- GEOS_LOG( GEOS_FMT( "{}: Rates CSV generated at {}", getName(), fileName ) );
- std::ofstream outputFile( fileName );
- outputFile << "Time [s],BHP [Pa],Total rate [kg/s],Total " << conditionKey << " volumetric rate ["<( getFlowSolverName() );
string_array const & targetRegionsNames = flowSolver.getTargetRegionNames();
auto const pos = std::find( targetRegionsNames.begin(), targetRegionsNames.end(), regionName );
GEOS_ERROR_IF( pos == targetRegionsNames.end(),
GEOS_FMT( "Region {} is not a target of the reservoir solver and cannot be used for referenceReservoirRegion in WellControl {}.",
- regionName, wellControls.getName() ),
+ regionName, getName() ),
getDataContext() );
}
}
- WellControls::Control currentControl = wellControls.getControl();
- real64 const targetTotalRate = wellControls.getTargetTotalRate( time_n );
- real64 const targetPhaseRate = wellControls.getTargetPhaseRate( time_n );
- GEOS_THROW_IF( currentControl == WellControls::Control::PHASEVOLRATE,
- "Phase rate control is not available for SinglePhaseWell",
- InputError, wellControls.getDataContext() );
- // The user always provides positive rates, but these rates are later multiplied by -1 internally for producers
- GEOS_THROW_IF( ( ( wellControls.isInjector() && targetTotalRate < 0.0 ) ||
- ( wellControls.isProducer() && targetTotalRate > 0.0) ),
- "Target total rate cannot be negative",
- InputError, wellControls.getDataContext() );
- GEOS_THROW_IF( !isZero( targetPhaseRate ),
- "Target phase rate cannot be used for SinglePhaseWell",
- InputError, wellControls.getDataContext() );
+}
+
+void SinglePhaseWell::initializeWellPostInitialConditionsPreSubGroups( WellElementSubRegion & subRegion )
+{
+
+ // set gravity coefficient
+ setGravCoef( subRegion, getParent().getParent().getReference< R1Tensor >( PhysicsSolverManager::viewKeyStruct::gravityVectorString() ));
+
+ // setup fluid model
+ createSeparator( subRegion );
+}
+void SinglePhaseWell::initializePostInitialConditionsPreSubGroups()
+{
+ WellControls::initializePostInitialConditionsPreSubGroups();
+}
+void SinglePhaseWell::postRestartInitialization( )
+{
+ // setup fluid separator
+ constitutive::SingleFluidBase & fluidSeparator = getSingleFluidSeparator();
+ fluidSeparator.allocateConstitutiveData( *this, 1 );
+ fluidSeparator.resize( 1 );
+
+}
+void SinglePhaseWell::createSeparator( WellElementSubRegion & subRegion )
+{
+
+ string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString() );
+ SingleFluidBase & fluid = subRegion.getConstitutiveModel< SingleFluidBase >( fluidName );
+ // setup fluid separator
+
+ string const fluidSeparatorName = getName() + "Separator";
+ std::unique_ptr< constitutive::ConstitutiveBase > fluidSeparatorPtr = fluid.deliverClone( fluidSeparatorName, this );
+ fluidSeparatorPtr->allocateConstitutiveData( *this, 1 );
+ fluidSeparatorPtr->resize( 1 );
+ setFluidSeparator( std::move( fluidSeparatorPtr ));
+
}
void SinglePhaseWell::updateBHPForConstraint( WellElementSubRegion & subRegion )
@@ -192,7 +208,6 @@ void SinglePhaseWell::updateBHPForConstraint( WellElementSubRegion & subRegion )
return;
}
-
localIndex const iwelemRef = subRegion.getTopWellElementIndex();
// subRegion data
@@ -209,47 +224,29 @@ void SinglePhaseWell::updateBHPForConstraint( WellElementSubRegion & subRegion )
arrayView2d< real64 const, constitutive::singlefluid::USD_FLUID > const & dens = fluid.density();
arrayView3d< real64 const, constitutive::singlefluid::USD_FLUID_DER > const & dDens = fluid.dDensity();
- // control data
-
- WellControls & wellControls = getWellControls( subRegion );
- string const wellControlsName = wellControls.getName();
- real64 const & refGravCoef = wellControls.getReferenceGravityCoef();
-
- real64 & currentBHP =
- wellControls.getReference< real64 >( SinglePhaseWell::viewKeyStruct::currentBHPString() );
- arrayView1d< real64 > const & dCurrentBHP =
- wellControls.getReference< array1d< real64 > >( SinglePhaseWell::viewKeyStruct::dCurrentBHPString() );
-
- geos::internal::kernelLaunchSelectorThermalSwitch( isThermal(), [&] ( auto ISTHERMAL )
+ real64 const & refGravCoef = getReferenceGravityCoef();
+ real64 & currentBHP = getReference< real64 >( SinglePhaseWell::viewKeyStruct::currentBHPString() );
+
+ // bring everything back to host, capture the scalars by reference
+ forAll< serialPolicy >( 1, [pres,
+ dens,
+ dDens,
+ wellElemGravCoef,
+ ¤tBHP,
+ &iwelemRef,
+ &refGravCoef] ( localIndex const )
{
- integer constexpr IS_THERMAL = ISTHERMAL();
- // bring everything back to host, capture the scalars by reference
- forAll< serialPolicy >( 1, [pres,
- dens,
- dDens,
- wellElemGravCoef,
- ¤tBHP,
- &dCurrentBHP,
- &iwelemRef,
- &refGravCoef] ( localIndex const )
- {
- real64 const diffGravCoef = refGravCoef - wellElemGravCoef[iwelemRef];
- currentBHP = pres[iwelemRef] + dens[iwelemRef][0] * diffGravCoef;
- dCurrentBHP[DerivOffset::dP] = 1.0 + dDens[iwelemRef][0][DerivOffset::dP] *diffGravCoef;
- if constexpr ( IS_THERMAL )
- {
- dCurrentBHP[DerivOffset::dT] = dDens[iwelemRef][0][DerivOffset::dT] * diffGravCoef;
- }
- } );
+ real64 const diffGravCoef = refGravCoef - wellElemGravCoef[iwelemRef];
+ currentBHP = pres[iwelemRef] + dens[iwelemRef][0] * diffGravCoef;
} );
GEOS_LOG_LEVEL_BY_RANK( logInfo::WellControl,
GEOS_FMT( "{}: The BHP (at the specified reference elevation) = {} Pa",
- wellControlsName, currentBHP ) );
+ getName(), currentBHP ) );
}
-void SinglePhaseWell::updateVolRateForConstraint( ElementRegionManager const & elemManager, WellElementSubRegion & subRegion )
+void SinglePhaseWell::calculateReferenceElementRates( WellElementSubRegion & subRegion )
{
GEOS_MARK_FUNCTION;
@@ -263,83 +260,123 @@ void SinglePhaseWell::updateVolRateForConstraint( ElementRegionManager const & e
// subRegion data
- arrayView1d< real64 const > const pres =
- subRegion.getField< well::pressure >();
-
arrayView1d< real64 const > const & connRate =
subRegion.getField< well::connectionRate >();
// fluid data
+ constitutive::SingleFluidBase & fluidSeparator = getSingleFluidSeparator();
+ arrayView2d< real64 const, constitutive::singlefluid::USD_FLUID > const & dens = fluidSeparator.density();
+
+ real64 & currentVolRate =
+ getReference< real64 >( WellControls::viewKeyStruct::currentVolRateString() );
+
+ // bring everything back to host, capture the scalars by reference
+ forAll< serialPolicy >( 1, [connRate,
+ dens,
+ ¤tVolRate,
+ &iwelemRef] ( localIndex const )
+ {
+ real64 const densInv = 1.0 / dens[iwelemRef][0];
+ currentVolRate = connRate[iwelemRef] * densInv;
+ // compute mass rate
+ } );
+
+
+}
+
+void SinglePhaseWell::updateFluidModel( WellElementSubRegion & subRegion ) const
+{
+ GEOS_MARK_FUNCTION;
+
+ arrayView1d< real64 const > const pres = subRegion.getField< well::pressure >();
+ arrayView1d< real64 const > const temp = subRegion.getField< well::temperature >();
string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString() );
SingleFluidBase & fluid = subRegion.getConstitutiveModel< SingleFluidBase >( fluidName );
- arrayView2d< real64 const, constitutive::singlefluid::USD_FLUID > const & dens = fluid.density();
- arrayView3d< real64 const, constitutive::singlefluid::USD_FLUID_DER > const & dDens = fluid.dDensity();
+
+ constitutiveUpdatePassThru( fluid, [&]( auto & castedFluid )
+ {
+ typename TYPEOFREF( castedFluid ) ::KernelWrapper fluidWrapper = castedFluid.createKernelWrapper();
+ singlePhaseBaseKernels::FluidUpdateKernel::launch( fluidWrapper, pres, temp );
+ } );
+}
+void SinglePhaseWell::updateSeparator( ElementRegionManager const & elemManager, WellElementSubRegion & subRegion )
+{
+ GEOS_MARK_FUNCTION;
+
+ // the rank that owns the reference well element is responsible for the calculations below.
+ if( !subRegion.isLocallyOwned() )
+ {
+ return;
+ }
+
+ localIndex const iwelemRef = subRegion.getTopWellElementIndex();
+
+ // subRegion data
+ arrayView1d< real64 const > const pres =
+ subRegion.getField< well::pressure >();
// control data
- WellControls & wellControls = getWellControls( subRegion );
- string const wellControlsName = wellControls.getName();
- bool const logSurfaceCondition = isLogLevelActive< logInfo::WellControl >( wellControls.getLogLevel());
- integer const useSurfaceConditions = wellControls.useSurfaceConditions();
+ string const wellControlsName = getName();
+ bool const logSurfaceCondition = isLogLevelActive< logInfo::WellControl >( getLogLevel());
+ integer const useSurfaceCond = useSurfaceConditions();
+
+ // fluid data
+ constitutive::SingleFluidBase & fluidSeparator = getSingleFluidSeparator();
+ arrayView2d< real64 const, constitutive::singlefluid::USD_FLUID > const & dens = fluidSeparator.density();
+
real64 flashPressure;
- if( useSurfaceConditions )
+ if( useSurfaceCond )
{
// use surface conditions
- flashPressure = wellControls.getSurfacePressure();
+ flashPressure = getSurfacePressure();
}
else
{
- if( !wellControls.referenceReservoirRegion().empty() )
+ if( !getReferenceReservoirRegion().empty() )
{
- ElementRegionBase const & region = elemManager.getRegion( wellControls.referenceReservoirRegion() );
+ ElementRegionBase const & region = elemManager.getRegion( getReferenceReservoirRegion() );
GEOS_ERROR_IF ( !region.hasWrapper( SinglePhaseStatistics::regionStatisticsName()),
GEOS_FMT( "WellControl {} referenceReservoirRegion field requires SinglePhaseStatistics to be configured for region {} ",
- wellControls.getName(), wellControls.referenceReservoirRegion() ),
+ getName(), getReferenceReservoirRegion() ),
getDataContext() );
- SinglePhaseStatistics::RegionStatistics const & stats = region.getReference< SinglePhaseStatistics::RegionStatistics >( SinglePhaseStatistics::regionStatisticsName() );
+ SinglePhaseStatistics::RegionStatistics const & stats = region.getReference< SinglePhaseStatistics::RegionStatistics >(
+ SinglePhaseStatistics::regionStatisticsName() );
+
GEOS_ERROR_IF( stats.averagePressure <= 0.0,
GEOS_FMT(
"No region average quantities computed. WellControl {} referenceReservoirRegion field requires SinglePhaseStatistics to be configured for region {} ",
- wellControls.getName(), wellControls.referenceReservoirRegion() ),
+ getName(), getReferenceReservoirRegion() ),
getDataContext());
- wellControls.setRegionAveragePressure( stats.averagePressure );
- wellControls.setRegionAverageTemperature( stats.averageTemperature );
+ setRegionAveragePressure( stats.averagePressure );
+ setRegionAverageTemperature( stats.averageTemperature );
}
// use region conditions
- flashPressure = wellControls.getRegionAveragePressure();
+ flashPressure = getRegionAveragePressure();
if( flashPressure < 0.0 )
{
// use segment conditions
flashPressure = pres[iwelemRef];
}
}
- real64 & currentVolRate =
- wellControls.getReference< real64 >( SinglePhaseWell::viewKeyStruct::currentVolRateString() );
- arrayView1d< real64 > const & dCurrentVolRate =
- wellControls.getReference< array1d< real64 > >( SinglePhaseWell::viewKeyStruct::dCurrentVolRateString() );
-
- constitutiveUpdatePassThru( fluid, [&]( auto & castedFluid )
+ constitutiveUpdatePassThru( fluidSeparator, [&]( auto & castedFluid )
{
- typename TYPEOFREF( castedFluid ) ::KernelWrapper fluidWrapper = castedFluid.createKernelWrapper();
+ typename TYPEOFREF( castedFluid ) ::KernelWrapper fluidSeparatorWrapper = castedFluid.createKernelWrapper();
geos::internal::kernelLaunchSelectorThermalSwitch( isThermal(), [&] ( auto ISTHERMAL )
{
integer constexpr IS_THERMAL = ISTHERMAL();
- using COFFSET_WJ = singlePhaseWellKernels::ColOffset_WellJac< IS_THERMAL >;
+ GEOS_UNUSED_VAR( IS_THERMAL );
// bring everything back to host, capture the scalars by reference
- forAll< serialPolicy >( 1, [fluidWrapper,
+ forAll< serialPolicy >( 1, [fluidSeparatorWrapper,
pres,
- connRate,
dens,
- dDens,
logSurfaceCondition,
- &useSurfaceConditions,
+ &useSurfaceCond,
&flashPressure,
- ¤tVolRate,
- dCurrentVolRate,
&iwelemRef,
&wellControlsName] ( localIndex const )
{
@@ -347,10 +384,10 @@ void SinglePhaseWell::updateVolRateForConstraint( ElementRegionManager const & e
// - Surface conditions: using the surface pressure provided by the user
// - Reservoir conditions: using the pressure in the top element
- if( useSurfaceConditions )
+ if( useSurfaceCond )
{
// we need to compute the surface density
- fluidWrapper.update( iwelemRef, 0, flashPressure );
+ fluidSeparatorWrapper.update( iwelemRef, 0, flashPressure );
if( logSurfaceCondition )
{
@@ -365,547 +402,532 @@ void SinglePhaseWell::updateVolRateForConstraint( ElementRegionManager const & e
}
else
{
- real64 const refPres = pres[iwelemRef];
- fluidWrapper.update( iwelemRef, 0, refPres );
- }
-
- real64 const densInv = 1.0 / dens[iwelemRef][0];
- currentVolRate = connRate[iwelemRef] * densInv;
-
- dCurrentVolRate[COFFSET_WJ::dP] = -( useSurfaceConditions == 0 ) * dDens[iwelemRef][0][DerivOffset::dP] * currentVolRate * densInv;
- dCurrentVolRate[COFFSET_WJ::dQ] = densInv;
- if constexpr ( IS_THERMAL )
- {
- dCurrentVolRate[COFFSET_WJ::dT] = -( useSurfaceConditions == 0 ) * dDens[iwelemRef][0][DerivOffset::dT] * currentVolRate * densInv;
- }
- if( logSurfaceCondition && useSurfaceConditions )
- {
- GEOS_LOG_RANK( GEOS_FMT( "{}: total fluid density at surface conditions = {} kg/sm3, total rate = {} kg/s, total surface volumetric rate = {} sm3/s",
- wellControlsName, dens[iwelemRef][0], connRate[iwelemRef], currentVolRate ) );
+ fluidSeparatorWrapper.update( iwelemRef, 0, flashPressure );
}
} );
} );
} );
}
-void SinglePhaseWell::updateFluidModel( WellElementSubRegion & subRegion ) const
-{
- GEOS_MARK_FUNCTION;
-
- arrayView1d< real64 const > const pres = subRegion.getField< well::pressure >();
- arrayView1d< real64 const > const temp = subRegion.getField< well::temperature >();
-
- string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString() );
- SingleFluidBase & fluid = subRegion.getConstitutiveModel< SingleFluidBase >( fluidName );
-
- constitutiveUpdatePassThru( fluid, [&]( auto & castedFluid )
- {
- typename TYPEOFREF( castedFluid ) ::KernelWrapper fluidWrapper = castedFluid.createKernelWrapper();
- singlePhaseBaseKernels::FluidUpdateKernel::launch( fluidWrapper, pres, temp );
- } );
-}
-
real64 SinglePhaseWell::updateSubRegionState( ElementRegionManager const & elemManager, WellElementSubRegion & subRegion )
{
- // update volumetric rates for the well constraints
- // Warning! This must be called before updating the fluid model
- updateVolRateForConstraint( elemManager, subRegion );
- // update density in the well elements
- updateFluidModel( subRegion );
-
- // update the current BHP
- updateBHPForConstraint( subRegion );
+ if( getWellState())
+ {
- // note: the perforation rates are updated separately
- return 0.0; // change in phasevolume fraction doesnt apply
+ // update volumetric rates for the well constraints
+ // Warning! This must be called before updating the fluid model
+ //calculateReferenceElementRates( subRegion );
+
+ // update density in the well elements
+ updateFluidModel( subRegion );
+ updateSeparator( elemManager, subRegion ); // Calculate fluid properties at control conditions
+
+ // Calculate the reference element rates
+ calculateReferenceElementRates( subRegion );
+ // update the current BHP
+ updateBHPForConstraint( subRegion );
+
+ // Broad case the updated well state to other ranks
+ real64 & currentBHP =
+ getReference< real64 >( SinglePhaseWell::viewKeyStruct::currentBHPString() );
+ real64 & currentTotalVolRate =
+ getReference< real64 >( SinglePhaseWell::viewKeyStruct::currentVolRateString() );
+ integer topRank = subRegion.getTopRank();
+ MpiWrapper::broadcast( currentBHP, topRank );
+ MpiWrapper::broadcast( currentTotalVolRate, topRank );
+ WellConstraintBase * constraint = getCurrentConstraint();
+ if( constraint != nullptr )
+ {
+ constraint->setBHP ( getReference< real64 >( SinglePhaseWell::viewKeyStruct::currentBHPString() ));
+ constraint->setTotalVolumeRate ( getReference< real64 >(
+ SinglePhaseWell::viewKeyStruct::currentVolRateString() ));
+ }
+ }
+ return 0.0; // change in phasevolume fraction doesnt apply
}
-
-void SinglePhaseWell::initializeWells( DomainPartition & domain, real64 const & time_n )
+void SinglePhaseWell::initializeWell( DomainPartition & domain, MeshLevel & mesh, WellElementSubRegion & subRegion, real64 const & time_n )
{
- GEOS_MARK_FUNCTION;
- GEOS_UNUSED_VAR( time_n );
+ GEOS_UNUSED_VAR( domain );
- // loop over the wells
- forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &,
- MeshLevel & meshLevel,
- string_array const & regionNames )
- {
- ElementRegionManager & elemManager = meshLevel.getElemManager();
+ PerforationData const & perforationData = *subRegion.getPerforationData();
+ ElementRegionManager const & elemManager = mesh.getElemManager();
+ // get the info stored on well elements
+ arrayView1d< real64 const > const wellElemGravCoef =
+ subRegion.getField< well::gravityCoefficient >();
- elemManager.forElementSubRegions< WellElementSubRegion >( regionNames,
- [&]( localIndex const,
- WellElementSubRegion & subRegion )
+ // get well primary variables on well elements
+ arrayView1d< real64 > const wellElemPressure =
+ subRegion.getField< well::pressure >();
+ arrayView1d< real64 > const connRate =
+ subRegion.getField< well::connectionRate >();
+ arrayView1d< real64 > const wellElemTemperature =
+ subRegion.getField< well::temperature >();
+ // get the element region, subregion, index
+ arrayView1d< localIndex const > const resElementRegion =
+ perforationData.getField< perforation::reservoirElementRegion >();
+ arrayView1d< localIndex const > const resElementSubRegion =
+ perforationData.getField< perforation::reservoirElementSubRegion >();
+ arrayView1d< localIndex const > const resElementIndex =
+ perforationData.getField< perforation::reservoirElementIndex >();
+
+ arrayView1d< real64 const > const & perfGravCoef =
+ perforationData.getField< well::gravityCoefficient >();
+
+ bool const hasNonZeroRate = MpiWrapper::max< integer >( hasNonZero( connRate ));
+
+ if( time_n <= 0.0 || ( isWellOpen() && !hasNonZeroRate ) )
+ {
+ setWellState( true );
+ if( getCurrentConstraint() == nullptr )
{
- WellControls const & wellControls = getWellControls( subRegion );
- PerforationData const & perforationData = *subRegion.getPerforationData();
-
- // get the info stored on well elements
- arrayView1d< real64 const > const wellElemGravCoef =
- subRegion.getField< well::gravityCoefficient >();
-
- // get well primary variables on well elements
- arrayView1d< real64 > const wellElemPressure =
- subRegion.getField< well::pressure >();
- arrayView1d< real64 > const connRate =
- subRegion.getField< well::connectionRate >();
- arrayView1d< real64 > const wellElemTemperature =
- subRegion.getField< well::temperature >();
- // get the element region, subregion, index
- arrayView1d< localIndex const > const resElementRegion =
- perforationData.getField< perforation::reservoirElementRegion >();
- arrayView1d< localIndex const > const resElementSubRegion =
- perforationData.getField< perforation::reservoirElementSubRegion >();
- arrayView1d< localIndex const > const resElementIndex =
- perforationData.getField< perforation::reservoirElementIndex >();
-
- arrayView1d< real64 const > const & perfGravCoef =
- perforationData.getField< well::gravityCoefficient >();
-
- bool const hasNonZeroRate = MpiWrapper::max< integer >( hasNonZero( connRate ));
-
- if( wellControls.isWellOpen() && !hasNonZeroRate )
+ if( isProducer() )
{
- // TODO: change the way we access the flowSolver here
- SinglePhaseBase const & flowSolver = getParent().getGroup< SinglePhaseBase >( getFlowSolverName() );
- PresTempInitializationKernel::SinglePhaseFlowAccessors resSinglePhaseFlowAccessors( meshLevel.getElemManager(), flowSolver.getName() );
- PresTempInitializationKernel::SingleFluidAccessors resSingleFluidAccessors( meshLevel.getElemManager(), flowSolver.getName() );
-
- // 1) Loop over all perforations to compute an average density
- // 2) Initialize the reference pressure
- // 3) Estimate the pressures in the well elements using the average density
- PresTempInitializationKernel::
- launch( isThermal(),
- perforationData.size(),
- subRegion.size(),
- perforationData.getNumPerforationsGlobal(),
- wellControls,
- 0.0, // initialization done at t = 0
- resSinglePhaseFlowAccessors.get( flow::pressure{} ),
- resSinglePhaseFlowAccessors.get( flow::temperature{} ),
- resSingleFluidAccessors.get( fields::singlefluid::density{} ),
- resElementRegion,
- resElementSubRegion,
- resElementIndex,
- perfGravCoef,
- wellElemGravCoef,
- wellElemPressure,
- wellElemTemperature );
-
- // 4) Recompute the pressure-dependent properties
- // Note: I am leaving that here because I would like to use the perforationRates (computed in UpdateState)
- // to better initialize the rates
- updateSubRegionState( elemManager, subRegion );
-
- string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString() );
- SingleFluidBase & fluid = subRegion.getConstitutiveModel< SingleFluidBase >( fluidName );
- arrayView2d< real64 const, constitutive::singlefluid::USD_FLUID > const & wellElemDens = fluid.density();
-
- // 5) Estimate the well rates
- RateInitializationKernel::launch( subRegion.size(),
- wellControls,
- 0.0, // initialization done at t = 0
- wellElemDens,
- connRate );
+ forSubGroups< MinimumBHPConstraint, ProductionConstraint< VolumeRateConstraint >, ProductionConstraint< MassRateConstraint >,
+ ProductionConstraint< PhaseVolumeRateConstraint > >( [&]( auto & constraint )
+ {
+ if( ConstraintTypeId( getControl()) == constraint.getControl() )
+ {
+ setCurrentConstraint( &constraint );
+ setControl( static_cast< WellControls::Control >(constraint.getControl()) );
+ }
+ } );
}
+ else
+ {
+ forSubGroups< MaximumBHPConstraint, InjectionConstraint< VolumeRateConstraint >, InjectionConstraint< MassRateConstraint >,
+ InjectionConstraint< PhaseVolumeRateConstraint > >( [&]( auto & constraint )
+ {
+ if( ConstraintTypeId( getControl()) == constraint.getControl() )
+ {
+ setCurrentConstraint( &constraint );
+ setControl( static_cast< WellControls::Control >(constraint.getControl()) ); // tjb old
+ }
+ } );
+ }
+ }
- } );
-
- } );
-}
-void SinglePhaseWell::shutDownWell( real64 const time_n,
- DomainPartition const & domain,
- DofManager const & dofManager,
- CRSMatrixView< real64, globalIndex const > const & localMatrix,
- arrayView1d< real64 > const & localRhs )
-{
- GEOS_MARK_FUNCTION;
- GEOS_UNUSED_VAR( time_n );
-
- string const wellDofKey = dofManager.getKey( wellElementDofName() );
-
- forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &,
- MeshLevel const & mesh,
- string_array const & regionNames )
+ PresTempInitializationKernel::SinglePhaseFlowAccessors resSinglePhaseFlowAccessors( elemManager, getFlowSolverName());
+ PresTempInitializationKernel::SingleFluidAccessors resSingleFluidAccessors( elemManager, getFlowSolverName() );
+
+ // 1) Loop over all perforations to compute an average density
+ // 2) Initialize the reference pressure
+ // 3) Estimate the pressures in the well elements using the average density
+ PresTempInitializationKernel::
+ launch( isThermal(),
+ perforationData.size(),
+ subRegion.size(),
+ perforationData.getNumPerforationsGlobal(),
+ *this,
+ 0.0, // initialization done at t = 0
+ resSinglePhaseFlowAccessors.get( flow::pressure{} ),
+ resSinglePhaseFlowAccessors.get( flow::temperature{} ),
+ resSingleFluidAccessors.get( fields::singlefluid::density{} ),
+ resElementRegion,
+ resElementSubRegion,
+ resElementIndex,
+ perfGravCoef,
+ wellElemGravCoef,
+ wellElemPressure,
+ wellElemTemperature );
+
+ // 4) Recompute the pressure-dependent properties
+ // Note: I am leaving that here because I would like to use the perforationRates (computed in UpdateState)
+ // to better initialize the rates
+ updateSubRegionState( elemManager, subRegion );
+
+ string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString() );
+ SingleFluidBase & fluid = subRegion.getConstitutiveModel< SingleFluidBase >( fluidName );
+ arrayView2d< real64 const, constitutive::singlefluid::USD_FLUID > const & wellElemDens = fluid.density();
+
+ // 5) Estimate the well rates
+ RateInitializationKernel::launch( subRegion.size(),
+ *this,
+ 0.0, // initialization done at t = 0
+ wellElemDens,
+ connRate );
+
+ calculateReferenceElementRates( subRegion );
+ WellConstraintBase * constraint = getCurrentConstraint();
+ constraint->setBHP ( getReference< real64 >( WellControls::viewKeyStruct::currentBHPString() ));
+ constraint->setTotalVolumeRate ( getReference< real64 >(
+ SinglePhaseWell::viewKeyStruct::currentVolRateString() ));
+ // 7) Copy well / fluid dofs to "prop"_n variables
+ saveState( subRegion );
+ }
+ else if( !hasNonZeroRate )
{
-
- ElementRegionManager const & elemManager = mesh.getElemManager();
-
- elemManager.forElementSubRegions< WellElementSubRegion >( regionNames,
- [&]( localIndex const,
- WellElementSubRegion const & subRegion )
+ setWellState( false );
+ }
+ else
+ {
+ setWellState( true );
+ // setup for restart
+ if( getCurrentConstraint() == nullptr )
{
-
- // if the well is open, we don't have to do anything, so we just return
- WellControls const & wellControls = getWellControls( subRegion );
- if( wellControls.isWellOpen( ) )
+ if( isProducer() )
{
- return;
+ forSubGroups< MinimumBHPConstraint, ProductionConstraint< VolumeRateConstraint >, ProductionConstraint< MassRateConstraint >,
+ ProductionConstraint< PhaseVolumeRateConstraint > >( [&](
+ auto
+ & constraint )
+ {
+ if( ConstraintTypeId( getControl()) == constraint.getControl() )
+ {
+ setCurrentConstraint( &constraint );
+ }
+ } );
}
-
- globalIndex const rankOffset = dofManager.rankOffset();
-
- arrayView1d< integer const > const ghostRank =
- subRegion.getReference< array1d< integer > >( ObjectManagerBase::viewKeyStruct::ghostRankString() );
- arrayView1d< globalIndex const > const dofNumber =
- subRegion.getReference< array1d< globalIndex > >( wellDofKey );
-
- arrayView1d< real64 const > const pres =
- subRegion.getField< fields::well::pressure >();
- arrayView1d< real64 const > const connRate =
- subRegion.getField< fields::well::connectionRate >();
-
- forAll< parallelDevicePolicy<> >( subRegion.size(), [=] GEOS_HOST_DEVICE ( localIndex const ei )
+ else
{
- if( ghostRank[ei] >= 0 )
+ forSubGroups< MaximumBHPConstraint, InjectionConstraint< VolumeRateConstraint >, InjectionConstraint< MassRateConstraint >, InjectionConstraint< PhaseVolumeRateConstraint > >( [&](
+ auto
+ &
+ constraint )
{
- return;
- }
+ if( ConstraintTypeId( getControl()) == constraint.getControl() )
+ {
+ setCurrentConstraint( &constraint );
+ }
+ } );
+ }
+ updateSubRegionState( elemManager, subRegion );
+ }
- globalIndex const dofIndex = dofNumber[ei];
- localIndex const localRow = dofIndex - rankOffset;
- real64 rhsValue;
-
- // 4.1. Apply pressure value to the matrix/rhs
- FieldSpecificationEqual::SpecifyFieldValue( dofIndex,
- rankOffset,
- localMatrix,
- rhsValue,
- pres[ei], // freeze the current pressure value
- pres[ei] );
- localRhs[localRow] = rhsValue;
-
- // 4.2. Apply rate value to the matrix/rhs
- FieldSpecificationEqual::SpecifyFieldValue( dofIndex + 1,
- rankOffset,
- localMatrix,
- rhsValue,
- connRate[ei], // freeze the current pressure value
- connRate[ei] );
- localRhs[localRow + 1] = rhsValue;
+ }
- } );
- } );
- } );
}
-void SinglePhaseWell::assembleSystem( real64 const time,
- real64 const dt,
- DomainPartition & domain,
- DofManager const & dofManager,
- CRSMatrixView< real64, globalIndex const > const & localMatrix,
- arrayView1d< real64 > const & localRhs )
+real64 SinglePhaseWell::updateWellState( ElementRegionManager const & elemManager, WellElementSubRegion & subRegion )
{
- string const wellDofKey = dofManager.getKey( wellElementDofName());
+ GEOS_MARK_FUNCTION;
- // assemble the accumulation term in the mass balance equations
- assembleAccumulationTerms( time, dt, domain, dofManager, localMatrix, localRhs );
+ updateSubRegionState( elemManager, subRegion );
+ return 0.0;
+}
- // then assemble the pressure relations between well elements
- assemblePressureRelations( time, dt, domain, dofManager, localMatrix, localRhs );
- // then compute the perforation rates (later assembled by the coupled solver)
- computePerforationRates( time, dt, domain );
+void SinglePhaseWell::assembleWellFluxTerms( real64 const & time,
+ real64 const & dt,
+ WellElementSubRegion const & subRegion,
+ DofManager const & dofManager,
+ CRSMatrixView< real64, globalIndex const > const & localMatrix,
+ arrayView1d< real64 > const & localRhs )
+{
+ GEOS_MARK_FUNCTION;
+ GEOS_UNUSED_VAR( time );
- // then assemble the flux terms in the mass balance equations
// get a reference to the degree-of-freedom numbers
- // then assemble the flux terms in the mass balance equations
- assembleFluxTerms( time, dt, domain, dofManager, localMatrix, localRhs );
+ string const wellDofKey = dofManager.getKey( wellElementDofName() );
+
+ if( isThermal() )
+ {
+ string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString() );
+ SingleFluidBase const & fluid = subRegion.getConstitutiveModel< SingleFluidBase >( fluidName );
+ thermalSinglePhaseWellKernels::
+ FaceBasedAssemblyKernelFactory::
+ createAndLaunch< parallelDevicePolicy<> >( dt,
+ dofManager.rankOffset(),
+ wellDofKey,
+ *this,
+ subRegion,
+ fluid,
+ localMatrix,
+ localRhs );
+ }
+ else
+ {
+ singlePhaseWellKernels::
+ FaceBasedAssemblyKernelFactory::
+ createAndLaunch< parallelDevicePolicy<> >( dt,
+ dofManager.rankOffset(),
+ wellDofKey,
+ *this,
+ subRegion,
+ localMatrix,
+ localRhs );
+ }
- // then apply a special treatment to the wells that are shut
- shutDownWell( time, domain, dofManager, localMatrix, localRhs );
}
-void SinglePhaseWell::assembleFluxTerms( real64 const & time_n,
- real64 const & dt,
- DomainPartition & domain,
- DofManager const & dofManager,
- CRSMatrixView< real64, globalIndex const > const & localMatrix,
- arrayView1d< real64 > const & localRhs )
+
+void SinglePhaseWell::assembleWellConstraintTerms( real64 const & time_n,
+ real64 const & GEOS_UNUSED_PARAM( dt ),
+ WellElementSubRegion const & subRegion,
+ DofManager const & dofManager,
+ CRSMatrixView< real64, globalIndex const > const & localMatrix,
+ arrayView1d< real64 > const & localRhs )
{
GEOS_MARK_FUNCTION;
- GEOS_UNUSED_VAR( time_n );
- GEOS_UNUSED_VAR( dt );
- // loop over the wells
- forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &,
- MeshLevel const & mesh,
- string_array const & regionNames )
- {
- ElementRegionManager const & elemManager = mesh.getElemManager();
+ // the rank that owns the reference well element is responsible for the calculations below.
- elemManager.forElementSubRegions< WellElementSubRegion >( regionNames,
- [&]( localIndex const,
- WellElementSubRegion const & subRegion )
+ if( !subRegion.isLocallyOwned() || !( getWellStatus() == WellControls::Status::OPEN ))
+ {
+ return;
+ }
+ {
+ forSubGroups< BHPConstraint, InjectionConstraint< VolumeRateConstraint >, ProductionConstraint< VolumeRateConstraint > >( [&]( auto & constraint )
{
+ if( constraint.getName() == getCurrentConstraint()->getName())
+ {
+ // found limiting constraint
- WellControls const & wellControls = getWellControls( subRegion );
- // get a reference to the degree-of-freedom numbers
- string const wellDofKey = dofManager.getKey( wellElementDofName() );
+ // fluid data
+ constitutive::SingleFluidBase & fluidSeparator = getSingleFluidSeparator();
+ integer isThermal = fluidSeparator.isThermal();
- if( isThermal() )
- {
- string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString() );
- SingleFluidBase const & fluid = subRegion.getConstitutiveModel< SingleFluidBase >( fluidName );
- thermalSinglePhaseWellKernels::
- FaceBasedAssemblyKernelFactory::
- createAndLaunch< parallelDevicePolicy<> >( dt,
- dofManager.rankOffset(),
- wellDofKey,
- wellControls,
- subRegion,
- fluid,
- localMatrix,
- localRhs );
- }
- else
- {
- singlePhaseWellKernels::
- FaceBasedAssemblyKernelFactory::
- createAndLaunch< parallelDevicePolicy<> >( dt,
- dofManager.rankOffset(),
- wellDofKey,
- wellControls,
- subRegion,
- localMatrix,
- localRhs );
+ geos::internal::kernelLaunchSelectorThermalSwitch( isThermal, [&] ( auto ISTHERMAL )
+ {
+ integer constexpr IS_THERMAL = ISTHERMAL();
+
+ singlePhaseWellConstraintKernels::ConstraintHelper< IS_THERMAL >::assembleConstraintEquation( time_n,
+ *this,
+ constraint,
+ subRegion,
+ dofManager.getKey( wellElementDofName() ),
+ dofManager.rankOffset(),
+ localMatrix,
+ localRhs );
+ } );
}
} );
+ }
- } );
}
-void SinglePhaseWell::assemblePressureRelations( real64 const & time_n,
- real64 const & GEOS_UNUSED_PARAM( dt ),
- DomainPartition const & domain,
- DofManager const & dofManager,
- CRSMatrixView< real64, globalIndex const > const & localMatrix,
- arrayView1d< real64 > const & localRhs )
+void SinglePhaseWell::assembleWellPressureRelations( real64 const & GEOS_UNUSED_PARAM( time_n ),
+ real64 const & GEOS_UNUSED_PARAM( dt ),
+ WellElementSubRegion const & subRegion,
+ DofManager const & dofManager,
+ CRSMatrixView< real64, globalIndex const > const & localMatrix,
+ arrayView1d< real64 > const & localRhs )
{
- GEOS_MARK_FUNCTION;
- forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &,
- MeshLevel const & mesh,
- string_array const & regionNames )
- {
+ // get the degrees of freedom numbers, depth, next well elem index
+ string const wellDofKey = dofManager.getKey( wellElementDofName() );
+ arrayView1d< globalIndex const > const & wellElemDofNumber =
+ subRegion.getReference< array1d< globalIndex > >( wellDofKey );
+ arrayView1d< real64 const > const & wellElemGravCoef =
+ subRegion.getField< well::gravityCoefficient >();
+ arrayView1d< localIndex const > const & nextWellElemIndex =
+ subRegion.getReference< array1d< localIndex > >( WellElementSubRegion::viewKeyStruct::nextWellElementIndexString() );
- ElementRegionManager const & elemManager = mesh.getElemManager();
+ // get primary variables on well elements
+ arrayView1d< real64 const > const & wellElemPressure =
+ subRegion.getField< well::pressure >();
+ // get well constitutive data
+ string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString() );
+ SingleFluidBase const & fluid = subRegion.getConstitutiveModel< SingleFluidBase >( fluidName );
+ arrayView2d< real64 const, constitutive::singlefluid::USD_FLUID > const & wellElemDensity = fluid.density();
+ arrayView3d< real64 const, constitutive::singlefluid::USD_FLUID_DER > const & dWellElemDensity = fluid.dDensity();
- elemManager.forElementSubRegions< WellElementSubRegion >( regionNames,
- [&]( localIndex const,
- WellElementSubRegion const & subRegion )
- {
+ geos::internal::kernelLaunchSelectorThermalSwitch( isThermal(), [&] ( auto ISTHERMAL )
+ {
+ PressureRelationKernel::launch< ISTHERMAL >( subRegion.size(),
+ dofManager.rankOffset(),
+ wellElemDofNumber,
+ wellElemGravCoef,
+ nextWellElemIndex,
+ wellElemPressure,
+ wellElemDensity,
+ dWellElemDensity,
+ localMatrix,
+ localRhs );
+ } );
- WellControls & wellControls = getWellControls( subRegion );
+}
- // get the degrees of freedom numbers, depth, next well elem index
- string const wellDofKey = dofManager.getKey( wellElementDofName() );
- arrayView1d< globalIndex const > const & wellElemDofNumber =
- subRegion.getReference< array1d< globalIndex > >( wellDofKey );
- arrayView1d< real64 const > const & wellElemGravCoef =
- subRegion.getField< well::gravityCoefficient >();
- arrayView1d< localIndex const > const & nextWellElemIndex =
- subRegion.getReference< array1d< localIndex > >( WellElementSubRegion::viewKeyStruct::nextWellElementIndexString() );
+void SinglePhaseWell::assembleWellAccumulationTerms( real64 const & time,
+ real64 const & dt,
+ WellElementSubRegion & subRegion,
+ DofManager const & dofManager,
+ CRSMatrixView< real64, globalIndex const > const & localMatrix,
+ arrayView1d< real64 > const & localRhs )
- // get primary variables on well elements
- arrayView1d< real64 const > const & wellElemPressure =
- subRegion.getField< well::pressure >();
+{
+ GEOS_UNUSED_VAR( time );
+ GEOS_UNUSED_VAR( dt );
+ // get a reference to the degree-of-freedom numbers
+ string const wellElemDofKey = dofManager.getKey( wellElementDofName() );
- // get well constitutive data
- string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString() );
- SingleFluidBase const & fluid = subRegion.getConstitutiveModel< SingleFluidBase >( fluidName );
- arrayView2d< real64 const, constitutive::singlefluid::USD_FLUID > const & wellElemDensity = fluid.density();
- arrayView3d< real64 const, constitutive::singlefluid::USD_FLUID_DER > const & dWellElemDensity = fluid.dDensity();
- geos::internal::kernelLaunchSelectorThermalSwitch( isThermal(), [&] ( auto ISTHERMAL )
+ string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString() );
+ SingleFluidBase const & fluid = subRegion.getConstitutiveModel< SingleFluidBase >( fluidName );
+ if( getWellStatus() == WellControls::Status::OPEN && !m_keepVariablesConstantDuringInitStep )
+ {
+ if( isThermal() )
+ {
+ thermalSinglePhaseWellKernels::
+ ElementBasedAssemblyKernelFactory::
+ createAndLaunch< parallelDevicePolicy<> >( isProducer(),
+ dofManager.rankOffset(),
+ wellElemDofKey,
+ subRegion,
+ fluid,
+ localMatrix,
+ localRhs );
+ }
+ else
+ {
+ singlePhaseWellKernels::
+ ElementBasedAssemblyKernelFactory::
+ createAndLaunch< parallelDevicePolicy<> >( dofManager.rankOffset(),
+ wellElemDofKey,
+ subRegion,
+ fluid,
+ localMatrix,
+ localRhs );
+ }
+ // get the degrees of freedom and ghosting info
+ arrayView1d< globalIndex const > const & wellElemDofNumber =
+ subRegion.getReference< array1d< globalIndex > >( wellElemDofKey );
+ arrayView1d< integer const > const wellElemGhostRank = subRegion.ghostRank();
+ arrayView1d< integer const > const elemStatus = subRegion.getLocalWellElementStatus();
+
+ arrayView1d< real64 > connRate = subRegion.getField< fields::well::connectionRate >();
+ localIndex rank_offset = dofManager.rankOffset();
+ forAll< parallelDevicePolicy<> >( subRegion.size(), [=] GEOS_HOST_DEVICE ( localIndex const ei )
+ {
+ if( wellElemGhostRank[ei] < 0 )
{
- localIndex controlHasSwitched=0;
- controlHasSwitched = PressureRelationKernel::launch< ISTHERMAL >( subRegion.size(),
- dofManager.rankOffset(),
- subRegion.isLocallyOwned(),
- subRegion.getTopWellElementIndex(),
- wellControls,
- time_n,
- wellElemDofNumber,
- wellElemGravCoef,
- nextWellElemIndex,
- wellElemPressure,
- wellElemDensity,
- dWellElemDensity,
- localMatrix,
- localRhs );
-
- if( controlHasSwitched == 1 )
+ if( elemStatus[ei]==WellElementSubRegion::WellElemStatus::CLOSED )
{
- // Note: if BHP control is not viable, we switch to TOTALVOLRATE
- // if TOTALVOLRATE is not viable, we switch to BHP
+ connRate[ei] = 0.0;
+ globalIndex const dofIndex = wellElemDofNumber[ei];
+ localIndex const localRow = dofIndex - rank_offset;
- if( wellControls.getControl() == WellControls::Control::BHP )
- {
- wellControls.switchToTotalRateControl( wellControls.getTargetTotalRate( time_n ) );
- GEOS_LOG_LEVEL_RANK_0( logInfo::WellControl,
- GEOS_FMT( "Control switch for well {} from BHP constraint to rate constraint", subRegion.getName()) );
- }
- else
+ real64 const unity = 1.0;
+ for( integer i=0; i < m_numDofPerWellElement; i++ )
{
- wellControls.switchToBHPControl( wellControls.getTargetBHP( time_n ) );
- GEOS_LOG_LEVEL_RANK_0( logInfo::WellControl,
- GEOS_FMT( "Control switch for well {} from rate constraint to BHP constraint", subRegion.getName()) );
+ globalIndex const rindex = localRow+i;
+ globalIndex const cindex =dofIndex + i;
+ localMatrix.template addToRow< serialAtomic >( rindex,
+ &cindex,
+ &unity,
+ 1 );
+ localRhs[rindex] = 0.0;
}
}
- } );
-
+ }
} );
- } );
-}
-
-void SinglePhaseWell::assembleAccumulationTerms( 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;
- GEOS_UNUSED_VAR( time_n );
- GEOS_UNUSED_VAR( dt );
- forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &,
- MeshLevel const & mesh,
- string_array const & regionNames )
+ }
+ else
{
-
- ElementRegionManager const & elemManager = mesh.getElemManager();
-
- elemManager.forElementSubRegions< WellElementSubRegion >( regionNames,
- [&]( localIndex const,
- WellElementSubRegion const & subRegion )
+ // Zero accumulation contribution
+ arrayView1d< globalIndex const > const & wellElemDofNumber =
+ subRegion.getReference< array1d< globalIndex > >( wellElemDofKey );
+ arrayView1d< integer const > const wellElemGhostRank = subRegion.ghostRank();
+
+ arrayView1d< real64 > connRate = subRegion.getField< fields::well::connectionRate >();
+ localIndex rank_offset = dofManager.rankOffset();
+ forAll< parallelDevicePolicy<> >( subRegion.size(), [=] GEOS_HOST_DEVICE ( localIndex const ei )
{
-
- // get a reference to the degree-of-freedom numbers
- string const wellElemDofKey = dofManager.getKey( wellElementDofName() );
-
- WellControls const & wellControls = getWellControls( subRegion );
- string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString() );
- SingleFluidBase const & fluid = subRegion.getConstitutiveModel< SingleFluidBase >( fluidName );
-
- if( isThermal() )
- {
- thermalSinglePhaseWellKernels::
- ElementBasedAssemblyKernelFactory::
- createAndLaunch< parallelDevicePolicy<> >( wellControls.isProducer(),
- dofManager.rankOffset(),
- wellElemDofKey,
- subRegion,
- fluid,
- localMatrix,
- localRhs );
- }
- else
+ if( wellElemGhostRank[ei] < 0 )
{
- singlePhaseWellKernels::
- ElementBasedAssemblyKernelFactory::
- createAndLaunch< parallelDevicePolicy<> >( dofManager.rankOffset(),
- wellElemDofKey,
- subRegion,
- fluid,
- localMatrix,
- localRhs );
+ connRate[ei] = 0.0;
+ globalIndex const dofIndex = wellElemDofNumber[ei];
+ localIndex const localRow = dofIndex - rank_offset;
+
+ real64 const unity = 1.0;
+ for( integer i=0; i < m_numDofPerWellElement; i++ )
+ {
+ globalIndex const rindex = localRow+i;
+ globalIndex const cindex =dofIndex + i;
+ localMatrix.template addToRow< serialAtomic >( rindex,
+ &cindex,
+ &unity,
+ 1 );
+ localRhs[rindex] = 0.0;
+ }
}
} );
- } );
- // then assemble the volume balance equations
- assembleVolumeBalanceTerms( domain, dofManager, localMatrix, localRhs );
-}
-
-void SinglePhaseWell::assembleVolumeBalanceTerms( DomainPartition const & GEOS_UNUSED_PARAM( domain ),
- DofManager const & GEOS_UNUSED_PARAM( dofManager ),
- CRSMatrixView< real64, globalIndex const > const & GEOS_UNUSED_PARAM( localMatrix ),
- arrayView1d< real64 > const & GEOS_UNUSED_PARAM( localRhs ) )
-{
- // not implemented for single phase flow
+ // zero out current state constraint quantities
+ getReference< real64 >( WellControls::viewKeyStruct::currentBHPString() ) = 0.0;
+ getReference< real64 >( WellControls::viewKeyStruct::currentVolRateString() )=0.0;
+ getReference< real64 >( WellControls::viewKeyStruct::currentVolRateString() )=0.0;
+ }
}
-void SinglePhaseWell::computePerforationRates( real64 const & time_n,
- real64 const & dt, DomainPartition & domain )
+void SinglePhaseWell::computeWellPerforationRates( real64 const & time_n,
+ real64 const & GEOS_UNUSED_PARAM( dt ),
+ ElementRegionManager & elemManager,
+ WellElementSubRegion & subRegion )
{
GEOS_MARK_FUNCTION;
GEOS_UNUSED_VAR( time_n );
- GEOS_UNUSED_VAR( dt );
- forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &,
- MeshLevel & mesh,
- string_array const & regionNames )
- {
+ // get the well data
+ PerforationData * const perforationData = subRegion.getPerforationData();
- // TODO: change the way we access the flowSolver here
- SinglePhaseBase const & flowSolver = getParent().getGroup< SinglePhaseBase >( getFlowSolverName() );
- PerforationKernel::SinglePhaseFlowAccessors resSinglePhaseFlowAccessors( mesh.getElemManager(), flowSolver.getName() );
- PerforationKernel::SingleFluidAccessors resSingleFluidAccessors( mesh.getElemManager(), flowSolver.getName() );
- ElementRegionManager & elemManager = mesh.getElemManager();
- elemManager.forElementSubRegions< WellElementSubRegion >( regionNames, [&]( localIndex const,
- WellElementSubRegion & subRegion )
- {
-
- // get the well data
- PerforationData * const perforationData = subRegion.getPerforationData();
- WellControls const & wellControls = getWellControls( subRegion );
- if( wellControls.isWellOpen() && !m_keepVariablesConstantDuringInitStep )
- {
+ if( isWellOpen() && !m_keepVariablesConstantDuringInitStep )
+ {
- string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString() );
- SingleFluidBase const & fluid = getConstitutiveModel< SingleFluidBase >( subRegion, fluidName );
+ string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString() );
+ SingleFluidBase const & fluid = getConstitutiveModel< SingleFluidBase >( subRegion, fluidName );
- if( isThermal() )
- {
- thermalSinglePhasePerforationFluxKernels::
- PerforationFluxKernelFactory::
- createAndLaunch< parallelDevicePolicy<> >( flowSolver.getName(),
- perforationData,
- subRegion,
- fluid,
- elemManager );
- }
- else
- {
- isothermalSinglePhasePerforationFluxKernels::
- PerforationFluxKernelFactory::
- createAndLaunch< parallelDevicePolicy<> >( flowSolver.getName(),
- perforationData,
- subRegion,
- fluid,
- elemManager );
- }
- }
- else
- {
- // Zero completion flow rate
- arrayView1d< real64 > const perfRate = perforationData->getField< fields::well::perforationRate >();
- for( integer iperf=0; iperfsize(); iperf++ )
- {
- perfRate[iperf] = 0.0;
- }
- }
- } );
- } );
+ if( isThermal() )
+ {
+ thermalSinglePhasePerforationFluxKernels::
+ PerforationFluxKernelFactory::
+ createAndLaunch< parallelDevicePolicy<> >( getFlowSolverName(),
+ perforationData,
+ subRegion,
+ fluid,
+ elemManager );
+ }
+ else
+ {
+ isothermalSinglePhasePerforationFluxKernels::
+ PerforationFluxKernelFactory::
+ createAndLaunch< parallelDevicePolicy<> >( getFlowSolverName(),
+ perforationData,
+ subRegion,
+ fluid,
+ elemManager );
+ }
+ }
+ else
+ {
+ // Zero completion flow rate
+ arrayView1d< real64 > const perfRate = perforationData->getField< fields::well::perforationRate >();
+ for( integer iperf=0; iperfsize(); iperf++ )
+ {
+ perfRate[iperf] = 0.0;
+ }
+ }
}
real64
-SinglePhaseWell::calculateResidualNorm( real64 const & time_n,
- real64 const & dt,
- DomainPartition const & domain,
- DofManager const & dofManager,
- arrayView1d< real64 const > const & localRhs )
+SinglePhaseWell::scalingForWellSystemSolution( WellElementSubRegion & subRegion,
+ DofManager const & dofManager,
+ arrayView1d< real64 const > const & localSolution )
{
GEOS_MARK_FUNCTION;
- integer numNorm = 1; // mass balance
+ GEOS_UNUSED_VAR( subRegion );
+ GEOS_UNUSED_VAR( dofManager );
+ GEOS_UNUSED_VAR( localSolution );
+
+ return 1.0;
+}
+array1d< real64 >
+SinglePhaseWell::calculateLocalWellResidualNorm( real64 const & time_n,
+ real64 const & dt,
+ NonlinearSolverParameters const & nonlinearSolverParameters,
+ WellElementSubRegion const & subRegion,
+ DofManager const & dofManager,
+ arrayView1d< real64 const > const & localRhs )
+{
+ GEOS_MARK_FUNCTION;
+ integer numNorm = 1; // mass balance
array1d< real64 > localResidualNorm;
array1d< real64 > localResidualNormalizer;
if( isThermal() )
{
- numNorm = 2; // mass balance and energy balance
+ numNorm = 2; // mass balance and energy balance
}
localResidualNorm.resize( numNorm );
localResidualNormalizer.resize( numNorm );
@@ -914,74 +936,101 @@ SinglePhaseWell::calculateResidualNorm( real64 const & time_n,
globalIndex const rankOffset = dofManager.rankOffset();
string const wellDofKey = dofManager.getKey( wellElementDofName() );
- forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &,
- MeshLevel const & mesh,
- string_array const & regionNames )
- {
- ElementRegionManager const & elemManager = mesh.getElemManager();
- elemManager.forElementSubRegions< WellElementSubRegion >( regionNames,
- [&]( localIndex const,
- WellElementSubRegion const & subRegion )
- {
+ string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString() );
+ SingleFluidBase const & fluid = subRegion.getConstitutiveModel< SingleFluidBase >( fluidName );
- string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString() );
- SingleFluidBase const & fluid = subRegion.getConstitutiveModel< SingleFluidBase >( fluidName );
- WellControls const & wellControls = getWellControls( subRegion );
-
- // step 1: compute the norm in the subRegion
- if( isThermal() )
+ if( isWellOpen() )
+ {
+ // step 1: compute the norm in the subRegion
+ if( isThermal() )
+ {
+ real64 subRegionResidualNorm[2]{};
+ thermalSinglePhaseWellKernels::ResidualNormKernelFactory::
+ createAndLaunch< parallelDevicePolicy<> >( rankOffset,
+ wellDofKey,
+ localRhs,
+ subRegion,
+ fluid,
+ *this,
+ time_n,
+ dt,
+ nonlinearSolverParameters.m_minNormalizer,
+ subRegionResidualNorm );
+ // step 2: reduction across meshBodies/regions/subRegions
+
+ for( integer i=0; i >( rankOffset,
- wellDofKey,
- localRhs,
- subRegion,
- fluid,
- wellControls,
- time_n,
- dt,
- m_nonlinearSolverParameters.m_minNormalizer,
- subRegionResidualNorm );
- // step 2: reduction across meshBodies/regions/subRegions
-
- for( integer i=0; i localResidualNorm[i] )
{
- if( subRegionResidualNorm[i] > localResidualNorm[i] )
- {
- localResidualNorm[i] = subRegionResidualNorm[i];
- }
+ localResidualNorm[i] = subRegionResidualNorm[i];
}
}
- else
+ }
+ else
+ {
+ real64 subRegionResidualNorm[1]{};
+ ResidualNormKernelFactory::
+ createAndLaunch< parallelDevicePolicy<> >( rankOffset,
+ wellDofKey,
+ localRhs,
+ subRegion,
+ fluid,
+ *this,
+ time_n,
+ dt,
+ nonlinearSolverParameters.m_minNormalizer,
+ subRegionResidualNorm );
+
+ // step 2: reduction across meshBodies/regions/subRegions
+
+ if( subRegionResidualNorm[0] > localResidualNorm[0] )
{
- real64 subRegionResidualNorm[1]{};
- ResidualNormKernelFactory::
- createAndLaunch< parallelDevicePolicy<> >( rankOffset,
- wellDofKey,
- localRhs,
- subRegion,
- fluid,
- wellControls,
- time_n,
- dt,
- m_nonlinearSolverParameters.m_minNormalizer,
- subRegionResidualNorm );
-
- // step 2: reduction across meshBodies/regions/subRegions
-
- if( subRegionResidualNorm[0] > localResidualNorm[0] )
- {
- localResidualNorm[0] = subRegionResidualNorm[0];
- }
+ localResidualNorm[0] = subRegionResidualNorm[0];
}
- } );
- } );
+ }
+ }
+ return localResidualNorm;
+
+
+}
+
+real64
+SinglePhaseWell::calculateWellResidualNorm( real64 const & time_n,
+ real64 const & dt,
+ NonlinearSolverParameters const & nonlinearSolverParameters,
+ WellElementSubRegion const & subRegion,
+ DofManager const & dofManager,
+ arrayView1d< real64 const > const & localRhs )
+{
+ GEOS_MARK_FUNCTION;
+ integer numNorm = 1; // mass balance
+ array1d< real64 > localResidualNorm;
+ array1d< real64 > localResidualNormalizer;
+ if( isThermal() )
+ {
+ numNorm = 2; // mass balance and energy balance
+ }
+ localResidualNorm.resize( numNorm );
+ localResidualNormalizer.resize( numNorm );
+
+
+ //globalIndex const rankOffset = dofManager.rankOffset();
+ string const wellDofKey = dofManager.getKey( wellElementDofName() );
+
+ if( isWellOpen() )
+ {
+ localResidualNorm = calculateLocalWellResidualNorm( time_n,
+ dt,
+ nonlinearSolverParameters,
+ subRegion,
+ dofManager,
+ localRhs );
+ }
real64 resNorm=localResidualNorm[0];
if( isThermal() )
{
@@ -993,8 +1042,8 @@ SinglePhaseWell::calculateResidualNorm( real64 const & time_n,
GEOS_LOG_LEVEL_RANK_0_NLR( logInfo::ResidualNorm, GEOS_FMT( " ( R{} ) = ( {:4.2e} ) ( Renergy ) = ( {:4.2e} )",
coupledSolverAttributePrefix(), globalResidualNorm[0], globalResidualNorm[1] ));
- getConvergenceStats().setResidualValue( GEOS_FMT( "R{}", coupledSolverAttributePrefix()), globalResidualNorm[0] );
- getConvergenceStats().setResidualValue( "Renergy", globalResidualNorm[1] );
+ //getConvergenceStats().setResidualValue( GEOS_FMT( "R{}", coupledSolverAttributePrefix()), globalResidualNorm[0] );
+ //getConvergenceStats().setResidualValue( "Renergy", globalResidualNorm[1] );
}
else
{
@@ -1002,16 +1051,15 @@ SinglePhaseWell::calculateResidualNorm( real64 const & time_n,
GEOS_LOG_LEVEL_RANK_0_NLR( logInfo::ResidualNorm, GEOS_FMT( " ( R{} ) = ( {:4.2e} )",
coupledSolverAttributePrefix(), resNorm ));
- getConvergenceStats().setResidualValue( GEOS_FMT( "R{}", coupledSolverAttributePrefix()), resNorm );
+ //getConvergenceStats().setResidualValue( GEOS_FMT( "R{}", coupledSolverAttributePrefix()), resNorm );
}
return resNorm;
}
-
-bool SinglePhaseWell::checkSystemSolution( DomainPartition & domain,
- DofManager const & dofManager,
- arrayView1d< real64 const > const & localSolution,
- real64 const scalingFactor )
+bool SinglePhaseWell::checkWellSystemSolution( WellElementSubRegion & subRegion,
+ DofManager const & dofManager,
+ arrayView1d< real64 const > const & localSolution,
+ real64 const scalingFactor )
{
GEOS_MARK_FUNCTION;
@@ -1019,36 +1067,24 @@ bool SinglePhaseWell::checkSystemSolution( DomainPartition & domain,
integer numNegativePressures = 0;
real64 minPressure = 0.0;
- forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &,
- MeshLevel const & mesh,
- string_array const & regionNames )
- {
- ElementRegionManager const & elemManager = mesh.getElemManager();
+ globalIndex const rankOffset = dofManager.rankOffset();
+ // get the degree of freedom numbers on well elements
+ arrayView1d< globalIndex const > const & dofNumber =
+ subRegion.getReference< array1d< globalIndex > >( wellDofKey );
+ arrayView1d< integer const > const & ghostRank = subRegion.ghostRank();
- elemManager.forElementSubRegions< WellElementSubRegion >( regionNames,
- [&]( localIndex const,
- WellElementSubRegion const & subRegion )
+ // get a reference to the primary variables on well elements
+ arrayView1d< real64 const > const & pres =
+ subRegion.getField< well::pressure >();
+
+ auto const statistics =
+ singlePhaseBaseKernels::SolutionCheckKernel::
+ launch< parallelDevicePolicy<> >( localSolution, rankOffset, dofNumber, ghostRank, pres, scalingFactor );
+
+ numNegativePressures += statistics.first;
+ minPressure = std::min( minPressure, statistics.second );
- {
- globalIndex const rankOffset = dofManager.rankOffset();
- // get the degree of freedom numbers on well elements
- arrayView1d< globalIndex const > const & dofNumber =
- subRegion.getReference< array1d< globalIndex > >( wellDofKey );
- arrayView1d< integer const > const & ghostRank = subRegion.ghostRank();
-
- // get a reference to the primary variables on well elements
- arrayView1d< real64 const > const & pres =
- subRegion.getField< well::pressure >();
-
- auto const statistics =
- singlePhaseBaseKernels::SolutionCheckKernel::
- launch< parallelDevicePolicy<> >( localSolution, rankOffset, dofNumber, ghostRank, pres, scalingFactor );
-
- numNegativePressures += statistics.first;
- minPressure = std::min( minPressure, statistics.second );
- } );
- } );
numNegativePressures = MpiWrapper::sum( numNegativePressures );
@@ -1062,14 +1098,19 @@ bool SinglePhaseWell::checkSystemSolution( DomainPartition & domain,
return (m_allowNegativePressure || numNegativePressures == 0) ? 1 : 0;
}
+
+
void
-SinglePhaseWell::applySystemSolution( DofManager const & dofManager,
- arrayView1d< real64 const > const & localSolution,
- real64 const scalingFactor,
- real64 const dt,
- DomainPartition & domain )
+SinglePhaseWell::applyWellSystemSolution( DofManager const & dofManager,
+ arrayView1d< real64 const > const & localSolution,
+ real64 const scalingFactor,
+ real64 const dt,
+ DomainPartition & domain,
+ MeshLevel & mesh,
+ WellElementSubRegion & subRegion )
{
GEOS_UNUSED_VAR( dt );
+ GEOS_UNUSED_VAR( subRegion );
DofManager::CompMask pressureMask( m_numDofPerWellElement, 0, 1 );
DofManager::CompMask connRateMask( m_numDofPerWellElement, 1, 2 );
dofManager.addVectorToField( localSolution,
@@ -1096,213 +1137,351 @@ SinglePhaseWell::applySystemSolution( DofManager const & dofManager,
}
- forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &,
- MeshLevel & mesh,
- string_array const & regionNames )
+
+ FieldIdentifiers fieldsToBeSync;
+ if( isThermal() )
{
- FieldIdentifiers fieldsToBeSync;
- if( isThermal() )
- {
- fieldsToBeSync.addElementFields( { well::pressure::key(),
- well::connectionRate::key(),
- well::temperature::key() },
- regionNames );
- }
- else
- {
- fieldsToBeSync.addElementFields( { well::pressure::key(),
- well::connectionRate::key() },
- regionNames );
- }
- CommunicationTools::getInstance().synchronizeFields( fieldsToBeSync,
- mesh,
- domain.getNeighbors(),
- true );
- } );
+ fieldsToBeSync.addElementFields( { well::pressure::key(),
+ well::connectionRate::key(),
+ well::temperature::key() },
+ getTargetRegionNames() );
+ }
+ else
+ {
+ fieldsToBeSync.addElementFields( { well::pressure::key(),
+ well::connectionRate::key() },
+ getTargetRegionNames() );
+ }
+ CommunicationTools::getInstance().synchronizeFields( fieldsToBeSync,
+ mesh,
+ domain.getNeighbors(),
+ true );
+
}
-void SinglePhaseWell::resetStateToBeginningOfStep( DomainPartition & domain )
+
+void SinglePhaseWell::resetStateToBeginningOfStep( ElementRegionManager const & elemManager, WellElementSubRegion & subRegion )
{
- forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &,
- MeshLevel & mesh,
- string_array const & regionNames )
- {
- ElementRegionManager & elemManager = mesh.getElemManager();
- elemManager.forElementSubRegions< WellElementSubRegion >( regionNames,
- [&]( localIndex const,
- WellElementSubRegion & subRegion )
- {
- // get a reference to the primary variables on well elements
- arrayView1d< real64 > const & wellElemPressure =
- subRegion.getField< well::pressure >();
- arrayView1d< real64 const > const & wellElemPressure_n =
- subRegion.getField< well::pressure_n >();
- wellElemPressure.setValues< parallelDevicePolicy<> >( wellElemPressure_n );
+ // get a reference to the primary variables on well elements
+ arrayView1d< real64 > const & wellElemPressure =
+ subRegion.getField< well::pressure >();
+ arrayView1d< real64 const > const & wellElemPressure_n =
+ subRegion.getField< well::pressure_n >();
+ wellElemPressure.setValues< parallelDevicePolicy<> >( wellElemPressure_n );
- if( isThermal() )
- {
- arrayView1d< real64 > const & wellElemTemperature =
- subRegion.getField< fields::well::temperature >();
- arrayView1d< real64 const > const & wellElemTemperature_n =
- subRegion.getField< fields::well::temperature_n >();
- wellElemTemperature.setValues< parallelDevicePolicy<> >( wellElemTemperature_n );
- }
- arrayView1d< real64 > const & connRate =
- subRegion.getField< well::connectionRate >();
- arrayView1d< real64 const > const & connRate_n =
- subRegion.getField< well::connectionRate_n >();
- connRate.setValues< parallelDevicePolicy<> >( connRate_n );
+ if( isThermal() )
+ {
+ arrayView1d< real64 > const & wellElemTemperature =
+ subRegion.getField< fields::well::temperature >();
+ arrayView1d< real64 const > const & wellElemTemperature_n =
+ subRegion.getField< fields::well::temperature_n >();
+ wellElemTemperature.setValues< parallelDevicePolicy<> >( wellElemTemperature_n );
+ }
+ arrayView1d< real64 > const & connRate =
+ subRegion.getField< well::connectionRate >();
+ arrayView1d< real64 const > const & connRate_n =
+ subRegion.getField< well::connectionRate_n >();
+ connRate.setValues< parallelDevicePolicy<> >( connRate_n );
+
+ updateSubRegionState( elemManager, subRegion );
- updateSubRegionState( elemManager, subRegion );
- } );
- } );
}
+void SinglePhaseWell::saveState( WellElementSubRegion & subRegion )
+{
+ arrayView1d< real64 const > const wellElemPressure = subRegion.getField< well::pressure >();
+ arrayView1d< real64 > const wellElemPressure_n = subRegion.getField< well::pressure_n >();
+ wellElemPressure_n.setValues< parallelDevicePolicy<> >( wellElemPressure );
+
+ if( isThermal() )
+ {
+ arrayView1d< real64 const > const wellElemTemperature = subRegion.getField< well::temperature >();
+ arrayView1d< real64 > const wellElemTemperature_n = subRegion.getField< well::temperature_n >();
+ wellElemTemperature_n.setValues< parallelDevicePolicy<> >( wellElemTemperature );
+ }
+ arrayView1d< real64 const > const connRate = subRegion.getField< well::connectionRate >();
+ arrayView1d< real64 > const connRate_n = subRegion.getField< well::connectionRate_n >();
+ connRate_n.setValues< parallelDevicePolicy<> >( connRate );
-void SinglePhaseWell::implicitStepSetup( real64 const & time,
+ SingleFluidBase const & fluid =
+ getConstitutiveModel< SingleFluidBase >( subRegion, subRegion.getReference< string >( viewKeyStruct::fluidNamesString() ) );
+ fluid.saveConvergedState();
+}
+
+void SinglePhaseWell::implicitStepSetup( real64 const & time_n,
real64 const & dt,
- DomainPartition & domain )
+ ElementRegionManager & elemManager,
+ WellElementSubRegion & subRegion )
{
- WellSolverBase::implicitStepSetup( time, dt, domain );
+ GEOS_MARK_FUNCTION;
+ WellControls::implicitStepSetup( time_n, dt, elemManager, subRegion );
+ arrayView1d< real64 const > const wellElemPressure = subRegion.getField< well::pressure >();
+ arrayView1d< real64 > const wellElemPressure_n = subRegion.getField< well::pressure_n >();
+ wellElemPressure_n.setValues< parallelDevicePolicy<> >( wellElemPressure );
- forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &,
- MeshLevel & mesh,
- string_array const & regionNames )
+ if( isThermal() )
{
+ arrayView1d< real64 const > const wellElemTemperature = subRegion.getField< well::temperature >();
+ arrayView1d< real64 > const wellElemTemperature_n = subRegion.getField< well::temperature_n >();
+ wellElemTemperature_n.setValues< parallelDevicePolicy<> >( wellElemTemperature );
+ }
+ arrayView1d< real64 const > const connRate = subRegion.getField< well::connectionRate >();
+ arrayView1d< real64 > const connRate_n = subRegion.getField< well::connectionRate_n >();
+ connRate_n.setValues< parallelDevicePolicy<> >( connRate );
- ElementRegionManager & elemManager = mesh.getElemManager();
- elemManager.forElementSubRegions< WellElementSubRegion >( regionNames,
- [&]( localIndex const,
- WellElementSubRegion & subRegion )
- {
- arrayView1d< real64 const > const wellElemPressure = subRegion.getField< well::pressure >();
- arrayView1d< real64 > const wellElemPressure_n = subRegion.getField< well::pressure_n >();
- wellElemPressure_n.setValues< parallelDevicePolicy<> >( wellElemPressure );
+ SingleFluidBase const & fluid =
+ getConstitutiveModel< SingleFluidBase >( subRegion, subRegion.getReference< string >( viewKeyStruct::fluidNamesString() ) );
+ fluid.saveConvergedState();
- if( isThermal() )
- {
- arrayView1d< real64 const > const wellElemTemperature = subRegion.getField< well::temperature >();
- arrayView1d< real64 > const wellElemTemperature_n = subRegion.getField< well::temperature_n >();
- wellElemTemperature_n.setValues< parallelDevicePolicy<> >( wellElemTemperature );
- }
- arrayView1d< real64 const > const connRate = subRegion.getField< well::connectionRate >();
- arrayView1d< real64 > const connRate_n = subRegion.getField< well::connectionRate_n >();
- connRate_n.setValues< parallelDevicePolicy<> >( connRate );
-
- SingleFluidBase const & fluid =
- getConstitutiveModel< SingleFluidBase >( subRegion, subRegion.getReference< string >( viewKeyStruct::fluidNamesString() ) );
- fluid.saveConvergedState();
+ validateWellConstraints( time_n, dt, subRegion );
- validateWellConstraints( time, dt, subRegion );
+ updateSubRegionState( elemManager, subRegion );
- updateSubRegionState( elemManager, subRegion );
- } );
- } );
}
void SinglePhaseWell::implicitStepComplete( real64 const & time_n,
real64 const & dt,
- DomainPartition & domain )
+ WellElementSubRegion const & subRegion )
+{
+ printRates( time_n, dt, subRegion );
+}
+
+void SinglePhaseWell::printRates( real64 const & time_n,
+ real64 const & dt,
+ WellElementSubRegion const & subRegion )
{
- WellSolverBase::implicitStepComplete( time_n, dt, domain );
- if( getLogLevel() > 0 )
+ GEOS_UNUSED_VAR( dt );
+ // the rank that owns the reference well element is responsible for the calculations below.
+ if( !subRegion.isLocallyOwned() )
{
- printRates( time_n, dt, domain );
+ return;
}
+
+ localIndex const iwelemRef = subRegion.getTopWellElementIndex();
+
+ // subRegion data
+
+ arrayView1d< real64 const > const & connRate =
+ subRegion.getField< well::connectionRate >();
+
+ // control data
+
+
+ string const wellControlsName = getName();
+
+ // format: time,total_rate,total_vol_rate
+ std::ofstream outputFile;
+ if( m_writeCSV > 0 )
+ {
+ outputFile.open( m_ratesOutputDir + "/" + wellControlsName + ".csv", std::ios_base::app );
+ outputFile << time_n;
+ }
+
+ if( !isWellOpen() )
+ {
+ GEOS_LOG( GEOS_FMT( "{}: well is shut", wellControlsName ) );
+ if( outputFile.is_open())
+ {
+ // print all zeros in the rates file
+ outputFile << ",0.0,0.0,0.0" << std::endl;
+ outputFile.close();
+ }
+ return;
+ }
+
+ integer const useSurfaceCond = useSurfaceConditions();
+
+ real64 const & currentBHP =
+ getReference< real64 >( WellControls::viewKeyStruct::currentBHPString() );
+ real64 const & currentTotalVolRate =
+ getReference< real64 >( WellControls::viewKeyStruct::currentVolRateString() );
+
+ // bring everything back to host, capture the scalars by reference
+ forAll< serialPolicy >( 1, [&useSurfaceCond,
+ ¤tBHP,
+ connRate,
+ ¤tTotalVolRate,
+ &iwelemRef,
+ &wellControlsName,
+ &outputFile] ( localIndex const )
+ {
+ string const conditionKey = useSurfaceCond ? "surface" : "reservoir";
+ string const unitKey = useSurfaceCond ? "s" : "r";
+
+ real64 const currentTotalRate = connRate[iwelemRef];
+ GEOS_LOG( GEOS_FMT( "{}: BHP (at the specified reference elevation): {} Pa",
+ wellControlsName, currentBHP ) );
+ GEOS_LOG( GEOS_FMT( "{}: Total rate: {} kg/s; total {} volumetric rate: {} {}m3/s",
+ wellControlsName, currentTotalRate, conditionKey, currentTotalVolRate, unitKey ) );
+ if( outputFile.is_open())
+ {
+ outputFile << "," << currentBHP;
+ outputFile << "," << currentTotalRate << "," << currentTotalVolRate << std::endl;
+ outputFile.close();
+ }
+ } );
}
-void SinglePhaseWell::printRates( real64 const & time_n,
- real64 const & GEOS_UNUSED_PARAM( dt ),
- DomainPartition & domain )
+bool SinglePhaseWell::solveWHPConstraint( real64 const & time_n,
+ real64 const & dt,
+ integer const cycleNumber,
+ integer const coupledIterationNumber,
+ DomainPartition & domain,
+ MeshLevel & mesh,
+ ElementRegionManager & elemManager,
+ WellElementSubRegion & subRegion )
{
- forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &,
- MeshLevel & mesh,
- string_array const & regionNames )
+ GEOS_UNUSED_VAR( time_n );
+ GEOS_UNUSED_VAR( dt );
+ GEOS_UNUSED_VAR( cycleNumber );
+ GEOS_UNUSED_VAR( coupledIterationNumber );
+ GEOS_UNUSED_VAR( domain );
+ GEOS_UNUSED_VAR( mesh );
+ GEOS_UNUSED_VAR( elemManager );
+ GEOS_UNUSED_VAR( subRegion );
+
+
+ GEOS_ERROR( "SinglePhaseWell::solveWHPConstraint(WHP constraint option not implemented for SinglePhaseWell." );
+ return false;
+
+}
+
+void SinglePhaseWell::outputSingleWellDebug( real64 const time,
+ real64 const dt,
+ integer current_newton_iteration,
+ MeshLevel & mesh,
+ WellElementSubRegion & subRegion,
+ DofManager const & dofManager,
+ CRSMatrixView< real64, globalIndex const > const & localMatrix,
+ arrayView1d< const real64 > const & localRhs )
+
+{
+ GEOS_UNUSED_VAR( time );
+ GEOS_UNUSED_VAR( dofManager );
+ GEOS_UNUSED_VAR( localMatrix );
+ GEOS_UNUSED_VAR( localRhs );
+
+ integer num_timestep_cuts=0;
+ if( m_writeSegDebug > 1 )
{
+ //SinglePhaseBase const & flowSolver = getParent().getParent().getGroup< SinglePhaseBase >( getFlowSolverName() );
+ //auto solver_names = getParent().getSubGroupsNames();
+ //integer n = solver_names.size();
+ // Bit of a hack, cases with > 3 solvers we need to find the base solver for wells
+ // Assume that solver definition order follows coupledreswell, res, and then well
+ //std::string coupled_solver_name = solver_names[n-3];
+
+ //GeosxState & gs = getGlobalState();
+
+ //CompositionalMultiphaseReservoirAndWells< CompositionalMultiphaseBase > * solver =
+ // &(gs.getProblemManager().getPhysicsSolverManager().getGroup< geos::CompositionalMultiphaseReservoirAndWells<
+ // geos::CompositionalMultiphaseBase > >( coupled_solver_name ));
+
+ EventManager const & event = getGroupByPath< EventManager >( "/Problem/Events" );
+ real64 const & ctime = event.getReference< real64 >( EventManager::viewKeyStruct::timeString() );
+ //real64 const dt = event.getReference< real64 >( EventManager::viewKeyStruct::dtString() );
+ integer const & cycle = event.getReference< integer >( EventManager::viewKeyStruct::cycleString() );
+ integer const & subevent = event.getReference< integer >( EventManager::viewKeyStruct::currentSubEventString() );
- ElementRegionManager & elemManager = mesh.getElemManager();
- elemManager.forElementSubRegions< WellElementSubRegion >( regionNames,
- [&]( localIndex const,
- WellElementSubRegion & subRegion )
+ // std::cout << "tjbtime1 " << ctime << " " << m_globalNumTimeSteps << " " << dt << " " << cycle << " " << subevent
+ // << " " << m_numTimeStepCuts << " " << m_currentNewtonIteration << std::endl;
+ if( true ) // need to fix for restarts cycle >= m_writeSegDebug )
{
+ //SolverStatistics & solver_stat = solver->getSolverStatistics();
+ //integer num_timesteps = solver_stat.getReference< integer >( SolverStatistics::viewKeyStruct::numTimeStepsString());
+ //integer current_newton_iteration = solver_stat.getReference< integer >(
+ // SolverStatistics::viewKeyStruct::numCurrentNonlinearIterationsString());
+ //integer num_timestep_cuts = solver_stat.getReference< integer >( SolverStatistics::viewKeyStruct::numTimeStepCutsString());
+ //std::cout << "tjbtime2 " << ctime << " " << m_globalNumTimeSteps << " " << dt << " " << cycle << " " << subevent
+ //<< " " << m_numTimeStepCuts << " " << m_currentNewtonIteration << std::endl;
+ string & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString() );
+ fluidName = getConstitutiveName< SingleFluidBase >( subRegion );
- // the rank that owns the reference well element is responsible for the calculations below.
- if( !subRegion.isLocallyOwned() )
- {
- return;
- }
- localIndex const iwelemRef = subRegion.getTopWellElementIndex();
- // subRegion data
+ SingleFluidBase const & fluid = subRegion.getConstitutiveModel< SingleFluidBase >( fluidName );
+ PerforationData & perforationData = *subRegion.getPerforationData();
+ using CompFlowAccessors =
+ StencilAccessors< fields::flow::pressure,
+ fields::flow::temperature >;
+
+ CompFlowAccessors compFlowAccessors( mesh.getElemManager(), getFlowSolverName() );
- arrayView1d< real64 const > const & connRate =
- subRegion.getField< well::connectionRate >();
+ using SingleFluidAccessors =
+ StencilMaterialAccessors< SingleFluidBase,
+ fields::singlefluid::enthalpy,
+ fields::singlefluid::density,
+ fields::singlefluid::viscosity,
+ fields::singlefluid::internalEnergy,
+ fields::singlefluid::dEnthalpy,
+ fields::singlefluid::dDensity,
+ fields::singlefluid::dViscosity,
+ fields::singlefluid::dInternalEnergy >;
+ SingleFluidAccessors singleFluidAccessors( mesh.getElemManager(), getFlowSolverName() );
- // control data
- WellControls const & wellControls = getWellControls( subRegion );
- string const wellControlsName = wellControls.getName();
+ string const srn = subRegion.getName();
- // format: time,total_rate,total_vol_rate
- std::ofstream outputFile;
- if( m_writeCSV > 0 )
+ std::vector< string > cp_der {"dP", "dT"};
+ for( integer i=0; i());
+ if( isThermal() )
+ {
+ m_wellPropWriter[srn].registerSegProp( "Temperature", subRegion.getField< fields::well::temperature >());
}
- integer const useSurfaceConditions = wellControls.useSurfaceConditions();
+ m_wellPropWriter[srn].registerSegProp( "TotalRate", subRegion.getField< fields::well::connectionRate >());
+ m_wellPropWriter[srn].registerSegPropf( "Density", fluid.density());
+ m_wellPropWriter[srn].registerSegPropf( "Viscosity", fluid.viscosity());
+ //m_wellPropWriter[srn].registerSegPropDer( "dDensity", cp_der, fluid.dDensity());
+ //m_wellPropWriter[srn].registerSegPropDer( "dViscosity", cp_der, fluid.dViscosity());
+ if( isThermal() )
+ {
+ m_wellPropWriter[srn].registerSegPropf( "InternalEnergy", fluid.internalEnergy());
+ //m_wellPropWriter[srn].registerSegPhasePropDerf( "dEnthalpy", cp_der, fluid.dEnthalpy());
- real64 const & currentBHP =
- wellControls.getReference< real64 >( SinglePhaseWell::viewKeyStruct::currentBHPString() );
- real64 const & currentTotalVolRate =
- wellControls.getReference< real64 >( SinglePhaseWell::viewKeyStruct::currentVolRateString() );
+ m_wellPropWriter[srn].registerSegPropf( "Enthalpy", fluid.enthalpy());
+ //m_wellPropWriter[srn].registerSegPhasePropDerf( "dInternalEnergy", cp_der, fluid.dInternalEnergy());
+ }
- // bring everything back to host, capture the scalars by reference
- forAll< serialPolicy >( 1, [&useSurfaceConditions,
- ¤tBHP,
- connRate,
- ¤tTotalVolRate,
- &iwelemRef,
- &wellControlsName,
- &outputFile] ( localIndex const )
+
+ // Perforation properties
+ m_wellPropWriter[srn].registerPerf2dProp( {"X", "Y", "Z"}, perforationData.getLocation());
+ m_wellPropWriter[srn].registerPerfResProp( "Pressure", compFlowAccessors.get( fields::flow::pressure {} ));
+ if( isThermal() )
{
- string const conditionKey = useSurfaceConditions ? "surface" : "reservoir";
- string const unitKey = useSurfaceConditions ? "s" : "r";
-
- real64 const currentTotalRate = connRate[iwelemRef];
- GEOS_LOG( GEOS_FMT( "{}: BHP (at the specified reference elevation): {} Pa",
- wellControlsName, currentBHP ) );
- GEOS_LOG( GEOS_FMT( "{}: Total rate: {} kg/s; total {} volumetric rate: {} {}m3/s",
- wellControlsName, currentTotalRate, conditionKey, currentTotalVolRate, unitKey ) );
- if( outputFile.is_open())
- {
- outputFile << "," << currentBHP;
- outputFile << "," << currentTotalRate << "," << currentTotalVolRate << std::endl;
- outputFile.close();
- }
- } );
- } );
- } );
+ m_wellPropWriter[srn].registerPerfResProp( "Temperature", compFlowAccessors.get( fields::flow::temperature{} ));
+ //m_wellPropWriter[srn].registerPerfResPropf( "Enthalpy", singleFluidAccessors.get( fields::singlefluid::enthalpy{} ));
+ //m_wellPropWriter[srn].registerPerfResPropf( "InternalEnergy", singleFluidAccessors.get( fields::singlefluid::internalEnergy{} ));
+ }
+ m_wellPropWriter[srn].registerPerf1dProp( {"PerfRate"}, perforationData.getField< fields::well::perforationRate >());
+ // m_wellPropWriter[srn].registerPerfResComponentProp( "ComponentDensity", compFlowAccessors.get(
+ // fields::flow::globalCompDensity{} ));
+ //m_wellPropWriter[srn].registerPerfResPropf( "Viscosity", singleFluidAccessors.get( fields::singlefluid::viscosity{} ));
+
+
+ m_wellPropWriter[srn].write( ctime, dt, cycle, subevent, m_numTimesteps,
+ current_newton_iteration,
+ num_timestep_cuts );
+ }
+ }
+
+
}
-REGISTER_CATALOG_ENTRY( PhysicsSolverBase, SinglePhaseWell, string const &, Group * const )
}// namespace geos
diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/SinglePhaseWell.hpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/SinglePhaseWell.hpp
index 8c07e223fb7..0fb323338eb 100644
--- a/src/coreComponents/physicsSolvers/fluidFlow/wells/SinglePhaseWell.hpp
+++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/SinglePhaseWell.hpp
@@ -20,7 +20,7 @@
#ifndef GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_SINGLEPHASEWELL_HPP_
#define GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_SINGLEPHASEWELL_HPP_
-#include "WellSolverBase.hpp"
+#include "physicsSolvers/fluidFlow/wells/WellControls.hpp"
#include "constitutive/fluid/singlefluid/SingleFluidLayouts.hpp"
@@ -43,7 +43,7 @@ class WellElementSubRegion;
*
* A single-phase well solver
*/
-class SinglePhaseWell : public WellSolverBase
+class SinglePhaseWell : public WellControls
{
public:
@@ -63,7 +63,7 @@ class SinglePhaseWell : public WellSolverBase
SinglePhaseWell( SinglePhaseWell const & ) = delete;
/// default move constructor
- SinglePhaseWell( SinglePhaseWell && ) = default;
+ SinglePhaseWell( SinglePhaseWell && ) = delete;
/// deleted assignment operator
SinglePhaseWell & operator=( SinglePhaseWell const & ) = delete;
@@ -76,57 +76,150 @@ class SinglePhaseWell : public WellSolverBase
*/
virtual ~SinglePhaseWell() override = default;
+ void registerWellDataOnMesh( WellElementSubRegion & subRegion ) override;
+
+ /**
+ * @defgroup WellManager Interface Functions
+ *
+ * These functions provide the primary interface that is required for derived classes
+ * The "Well" versions apply to individual well subRegions, whereas the others apply to all wells
+ */
+ /**@{*/
+ /**
+ * * @brief Initialize well for the beginning of a simulation or restart
+ * @param domain the domain
+ * @param mesh the mesh level
+ * @param subRegion the well subRegion
+ * @param time_n the current time
+ */
+ virtual void initializeWell( DomainPartition & domain, MeshLevel & mesh, WellElementSubRegion & subRegion, real64 const & time_n )override;
+
+ virtual void initializeWellPostInitialConditionsPreSubGroups( WellElementSubRegion & subRegion )override;
+
+ virtual bool isCompositional() const override { return false; }
/**
- * @brief name of the node manager in the object catalog
- * @return string that contains the catalog name to generate a new NodeManager object through the object catalog.
+ * @copydoc WellControls::assembleWellAccumulationTerms()
*/
- static string catalogName() { return "SinglePhaseWell"; }
+ virtual void assembleWellAccumulationTerms( real64 const & time,
+ real64 const & dt,
+ WellElementSubRegion & subRegion,
+ DofManager const & dofManager,
+ CRSMatrixView< real64, globalIndex const > const & localMatrix,
+ arrayView1d< real64 > const & localRhs ) override;
/**
- * @copydoc PhysicsSolverBase::getCatalogName()
+ * @copydoc WellControls::assembleWellConstraintTerms()
*/
- string getCatalogName() const override { return catalogName(); }
+ virtual void assembleWellPressureRelations( real64 const & time_n,
+ real64 const & dt,
+ WellElementSubRegion const & subRegion,
+ DofManager const & dofManager,
+ CRSMatrixView< real64, globalIndex const > const & localMatrix,
+ arrayView1d< real64 > const & localRhs ) override;
- virtual void registerDataOnMesh( Group & meshBodies ) override;
+ /**
+ * @copydoc WellControls::assembleWellConstraintTerms()
+ */
+ virtual void assembleWellConstraintTerms( real64 const & time_n,
+ real64 const & dt,
+ WellElementSubRegion const & subRegion,
+ DofManager const & dofManager,
+ CRSMatrixView< real64, globalIndex const > const & localMatrix,
+ arrayView1d< real64 > const & localRhs ) override;
+
+ /**
+ * @copydoc WellControls::computeWellPerforationRates()
+ */
+ virtual void computeWellPerforationRates( real64 const & time_n,
+ real64 const & GEOS_UNUSED_PARAM( dt ),
+ ElementRegionManager & elemManager,
+ WellElementSubRegion & subRegion ) override;
+
+ /**
+ * @copydoc WellControls::assembleFluxTerms()
+ */
+ virtual void assembleWellFluxTerms( real64 const & time,
+ real64 const & dt,
+ WellElementSubRegion const & subRegion,
+ DofManager const & dofManager,
+ CRSMatrixView< real64, globalIndex const > const & localMatrix,
+ arrayView1d< real64 > const & localRhs ) override;
+ /**@}*/
/**
* @defgroup Solver Interface Functions
*
* These functions provide the primary interface that is required for derived classes
+ * The "Well" versions apply to individual well subRegions, whereas the others apply to all wells
*/
/**@{*/
- virtual real64
- calculateResidualNorm( real64 const & time_n,
- real64 const & dt,
- DomainPartition const & domain,
- DofManager const & dofManager,
- arrayView1d< real64 const > const & localRhs ) override;
+ virtual array1d< real64 >
+ calculateLocalWellResidualNorm( real64 const & time_n,
+ real64 const & dt,
+ NonlinearSolverParameters const & nonlinearSolverParameters,
+ WellElementSubRegion const & subRegion,
+ DofManager const & dofManager,
+ arrayView1d< real64 const > const & localRhs )override;
- virtual bool
- checkSystemSolution( DomainPartition & domain,
- DofManager const & dofManager,
- arrayView1d< real64 const > const & localSolution,
- real64 const scalingFactor ) override;
- virtual void
- applySystemSolution( DofManager const & dofManager,
- arrayView1d< real64 const > const & localSolution,
- real64 const scalingFactor,
- real64 const dt,
- DomainPartition & domain ) override;
+ virtual real64
+ calculateWellResidualNorm( real64 const & time_n,
+ real64 const & dt,
+ NonlinearSolverParameters const & nonlinearSolverParameters,
+ WellElementSubRegion const & subRegion,
+ DofManager const & dofManager,
+ arrayView1d< real64 const > const & localRhs ) override;
+
+ virtual real64 scalingForWellSystemSolution( WellElementSubRegion & subRegion,
+ DofManager const & dofManager,
+ arrayView1d< real64 const > const & localSolution ) override;
+ /**
+ * @copydoc WellControls::checkSystemSolution()
+ */
- virtual void
- resetStateToBeginningOfStep( DomainPartition & domain ) override;
+ virtual bool
+ checkWellSystemSolution( WellElementSubRegion & subRegion,
+ DofManager const & dofManager,
+ arrayView1d< real64 const > const & localSolution,
+ real64 const scalingFactor ) override;
+ /**
+ * @copydoc WellControls::applyWellSystemSolution()
+ */
virtual void
- implicitStepSetup( real64 const & time,
- real64 const & dt,
- DomainPartition & domain ) override;
+ applyWellSystemSolution( DofManager const & dofManager,
+ arrayView1d< real64 const > const & localSolution,
+ real64 const scalingFactor,
+ real64 const dt,
+ DomainPartition & domain,
+ MeshLevel & mesh,
+ WellElementSubRegion & subRegion ) override;
+
+ virtual void applyWellBoundaryConditions ( real64 const GEOS_UNUSED_PARAM( time_n ),
+ real64 const GEOS_UNUSED_PARAM( dt ),
+ ElementRegionManager & GEOS_UNUSED_PARAM( elemManager ),
+ WellElementSubRegion & GEOS_UNUSED_PARAM( subRegion ),
+ DofManager const & GEOS_UNUSED_PARAM( dofManager ),
+ arrayView1d< real64 > const & GEOS_UNUSED_PARAM( localRhs ),
+ CRSMatrixView< real64, globalIndex const > const & GEOS_UNUSED_PARAM( localMatrix ) )override {};
+
+ virtual void resetStateToBeginningOfStep( ElementRegionManager const & elemManager, WellElementSubRegion & subRegion ) override;
+
+ virtual void implicitStepSetup( real64 const & time_n,
+ real64 const & GEOS_UNUSED_PARAM( dt ),
+ ElementRegionManager & elemManager,
+ WellElementSubRegion & subRegion )override;
virtual void
implicitStepComplete( real64 const & time,
real64 const & dt,
- DomainPartition & domain ) override;
+ WellElementSubRegion const & subRegion ) override;
+
+ virtual void printRates( real64 const & time_n,
+ real64 const & dt,
+ WellElementSubRegion const & subRegion ) override;
+
+ virtual real64 updateSubRegionState( ElementRegionManager const & elemManager, WellElementSubRegion & subRegion ) override;
/**@}*/
@@ -134,7 +227,7 @@ class SinglePhaseWell : public WellSolverBase
virtual string resElementDofName() const override;
- virtual localIndex numFluidComponents() const override { return 1; }
+ virtual localIndex numFluidComponents() const override { return 0; }
virtual localIndex numFluidPhases() const override { return 1; }
@@ -143,7 +236,7 @@ class SinglePhaseWell : public WellSolverBase
* @param elemManager the well region manager
* @param subRegion the well subregion containing all the primary and dependent fields
*/
- virtual void updateVolRateForConstraint( ElementRegionManager const & elemManager, WellElementSubRegion & subRegion );
+ virtual void calculateReferenceElementRates( WellElementSubRegion & subRegion );
/**
* @brief Recompute the BHP pressure that is used in the well constraints
@@ -156,124 +249,69 @@ class SinglePhaseWell : public WellSolverBase
* @param subRegion the well subRegion containing the well elements and their associated fields
*/
virtual void updateFluidModel( WellElementSubRegion & subRegion ) const;
-
- /**
- * @brief Recompute the perforation rates for all the wells
- * @param domain the domain containing the mesh and fields
- */
- virtual void computePerforationRates( real64 const & time_n,
- real64 const & dt, DomainPartition & domain ) override;
-
/**
- * @brief Recompute all dependent quantities from primary variables (including constitutive models)
- * @param elemManager the elemManager containing the well
- * @param subRegion the well subRegion containing the well elements and their associated fields
+ * @brief Update separator model state
+ * @param elemManager the element region manager
+ * @param subRegion the well subRegion containing the separator
*/
- virtual real64 updateSubRegionState( ElementRegionManager const & elemManager, WellElementSubRegion & subRegion ) override;
+ void updateSeparator( ElementRegionManager const & elemManager, WellElementSubRegion & subRegion );
/**
- * @brief function to assemble the linear system matrix and rhs
- * @param time the time at the beginning of the step
- * @param dt the desired timestep
- * @param domain the domain partition
- * @param dofManager degree-of-freedom manager associated with the linear system
- * @param matrix the system matrix
- * @param rhs the system right-hand side vector
- */
- virtual void assembleSystem( real64 const time,
- real64 const dt,
- DomainPartition & domain,
- DofManager const & dofManager,
- CRSMatrixView< real64, globalIndex const > const & localMatrix,
- arrayView1d< real64 > const & localRhs ) override;
+ * @brief Recompute all dependent quantities from primary variables (including constitutive
+ * models)
- /**
- * @brief assembles the flux terms for all connections between well elements
- * @param time_n previous time value
- * @param dt time step
- * @param domain the physical domain object
- * @param dofManager degree-of-freedom manager associated with the linear system
- * @param matrix the system matrix
- * @param rhs the system right-hand side vector
+ * @param
+ * @param subRegion the well subRegion containing the well elements and their associated
*/
- virtual void assembleFluxTerms( real64 const & time_n,
- real64 const & dt,
- DomainPartition & domain,
- DofManager const & dofManager,
- CRSMatrixView< real64, globalIndex const > const & localMatrix,
- arrayView1d< real64 > const & localRhs ) override;
+ virtual real64 updateWellState( ElementRegionManager const & elemManager, WellElementSubRegion & subRegion ) override;
- /**
- * @brief assembles the accumulation term for all the well elements
- * @param domain the physical domain object
- * @param dofManager degree-of-freedom manager associated with the linear system
- * @param matrix the system matrix
- * @param rhs the system right-hand side vector
- */
- virtual void assembleAccumulationTerms( real64 const & time_n,
- real64 const & dt, DomainPartition & domain,
- DofManager const & dofManager,
- CRSMatrixView< real64, globalIndex const > const & localMatrix,
- arrayView1d< real64 > const & localRhs ) override;
-
- /**
- * @brief assembles the volume balance terms for all well elements
- * @param domain the physical domain object
- * @param dofManager degree-of-freedom manager associated with the linear system
- * @param matrix the system matrix
- * @param rhs the system right-hand side vector
- */
- void assembleVolumeBalanceTerms( DomainPartition const & domain,
- DofManager const & dofManager,
- CRSMatrixView< real64, globalIndex const > const & localMatrix,
- arrayView1d< real64 > const & localRhs );
/**
- * @brief assembles the pressure relations at all connections between well elements except at the well head
- * @param time_n time at the beginning of the time step
- * @param dt the time step size
- * @param domain the physical domain object
- * @param dofManager degree-of-freedom manager associated with the linear system
- * @param matrix the system matrix
- * @param rhs the system right-hand side vector
+ * @brief Recompute all dependent quantities from primary variables (including constitutive
+ * models)
+ * @param elemManager the element region manager
+ * @param subRegion the well subRegion containing the well elements and their associated fields
*/
- virtual void assemblePressureRelations( real64 const & time_n,
- real64 const & dt,
- DomainPartition const & domain,
- DofManager const & dofManager,
- CRSMatrixView< real64, globalIndex const > const & localMatrix,
- arrayView1d< real64 > const & localRhs ) override;
-
/*
* @brief apply a special treatment to the wells that are shut
- * @param time_n the time at the previous converged time step
- * @param domain the physical domain object
+
* @param dofManager degree-of-freedom manager associated with the linear system
* @param matrix the system matrix
* @param rhs the system right-hand side vector
*/
- void shutDownWell( real64 const time_n,
- DomainPartition const & domain,
+ void shutDownWell( WellElementSubRegion & subRegion,
DofManager const & dofManager,
CRSMatrixView< real64, globalIndex const > const & localMatrix,
arrayView1d< real64 > const & localRhs );
- struct viewKeyStruct : WellSolverBase::viewKeyStruct
+ struct viewKeyStruct : WellControls::viewKeyStruct
{
- static constexpr char const * dofFieldString() { return "singlePhaseWellVars"; }
+ static constexpr char const * dofFieldString() { return "wellVars"; }
+
- // control data (not registered on the mesh)
- static constexpr char const * currentBHPString() { return "currentBHP"; }
- static constexpr char const * dCurrentBHPString() { return "dCurrentBHP"; }
- static constexpr char const * currentVolRateString() { return "currentVolumetricRate"; }
- static constexpr char const * dCurrentVolRateString() { return "dCurrentVolRate"; }
};
+ virtual bool solveWHPConstraint( real64 const & time_n,
+ real64 const & dt,
+ integer const cycleNumber,
+ integer const coupledIterationNumber,
+ DomainPartition & domain,
+ MeshLevel & mesh,
+ ElementRegionManager & elemManager,
+ WellElementSubRegion & subRegion )override;virtual void outputSingleWellDebug( real64 const time,
+ real64 const dt,
+ integer current_newton_iteration,
+ MeshLevel & mesh,
+ WellElementSubRegion & subRegion,
+ DofManager const & dofManager,
+ CRSMatrixView< real64, globalIndex const > const & GEOS_UNUSED_PARAM( localMatrix ),
+ arrayView1d< const real64 > const & GEOS_UNUSED_PARAM( localRhs ) ) override;
protected:
- void printRates( real64 const & time_n,
- real64 const & dt,
- DomainPartition & domain ) override;
+ virtual void initializePostInitialConditionsPreSubGroups() override;
+
+ void saveState( WellElementSubRegion & subRegion );
+ virtual void postRestartInitialization( )override;
/// flag if negative pressure is allowed
integer m_allowNegativePressure;
@@ -281,11 +319,6 @@ class SinglePhaseWell : public WellSolverBase
virtual void setConstitutiveNames( ElementSubRegionBase & subRegion ) const override;
- /**
- * @brief Initialize all the primary and secondary variables in all the wells
- * @param domain the domain containing the well manager to access individual wells
- */
- void initializeWells( DomainPartition & domain, real64 const & time_n ) override;
/**
* @brief Make sure that the well constraints are compatible
@@ -295,7 +328,15 @@ class SinglePhaseWell : public WellSolverBase
*/
virtual void validateWellConstraints( real64 const & time_n,
real64 const & dt,
- WellElementSubRegion const & subRegion ) override;
+ WellElementSubRegion const & subRegion
+ ) override;
+
+
+
+ /**
+ * @brief Create well separator
+ */
+ virtual void createSeparator( WellElementSubRegion & subRegion ) override;
};
diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/SinglePhaseWellFields.hpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/SinglePhaseWellFields.hpp
index 2cd21f32866..9e0d5472826 100644
--- a/src/coreComponents/physicsSolvers/fluidFlow/wells/SinglePhaseWellFields.hpp
+++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/SinglePhaseWellFields.hpp
@@ -33,21 +33,6 @@ namespace fields
namespace well
{
-DECLARE_FIELD( connectionRate,
- "connectionRate",
- array1d< real64 >,
- 0,
- LEVEL_0,
- WRITE_AND_READ,
- "Connection rate" );
-
-DECLARE_FIELD( connectionRate_n,
- "connectionRate_n",
- array1d< real64 >,
- 0,
- NOPLOT,
- WRITE_AND_READ,
- "Connection rate at the previous converged time step" );
DECLARE_FIELD( density_n,
"density_n",
diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/WellBHPConstraints.cpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellBHPConstraints.cpp
new file mode 100644
index 00000000000..70f6e79b86a
--- /dev/null
+++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellBHPConstraints.cpp
@@ -0,0 +1,116 @@
+/*
+ * ------------------------------------------------------------------------------------------------------------
+ * 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 WellBHPConstraints.cpp
+ */
+
+#include "LogLevelsInfo.hpp"
+#include "WellBHPConstraints.hpp"
+#include "WellConstants.hpp"
+#include "dataRepository/InputFlags.hpp"
+#include "functions/FunctionManager.hpp"
+
+
+namespace geos
+{
+
+using namespace dataRepository;
+
+BHPConstraint::BHPConstraint( string const & name, Group * const parent )
+ : WellConstraintBase( name, parent ),
+ m_refElevation( 0.0 ),
+ m_refGravCoef( 0.0 )
+{
+ setInputFlags( InputFlags::OPTIONAL_NONUNIQUE );
+
+ registerWrapper( viewKeyStruct::targetBHPString(), &m_constraintValue ).
+ setDefaultValue( 0.0 ).
+ setInputFlag( InputFlags::OPTIONAL ).
+ setRestartFlags( RestartFlags::WRITE_AND_READ ).
+ setDescription( "Minimun bottom-hole production pressure [Pa]" );
+
+ registerWrapper( viewKeyStruct::refElevString(), &m_refElevation ).
+ setDefaultValue( -1 ).
+ setInputFlag( InputFlags::REQUIRED ).
+ setDescription( "Reference elevation where BHP control is enforced [m]" );
+
+}
+
+
+BHPConstraint::~BHPConstraint()
+{}
+
+void BHPConstraint::postInputInitialization()
+{
+
+ WellConstraintBase::postInputInitialization();
+
+}
+
+MinimumBHPConstraint::MinimumBHPConstraint( string const & name, Group * const parent )
+ : BHPConstraint( name, parent )
+{
+ setInputFlags( InputFlags::OPTIONAL_NONUNIQUE );
+
+ registerWrapper( viewKeyStruct::targetBHPString(), &m_constraintValue ).
+ setDefaultValue( 0.0 ).
+ setInputFlag( InputFlags::OPTIONAL ).
+ setRestartFlags( RestartFlags::WRITE_AND_READ ).
+ setDescription( "Minimun bottom-hole production pressure [Pa]" );
+}
+
+
+MinimumBHPConstraint::~MinimumBHPConstraint()
+{}
+
+void MinimumBHPConstraint::postInputInitialization()
+{
+
+ BHPConstraint::postInputInitialization();
+
+}
+
+bool MinimumBHPConstraint::checkViolation( WellConstraintBase const & currentConstraint, real64 const & currentTime ) const
+{
+ return currentConstraint.bottomHolePressure() < getConstraintValue( currentTime );
+}
+
+MaximumBHPConstraint::MaximumBHPConstraint( string const & name, Group * const parent )
+ : BHPConstraint( name, parent )
+{
+ setInputFlags( InputFlags::OPTIONAL_NONUNIQUE );
+
+}
+
+
+MaximumBHPConstraint::~MaximumBHPConstraint()
+{}
+
+void MaximumBHPConstraint::postInputInitialization()
+{
+ // Validate value and table options
+ BHPConstraint::postInputInitialization();
+
+}
+bool MaximumBHPConstraint::checkViolation( WellConstraintBase const & currentConstraint, real64 const & currentTime ) const
+{
+ return currentConstraint.bottomHolePressure() > getConstraintValue( currentTime );
+}
+
+REGISTER_CATALOG_ENTRY( WellConstraintBase, MinimumBHPConstraint, string const &, Group * const )
+REGISTER_CATALOG_ENTRY( WellConstraintBase, MaximumBHPConstraint, string const &, Group * const )
+
+} //namespace geos
diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/WellBHPConstraints.hpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellBHPConstraints.hpp
new file mode 100644
index 00000000000..3002d303e39
--- /dev/null
+++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellBHPConstraints.hpp
@@ -0,0 +1,334 @@
+/*
+ * ------------------------------------------------------------------------------------------------------------
+ * 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 WellBHPConstraints.hpp
+ */
+
+
+#ifndef GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_WELLBHPCONSTRAINTS_HPP
+#define GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_WELLBHPCONSTRAINTS_HPP
+
+#include "common/format/EnumStrings.hpp"
+#include "dataRepository/Group.hpp"
+#include "functions/TableFunction.hpp"
+#include "WellConstraintsBase.hpp"
+namespace geos
+{
+
+/**
+ * @class BHPConstraint
+ * @brief This class describes a minimum pressure constraint used to control a injection well.
+ */
+class BHPConstraint : public WellConstraintBase
+{
+public:
+
+ /**
+ * @name Constructor / Destructor
+ */
+ ///@{
+
+ /**
+ * @brief Constructor for WellControls Objects.
+ * @param[in] name the name of this instantiation of WellControls in the repository
+ * @param[in] parent the parent group of this instantiation of WellControls
+ */
+ explicit BHPConstraint( string const & name, dataRepository::Group * const parent );
+
+
+ /**
+ * @brief Default destructor.
+ */
+ ~BHPConstraint() override;
+
+ /**
+ * @brief Deleted default constructor.
+ */
+ BHPConstraint() = delete;
+
+ /**
+ * @brief Deleted copy constructor.
+ */
+ BHPConstraint( BHPConstraint const & ) = delete;
+
+ /**
+ * @brief Deleted move constructor.
+ */
+ BHPConstraint( BHPConstraint && ) = delete;
+
+ /**
+ * @brief Deleted assignment operator.
+ * @return a reference to a constraint object
+ */
+ BHPConstraint & operator=( BHPConstraint const & ) = delete;
+
+ /**
+ * @brief Deleted move operator.
+ * @return a reference to a constraint object
+ */
+ BHPConstraint & operator=( BHPConstraint && ) = delete;
+
+ ///@}
+
+ /**
+ * @name Getters / Setters
+ */
+ ///@{
+
+ // Temp interface - tjb
+ virtual ConstraintTypeId getControl() const override { return ConstraintTypeId::BHP; };
+
+ ///@}
+ /**
+ * @brief Struct to serve as a container for variable strings and keys.
+ * @struct viewKeyStruct
+ */
+ struct viewKeyStruct
+ {
+ /// String key for the well target BHP
+ static constexpr char const * targetBHPString() { return "targetBHP"; }
+ /// String key for the well reference elevation (for BHP control)
+ static constexpr char const * refElevString() { return "referenceElevation"; }
+ }
+ viewKeysWellBHPConstraint;
+
+ //virtual bool checkViolation( WellConstraintBase const & currentConstraint, real64 const & currentTime ) const override;
+
+ /**
+ * @brief Getter for the reference elevation where the BHP control is enforced
+ * @return the reference elevation
+ */
+ real64 getReferenceElevation() const { return m_refElevation; }
+
+ /**
+ * @brief Set the reference elevation where the BHP control is enforced
+ * @return the reference elevation
+ */
+ void setReferenceElevation( real64 const & refElevation ) { m_refElevation=refElevation; }
+
+ /**
+ * @brief Getter for the reference gravity coefficient
+ * @return the reference gravity coefficient
+ */
+ real64 getReferenceGravityCoef() const { return m_refGravCoef; }
+
+ /**
+ * @brief Setter for the reference gravity
+ */
+ void setReferenceGravityCoef( real64 const & refGravCoef ) { m_refGravCoef = refGravCoef; }
+
+protected:
+
+ virtual void postInputInitialization() override;
+
+ /// Reference elevation
+ real64 m_refElevation;
+
+ /// Gravity coefficient of the reference elevation
+ real64 m_refGravCoef;
+
+};
+
+/**
+ * @class MinimumBHPConstraint
+ * @brief This class describes a minimum pressure constraint used to control a injection well.
+ */
+class MinimumBHPConstraint : public BHPConstraint
+{
+public:
+
+ /**
+ * @name Constructor / Destructor
+ */
+ ///@{
+
+ /**
+ * @brief Constructor for WellControls Objects.
+ * @param[in] name the name of this instantiation of WellControls in the repository
+ * @param[in] parent the parent group of this instantiation of WellControls
+ */
+ explicit MinimumBHPConstraint( string const & name, dataRepository::Group * const parent );
+
+
+ /**
+ * @brief Default destructor.
+ */
+ ~MinimumBHPConstraint() override;
+
+ /**
+ * @brief Deleted default constructor.
+ */
+ MinimumBHPConstraint() = delete;
+
+ /**
+ * @brief Deleted copy constructor.
+ */
+ MinimumBHPConstraint( MinimumBHPConstraint const & ) = delete;
+
+ /**
+ * @brief Deleted move constructor.
+ */
+ MinimumBHPConstraint( MinimumBHPConstraint && ) = delete;
+
+ /**
+ * @brief Deleted assignment operator.
+ * @return a reference to a constraint object
+ */
+ MinimumBHPConstraint & operator=( MinimumBHPConstraint const & ) = delete;
+
+ /**
+ * @brief Deleted move operator.
+ * @return a reference to a constraint object
+ */
+ MinimumBHPConstraint & operator=( MinimumBHPConstraint && ) = delete;
+
+ ///@}
+
+ /**
+ * @brief name of the node manager in the object catalog
+ * @return string that contains the catalog name to generate a new Constraint object through the object catalog.
+ */
+ static string catalogName()
+ {
+ return "MinimumBHPConstraint";
+ }
+ virtual string getCatalogName() const override { return catalogName(); }
+ /**
+ * @name Getters / Setters
+ */
+ ///@{
+
+
+ /**
+ * @brief Struct to serve as a container for variable strings and keys.
+ * @struct viewKeyStruct
+ */
+ struct viewKeyStruct
+ {
+ /// String key for the well target BHP
+ static constexpr char const * targetBHPString() { return "targetBHP"; }
+ }
+ viewKeysWellBHPConstraint;
+
+ virtual bool checkViolation( WellConstraintBase const & currentConstraint, real64 const & currentTime ) const override;
+
+protected:
+
+ virtual void postInputInitialization() override;
+
+
+};
+
+/**
+ * @class WellMinimumBHPConstraint
+ * @brief This class describes a maximum pressure constraint used to control a injection well.
+ */
+class MaximumBHPConstraint : public BHPConstraint
+{
+public:
+
+ /**
+ * @name Constructor / Destructor
+ */
+ ///@{
+
+ /**
+ * @brief Constructor for WellControls Objects.
+ * @param[in] name the name of this instantiation of WellControls in the repository
+ * @param[in] parent the parent group of this instantiation of WellControls
+ */
+ explicit MaximumBHPConstraint( string const & name, dataRepository::Group * const parent );
+
+
+ /**
+ * @brief Default destructor.
+ */
+ ~MaximumBHPConstraint() override;
+
+ /**
+ * @brief Deleted default constructor.
+ */
+ MaximumBHPConstraint() = delete;
+
+ /**
+ * @brief Deleted copy constructor.
+ */
+ MaximumBHPConstraint( MaximumBHPConstraint const & ) = delete;
+
+ /**
+ * @brief Deleted move constructor.
+ */
+ MaximumBHPConstraint( MaximumBHPConstraint && ) = delete;
+
+ /**
+ * @brief Deleted assignment operator.
+ * @return a reference to a constraint object
+ */
+ MaximumBHPConstraint & operator=( MaximumBHPConstraint const & ) = delete;
+
+ /**
+ * @brief Deleted move operator.
+ * @return a reference to a constraint object
+ */
+ MaximumBHPConstraint & operator=( MaximumBHPConstraint && ) = delete;
+
+ ///@}
+
+ /**
+ * @brief name of the node manager in the object catalog
+ * @return string that contains the catalog name to generate a new Constraint object through the object catalog.
+ */
+ static string catalogName()
+ {
+ return "MaximumBHPConstraint";
+ }
+ virtual string getCatalogName() const override { return catalogName(); }
+ /**
+ * @name Getters / Setters
+ */
+ ///@{
+ // Temp interface - tjb
+ virtual ConstraintTypeId getControl() const override { return ConstraintTypeId::BHP; };
+
+
+ ///@}
+ /**
+ * @brief Struct to serve as a container for variable strings and keys.
+ * @struct viewKeyStruct
+ */
+ struct viewKeyStruct
+ {
+ /// String key for the well target BHP
+ static constexpr char const * targetBHPString() { return "targetBHP"; }
+ }
+ viewKeysWellBHPConstraint;
+
+ virtual bool checkViolation( WellConstraintBase const & currentConstraint, real64 const & currentTime ) const override;
+protected:
+
+ virtual void postInputInitialization() override;
+
+
+
+private:
+
+
+};
+
+
+} //namespace geos
+
+#endif //GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_WELLBHPCONSTRAINTS_HPP
diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/WellConstants.hpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellConstants.hpp
index 58b8b421c3a..83eadb7c2ff 100644
--- a/src/coreComponents/physicsSolvers/fluidFlow/wells/WellConstants.hpp
+++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellConstants.hpp
@@ -36,6 +36,11 @@ struct WellConstants
static constexpr real64 defaultInjectorBHP = 1.01325e8;
};
+enum class WellTypes : integer
+{
+ PRODUCER, /**< A production well */
+ INJECTOR /**< An injection well */
+};
} //namespace geos
#endif //GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_WELLCONSTANTS_HPP
diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/WellConstraintsBase.cpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellConstraintsBase.cpp
new file mode 100644
index 00000000000..9017e5e36bd
--- /dev/null
+++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellConstraintsBase.cpp
@@ -0,0 +1,146 @@
+/*
+ * ------------------------------------------------------------------------------------------------------------
+ * 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 WellConstraintBase.cpp
+ */
+
+#include "LogLevelsInfo.hpp"
+#include "WellConstraintsBase.hpp"
+#include "WellConstants.hpp"
+#include "dataRepository/InputFlags.hpp"
+#include "functions/FunctionManager.hpp"
+
+
+namespace geos
+{
+
+using namespace dataRepository;
+
+// Provide a properly-typed static catalog for WellConstraintBase so that
+// CatalogInterface< WellConstraintBase, ... >::getCatalog() can return
+// a catalog of CatalogInterface objects instead of
+// inheriting Group::getCatalog() which returns a catalog of Group entries.
+WellConstraintBase::CatalogInterface::CatalogType & WellConstraintBase::getCatalog()
+{
+ static WellConstraintBase::CatalogInterface::CatalogType catalog;
+ return catalog;
+}
+
+namespace
+{
+
+
+#if 0
+/// Utility function to create a one-value table internally when not provided by the user
+TableFunction * createConstraintScheduleTable( string const & tableName,
+ real64 const & constantValue )
+{
+ array1d< array1d< real64 > > timeCoord;
+ timeCoord.resize( 1 );
+ timeCoord[0].emplace_back( 0 );
+ array1d< real64 > constantValueArray;
+ constantValueArray.emplace_back( constantValue );
+
+ FunctionManager & functionManager = FunctionManager::getInstance();
+ TableFunction * table = dynamicCast< TableFunction * >( functionManager.createChild( TableFunction::catalogName(), tableName ));
+ table->setTableCoordinates( timeCoord, { units::Time } );
+ table->setTableValues( constantValueArray );
+ table->setInterpolationMethod( TableFunction::InterpolationType::Lower );
+ return table;
+}
+#endif
+
+
+}
+
+WellConstraintBase::WellConstraintBase( string const & name, Group * const parent )
+ : Group( name, parent ),
+ m_isConstraintActive( 1 ),
+ m_useScheduleTable( false ),
+ m_constraintValue( 0 ),
+ m_constraintScheduleTable( nullptr ),
+ m_rateSign( 1.0 ) // Default to positive rate sign for injection, set to -1.0 for production wells
+
+{
+ setInputFlags( InputFlags::OPTIONAL_NONUNIQUE );
+
+ registerWrapper( viewKeyStruct::constraintScheduleTableNameString(), &m_constraintScheduleTableName ).
+ setRTTypeName( rtTypes::CustomTypes::groupNameRef ).
+ setInputFlag( InputFlags::OPTIONAL ).
+ setDescription( "Name of the well constraint schedule table when the constraint value is a time dependent function. \n" );
+
+ registerWrapper( viewKeyStruct::constraintActiveString(), &m_isConstraintActive ).
+ setDefaultValue( 1 ).
+ setInputFlag( InputFlags::OPTIONAL ).
+ setDescription( "Flag to enable constraint. Currently only supported for injectors: \n"
+ " - If the flag is set to 1, constraint included in boundary condition selection. \n"
+ " - If the flag is set to 0, constraint excluded from boundary condition selection." );
+
+ registerWrapper( viewKeyStruct::constraintValueString(), &m_constraintValue ).
+ setRTTypeName( rtTypes::CustomTypes::groupNameRef ).
+ setInputFlag( InputFlags::OPTIONAL ).
+ setDescription( "Constraint value. \n" );
+
+}
+
+
+WellConstraintBase::~WellConstraintBase()
+{}
+
+
+void WellConstraintBase::postInputInitialization()
+{
+
+ GEOS_THROW_IF( ((m_constraintValue > 0.0 && !m_constraintScheduleTableName.empty())|| (!(m_constraintValue > 0.0) && m_constraintScheduleTableName.empty())),
+ this->getDataContext() << ": You have provided redundant information for well constraint value ." <<
+ " A constraint value and table of constraint values cannot be specified together",
+ InputError );
+
+ // Create time-dependent constraint table
+ if( !m_constraintScheduleTableName.empty() )
+ {
+ FunctionManager & functionManager = FunctionManager::getInstance();
+ m_constraintScheduleTable = &(functionManager.getGroup< TableFunction const >( m_constraintScheduleTableName ));
+
+ GEOS_THROW_IF( m_constraintScheduleTable->getInterpolationMethod() != TableFunction::InterpolationType::Lower,
+ this->getName() << " " << this->getDataContext() << ": The interpolation method for the schedule table "
+ << m_constraintScheduleTable->getName() << " should be TableFunction::InterpolationType::Lower",
+ InputError );
+ }
+
+}
+
+void WellConstraintBase::setNextDtFromTables( real64 const currentTime, real64 & nextDt )
+{
+ setNextDtFromTable( m_constraintScheduleTable, currentTime, nextDt );
+}
+
+void WellConstraintBase::setNextDtFromTable( TableFunction const * table, real64 const currentTime, real64 & nextDt )
+{
+ if( table )
+ {
+ // small epsilon to make sure we land on the other side of table interval and pick up the right rate
+ real64 const eps = 1e-6;
+ real64 const dtLimit = (table->getCoord( ¤tTime, 0, TableFunction::InterpolationType::Upper ) - currentTime) * ( 1.0 + eps );
+ if( dtLimit > eps && dtLimit < nextDt )
+ {
+ nextDt = dtLimit;
+ }
+ }
+}
+
+
+} //namespace geos
diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/WellConstraintsBase.hpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellConstraintsBase.hpp
new file mode 100644
index 00000000000..f6b2be1fd83
--- /dev/null
+++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellConstraintsBase.hpp
@@ -0,0 +1,291 @@
+/*
+ * ------------------------------------------------------------------------------------------------------------
+ * 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 WellConstraintBase.hpp
+ */
+
+
+#ifndef GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_WELLCONSTRAINTBASE_HPP
+#define GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_WELLCONSTRAINTBASE_HPP
+
+#include "common/format/EnumStrings.hpp"
+
+#include "functions/TableFunction.hpp"
+#include "dataRepository/Group.hpp"
+namespace geos
+{
+
+
+
+enum class ConstraintTypeId : integer
+{
+ BHP, /**< The well operates at a specified bottom hole pressure (BHP) */
+ PHASEVOLRATE, /**< The well operates at a specified phase volumetric flow rate */
+ TOTALVOLRATE, /**< The well operates at a specified total volumetric flow rate */
+ MASSRATE, /**;
+
+ /// Get the singleton catalog for WellConstraintBase
+ static CatalogInterface::CatalogType & getCatalog();
+
+ /**
+ * @brief function to return the catalog name of the derived class
+ * @return a string that contains the catalog name of the derived class
+ */
+ virtual string getCatalogName() const = 0;
+ /**
+ * @name Constructor / Destructor
+ */
+ ///@{
+
+ /**
+ * @brief Constructor for WellControls Objects.
+ * @param[in] name the name of this instantiation of WellControls in the repository
+ * @param[in] parent the parent group of this instantiation of WellControls
+ */
+ explicit WellConstraintBase( string const & name, dataRepository::Group * const parent );
+
+
+ /**
+ * @brief Default destructor.
+ */
+ ~WellConstraintBase() override;
+
+ /**
+ * @brief Deleted default constructor.
+ */
+ WellConstraintBase() = delete;
+
+ /**
+ * @brief Deleted copy constructor.
+ */
+ WellConstraintBase( WellConstraintBase const & ) = delete;
+
+ /**
+ * @brief Deleted move constructor.
+ */
+ WellConstraintBase( WellConstraintBase && ) = delete;
+
+ /**
+ * @brief Deleted assignment operator.
+ * @return a reference to a constraint object
+ */
+ WellConstraintBase & operator=( WellConstraintBase const & ) = delete;
+
+ /**
+ * @brief Deleted move operator.
+ * @return a reference to a constraint object
+ */
+ WellConstraintBase & operator=( WellConstraintBase && ) = delete;
+
+ ///@}
+
+
+ /**
+ * @name Getters / Setters
+ */
+ ///@{
+
+ // Temp interface - tjb
+ virtual ConstraintTypeId getControl() const = 0;
+
+ /**
+ * @brief Defines whether the constraint should be evaluated or not
+ * @brief Some workflows require the well model to define a constraint
+ * @brief of similar type to user defined constraints. For example,
+ * @brief rate constraints to evaluated WHP constraints.
+ * @return true if the constraint is active, false otherwise
+ */
+ bool isConstraintActive( ) const { return m_isConstraintActive; }
+
+ /**
+ * @brief Sets constraint active status
+ * @param[in] constraintActive true if the constraint is active, false otherwise
+ */
+ bool setConstraintActive( bool const & constraintActive ) { return m_isConstraintActive=constraintActive; }
+
+ /**
+ * @brief Sets constraint value
+ * @param[in] constraint value
+ */
+ void setConstraintValue( real64 const & constraintValue )
+ {
+ m_constraintValue = constraintValue;
+ }
+
+ /**
+ * @brief Get the target bottom hole pressure value.
+ * @return a value for the target bottom hole pressure
+ */
+ real64 getConstraintValue( real64 const & currentTime ) const
+ {
+ if( m_constraintScheduleTableName.empty() )
+ {
+ return m_rateSign*m_constraintValue;
+ }
+
+ return m_rateSign*m_constraintScheduleTable->evaluate( ¤tTime );
+ }
+
+ ///@}
+
+ /**
+ * @brief Struct to serve as a container for variable strings and keys.
+ * @struct viewKeyStruct
+ */
+ struct viewKeyStruct
+ {
+ /// string key for schedule table name
+ static constexpr char const * constraintScheduleTableNameString() { return "constraintScheduleTableName"; }
+
+ /// String key for the well constraint value
+ static constexpr char const * constraintValueString() { return "value"; }
+
+ /// String key for the well constraint active flag
+ static constexpr char const * constraintActiveString() { return "constraintActive"; }
+
+ }
+ /// ViewKey struct for the WellControls class
+ viewKeysWellConstraint;
+
+ // Quantities computed from well constraint solve with this boundary condition
+ // This needs to be somewhere else tjb
+ void setBHP( real64 bhp ){ m_BHP=bhp;};
+ void setPhaseVolumeRates( array1d< real64 > const & phaseVolumeRates ) { m_phaseVolumeRates = phaseVolumeRates; };
+ void setTotalVolumeRate( real64 totalVolumeRate ){ m_totalVolumeRate = totalVolumeRate; };
+ void setMassRate( real64 massRate ){ m_massRate = massRate; };
+
+ /**
+ * @brief Getter for the bottom hole pressure
+ * @return bottom hole pressure
+ */
+ real64 bottomHolePressure() const { return m_BHP; }
+
+ /**
+ * @brief Getter for the phase volume rates
+ * @return an arrayView1d storing the phase volume rates
+ */
+ arrayView1d< real64 const > phaseVolumeRates() const { return m_phaseVolumeRates; }
+
+ /**
+ * @brief Getter for the total volume rate
+ * @return mass rate
+ */
+ real64 totalVolumeRate() const { return m_totalVolumeRate; }
+
+ /**
+ * @brief Getter for the liquid rate
+ * @return liquid rate
+ */
+ real64 liquidRate() const { return m_liquidRate; }
+
+ /**
+ * @brief Getter for the mass rate
+ * @return mass rate
+ */
+ real64 massRate() const { return m_massRate; }
+
+ // endof This needs to be somewhere else tjb
+ /**
+ * @brief Check if this constraint is violated
+ * @return true if limiting constraint, false otherwise
+ */
+ virtual bool checkViolation( WellConstraintBase const & currentConstraint, real64 const & currentTime ) const = 0;
+
+protected:
+
+ virtual void postInputInitialization() override;
+
+ /**
+ * @brief set next time step based on tables intervals
+ * @param[in] currentTime the current time
+ * @param[inout] nextDt the time step
+ */
+ void setNextDtFromTables( real64 const currentTime, real64 & nextDt );
+
+
+protected:
+
+ /// Constraint status
+ integer m_isConstraintActive;
+
+ /// Flag to indicate whether a schedule table should be generated for constraint value;
+ bool m_useScheduleTable;
+
+ /// Constraint value
+ real64 m_constraintValue;
+
+ void setNextDtFromTable( TableFunction const * table, real64 const currentTime, real64 & nextDt );
+
+ /// Constraint schedule table name
+ string m_constraintScheduleTableName;
+
+ /// Constraint values versus time
+ TableFunction const * m_constraintScheduleTable;
+
+ // Quantities computed from well constraint solve with this boundary condition
+
+ // botton hole pressure
+ real64 m_BHP;
+
+ // phase rates
+ array1d< real64 > m_phaseVolumeRates;
+
+ // liquid rate
+ real64 m_liquidRate;
+
+ // total volume rate
+ real64 m_totalVolumeRate;
+
+ // mass rate
+ real64 m_massRate;
+
+ /// Rate sign. +1 for injector, -1 for producer
+ real64 m_rateSign;
+};
+
+
+} //namespace geos
+
+#endif //GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_WELLCONSTRAINTBASE_HPP
diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/WellControls.cpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellControls.cpp
index 684306cce46..76f927072bb 100644
--- a/src/coreComponents/physicsSolvers/fluidFlow/wells/WellControls.cpp
+++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellControls.cpp
@@ -20,10 +20,14 @@
#include "LogLevelsInfo.hpp"
#include "WellControls.hpp"
#include "WellConstants.hpp"
+#include "PipeFlowTableFunction.hpp"
#include "dataRepository/InputFlags.hpp"
#include "functions/FunctionManager.hpp"
+#include "mesh/PerforationFields.hpp"
+#include "fileIO/Outputs/OutputBase.hpp"
+#include "physicsSolvers/fluidFlow/wells/WellFields.hpp"
-
+#include "functions/FunctionManager.hpp"
namespace geos
{
@@ -32,88 +36,66 @@ using namespace dataRepository;
WellControls::WellControls( string const & name, Group * const parent )
: Group( name, parent ),
m_type( Type::PRODUCER ),
- m_refElevation( 0.0 ),
- m_refGravCoef( 0.0 ),
+ m_numPhases( 0 ),
+ m_numComponents( 0 ),
+ m_numDofPerWellElement( 0 ),
+ m_numDofPerResElement( 0 ),
+ m_isThermal( 0 ),
+ m_keepVariablesConstantDuringInitStep( false ),
+ //m_ratesOutputDir( joinPath( OutputBase::getOutputDirectory(), "_rates" ) ),
+ m_ratesOutputDir( joinPath( OutputBase::getOutputDirectory(), parent->getName() + "_rates" ) ),
m_inputControl( Control::UNINITIALIZED ),
m_currentControl( Control::UNINITIALIZED ),
- m_targetBHP( 0.0 ),
- m_targetTotalRate( 0.0 ),
- m_targetPhaseRate( 0.0 ),
- m_targetMassRate( 0.0 ),
m_useSurfaceConditions( 0 ),
- m_surfacePres( 0.0 ),
- m_surfaceTemp( 0.0 ),
+ m_surfacePres( -1.0 ),
+ m_surfaceTemp( -1.0 ),
m_isCrossflowEnabled( 1 ),
- m_initialPressureCoefficient( 0.1 ),
- m_rateSign( -1.0 ),
- m_targetTotalRateTable( nullptr ),
- m_targetPhaseRateTable( nullptr ),
- m_targetBHPTable( nullptr ),
- m_statusTable( nullptr ),
+ m_initialPressureCoefficient( 0.5 ),
+ m_currentConstraint( nullptr ),
m_wellStatus( WellControls::Status::OPEN ),
- m_regionAveragePressure( -1 )
+ m_wellOpen( false ),
+ m_statusTable( nullptr ),
+ m_regionAveragePressure( -1 ),
+ m_estimateSolution( 0 ),
+ m_enableIsoThermalEstimator( 0 ),
+ /// Nonlinear solver parameters
+ m_wellNewtonSolver( viewKeyStruct::wellNewtonSolverString(), this ),
+ m_estimatorDoFManager( name ),
+ m_dofManagerInitialized( false ),
+ m_writeSegDebug( 0 ),
+
+ m_numTimesteps( 0 ),
+ m_wellDebugInit( false )
{
setInputFlags( InputFlags::OPTIONAL_NONUNIQUE );
-
+ m_minWHPConstraint=nullptr;
registerWrapper( viewKeyStruct::typeString(), &m_type ).
setInputFlag( InputFlags::REQUIRED ).
setDescription( "Well type. Valid options:\n* " + EnumStrings< Type >::concat( "\n* " ) );
- registerWrapper( viewKeyStruct::inputControlString(), &m_inputControl ).
- setInputFlag( InputFlags::REQUIRED ).
- setDescription( "Well control. Valid options:\n* " + EnumStrings< Control >::concat( "\n* " ) );
+ this->registerWrapper( viewKeyStruct::writeCSVFlagString(), &m_writeCSV ).
+ setApplyDefaultValue( 1 ).
+ setInputFlag( dataRepository::InputFlags::OPTIONAL ).
+ setDescription( "When set to 1, write the rates into a CSV file." );
+
+ this->registerWrapper( viewKeyStruct::writeSegDebugFlagString(), &m_writeSegDebug ).
+ setApplyDefaultValue( 1 ).
+ setInputFlag( dataRepository::InputFlags::OPTIONAL ).
+ setDescription( "When set to 1, write the segment debug information into a CSV file." );
+
+ this->registerWrapper( viewKeyStruct::timeStepFromTablesFlagString(), &m_timeStepFromTables ).
+ setApplyDefaultValue( 0 ).
+ setInputFlag( dataRepository::InputFlags::OPTIONAL ).
+ setDescription( "Choose time step to honor rates/bhp tables time intervals" );
registerWrapper( viewKeyStruct::currentControlString(), &m_currentControl ).
setDefaultValue( Control::UNINITIALIZED ).
setInputFlag( InputFlags::FALSE ).
setDescription( "Current well control" );
- registerWrapper( viewKeyStruct::targetBHPString(), &m_targetBHP ).
- setDefaultValue( 0.0 ).
- setInputFlag( InputFlags::OPTIONAL ).
- setRestartFlags( RestartFlags::WRITE_AND_READ ).
- setDescription( "The target bottom-hole pressure [Pa] for the well." );
-
- registerWrapper( viewKeyStruct::targetTotalRateString(), &m_targetTotalRate ).
- setDefaultValue( 0.0 ).
- setInputFlag( InputFlags::OPTIONAL ).
- setRestartFlags( RestartFlags::WRITE_AND_READ ).
- setDescription( "Target total volumetric rate (if useSurfaceConditions: [surface m^3/s]; else [reservoir m^3/s])" );
-
- registerWrapper( viewKeyStruct::targetPhaseRateString(), &m_targetPhaseRate ).
- setDefaultValue( 0.0 ).
- setInputFlag( InputFlags::OPTIONAL ).
- setRestartFlags( RestartFlags::WRITE_AND_READ ).
- setDescription( "Target phase volumetric rate (if useSurfaceConditions: [surface m^3/s]; else [reservoir m^3/s])" );
-
- registerWrapper( viewKeyStruct::targetMassRateString(), &m_targetMassRate ).
- setDefaultValue( 0.0 ).
- setInputFlag( InputFlags::OPTIONAL ).
- setRestartFlags( RestartFlags::WRITE_AND_READ ).
- setDescription( "Target Mass Rate rate ( [kg^3/s])" );
-
- registerWrapper( viewKeyStruct::targetPhaseNameString(), &m_targetPhaseName ).
- setRTTypeName( rtTypes::CustomTypes::groupNameRef ).
- setDefaultValue( "" ).
- setInputFlag( InputFlags::OPTIONAL ).
- setRestartFlags( RestartFlags::WRITE_AND_READ ).
- setDescription( "Name of the target phase" );
-
- registerWrapper( viewKeyStruct::refElevString(), &m_refElevation ).
- setDefaultValue( -1 ).
+ registerWrapper( viewKeyStruct::inputControlString(), &m_inputControl ).
setInputFlag( InputFlags::REQUIRED ).
- setDescription( "Reference elevation where BHP control is enforced [m]" );
-
- registerWrapper( viewKeyStruct::injectionStreamString(), &m_injectionStream ).
- setDefaultValue( -1 ).
- setSizedFromParent( 0 ).
- setInputFlag( InputFlags::OPTIONAL ).
- setDescription( "Defines the global component fractions of the injected fluid." );
-
- registerWrapper( viewKeyStruct::injectionTemperatureString(), &m_injectionTemperature ).
- setDefaultValue( -1 ).
- setInputFlag( InputFlags::OPTIONAL ).
- setDescription( "Temperature of the injection stream [K]" );
+ setDescription( "Well control. Valid options:\n* " + EnumStrings< Control >::concat( "\n* " ) );
registerWrapper( viewKeyStruct::useSurfaceConditionsString(), &m_useSurfaceConditions ).
setDefaultValue( 0 ).
@@ -155,31 +137,17 @@ WellControls::WellControls( string const & name, Group * const parent )
" - Injector pressure at reference depth initialized as: (1+initialPressureCoefficient)*reservoirPressureAtClosestPerforation + density*g*( zRef - zPerf ) \n"
" - Producer pressure at reference depth initialized as: (1-initialPressureCoefficient)*reservoirPressureAtClosestPerforation + density*g*( zRef - zPerf ) " );
- registerWrapper( viewKeyStruct::targetBHPTableNameString(), &m_targetBHPTableName ).
- setRTTypeName( rtTypes::CustomTypes::groupNameRef ).
- setInputFlag( InputFlags::OPTIONAL ).
- setDescription( "Name of the BHP table when the rate is a time dependent function" );
-
- registerWrapper( viewKeyStruct::targetTotalRateTableNameString(), &m_targetTotalRateTableName ).
- setRTTypeName( rtTypes::CustomTypes::groupNameRef ).
- setInputFlag( InputFlags::OPTIONAL ).
- setDescription( "Name of the total rate table when the rate is a time dependent function" );
-
- registerWrapper( viewKeyStruct::targetPhaseRateTableNameString(), &m_targetPhaseRateTableName ).
- setRTTypeName( rtTypes::CustomTypes::groupNameRef ).
+ this->registerWrapper( viewKeyStruct::estimateWellSolutionString(), &m_estimateSolution ).
+ setApplyDefaultValue( 0 ).
setInputFlag( InputFlags::OPTIONAL ).
- setDescription( "Name of the phase rate table when the rate is a time dependent function" );
+ setDescription( "Flag to esitmate well solution prior to coupled reservoir and well solve." );
- registerWrapper( viewKeyStruct::targetMassRateTableNameString(), &m_targetMassRateTableName ).
- setRTTypeName( rtTypes::CustomTypes::groupNameRef ).
+ this->registerWrapper( viewKeyStruct::enableIsoThermalEstimatorString(), &m_enableIsoThermalEstimator ).
+ setApplyDefaultValue( 0 ).
setInputFlag( InputFlags::OPTIONAL ).
- setDescription( "Name of the mass rate table when the rate is a time dependent function" );
+ setDescription( "Flag to enable isothermal estimator prior to coupled reservoir and well solve." );
- registerWrapper( viewKeyStruct::statusTableNameString(), &m_statusTableName ).
- setRTTypeName( rtTypes::CustomTypes::groupNameRef ).
- setInputFlag( InputFlags::OPTIONAL ).
- setDescription( "Name of the well status table when the status of the well is a time dependent function. \n"
- "If the status function evaluates to a positive value at the current time, the well will be open otherwise the well will be shut." );
+ registerGroup( viewKeyStruct::wellNewtonSolverString(), &m_wellNewtonSolver );
addLogLevel< logInfo::WellControl >();
}
@@ -188,28 +156,109 @@ WellControls::WellControls( string const & name, Group * const parent )
WellControls::~WellControls()
{}
-void WellControls::switchToBHPControl( real64 const & val )
+Group * WellControls::createChild( string const & childKey, string const & childName )
{
- m_currentControl = Control::BHP;
- m_targetBHP = val;
+
+ Group * child = nullptr;
+ if( childKey == viewKeyStruct::minimumBHPConstraintString() )
+ {
+ MinimumBHPConstraint & bhpConstraint = registerGroup< MinimumBHPConstraint >( childName );
+ m_minBHPConstraint = &bhpConstraint;
+ child = &bhpConstraint;
+ }
+ else if( childKey == viewKeyStruct::maximumBHPConstraintString() )
+ {
+ MaximumBHPConstraint & bhpConstraint = registerGroup< MaximumBHPConstraint >( childName );
+ m_maxBHPConstraint = &bhpConstraint;
+ child = &bhpConstraint;
+ }
+ else if( childKey == viewKeyStruct::minimumWHPConstraintString() )
+ {
+ MinimumWHPConstraint & whpConstraint = registerGroup< MinimumWHPConstraint >( childName );
+ m_minWHPConstraint = &whpConstraint;
+ child = &whpConstraint;
+ }
+ else if( childKey == viewKeyStruct::productionPhaseVolumeRateConstraintString() )
+ {
+ ProductionConstraint< PhaseVolumeRateConstraint > & phaseConstraint = registerGroup< ProductionConstraint< PhaseVolumeRateConstraint > >( childName );
+ m_productionRateConstraintList.emplace_back( &phaseConstraint );
+ child = &phaseConstraint;
+ }
+ else if( childKey == viewKeyStruct::injectionPhaseVolumeRateConstraint() )
+ {
+ InjectionConstraint< PhaseVolumeRateConstraint > & phaseConstraint = registerGroup< InjectionConstraint< PhaseVolumeRateConstraint > >( childName );
+ m_injectionRateConstraintList.emplace_back( &phaseConstraint );
+ child = &phaseConstraint;
+ }
+ else if( childKey == viewKeyStruct::productionVolumeRateConstraint() )
+ {
+ ProductionConstraint< VolumeRateConstraint > & volConstraint = registerGroup< ProductionConstraint< VolumeRateConstraint > >( childName );
+ m_productionRateConstraintList.emplace_back( &volConstraint );
+ child = &volConstraint;
+ }
+ else if( childKey == viewKeyStruct::injectionVolumeRateConstraint() )
+ {
+ InjectionConstraint< VolumeRateConstraint > & volConstraint = registerGroup< InjectionConstraint< VolumeRateConstraint > >( childName );
+ m_injectionRateConstraintList.emplace_back( &volConstraint );
+ child = &volConstraint;
+ }
+ else if( childKey == viewKeyStruct::productionMassRateConstraint() )
+ {
+ ProductionConstraint< MassRateConstraint > & massConstraint = registerGroup< ProductionConstraint< MassRateConstraint > >( childName );
+ m_productionRateConstraintList.emplace_back( &massConstraint );
+ child = &massConstraint;
+
+ }
+ else if( childKey == viewKeyStruct::injectionMassRateConstraint() )
+ {
+ InjectionConstraint< MassRateConstraint > & massConstraint = registerGroup< InjectionConstraint< MassRateConstraint > >( childName );
+ m_injectionRateConstraintList.emplace_back( &massConstraint );
+ child = &massConstraint;
+ }
+ else if( childKey == viewKeyStruct::productionLiquidRateConstraint() )
+ {
+ ProductionConstraint< LiquidRateConstraint > & liquidConstraint = registerGroup< ProductionConstraint< LiquidRateConstraint > >( childName );
+ m_productionRateConstraintList.emplace_back( &liquidConstraint );
+ child = &liquidConstraint;
+ }
+
+ return child;
}
-void WellControls::switchToTotalRateControl( real64 const & val )
+void WellControls::createMinBHPConstraintForWHP()
{
- m_currentControl = Control::TOTALVOLRATE;
- m_targetTotalRate = val;
+ // Create constraint and set local pointer
+ MinimumBHPConstraint & bhpConstraint = registerGroup< MinimumBHPConstraint >( m_minWHPConstraint->getName()+"MinimumBHPConstraint" );
+ m_minBHPConstraintForWHP = &bhpConstraint;
+ // Set properties from the original minBHP constraint
+ m_minBHPConstraintForWHP->setReferenceElevation( m_minBHPConstraint->getReferenceElevation() );
+ m_minBHPConstraintForWHP->setReferenceGravityCoef ( m_minBHPConstraint->getReferenceGravityCoef() );
+ // Set to inactive. WHP estimator solve will set status
+ m_minBHPConstraintForWHP->setConstraintActive( false );
}
-
-void WellControls::switchToPhaseRateControl( real64 const & val )
+void WellControls::createMaxLiquidConstraintForWHP()
{
- m_currentControl = Control::PHASEVOLRATE;
- m_targetPhaseRate = val;
+ // Create constraint and set local pointer
+ ProductionConstraint< LiquidRateConstraint > & liquidConstraint = registerGroup< ProductionConstraint< LiquidRateConstraint > >( m_minWHPConstraint->getName()+"LiquidProductionConstraint" );
+ m_maxLiquidConstraintForWHP = &liquidConstraint;
+ // Set properties from VFP table
+ FunctionManager & functionManager = FunctionManager::getInstance();
+ const PipeFlowTableFunction & m_flowTable = functionManager.getGroup< PipeFlowTableFunction const >( m_minWHPConstraint->getFlowTableName());
+ string_array ratePhases = m_flowTable.getRatePhases();
+ m_maxLiquidConstraintForWHP->setPhaseNames( ratePhases );
+ m_maxLiquidConstraintForWHP->validateLiquidType( getMultiFluidSeparator());
+ // WHP estimator solve will set status
+ m_maxLiquidConstraintForWHP->setConstraintActive( false );
}
-void WellControls::switchToMassRateControl( real64 const & val )
+void WellControls::expandObjectCatalogs()
{
- m_currentControl = Control::MASSRATE;
- m_targetMassRate = val;
+ // During schema generation, register one of each type derived from ConstitutiveBase here
+ for( auto & catalogIter: WellConstraintBase::getCatalog())
+ {
+ createChild( catalogIter.first, catalogIter.first );
+ }
+ // tjbcreateChild( WellNewtonSolver::catalogName(), WellNewtonSolver::catalogName() );
}
namespace
@@ -234,9 +283,30 @@ TableFunction * createWellTable( string const & tableName,
}
}
+void WellControls::registerWellDataOnMesh( WellElementSubRegion & subRegion )
+{
+ std::string const & regionName = subRegion.getName();
+ std::string addrWithMask( regionName );
+ std::size_t pos = addrWithMask.find( "UniqueSubRegion" );
+ std::string addr = addrWithMask.substr( 0, pos );
+ m_targetRegionNames.push_back( addr );
+
+ registerWrapper< real64 >( viewKeyStruct::currentBHPString() );
+ if( hasMinimumWHPConstraint() )
+ registerWrapper< real64 >( viewKeyStruct::currentWHPString() );
+ registerWrapper< real64 >( viewKeyStruct::currentVolRateString() );
+
+ registerWrapper< array1d< real64 > >( viewKeyStruct::currentPhaseVolRateString() ).
+ setSizedFromParent( 0 ).
+ reference().resizeDimension< 0 >( m_numPhases );
+ registerWrapper< real64 >( viewKeyStruct::massDensityString() );
+ registerWrapper< real64 >( viewKeyStruct::currentTotalVolRateString() );
+ registerWrapper< real64 >( viewKeyStruct::currentMassRateString() );
+}
void WellControls::postInputInitialization()
{
+ Group::postInputInitialization();
// 0) Assign the value of the current well control
// When the simulation starts from a restart file, we don't want to use the inputControl,
// because the control may have switched in the simulation that generated the restart
@@ -249,118 +319,13 @@ void WellControls::postInputInitialization()
m_currentControl = m_inputControl;
}
- // 1.a) check target BHP
- GEOS_THROW_IF( m_targetBHP < 0,
- "Target bottom-hole pressure is negative",
- InputError, getWrapperDataContext( viewKeyStruct::targetBHPString() ) );
-
- // 1.b) check target rates
- GEOS_THROW_IF( m_targetTotalRate < 0,
- "Target rate is negative",
- InputError, getWrapperDataContext( viewKeyStruct::targetTotalRateString() ) );
-
- GEOS_THROW_IF( m_targetPhaseRate < 0,
- "Target oil rate is negative",
- InputError, getWrapperDataContext( viewKeyStruct::targetPhaseRateString() ) );
-
- GEOS_THROW_IF( m_targetMassRate < 0,
- "Target mass rate is negative",
- InputError, getWrapperDataContext( viewKeyStruct::targetMassRateString() ) );
-
- GEOS_THROW_IF( (m_injectionStream.empty() && m_injectionTemperature >= 0) ||
- (!m_injectionStream.empty() && m_injectionTemperature < 0),
- "Both "
- << viewKeyStruct::injectionStreamString() << " and " << viewKeyStruct::injectionTemperatureString()
- << " must be specified for multiphase simulations",
- InputError, getDataContext() );
-
- // 1.c) Set the multiplier for the rates
- if( isProducer() )
- {
- m_rateSign = -1.0;
- }
- else
- {
- m_rateSign = 1.0;
- }
-
- // 2) check injection stream
- if( !m_injectionStream.empty())
- {
- real64 sum = 0.0;
- for( localIndex ic = 0; ic < m_injectionStream.size(); ++ic )
- {
- GEOS_ERROR_IF( m_injectionStream[ic] < 0.0 || m_injectionStream[ic] > 1.0,
- "Invalid injection stream",
- getWrapperDataContext( viewKeyStruct::injectionStreamString() ) );
- sum += m_injectionStream[ic];
- }
- GEOS_THROW_IF( LvArray::math::abs( 1.0 - sum ) > std::numeric_limits< real64 >::epsilon(),
- "Invalid injection stream",
- InputError, getWrapperDataContext( viewKeyStruct::injectionStreamString() ) );
- }
// 3) check the flag for surface / reservoir conditions
GEOS_THROW_IF( m_useSurfaceConditions != 0 && m_useSurfaceConditions != 1,
"The flag to select surface/reservoir conditions must be equal to 0 or 1",
InputError, getWrapperDataContext( viewKeyStruct::useSurfaceConditionsString() ) );
- // 4) check that at least one rate constraint has been defined
- GEOS_THROW_IF( ((m_targetPhaseRate <= 0.0 && m_targetPhaseRateTableName.empty()) &&
- (m_targetMassRate <= 0.0 && m_targetMassRateTableName.empty()) &&
- (m_targetTotalRate <= 0.0 && m_targetTotalRateTableName.empty())),
- "You need to specify a phase, mass, or total rate constraint. \n" <<
- "The phase rate constraint can be specified using " <<
- "either " << viewKeyStruct::targetPhaseRateString() <<
- " or " << viewKeyStruct::targetPhaseRateTableNameString() << ".\n" <<
- "The total rate constraint can be specified using " << ".\n" <<
- "either " << viewKeyStruct::targetTotalRateString() <<
- " or " << viewKeyStruct::targetTotalRateTableNameString() << ".\n" <<
- "The mass rate constraint can be specified using " <<
- "either " << viewKeyStruct::targetMassRateString() <<
- " or " << viewKeyStruct::targetMassRateTableNameString(),
- InputError, getDataContext() );
- // 5) check whether redundant information has been provided
- GEOS_THROW_IF( ((m_targetPhaseRate > 0.0 && !m_targetPhaseRateTableName.empty())),
- "You have provided redundant information for well phase rate." <<
- " The keywords " << viewKeyStruct::targetPhaseRateString() << " and " << viewKeyStruct::targetPhaseRateTableNameString() << " cannot be specified together",
- InputError, getDataContext() );
-
- GEOS_THROW_IF( ((m_targetTotalRate > 0.0 && !m_targetTotalRateTableName.empty())),
- "You have provided redundant information for well total rate." <<
- " The keywords " << viewKeyStruct::targetTotalRateString() << " and " << viewKeyStruct::targetTotalRateTableNameString() << " cannot be specified together",
- InputError, getDataContext() );
-
- GEOS_THROW_IF( ((m_targetBHP > 0.0 && !m_targetBHPTableName.empty())),
- "You have provided redundant information for well BHP." <<
- " The keywords " << viewKeyStruct::targetBHPString() << " and " << viewKeyStruct::targetBHPTableNameString() << " cannot be specified together",
- InputError, getDataContext() );
-
- GEOS_THROW_IF( ((m_targetMassRate > 0.0 && !m_targetMassRateTableName.empty())),
- "You have provided redundant information for well mass rate." <<
- " The keywords " << viewKeyStruct::targetMassRateString() << " and " << viewKeyStruct::targetMassRateTableNameString() << " cannot be specified together",
- InputError, getDataContext() );
-
- GEOS_THROW_IF( ((m_targetMassRate > 0.0 && m_useSurfaceConditions==0)),
- "Option only valid if useSurfaceConditions set to 1",
- InputError, getDataContext() );
-
- // 6.1) If the well is under BHP control then the BHP must be specified.
- // Otherwise the BHP will be set to a default value.
- if( m_currentControl == Control::BHP )
- {
- GEOS_THROW_IF( ((m_targetBHP <= 0.0 && m_targetBHPTableName.empty())),
- "You have to provide well BHP by specifying either "
- << viewKeyStruct::targetBHPString() << " or " << viewKeyStruct::targetBHPTableNameString(),
- InputError, getDataContext() );
- }
- else if( m_targetBHP <= 0.0 && m_targetBHPTableName.empty() )
- {
- m_targetBHP = isProducer() ? WellConstants::defaultProducerBHP : WellConstants::defaultInjectorBHP;
- GEOS_LOG_LEVEL_RANK_0( logInfo::WellControl,
- GEOS_FMT( "WellControls {}: Setting {} to default value {}", getDataContext(), viewKeyStruct::targetBHPString(), m_targetBHP ));
- }
// 6.2) Check incoherent information
@@ -378,77 +343,12 @@ void WellControls::postInputInitialization()
// 8) Make sure that the initial pressure coefficient is positive
GEOS_THROW_IF( m_initialPressureCoefficient < 0,
- viewKeyStruct::initialPressureCoefficientString() <<
- "This tuning coefficient is negative",
+ getWrapperDataContext( viewKeyStruct::initialPressureCoefficientString() ) <<
+ ": This tuning coefficient is negative",
InputError, getWrapperDataContext( viewKeyStruct::initialPressureCoefficientString() ) );
- // 9) Create time-dependent BHP table
- if( m_targetBHPTableName.empty() )
- {
- m_targetBHPTableName = getName()+"_ConstantBHP_table";
- m_targetBHPTable = createWellTable( m_targetBHPTableName, m_targetBHP );
- }
- else
- {
- FunctionManager & functionManager = FunctionManager::getInstance();
- m_targetBHPTable = &(functionManager.getGroup< TableFunction const >( m_targetBHPTableName ));
-
- GEOS_THROW_IF( m_targetBHPTable->getInterpolationMethod() != TableFunction::InterpolationType::Lower,
- "The interpolation method for the time-dependent BHP table "
- << m_targetBHPTable->getName() << " should be TableFunction::InterpolationType::Lower",
- InputError, getDataContext() );
- }
-
- // 10) Create time-dependent total rate table
- if( m_targetTotalRateTableName.empty() )
- {
- m_targetTotalRateTableName = getName()+"_ConstantTotalRate_table";
- m_targetTotalRateTable = createWellTable( m_targetTotalRateTableName, m_targetTotalRate );
- }
- else
- {
- FunctionManager & functionManager = FunctionManager::getInstance();
- m_targetTotalRateTable = &(functionManager.getGroup< TableFunction const >( m_targetTotalRateTableName ));
-
- GEOS_THROW_IF( m_targetTotalRateTable->getInterpolationMethod() != TableFunction::InterpolationType::Lower,
- "The interpolation method for the time-dependent total rate table "
- << m_targetTotalRateTable->getName() << " should be TableFunction::InterpolationType::Lower",
- InputError, getDataContext() );
- }
-
- // 11) Create time-dependent phase rate table
- if( m_targetPhaseRateTableName.empty() )
- {
- m_targetPhaseRateTableName = getName()+"_ConstantPhaseRate_table";
- m_targetPhaseRateTable = createWellTable( m_targetPhaseRateTableName, m_targetPhaseRate );
- }
- else
- {
- FunctionManager & functionManager = FunctionManager::getInstance();
- m_targetPhaseRateTable = &(functionManager.getGroup< TableFunction const >( m_targetPhaseRateTableName ));
-
- GEOS_THROW_IF( m_targetPhaseRateTable->getInterpolationMethod() != TableFunction::InterpolationType::Lower,
- "The interpolation method for the time-dependent phase rate table "
- << m_targetPhaseRateTable->getName() << " should be TableFunction::InterpolationType::Lower",
- InputError, getDataContext() );
- }
- // Create time-dependent mass rate table
- if( m_targetMassRateTableName.empty() )
- {
- m_targetMassRateTableName = getName()+"_ConstantMassRate_table";
- m_targetMassRateTable = createWellTable( m_targetMassRateTableName, m_targetMassRate );
- }
- else
- {
- FunctionManager & functionManager = FunctionManager::getInstance();
- m_targetMassRateTable = &(functionManager.getGroup< TableFunction const >( m_targetMassRateTableName ));
- GEOS_THROW_IF( m_targetMassRateTable->getInterpolationMethod() != TableFunction::InterpolationType::Lower,
- "The interpolation method for the time-dependent mass rate table "
- << m_targetMassRateTable->getName() << " should be TableFunction::InterpolationType::Lower",
- InputError, getDataContext() );
- }
// 12) Create the time-dependent well status table
if( m_statusTableName.empty())
{
@@ -473,37 +373,110 @@ void WellControls::postInputInitialization()
}
}
-
+void WellControls::postRestartInitialization( )
+{}
void WellControls::setWellStatus( real64 const & currentTime, WellControls::Status status )
{
m_wellStatus = status;
if( m_wellStatus == WellControls::Status::OPEN )
{
-
- if( isZero( getTargetTotalRate( currentTime ) ) && isZero( getTargetPhaseRate( currentTime ) )
- && isZero( getTargetMassRate( currentTime ) ) )
+ if( isProducer())
{
- m_wellStatus = WellControls::Status::CLOSED;
+ std::vector< WellConstraintBase * > const constraints = getProdRateConstraints();
+ for( auto const & constraint : constraints )
+ {
+ if( isZero( constraint->getConstraintValue( currentTime ) ) )
+ {
+ m_wellStatus = WellControls::Status::CLOSED;
+ m_currentConstraint=nullptr;
+ break;
+ }
+ }
+ }
+ else
+ {
+ std::vector< WellConstraintBase * > const constraints = getInjRateConstraints();
+ for( auto const & constraint : constraints )
+ {
+ if( isZero( constraint->getConstraintValue( currentTime ) ) )
+ {
+ m_wellStatus = WellControls::Status::CLOSED;
+ m_currentConstraint=nullptr;
+ break;
+ }
+ }
}
+
if( m_statusTable->evaluate( ¤tTime ) < LvArray::NumericLimits< real64 >::epsilon )
{
m_wellStatus = WellControls::Status::CLOSED;
+ m_currentConstraint=nullptr;
}
}
}
+real64 WellControls::setNextDt( real64 const & currentTime,
+ real64 const & currentDt,
+ WellElementSubRegion & subRegion )
+{
+ real64 nextDt = currentDt;
+ real64 nextDt_perf=nextDt;
+ // Find min dt from perf status tables
+ PerforationData & perforationData = *subRegion.getPerforationData();
+ string_array const & perfStatusTableName = perforationData.getPerfStatusTableName();
+ FunctionManager & functionManager = FunctionManager::getInstance();
+ // Get dt for local perforations
+ for( integer i=0; i( perfStatusTableName[i] );
+ setNextDtFromTable( tableFunction, currentTime, nextDt_perf );
+ }
+ nextDt = MpiWrapper::min< real64 >( nextDt_perf );
+ // Find min dt including rate and status tables
+ real64 const nextDt_orig = nextDt;
+ setNextDtFromTables( currentTime, nextDt );
+ //if( m_nonlinearSolverParameters.getLogLevel() > 0 && nextDt < nextDt_orig )
+ if( getLogLevel() > 0 && nextDt < nextDt_orig )
+ GEOS_LOG_RANK_0( GEOS_FMT( "{}: next time step based on tables coordinates = {}", getName(), nextDt ));
+ return nextDt;
+}
bool WellControls::isWellOpen() const
{
return getWellStatus() == WellControls::Status::OPEN;
}
+void WellControls::setWellState( bool open )
+{
+ m_wellOpen = open;
+}
+
+bool WellControls::getWellState() const
+{
+ return m_wellOpen;
+}
void WellControls::setNextDtFromTables( real64 const & currentTime, real64 & nextDt )
{
- WellControls::setNextDtFromTable( m_targetBHPTable, currentTime, nextDt );
- WellControls::setNextDtFromTable( m_targetMassRateTable, currentTime, nextDt );
- WellControls::setNextDtFromTable( m_targetPhaseRateTable, currentTime, nextDt );
- WellControls::setNextDtFromTable( m_targetTotalRateTable, currentTime, nextDt );
+ if( isProducer() )
+ {
+ if( getMinBHPConstraint() != nullptr )
+ {
+ getMinBHPConstraint()->setNextDtFromTables( currentTime, nextDt );
+ }
+ for( auto const & constraint : m_productionRateConstraintList )
+ {
+ constraint->setNextDtFromTables( currentTime, nextDt );
+ }
+ }
+ else
+ {
+ getMaxBHPConstraint()->setNextDtFromTables( currentTime, nextDt );
+ for( auto const & constraint : m_injectionRateConstraintList )
+ {
+ constraint->setNextDtFromTables( currentTime, nextDt );
+ }
+ }
+
WellControls::setNextDtFromTable( m_statusTable, currentTime, nextDt );
}
@@ -521,4 +494,623 @@ void WellControls::setNextDtFromTable( TableFunction const * table, real64 const
}
}
+real64 WellControls::getTargetBHP( real64 const & targetTime ) const
+{
+ if( isProducer())
+ {
+ return m_minBHPConstraint->getConstraintValue( targetTime );
+ }
+ return m_maxBHPConstraint->getConstraintValue( targetTime );
+}
+
+
+real64 WellControls::getInjectionTemperature() const
+{
+ real64 injectionTemperature = 0.0;
+ this->forInjectionConstraints< InjectionConstraint< PhaseVolumeRateConstraint >, InjectionConstraint< VolumeRateConstraint >, InjectionConstraint< MassRateConstraint > >( [&] ( auto & constraint )
+ {
+ if( constraint.isConstraintActive())
+ {
+ injectionTemperature = constraint.getInjectionTemperature();
+ return;
+ }
+ } );
+ return injectionTemperature;
+}
+
+
+arrayView1d< real64 const > WellControls::getInjectionStream() const
+{
+ arrayView1d< real64 const > injectionStream;
+ forInjectionConstraints< InjectionConstraint< PhaseVolumeRateConstraint >, InjectionConstraint< VolumeRateConstraint >, InjectionConstraint< MassRateConstraint > >( [&] ( auto & constraint )
+ {
+ if( constraint.isConstraintActive() )
+ {
+ injectionStream = constraint.getInjectionStream();
+ return;
+ }
+ } );
+
+ return injectionStream;
+}
+
+integer WellControls::getConstraintPhaseIndex() const
+{
+ integer phaseIndex = -1;
+
+ if( isProducer() )
+ {
+ forProductionConstraints< ProductionConstraint< PhaseVolumeRateConstraint > >( [&] ( auto & constraint )
+ {
+ if( constraint.isConstraintActive() )
+ {
+ phaseIndex = constraint.getPhaseIndex();
+ }
+ } );
+ }
+ else
+ {
+ forInjectionConstraints< InjectionConstraint< PhaseVolumeRateConstraint > >( [&] ( auto & constraint )
+ {
+ if( constraint.isConstraintActive() )
+ {
+ phaseIndex = constraint.getPhaseIndex();
+ }
+ } );
+ }
+
+ return phaseIndex;
+}
+
+real64 WellControls::getReferenceElevation() const
+{
+ if( isProducer () )
+ {
+ return getMinBHPConstraint()->getReferenceElevation();
+ }
+ return getMaxBHPConstraint()->getReferenceElevation();
+}
+
+void WellControls::implicitStepSetup( real64 const & time_n,
+ real64 const & GEOS_UNUSED_PARAM( dt ),
+ ElementRegionManager & elemManager,
+ WellElementSubRegion & subRegion )
+{
+
+ GEOS_UNUSED_VAR( elemManager );
+ // Set perforation status
+ setPerforationStatus( time_n, subRegion );
+}
+void WellControls::setPerforationStatus( real64 const & time_n, WellElementSubRegion & subRegion )
+{
+ FunctionManager & functionManager = FunctionManager::getInstance();
+
+ // Set perforation status
+
+ PerforationData & perforationData = *subRegion.getPerforationData();
+ string_array const & perfStatusTableName = perforationData.getPerfStatusTableName();
+ arrayView1d< integer > perfStatus = perforationData.getLocalPerfStatus();
+ // for now set to open
+ for( integer i=0; i( perfStatusTableName[i] );
+ perfStatus[i]=PerforationData::PerforationStatus::OPEN;
+ if( tableFunction->evaluate( &time_n ) < LvArray::NumericLimits< real64 >::epsilon )
+ {
+ perfStatus[i]=PerforationData::PerforationStatus::CLOSED;
+ }
+ }
+
+ array1d< localIndex > const perfWellElemIndex = perforationData.getField< fields::perforation::wellElementIndex >();
+ // global index local elements (size == subregion.size)
+ arrayView1d< globalIndex const > globalWellElementIndex = subRegion.getGlobalWellElementIndex();
+
+ arrayView1d< integer const > const elemGhostRank = subRegion.ghostRank();
+ array1d< integer > & currentStatus = subRegion.getWellElementStatus();
+ // Local elements
+ array1d< integer > & localElemStatus = subRegion.getWellLocalElementStatus();
+
+ integer numLocalElements = subRegion.getNumLocalElements();
+ array1d< integer > segStatus( numLocalElements );
+
+ // Local perforations
+ for( integer j = 0; j < perforationData.size(); j++ )
+ {
+ localIndex const iwelem = perfWellElemIndex[j];
+ if( elemGhostRank[iwelem] < 0 )
+ {
+ if( perfStatus[j] )
+ {
+ segStatus[iwelem] +=1;
+ }
+ }
+ }
+ // Broadcast segment status so all cores have same well status
+ subRegion.setElementStatus( segStatus );
+ integer numOpenElements = 0;
+ array1d< integer > const & updatedStatus = subRegion.getWellElementStatus();
+ for( integer i=0; i0 ? setWellStatus( time_n, WellControls::Status::OPEN ) : setWellStatus( time_n, WellControls::Status::CLOSED );
+
+
+ // Set local well element status array
+ for( integer i=0; i const wellElemLocation = subRegion.getElementCenter();
+ arrayView1d< real64 > const wellElemGravCoef = subRegion.getField< fields::well::gravityCoefficient >();
+
+ arrayView2d< real64 const > const perfLocation = perforationData.getField< fields::perforation::location >();
+ arrayView1d< real64 > const perfGravCoef = perforationData.getField< fields::well::gravityCoefficient >();
+
+ forAll< serialPolicy >( perforationData.size(), [=]( localIndex const iperf )
+ {
+ // precompute the depth of the perforations
+ perfGravCoef[iperf] = LvArray::tensorOps::AiBi< 3 >( perfLocation[iperf], gravVector );
+ } );
+
+ forAll< serialPolicy >( subRegion.size(), [=]( localIndex const iwelem )
+ {
+ // precompute the depth of the well elements
+ wellElemGravCoef[iwelem] = LvArray::tensorOps::AiBi< 3 >( wellElemLocation[iwelem], gravVector );
+ } );
+
+ forSubGroups< BHPConstraint >( [&]( auto & constraint )
+ {
+ // set the reference well element where the BHP control is applied
+ real64 const refElev1 = constraint.getReferenceElevation();
+ constraint.setReferenceGravityCoef( refElev1 * gravVector[2] );
+ } );
+ // set the reference well element where the BHP control is applied
+ setReferenceGravityCoef( refElev * gravVector[2] ); // tjb remove
+}
+
+void WellControls::selectWellConstraint( real64 const & time_n,
+ real64 const & dt,
+ integer const cycleNumber,
+ integer const coupledIterationNumber,
+ DomainPartition & domain,
+ MeshLevel & meshLevel,
+ ElementRegionManager & elemManager,
+ WellElementSubRegion & subRegion,
+ DofManager const & dofManager )
+
+{
+
+ // Well state estimated from reservoir conditions
+ if( isWellOpen() )
+ {
+ if( !getWellState() )
+ {
+ setWellState( 1 );
+
+ initializeWell( domain, meshLevel, subRegion, time_n );
+ }
+ }
+ else
+ {
+ setWellState( 0 );
+ }
+
+ bool useEstimator = coupledIterationNumber < estimateSolution();
+
+
+
+ if( getWellState())
+ {
+ if( useEstimator )
+ {
+ // Estimate well solution prior to coupled solve
+ std::cout << "Estimating well solution for well " << subRegion.getName() << " at time " << time_n << std::endl;
+ evaluateConstraints( time_n,
+ dt,
+ cycleNumber,
+ coupledIterationNumber,
+ domain,
+ meshLevel,
+ elemManager,
+ subRegion,
+ dofManager );
+ }
+ else
+ {
+ // Evaluate well constraints based on current solution
+ std::cout << "Evaluating well constraints for well " << subRegion.getName() << " at time " << time_n << std::endl;
+ evaluateConstraints( time_n,
+ subRegion );
+ }
+
+ // If a well is opened and then timestep is cut resulting in the well being shut, if the well is opened
+ // the well initialization code requires control type to by synced
+ integer owner = -1;
+ // Only subregion owner evaluates well control and control changes need to be broadcast to all ranks
+ if( subRegion.isLocallyOwned() )
+ {
+ owner = MpiWrapper::commRank( MPI_COMM_GEOS );
+ }
+ owner = MpiWrapper::max( owner );
+ WellControls::Control wellControl = getControl();
+ MpiWrapper::broadcast( wellControl, owner );
+ setControl( wellControl );
+ }
+
+
+}
+
+bool WellControls::evaluateConstraints( real64 const & time_n,
+ WellElementSubRegion & subRegion )
+{
+
+ // create list of all constraints to process
+ std::vector< WellConstraintBase * > constraintList;
+ if( isProducer() )
+ {
+ constraintList = getProdRateConstraints();
+ //if constraints arent updated with estimator and WHP is binding dont check allow constraint to be switch during remainder of timestep
+ if( hasMinimumWHPConstraint() )
+ {
+ MinimumBHPConstraint * minBHPForWHP = getMinimumBHPConstraintForWHP();
+ if( minBHPForWHP != nullptr && minBHPForWHP->isConstraintActive())
+ {
+ std::cout << "we not active " << subRegion.getName() << " Constraint " << minBHPForWHP->getName() << " active " << minBHPForWHP->isConstraintActive() <<
+ " value " << minBHPForWHP->getConstraintValue( time_n ) << std::endl;
+ constraintList.insert( constraintList.begin(), minBHPForWHP );
+
+ }
+ else
+ {
+ ProductionConstraint< LiquidRateConstraint > * maxLiqForWHP = getMaxLiquidConstraintForWHP();
+ if( maxLiqForWHP != nullptr && maxLiqForWHP->isConstraintActive())
+ {
+ std::cout << "we not active " << subRegion.getName() << " Constraint " << maxLiqForWHP->getName() << " active " << maxLiqForWHP->isConstraintActive() <<
+ " value " << maxLiqForWHP->getConstraintValue( time_n ) << std::endl;
+ constraintList.insert( constraintList.begin(), maxLiqForWHP );
+
+ }
+ else
+ {
+ // Solve minimum bhp constraint first
+ if( getMinBHPConstraint()->isConstraintActive() )
+ {
+ std::cout << "we not active " << subRegion.getName() << " Constraint add minbp " << std::endl;
+ constraintList.insert( constraintList.begin(), getMinBHPConstraint() );
+ }
+ }
+ }
+ }
+ else
+ {
+ std::cout << "we not active " << subRegion.getName() << " Constraint " << getMinBHPConstraint()->getName() << " active " << getMinBHPConstraint()->isConstraintActive() <<
+ " value " << getMinBHPConstraint()->getConstraintValue( time_n ) << std::endl;
+ constraintList.insert( constraintList.begin(), getMinBHPConstraint() );
+ }
+ }
+ else
+ {
+ constraintList = getInjRateConstraints();
+ // Solve maximum bhp constraint first;
+ constraintList.insert( constraintList.begin(), getMaxBHPConstraint() );
+ }
+
+ // Get current constraint
+ WellConstraintBase * limitingConstraint = nullptr;
+ for( auto & constraint : constraintList )
+ {
+ if( constraint->getName() == getCurrentConstraint()->getName())
+ {
+ limitingConstraint = constraint;
+ GEOS_LOG_RANK_IF ( getLogLevel() > 4 && subRegion.isLocallyOwned(),
+ " Well " << subRegion.getName() << " Limiting Constraint " << limitingConstraint->getName() << " " << limitingConstraint->bottomHolePressure() << " " <<
+ limitingConstraint->phaseVolumeRates() << " " <<
+ limitingConstraint->totalVolumeRate() << " " << limitingConstraint->massRate());
+ }
+ }
+ // Check current against other constraints
+ std::cout << "Current constraint for well " << subRegion.getName() << " is " << limitingConstraint->getName() << std::endl;
+ constraintList.erase(std::remove( constraintList.begin(), constraintList.end(), limitingConstraint ), constraintList.end());
+ std::vector< int > constraintChecked( constraintList.size(), 0 );
+ for( int i = 0; i < static_cast< int >(constraintList.size()); ++i )
+ {
+ auto & constraint = constraintList[i];
+ if( limitingConstraint->getName() != constraint->getName())
+ {
+ if( !constraintChecked[i] && constraint->checkViolation( *limitingConstraint, time_n ) )
+ {
+ limitingConstraint = constraint;
+ setControl( static_cast< WellControls::Control >(constraint->getControl()) ); // tjb old
+ setCurrentConstraint( constraint );
+ GEOS_LOG_RANK_IF ( subRegion.isLocallyOwned(),
+ " Well " << subRegion.getName() << " Control switch " << constraint->getName() << " " << constraint->getConstraintValue( time_n ) );
+ }
+ }
+ constraintChecked[i]=1;
+ }
+ GEOS_LOG_RANK_IF ( getLogLevel() > 4 && subRegion.isLocallyOwned(),
+ " Well " << subRegion.getName() << " Limiting Constraint " << limitingConstraint->getName() << " " << limitingConstraint->bottomHolePressure() << " " <<
+ limitingConstraint->phaseVolumeRates() << " " <<
+ limitingConstraint->totalVolumeRate() << " " << limitingConstraint->massRate());
+
+ return true;
+}
+
+bool WellControls::evaluateConstraints( real64 const & time_n,
+ real64 const & dt,
+ integer const cycleNumber,
+ integer const coupledIterationNumber,
+ DomainPartition & domain,
+ MeshLevel & mesh,
+ ElementRegionManager & elemManager,
+ WellElementSubRegion & subRegion,
+ DofManager const & dofManager )
+{
+
+
+ // create list of all constraints to solve
+ // note that initializeWells sets the initial constraint
+ // tjb reactive control schema to allow use to set if needed
+ std::vector< WellConstraintBase * > constraintList;
+ WellConstraintBase * limitingConstraint = getCurrentConstraint();
+ if( isProducer() )
+ {
+ constraintList = getProdRateConstraints();
+
+ if( limitingConstraint->getControl() != ConstraintTypeId::BHP )
+ {
+ { // set BHP constraint to be first constraint evaluated
+ if( getMinBHPConstraint()->isConstraintActive() )
+ {
+ constraintList.insert( constraintList.begin(), getMinBHPConstraint() );
+ }
+ }
+ }
+ // Solve minimum bhp constraint first
+ if( false && getMinBHPConstraint()->isConstraintActive() )
+ {
+ // this is related to WHP option which introduces a new BHP constraint
+ limitingConstraint = getMinBHPConstraint();
+ }
+ else if( limitingConstraint == nullptr )
+ {
+ limitingConstraint = constraintList[0];
+ }
+ }
+ else
+ {
+ constraintList = getInjRateConstraints();
+ // remove the limiting constraint from the list if present
+ {
+ if( limitingConstraint->getControl() != ConstraintTypeId::BHP )
+ { // remove from list and add BHP constraint
+ //auto it = std::find( constraintList.begin(), constraintList.end(), limitingConstraint );
+ //if( it != constraintList.end() )
+ //{
+ // constraintList.erase( it );
+ //}
+ constraintList.push_back( getMaxBHPConstraint() );
+ }
+ }
+ }
+ if( isoThermalEstimatorEnabled() )
+ {
+ enableThermalEffects( false );
+ solveConstraint ( limitingConstraint, time_n,
+ dt,
+ cycleNumber,
+ coupledIterationNumber,
+ domain,
+ mesh,
+ elemManager,
+ subRegion,
+ dofManager );
+ enableThermalEffects( true );
+ }
+ solveConstraint ( limitingConstraint, time_n,
+ dt,
+ cycleNumber,
+ coupledIterationNumber,
+ domain,
+ mesh,
+ elemManager,
+ subRegion,
+ dofManager );
+
+ constraintList.erase(std::remove( constraintList.begin(), constraintList.end(), limitingConstraint ), constraintList.end());
+
+
+ std::vector< int > constraintChecked( constraintList.size(), 0 );
+ for( int i = 0; i < static_cast< int >(constraintList.size()); ++i )
+ {
+ auto & constraint = constraintList[i];
+ GEOS_LOG_RANK_IF ( getLogLevel() > 4 && subRegion.isLocallyOwned(),
+ " Well " << subRegion.getName() << " Constraint " << constraint->getName() << " active " << constraint->isConstraintActive() <<
+ " value " << constraint->getConstraintValue( time_n ) );
+ if( limitingConstraint->getName() != constraint->getName())
+ {
+ if( !constraintChecked[i] &&constraint->isConstraintActive() && constraint->checkViolation( *limitingConstraint, time_n ))
+ {
+ limitingConstraint=constraint;
+ setControl( static_cast< WellControls::Control >(constraint->getControl()) ); // tjb old
+ setCurrentConstraint( limitingConstraint );
+ GEOS_LOG_RANK_IF ( getLogLevel() > 4 && subRegion.isLocallyOwned(),
+ " Well " << subRegion.getName() << " New Limiting Constraint " << constraint->getName() << " active " << constraint->isConstraintActive() <<
+ " value " << constraint->getConstraintValue( time_n ) );
+ solveConstraint ( constraint, time_n,
+ dt,
+ cycleNumber,
+ coupledIterationNumber,
+ domain,
+ mesh,
+ elemManager,
+ subRegion,
+ dofManager );
+ // tjb. this is likely not needed. set in update state
+ constraint->setBHP ( getReference< real64 >( viewKeyStruct::currentBHPString() ));
+ if( isCompositional())
+ {
+ constraint->setPhaseVolumeRates ( getReference< array1d< real64 > >(
+ viewKeyStruct::currentPhaseVolRateString() ) );
+ constraint->setTotalVolumeRate ( getReference< real64 >(
+ viewKeyStruct::currentTotalVolRateString() ));
+ constraint->setMassRate( getReference< real64 >( viewKeyStruct::currentMassRateString() ));
+ }
+ else
+ {
+ constraint->setBHP ( getReference< real64 >( viewKeyStruct::currentBHPString() ));
+ constraint->setTotalVolumeRate ( getReference< real64 >(
+ viewKeyStruct::currentVolRateString() ));
+ }
+
+ }
+ }
+ constraintChecked[i]=1;
+ }
+ solveConstraint ( limitingConstraint, time_n,
+ dt,
+ cycleNumber,
+ coupledIterationNumber,
+ domain,
+ mesh,
+ elemManager,
+ subRegion,
+ dofManager );
+
+ GEOS_LOG_RANK_IF ( getLogLevel() > 4 && subRegion.isLocallyOwned(),
+ " Well " << subRegion.getName() << " Limiting Constraint " << limitingConstraint->getName() << " " << limitingConstraint->bottomHolePressure() << " " << limitingConstraint->phaseVolumeRates() << " " <<
+ limitingConstraint->totalVolumeRate() << " " << limitingConstraint->massRate());
+
+ if( hasMinimumWHPConstraint() )
+ {
+ bool whpLimiting= solveWHPConstraint ( time_n,
+ dt,
+ cycleNumber,
+ coupledIterationNumber,
+ domain,
+ mesh,
+ elemManager,
+ subRegion );
+
+ if( whpLimiting )
+ {
+ // WHP option can use different constraint as limiting constraint, so need to update the rates for the current limiting constraint
+ limitingConstraint= getCurrentConstraint();
+
+ GEOS_LOG_RANK_IF ( getLogLevel() > 4 && subRegion.isLocallyOwned(),
+ " Well " << subRegion.getName() << " Limiting Constraint " << limitingConstraint->getName() << " " << limitingConstraint->bottomHolePressure() << " " << limitingConstraint->phaseVolumeRates() << " " <<
+ limitingConstraint->totalVolumeRate() << " " << limitingConstraint->massRate());
+
+ }
+ }
+ return true;
+}
+
+void WellControls::setupWellDofs( DomainPartition & domain, WellElementRegion & wellElementRegion,
+ string const & meshBodyName, MeshLevel const & meshLevel )
+{
+ if( !m_dofManagerInitialized )
+ {
+ m_dofManagerInitialized=true;
+ m_wellNewtonSolver.setupSystem( *this, domain, meshBodyName, meshLevel, wellElementRegion );
+
+ }
+}
+
+
+void WellControls::solveConstraint( WellConstraintBase *constraint,
+ real64 const & time_n,
+ real64 const & dt,
+ integer const cycleNumber,
+ integer const coupledIterationNumber,
+ DomainPartition & domain,
+ MeshLevel & mesh,
+ ElementRegionManager & elemManager,
+ WellElementSubRegion & subRegion,
+ DofManager const & dofManager )
+{
+ GEOS_UNUSED_VAR( dt );
+ GEOS_UNUSED_VAR( cycleNumber );
+ GEOS_UNUSED_VAR( domain );
+ GEOS_UNUSED_VAR( mesh );
+ GEOS_UNUSED_VAR( elemManager );
+ GEOS_UNUSED_VAR( dofManager );
+
+ bool useEstimator = coupledIterationNumber < estimateSolution();
+ if( useEstimator )
+ {
+
+ if( getLogLevel() > 4 )
+ {
+ GEOS_LOG_RANK_0( "Well " <getName() << " value " << constraint->getConstraintValue( time_n ) << " active " <<
+ constraint->isConstraintActive() );
+ }
+ if( constraint->isConstraintActive() )
+ {
+ setControl( static_cast< WellControls::Control >(constraint->getControl()) ); // tjb old
+ setCurrentConstraint( constraint );
+ // If a well is opened and then timestep is cut resulting in the well being shut, if the well is opened
+// the well initialization code requires control type to by synced
+ integer owner = -1;
+// Only subregion owner evaluates well control and control changes need to be broadcast to all ranks
+ if( subRegion.isLocallyOwned() )
+ {
+ owner = MpiWrapper::commRank( MPI_COMM_GEOS );
+ }
+ owner = MpiWrapper::max( owner );
+ WellControls::Control wellControl = getControl();
+ MpiWrapper::broadcast( wellControl, owner );
+ setControl( wellControl );
+
+ m_wellNewtonSolver.solveNonlinearSystem( *this, time_n,
+ dt,
+ cycleNumber,
+ domain,
+ mesh,
+ elemManager,
+ subRegion );
+
+ if( getLogLevel() > 4 )
+ {
+ GEOS_LOG_RANK_0( "Well " <getName() << " bhp " << constraint->bottomHolePressure() << " phaseVolRate " <<
+ constraint->phaseVolumeRates() << " totalVolRate " << constraint->totalVolumeRate() << " massRate " << constraint->massRate());
+ }
+ }
+
+
+ }
+
+}
+
+void
+WellControls::assembleSystem( real64 const & time_n,
+ real64 const & dt,
+ integer const cycleNumber,
+ ElementRegionManager & elemManager,
+ WellElementSubRegion & subRegion,
+ DofManager const & dofManager,
+ CRSMatrixView< real64, globalIndex const > const & localMatrix,
+ arrayView1d< real64 > const & localRhs )
+{
+ GEOS_UNUSED_VAR( cycleNumber );
+ assembleWellAccumulationTerms( time_n, dt, subRegion, dofManager, localMatrix.toViewConstSizes(), localRhs );
+
+ assembleWellConstraintTerms( time_n, dt, subRegion, dofManager, localMatrix.toViewConstSizes(), localRhs );
+
+ assembleWellPressureRelations( time_n, dt, subRegion, dofManager, localMatrix.toViewConstSizes(), localRhs );
+ computeWellPerforationRates( time_n, dt, elemManager, subRegion );
+ assembleWellFluxTerms( time_n, dt, subRegion, dofManager, localMatrix.toViewConstSizes(), localRhs );
+
+}
} //namespace geos
diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/WellControls.hpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellControls.hpp
index 301c3c42f7c..8e488016db4 100644
--- a/src/coreComponents/physicsSolvers/fluidFlow/wells/WellControls.hpp
+++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellControls.hpp
@@ -20,12 +20,24 @@
#ifndef GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_WELLCONTROLS_HPP
#define GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_WELLCONTROLS_HPP
-
+#include "physicsSolvers/PhysicsSolverBase.hpp"
#include "common/format/EnumStrings.hpp"
#include "dataRepository/Group.hpp"
#include "functions/TableFunction.hpp"
#include "constitutive/fluid/multifluid/MultiFluidBase.hpp"
+#include "physicsSolvers/fluidFlow/wells/WellInjectionConstraint.hpp"
+#include "physicsSolvers/fluidFlow/wells/WellProductionConstraint.hpp"
+#include "physicsSolvers/fluidFlow/wells/WellBHPConstraints.hpp"
+#include "physicsSolvers/fluidFlow/wells/WellVolumeRateConstraint.hpp"
+#include "physicsSolvers/fluidFlow/wells/WellPhaseVolumeRateConstraint.hpp"
+#include "physicsSolvers/fluidFlow/wells/WellMassRateConstraint.hpp"
+#include "physicsSolvers/fluidFlow/wells/WellLiquidRateConstraint.hpp"
+#include "physicsSolvers/fluidFlow/wells/WellWHPConstraint.hpp"
+#include "constitutive/fluid/multifluid/MultiFluidBase.hpp"
+#include "constitutive/fluid/singlefluid/SingleFluidBase.hpp"
+#include "physicsSolvers/fluidFlow/wells/WellNewtonSolver.hpp"
+#include "physicsSolvers/fluidFlow/wells/WellPropWriter.hpp"
namespace geos
{
namespace dataRepository
@@ -41,12 +53,12 @@ static constexpr auto wellControls = "WellControls";
* @class WellControls
* @brief This class describes the controls used to operate a well.
*/
-class WellControls : public dataRepository::Group
+class WellControls : public dataRepository::Group //public PhysicsSolverBase
{
public:
/** Type of wells
- * Either producer or injector.
+ * Either producer or injector
*/
enum class Type : integer
{
@@ -123,34 +135,399 @@ class WellControls : public dataRepository::Group
///@}
+
+ /// String used to form the solverName used to register single-physics solvers in CoupledSolver
+ static string coupledSolverAttributePrefix() { return "well"; }
+ /**
+ * @brief Create a new geometric object (box, plane, etc) as a child of this group.
+ * @param childKey the catalog key of the new geometric object to create
+ * @param childName the name of the new geometric object in the repository
+ * @return the group child
+ */
+ virtual Group * createChild( string const & childKey, string const & childName ) override;
+ /// Expand catalog for schema generation
+
+ virtual void expandObjectCatalogs() override;
+
+ virtual void registerWellDataOnMesh( WellElementSubRegion & subRegion ) = 0;
+ virtual void setConstitutiveNames( ElementSubRegionBase & subRegion ) const = 0;
+ /**
+ * @brief Create well separator
+ */
+ virtual void createSeparator( WellElementSubRegion & subRegion ) = 0;
+
+ /**
+ * @defgroup WellManager Interface Functions
+ *
+ * These functions provide the primary interface that is required for derived classes
+ * The "Well" versions apply to individual well subRegions, whereas the others apply to all wells
+ */
+ /**@{*/
+
+ virtual void validateWellConstraints( real64 const & time_n,
+ real64 const & dt,
+ WellElementSubRegion const & subRegion ) = 0;
+
+ virtual bool isCompositional() const = 0;
+ /**
+ * * @brief Initialize well for the beginning of a simulation or restart
+ * @param domain the domain
+ * @param mesh the mesh level
+ * @param subRegion the well subRegion
+ * @param time_n the current time
+ */
+ virtual void initializeWell( DomainPartition & domain, MeshLevel & mesh, WellElementSubRegion & subRegion, real64 const & time_n ) = 0;
+ /**
+ * @brief function to set the next time step size
+ * @param[in] currentTime the current time
+ * @param[in] currentDt the current time step size
+ * @param[in] domain the domain object
+ * @return the prescribed time step size
+ */
+ real64 setNextDt( real64 const & currentTime,
+ real64 const & currentDt,
+ WellElementSubRegion & subRegion );
+ // Bring the base class implicitStepSetup into scope to avoid hiding the overloaded virtual function
+
+
+ virtual void implicitStepSetup( real64 const & time_n,
+ real64 const & GEOS_UNUSED_PARAM( dt ),
+ ElementRegionManager & elemManager,
+ WellElementSubRegion & subRegion ) = 0;
+
+ virtual void
+ implicitStepComplete( real64 const & time,
+ real64 const & dt,
+ WellElementSubRegion const & subRegion ) = 0;
+ virtual real64 updateSubRegionState( ElementRegionManager const & elemManager, WellElementSubRegion & subRegion ) = 0;
+
+ /**
+ * @brief Function to evaluate well constraints after applying the solution update
+ * @param time_n the time at the beginning of the time step
+ * @param subRegion the well subRegion
+ * @return true if all constraints are satisfied, false otherwise
+ */
+ bool evaluateConstraints( real64 const & time_n,
+ WellElementSubRegion & subRegion );
+
+ /**
+ * @brief Function to evaluate well constraints after applying the solution update
+ * @param time_n the time at the beginning of the time step
+ * @param subRegion the well subRegion
+ * @return true if all constraints are satisfied, false otherwise
+ */
+ bool evaluateConstraints( real64 const & time_n,
+ real64 const & dt,
+ integer const cycleNumber,
+ integer const coupledIterationNumber,
+ DomainPartition & domain,
+ MeshLevel & mesh,
+ ElementRegionManager & elemManager,
+ WellElementSubRegion & subRegion,
+ DofManager const & dofManager );
+
+ void solveConstraint( WellConstraintBase *constraint,
+ real64 const & time_n,
+ real64 const & dt,
+ integer const cycleNumber,
+ integer const coupledIterationNumber,
+ DomainPartition & domain,
+ MeshLevel & mesh,
+ ElementRegionManager & elemManager,
+ WellElementSubRegion & subRegion,
+ DofManager const & dofManager );
+
+ void assembleSystem( real64 const & time_n,
+ real64 const & dt,
+ integer const cycleNumber,
+ ElementRegionManager & elemManager,
+ WellElementSubRegion & subRegion,
+ DofManager const & dofManager,
+ CRSMatrixView< real64, globalIndex const > const & localMatrix,
+ arrayView1d< real64 > const & localRhs );
+ /**
+ * @brief assembles the accumulation term for an individual well
+ * @param time_n time at the beginning of the time step
+ * @param dt the time step size
+ * @param subRegion the well subregion containing all the primary and dependent fields
+ * @param dofManager degree-of-freedom manager associated with the linear system
+ * @param matrix the system matrix
+ * @param rhs the system right-hand side vector
+ */
+ virtual void assembleWellAccumulationTerms( real64 const & time,
+ real64 const & dt,
+ WellElementSubRegion & subRegion,
+ DofManager const & dofManager,
+ CRSMatrixView< real64, globalIndex const > const & localMatrix,
+ arrayView1d< real64 > const & localRhs ) = 0;
+ /**
+ * @brief assembles the well momentum terms for an individual well
+ * @param time_n time at the beginning of the time step
+ * @param dt the time step size
+ * @param subRegion the well subregion containing all the primary and dependent fields
+ * @param dofManager degree-of-freedom manager associated with the linear system
+ * @param matrix the system matrix
+ * @param rhs the system right-hand side vector
+ */
+ virtual void assembleWellPressureRelations( real64 const & time_n,
+ real64 const & dt,
+ WellElementSubRegion const & subRegion,
+ DofManager const & dofManager,
+ CRSMatrixView< real64, globalIndex const > const & localMatrix,
+ arrayView1d< real64 > const & localRhs ) = 0;
+ /**
+ * @brief assembles the well constraint terms for an individual well
+ * @param time_n time at the beginning of the time step
+ * @param dt the time step size
+ * @param subRegion the well subregion containing all the primary and dependent fields
+ * @param dofManager degree-of-freedom manager associated with the linear system
+ * @param matrix the system matrix
+ * @param rhs the system right-hand side vector
+ */
+ virtual void assembleWellConstraintTerms( real64 const & time_n,
+ real64 const & dt,
+ WellElementSubRegion const & subRegion,
+ DofManager const & dofManager,
+ CRSMatrixView< real64, globalIndex const > const & localMatrix,
+ arrayView1d< real64 > const & localRhs ) = 0;
+
+ /**
+ * @brief Recompute the perforation rates for all the wells
+ * @param time_n the time at the beginning of the time step
+ * @param dt the time step size
+ * @param elemManager the element region manager
+ * @param subRegion the well subregion containing all the primary and dependent fields
+ */
+ virtual void computeWellPerforationRates( real64 const & time_n,
+ real64 const & GEOS_UNUSED_PARAM( dt ),
+ ElementRegionManager & elemManager,
+ WellElementSubRegion & subRegion ) = 0;
+
+ /**
+ * @brief assembles the flux terms for individual well for all connections between well elements
+ * @param time_n previous time value
+ * @param dt time step
+ * @param subRegion the well subregion containing all the primary and dependent fields
+ * @param dofManager degree-of-freedom manager associated with the linear system
+ * @param matrix the system matrix
+ * @param rhs the system right-hand side vector
+ */
+ virtual void assembleWellFluxTerms( real64 const & time,
+ real64 const & dt,
+ WellElementSubRegion const & subRegion,
+ DofManager const & dofManager,
+ CRSMatrixView< real64, globalIndex const > const & localMatrix,
+ arrayView1d< real64 > const & localRhs ) = 0;
+ virtual real64
+ calculateWellResidualNorm( real64 const & time_n,
+ real64 const & dt,
+ NonlinearSolverParameters const & nonlinearSolverParameters,
+ WellElementSubRegion const & subRegion,
+ DofManager const & dofManager,
+ arrayView1d< real64 const > const & localRhs ) = 0;
+
+ virtual array1d< real64 >
+ calculateLocalWellResidualNorm( real64 const & time_n,
+ real64 const & dt,
+ NonlinearSolverParameters const & nonlinearSolverParameters,
+ WellElementSubRegion const & subRegion,
+ DofManager const & dofManager,
+ arrayView1d< real64 const > const & localRhs ) = 0;
+
+ virtual real64
+ scalingForWellSystemSolution( WellElementSubRegion & subRegion,
+ DofManager const & dofManager,
+ arrayView1d< real64 const > const & localSolution ) = 0;
+
+ virtual bool
+ checkWellSystemSolution( WellElementSubRegion & subRegion,
+ DofManager const & dofManager,
+ arrayView1d< real64 const > const & localSolution,
+ real64 const scalingFactor ) = 0;
+
+ virtual void
+ applyWellSystemSolution( DofManager const & dofManager,
+ arrayView1d< real64 const > const & localSolution,
+ real64 const scalingFactor,
+ real64 const dt,
+ DomainPartition & domain,
+ MeshLevel & mesh,
+ WellElementSubRegion & subRegion ) = 0;
+
+ virtual void applyWellBoundaryConditions( real64 const time_n,
+ real64 const dt,
+ ElementRegionManager & elemManager,
+ WellElementSubRegion & subRegion,
+ DofManager const & dofManager,
+ arrayView1d< real64 > const & localRhs,
+ CRSMatrixView< real64, globalIndex const > const & localMatrix ) = 0;
+ /**
+ * @brief Recompute all dependent quantities from primary variables (including constitutive models)
+ * @param subRegion the well subregion containing all the primary and dependent fields
+ */
+ virtual real64 updateWellState( ElementRegionManager const & elemManager, WellElementSubRegion & subRegion ) = 0;
+
+ /**
+ * @brief Reset the well state to the beginning of the time step
+ * @param subRegion the well subregion containing all the primary and dependent fields
+ */
+ virtual void resetStateToBeginningOfStep( ElementRegionManager const & elemManager, WellElementSubRegion & subRegion ) = 0;
+
+ virtual void postInputInitialization() override;
+
+ virtual void initializeWellPostInitialConditionsPreSubGroups( WellElementSubRegion & subRegion ) = 0;
+ virtual void printRates( real64 const & time_n,
+ real64 const & dt,
+ WellElementSubRegion const & subRegion ) = 0;
+ /**@}*/
+ /**
+ * @brief Apply a given functor to a container if the container can be
+ * cast to one of the specified types.
+ * @tparam CASTTYPE the first type that will be used in the attempted casting of container
+ * @tparam CASTTYPES a variadic list of types that will be used in the attempted casting of container
+ * @tparam CONTAINERTYPE the type of container
+ * @tparam LAMBDA the type of lambda function to call in the function
+ * @param[in] container a pointer to the container which will be passed to the lambda function
+ * @param[in] lambda the lambda function to call in the function
+ * @return a boolean to indicate whether the lambda was successfully applied to the container.
+ */
+ template< typename T0, typename T1, typename ... CASTTYPES, typename CONTAINERTYPE, typename LAMBDA >
+ static bool applyLambdaToContainer( CONTAINERTYPE container, LAMBDA && lambda )
+ {
+ using Pointee = std::remove_pointer_t< std::remove_reference_t< CONTAINERTYPE > >;
+ using T = std::conditional_t< std::is_const< Pointee >::value, T0 const, T0 >;
+ T * const castedContainer = dynamic_cast< T * >( container );
+
+ if( castedContainer != nullptr )
+ {
+ lambda( *castedContainer );
+ return true;
+ }
+
+ return applyLambdaToContainer< T1, CASTTYPES... >( container, std::forward< LAMBDA >( lambda ) );
+ }
+
+ // Base case: no more types to try
+ template< typename CONTAINERTYPE, typename LAMBDA >
+ static bool applyLambdaToContainer( CONTAINERTYPE /*container*/, LAMBDA && /*lambda*/ )
+ {
+ return false;
+ }
+
+ // Single-type overload: try only T0 and stop
+ template< typename T0, typename CONTAINERTYPE, typename LAMBDA >
+ static bool applyLambdaToContainer( CONTAINERTYPE container, LAMBDA && lambda )
+ {
+ using Pointee = std::remove_pointer_t< std::remove_reference_t< CONTAINERTYPE > >;
+ using T = std::conditional_t< std::is_const< Pointee >::value, T0 const, T0 >;
+ T * const castedContainer = dynamic_cast< T * >( container );
+
+ if( castedContainer != nullptr )
+ {
+ lambda( *castedContainer );
+ return true;
+ }
+
+ return false;
+ }
+
+
+ /**
+ * @copydoc forInjectionConstraints(LAMBDA &&)
+ */
+ template< typename GROUPTYPE = Group, typename ... GROUPTYPES, typename LAMBDA >
+ void forInjectionConstraints( LAMBDA && lambda ) const
+ {
+ for( auto const * constraintIter : m_injectionRateConstraintList )
+ {
+ applyLambdaToContainer< GROUPTYPE, GROUPTYPES... >( constraintIter, [&]( auto const & castedSubGroup )
+ {
+ lambda( castedSubGroup );
+ } );
+ }
+ }
+
+ // non-const overload
+ template< typename GROUPTYPE = Group, typename ... GROUPTYPES, typename LAMBDA >
+ void forInjectionConstraints( LAMBDA && lambda )
+ {
+ for( auto * constraintIter : m_injectionRateConstraintList )
+ {
+ applyLambdaToContainer< GROUPTYPE, GROUPTYPES... >( constraintIter, [&]( auto & castedSubGroup )
+ {
+ lambda( castedSubGroup );
+ } );
+ }
+ }
+ /**
+ * @copydoc forProductionConstraints(LAMBDA &&)
+ */
+ template< typename GROUPTYPE = Group, typename ... GROUPTYPES, typename LAMBDA >
+ void forProductionConstraints( LAMBDA && lambda ) const
+ {
+ for( auto const * constraintIter : m_productionRateConstraintList )
+ {
+ applyLambdaToContainer< GROUPTYPE, GROUPTYPES... >( constraintIter, [&]( auto const & castedSubGroup )
+ {
+ lambda( castedSubGroup );
+ } );
+ }
+ }
+
+ // non-const overload
+ template< typename GROUPTYPE = Group, typename ... GROUPTYPES, typename LAMBDA >
+ void forProductionConstraints( LAMBDA && lambda )
+ {
+ for( auto * constraintIter : m_productionRateConstraintList )
+ {
+ applyLambdaToContainer< GROUPTYPE, GROUPTYPES... >( constraintIter, [&]( auto & castedSubGroup )
+ {
+ lambda( castedSubGroup );
+ } );
+ }
+ }
/**
* @name Getters / Setters
*/
///@{
/**
- * @brief Set the control type to BHP and set a numerical value for the control.
- * @param[in] val value for the BHP control
+ * @brief Get the Constitutive Name object
+ *
+ * @tparam CONSTITUTIVE_BASE_TYPE the base type of the constitutive model.
+ * @param subRegion the element subregion on which the constitutive model is registered
+ * @return the name name of the constitutive model of type CONSTITUTIVE_BASE_TYPE registered on the subregion.
*/
- void switchToBHPControl( real64 const & val );
+ template< typename CONSTITUTIVE_BASE_TYPE >
+ static string getConstitutiveName( ElementSubRegionBase const & subRegion );
/**
- * @brief Set the control type to total rate and set a numerical value for the control.
- * @param[in] val value for the total volumetric rate
+ * @brief Register wrapper with given name and store constitutive model name on the subregion
+ *
+ * @tparam CONSTITUTIVE the base type of the constitutive model.
+ * @param subRegion the subregion on which the constitutive model is registered.
+ * @param wrapperName the wrapper name to register.
+ * @param constitutiveType the type description of the constitutive model.
*/
- void switchToTotalRateControl( real64 const & val );
+ template< typename CONSTITUTIVE >
+ void setConstitutiveName( ElementSubRegionBase & subRegion, string const & wrapperName, string const & constitutiveType ) const;
/**
- * @brief Set the control type to mass rate and set a numerical value for the control.
- * @param[in] val value for the mass rate
+ * @brief return the list of target regions
+ * @return the array of region names
+ */
+ string_array const & getTargetRegionNames() const {return m_targetRegionNames;}
+ /**
+ * @brief Get the control type for the well.
+ * @return the Control enum enforced at the well
*/
- void switchToMassRateControl( real64 const & val );
+ std::string getFlowSolverName() const { return m_flowSolverName; }
/**
- * @brief Set the control type to phase rate and set a numerical value for the control.
- * @param[in] val value for the phase volumetric rate
+ * @brief Set the control type for the well.
+ * @param[in] flowSolverName the name of the flow solver
*/
- void switchToPhaseRateControl( real64 const & val );
+ void setFlowSolverName( const std::string & flowSolverName ) { m_flowSolverName = flowSolverName; }
/**
* @brief Get the control type for the well.
@@ -171,10 +548,10 @@ class WellControls : public dataRepository::Group
Control getInputControl() const { return m_inputControl; }
/**
- * @brief Getter for the reference elevation where the BHP control is enforced
- * @return the reference elevation
+ * @brief getter for esitmator switch
+ * @return True if estimate well solution
*/
- real64 getReferenceElevation() const { return m_refElevation; }
+ integer estimateSolution() const { return m_estimateSolution; }
/**
* @brief Getter for the reference gravity coefficient
@@ -187,60 +564,38 @@ class WellControls : public dataRepository::Group
*/
void setReferenceGravityCoef( real64 const & refGravCoef ) { m_refGravCoef = refGravCoef; }
-
/**
- * @brief Get the target bottom hole pressure value.
- * @return a value for the target bottom hole pressure
+ * @brief Returns the target bottom hole pressure value.
+ * @param[in] targetTime time at which to evaluate the constraint
+ * @return the injector maximum bottom hole pressure or producer minimum bottom hole pressure
*/
- real64 getTargetBHP( real64 const & currentTime ) const
- {
- return m_targetBHPTable->evaluate( ¤tTime );
- }
+ real64 getTargetBHP( real64 const & targetTime ) const;
- /**
- * @brief Get the target total rate
- * @return the target total rate
- */
- real64 getTargetTotalRate( real64 const & currentTime ) const
- {
- return m_rateSign * m_targetTotalRateTable->evaluate( ¤tTime );
- }
/**
- * @brief Get the target phase rate
- * @return the target phase rate
+ * @brief Const accessor for the temperature of the injection stream
+ * @return the temperature of the injection stream
*/
- real64 getTargetPhaseRate( real64 const & currentTime ) const
- {
- return m_rateSign * m_targetPhaseRateTable->evaluate( ¤tTime );
- }
+ real64 getInjectionTemperature() const;
/**
- * @brief Get the target mass rate
- * @return the target mass rate
+ * @brief Const accessor for the injection stream
+ * @return the injection stream
*/
- real64 getTargetMassRate( real64 const & currentTime ) const
- {
- return m_rateSign * m_targetMassRateTable->evaluate( ¤tTime );
- }
+ arrayView1d< real64 const > getInjectionStream() const;
/**
- * @brief Get the target phase name
- * @return the target phase name
+ * @brief Const accessor for the phase constraint index
+ * @return phase index associated with phase constraint
*/
- const string & getTargetPhaseName() const { return m_targetPhaseName; }
+ integer getConstraintPhaseIndex() const;
/**
- * @brief Const accessor for the composition of the injection stream
- * @return a global component fraction vector
+ * @brief Return the reference elvation where pressure constraint is measured
+ * @return vertical location of constraint
*/
- arrayView1d< real64 const > getInjectionStream() const { return m_injectionStream; }
+ real64 getReferenceElevation() const;
- /**
- * @brief Const accessor for the temperature of the injection stream
- * @return the temperature of the injection stream
- */
- real64 getInjectionTemperature() const { return m_injectionTemperature; }
/**
* @brief Getter for the flag specifying whether we check rates at surface or reservoir conditions
@@ -252,7 +607,7 @@ class WellControls : public dataRepository::Group
* @brief Getter for the reservoir region associated with reservoir volume constraint
* @return name of reservoir region
*/
- string referenceReservoirRegion() const { return m_referenceReservoirRegion; }
+ string getReferenceReservoirRegion() const { return m_referenceReservoirRegion; }
/**
* @brief Getter for the surface pressure when m_useSurfaceConditions == 1
@@ -278,12 +633,49 @@ class WellControls : public dataRepository::Group
*/
bool isProducer() const { return ( m_type == Type::PRODUCER ); }
+ /**
+ * @brief getter for iso/thermal switch
+ * @return True if thermal
+ */
+ integer isThermal() const { return m_isThermal; }
+
+ /**
+ * @brief setter for iso/thermal switch
+ * @param[in] isThermal
+ */
+
+ void setThermal( bool isThermal ) { m_isThermal=isThermal; }
/**
* @brief Is the well open (or shut) at currentTime, status initalized in WellSolverBase::implicitStepSetup
* @return a boolean
*/
bool isWellOpen() const;
+ /**
+ * @brief Set the well state
+ * @param[in] open boolean
+ */
+ void setWellState( bool open );
+ /**
+ * @brief Get the well state
+ * @return a boolean
+ */
+ bool getWellState() const;
+
+
+ /**
+ * @brief Set the current consrtaint
+ * @param[in] currentConstraint pointer to constraint
+ */
+ void setCurrentConstraint( WellConstraintBase * currentConstraint ) { m_currentConstraint = currentConstraint;}
+ /**
+ * @brief Get the current consrtaint
+ * @return pointer to constraint
+ */
+ WellConstraintBase * getCurrentConstraint() { return m_currentConstraint; }
+ WellConstraintBase const * getCurrentConstraint() const { return m_currentConstraint; }
+
+
/**
* @brief Getter for the flag to enable crossflow
* @return the flag deciding whether crossflow is allowed or not
@@ -302,6 +694,17 @@ class WellControls : public dataRepository::Group
* @param[inout] nextDt the time step
*/
void setNextDtFromTables( real64 const & currentTime, real64 & nextDt );
+ /**
+ * @brief Utility function to keep the well variables during a time step (used in
+ * poromechanics simulations)
+ * @param[in] keepVariablesConstantDuringInitStep flag to tell the solver to freeze its
+ * primary variables during a time step
+ * @detail This function is meant to be called by a specific task before/after the
+ * initialization step
+ */
+ void setKeepVariablesConstantDuringInitStep( bool const keepVariablesConstantDuringInitStep )
+ { m_keepVariablesConstantDuringInitStep = keepVariablesConstantDuringInitStep; }
+
/**
* @brief setter for multi fluid separator
@@ -314,6 +717,12 @@ class WellControls : public dataRepository::Group
*/
constitutive::MultiFluidBase & getMultiFluidSeparator() { return dynamicCast< constitutive::MultiFluidBase & >( *m_fluidSeparatorPtr ); }
+ /**
+ * @brief Getter for single fluid separator
+ * @return reference to separator
+ */
+ constitutive::SingleFluidBase & getSingleFluidSeparator() { return dynamicCast< constitutive::SingleFluidBase & >( *m_fluidSeparatorPtr ); }
+
/**
* @brief Getter for the reservoir average pressure when m_useSurfaceConditions == 0
* @return the pressure
@@ -350,36 +759,61 @@ class WellControls : public dataRepository::Group
* @return a Status
*/
WellControls::Status getWellStatus () const { return m_wellStatus; }
+
+ /**
+ * @brief getter for presence of production WHP constraint
+ * @return True if constraint exists
+ */
+ bool hasMinimumWHPConstraint() const
+ {
+ return static_cast< bool >(m_minWHPConstraint);
+ }
+
///@}
+
+ virtual string wellElementDofName() const = 0;
+
+ virtual string resElementDofName() const = 0;
+
+ /**
+ * @brief getter for the number of degrees of freedom per well element
+ * @return the number of dofs
+ */
+ localIndex numDofPerWellElement() const { return m_numDofPerWellElement; }
+
+ /**
+ * @brief getter for the number of degrees of freedom per mesh element
+ * @return the number of dofs
+ */
+ localIndex numDofPerResElement() const { return m_numDofPerResElement; }
+
+
+ virtual localIndex numFluidComponents() const = 0;
+
+ virtual localIndex numFluidPhases() const = 0;
/**
* @brief Struct to serve as a container for variable strings and keys.
* @struct viewKeyStruct
*/
struct viewKeyStruct
{
+ /// String key for the fluid model names
+ static constexpr char const * fluidNamesString() { return "fluidNames"; }
+ /// String key for the write CSV flag
+ static constexpr char const * writeCSVFlagString() { return "writeCSV"; }
+ static constexpr char const * timeStepFromTablesFlagString() { return "timeStepFromTables"; }
+/// @return string for the targetRegions wrapper
+ static constexpr char const * targetRegionsString() { return "targetRegions"; }
+
/// String key for the well reference elevation (for BHP control)
static constexpr char const * refElevString() { return "referenceElevation"; }
/// String key for the well type
static constexpr char const * typeString() { return "type"; }
- /// String key for the well input control
- static constexpr char const * inputControlString() { return "control"; }
/// String key for the well current control
static constexpr char const * currentControlString() { return "currentControl"; }
- /// String key for the well target BHP
- static constexpr char const * targetBHPString() { return "targetBHP"; }
- /// String key for the well target rate
- static constexpr char const * targetTotalRateString() { return "targetTotalRate"; }
- /// String key for the well target phase rate
- static constexpr char const * targetPhaseRateString() { return "targetPhaseRate"; }
- /// String key for the well target phase name
- static constexpr char const * targetPhaseNameString() { return "targetPhaseName"; }
- /// String key for the well target phase name
- static constexpr char const * targetMassRateString() { return "targetMassRate"; }
- /// String key for the well injection stream
- static constexpr char const * injectionStreamString() { return "injectionStream"; }
- /// String key for the well injection temperature
- static constexpr char const * injectionTemperatureString() { return "injectionTemperature"; }
+ /// String key for the well input control
+ static constexpr char const * inputControlString() { return "control"; }
/// String key for checking the rates at surface conditions
static constexpr char const * useSurfaceConditionsString() { return "useSurfaceConditions"; }
/// String key for reference reservoir region
@@ -388,14 +822,7 @@ class WellControls : public dataRepository::Group
static constexpr char const * surfacePressureString() { return "surfacePressure"; }
/// String key for the surface temperature
static constexpr char const * surfaceTemperatureString() { return "surfaceTemperature"; }
- /// string key for total rate table name
- static constexpr char const * targetTotalRateTableNameString() { return "targetTotalRateTableName"; }
- /// string key for phase rate table name
- static constexpr char const * targetPhaseRateTableNameString() { return "targetPhaseRateTableName"; }
- /// string key for mass rate table name
- static constexpr char const * targetMassRateTableNameString() { return "targetMassRateTableName"; }
- /// string key for BHP table name
- static constexpr char const * targetBHPTableNameString() { return "targetBHPTableName"; }
+
/// string key for status table name
static constexpr char const * statusTableNameString() { return "statusTableName"; }
/// string key for perforation status table name
@@ -405,23 +832,183 @@ class WellControls : public dataRepository::Group
/// string key for the initial pressure coefficient
static constexpr char const * initialPressureCoefficientString() { return "initialPressureCoefficient"; }
+ /// string key for the minimum BHP presssure for a producer
+ static constexpr char const * minimumBHPConstraintString() { return "MinimumBHPConstraint"; }
+ /// string key for the maximum BHP presssure for a injection
+ static constexpr char const * maximumBHPConstraintString() { return "MaximumBHPConstraint"; }
+ /// string key for the minimum WHP presssure for a injection
+ static constexpr char const * minimumWHPConstraintString() { return "MinimumWHPConstraint"; }
+ /// string key for the maximum WHP presssure for a injection
+ static constexpr char const * maximumWHPConstraintString() { return "MaximumWHPConstraint"; }
+ /// string key for the maximum phase rate for a producer
+ static constexpr char const * productionPhaseVolumeRateConstraintString() { return "ProductionPhaseVolumeRateConstraint"; }
+ /// string key for the maximum phase rate for a injection
+ static constexpr char const * injectionPhaseVolumeRateConstraint() { return "InjectionPhaseVolumeRateConstraint"; }
+ /// string key for the maximum volume rate for a producer
+ static constexpr char const * productionVolumeRateConstraint() { return "ProductionVolumeRateConstraint"; }
+ /// string key for the maximum volume rate for a injector
+ static constexpr char const * injectionVolumeRateConstraint() { return "InjectionVolumeRateConstraint"; }
+ /// string key for the maximum mass rate for a producer
+ static constexpr char const * productionMassRateConstraint() { return "ProductionMassRateConstraint"; }
+ /// string key for the maximum mass rate for a injector
+ static constexpr char const * injectionMassRateConstraint() { return "InjectionMassRateConstraint"; }
+ /// string key for the liquid rate for a producer
+ static constexpr char const * productionLiquidRateConstraint() { return "ProductionLiquidRateConstraint"; }
+ /// string key for the minimum BHP presssure for a producer
+ static constexpr char const * wellNewtonSolverString() { return "WellNewtonSolver"; }
+
+ /// string key for the esitmate well solution flag
+ static constexpr char const * estimateWellSolutionString() { return "estimateWellSolution"; }
+ /// string key for the enable iso thermal estimator flag
+ static constexpr char const * enableIsoThermalEstimatorString() { return "enableIsoThermalEstimator"; }
+
+ // control data (not registered on the mesh)
+ static constexpr char const * writeSegDebugFlagString() { return "writeSegDebug"; }
+
+ static constexpr char const * massDensityString() { return "massDensity";}
+
+ static constexpr char const * currentBHPString() { return "currentBHP"; }
+ static constexpr char const * currentWHPString() { return "currentWHP"; }
+ static constexpr char const * currentPhaseVolRateString() { return "currentPhaseVolumetricRate"; }
+ static constexpr char const * currentVolRateString() { return "currentVolRate"; }
+
+ static constexpr char const * currentTotalVolRateString() { return "currentTotalVolumetricRate"; }
+
+ static constexpr char const * currentMassRateString() { return "currentMassRate"; }
+
}
/// ViewKey struct for the WellControls class
viewKeysWellControls;
+ void setPerforationStatus( real64 const & time_n, WellElementSubRegion & subRegion );
+ void setGravCoef( WellElementSubRegion & subRegion, R1Tensor const & gravVector );
+ /**
+ * @brief Set next time step based on a table function
+ * @param[in] table the table function
+ * @param[in] currentTime the current time
+ * @param[inout] nextDt the time step
+ */
static void setNextDtFromTable( TableFunction const * table, real64 const currentTime, real64 & nextDt );
-protected:
+ /**
+ * @brief Create a constraint
+ * @tparam ConstraintType the type of constraint to create
+ * @param[in] constraintName name to assign to the constraint
+ */
+ template< typename ConstraintType > void createConstraint ( string const & constraintName );
+ /**
+ * @brief Creates for internal constraints used by WHP constraints
+ */
+ void createMinBHPConstraintForWHP();
+ void createMaxLiquidConstraintForWHP();
- virtual void postInputInitialization() override;
+ /**
+ * @brief Getters for constraints
+ */
+ MinimumBHPConstraint * getMinBHPConstraint() { return m_minBHPConstraint; };
+ MinimumBHPConstraint * getMinBHPConstraint() const { return m_minBHPConstraint; };
+ MaximumBHPConstraint * getMaxBHPConstraint() { return m_maxBHPConstraint; };
+ MaximumBHPConstraint * getMaxBHPConstraint() const { return m_maxBHPConstraint; };
+
+ // WHP constraint getters
+ MinimumWHPConstraint * getMinWHPConstraint() { return m_minWHPConstraint; };
+ MinimumWHPConstraint * getMinWHPConstraint() const { return m_minWHPConstraint; };
+ ProductionConstraint< LiquidRateConstraint > * getMaxLiquidConstraintForWHP() { return m_maxLiquidConstraintForWHP; };
+ MinimumBHPConstraint * getMinimumBHPConstraintForWHP() { return m_minBHPConstraintForWHP; };
+ // Lists of rate constraints
+ std::vector< WellConstraintBase * > getProdRateConstraints() { return m_productionRateConstraintList; };
+ std::vector< WellConstraintBase * > getProdRateConstraints() const { return m_productionRateConstraintList; };
+ std::vector< WellConstraintBase * > getInjRateConstraints() { return m_injectionRateConstraintList; }
+ std::vector< WellConstraintBase * > getInjRateConstraints() const { return m_injectionRateConstraintList; }
+
+ /**
+ * @brief Set thermal effects enable
+ * @param[in] true/false
+ */
+ void enableThermalEffects ( bool enable ) { m_thermalEffectsEnabled = enable; };
+
+ /**
+ * @brief Are thermal effects enabled
+ * @return true if thermal effects are enabled, false otherwise
+ */
+ bool thermalEffectsEnabled() const { return m_thermalEffectsEnabled; }
+
+ /**
+ * @brief Is isoThermalEstimator enabled
+ * @return true if isoThermalEstimator is enabled, false otherwise
+ */
+ bool isoThermalEstimatorEnabled() const { return m_enableIsoThermalEstimator; }
+
+ void setupWellDofs( DomainPartition & domain, WellElementRegion & wellElementRegion, string const & meshBodyName, MeshLevel const & meshLevel );
+ WellNewtonSolver & getWellNewtonSolver() { return m_wellNewtonSolver; }
+
+ void selectWellConstraint( real64 const & time_n,
+ real64 const & dt,
+ integer const cycleNumber,
+ integer const coupledIterationNumber,
+ DomainPartition & domain,
+ MeshLevel & mesh,
+ ElementRegionManager & elemManager,
+ WellElementSubRegion & subRegion,
+ DofManager const & dofManager );
+
+ virtual bool solveWHPConstraint( real64 const & time_n,
+ real64 const & dt,
+ integer const cycleNumber,
+ integer const coupledIterationNumber,
+ DomainPartition & domain,
+ MeshLevel & mesh,
+ ElementRegionManager & elemManager,
+ WellElementSubRegion & subRegion ) = 0;
+ virtual void outputSingleWellDebug( real64 const time,
+ real64 const dt,
+ integer current_newton_iteration,
+ MeshLevel & mesh,
+ WellElementSubRegion & subRegion,
+ DofManager const & dofManager,
+ CRSMatrixView< real64, globalIndex const > const & GEOS_UNUSED_PARAM( localMatrix ),
+ arrayView1d< const real64 > const & GEOS_UNUSED_PARAM( localRhs ) ) = 0;
+
+protected:
+
+ virtual void postRestartInitialization( )override;
private:
+ /// List of names of regions the solver will be applied to
+ string_array m_targetRegionNames;
+
+protected:
/// Well type (as Type enum)
Type m_type;
+ /// Name of the flow solver managing this well
+ std::string m_flowSolverName;
+
+ /// the max number of fluid phases
+ integer m_numPhases;
+
+ /// the number of fluid components
+ integer m_numComponents;
+
+ /// the number of Degrees of Freedom per well element
+ integer m_numDofPerWellElement;
+
+ /// the number of Degrees of Freedom per reservoir element
+ integer m_numDofPerResElement;
+
+ /// flag indicating whether thermal formulation is used
+ integer m_isThermal;
+ /// flag to freeze the initial state during initialization in coupled problems
+ bool m_keepVariablesConstantDuringInitStep;
+ /// rates output
+ integer m_writeCSV;
+ string const m_ratesOutputDir;
+
+ // flag to enable time step selection base on rates/bhp tables coordinates
+ integer m_timeStepFromTables;
/// Reference elevation
real64 m_refElevation;
@@ -434,33 +1021,15 @@ class WellControls : public dataRepository::Group
/// Well controls as a Control enum
Control m_currentControl;
- /// Target bottom hole pressure value
- real64 m_targetBHP;
-
- /// Target rate value
- real64 m_targetTotalRate;
-
- /// Target phase rate value
- real64 m_targetPhaseRate;
-
- /// Name of the targeted phase
- string m_targetPhaseName;
-
- /// Target MassRate
- real64 m_targetMassRate;
-
- /// Vector with global component fractions at the injector
- array1d< real64 > m_injectionStream;
-
- /// Temperature at the injector
- real64 m_injectionTemperature;
-
/// Flag to decide whether rates are controlled at rates or surface conditions
integer m_useSurfaceConditions;
// Fuild model to compute properties for constraint equation user specified conditions
std::unique_ptr< constitutive::ConstitutiveBase > m_fluidSeparatorPtr;
+ /// name of the fluid constitutive model used as a reference for component/phase description on subregion
+ string m_referenceFluidModelName;
+
/// Reservoir region associated with reservoir volume constraint
string m_referenceReservoirRegion;
@@ -470,21 +1039,6 @@ class WellControls : public dataRepository::Group
/// Surface temperature
real64 m_surfaceTemp;
- /// Total rate table name
- string m_targetTotalRateTableName;
-
- /// Phase rate table name
- string m_targetPhaseRateTableName;
-
- /// Mass rate table name
- string m_targetMassRateTableName;
-
- /// BHP table name
- string m_targetBHPTableName;
-
- /// Well status table name
- string m_statusTableName;
-
/// Perforation status table name
string m_perfStatusTableName;
@@ -494,27 +1048,33 @@ class WellControls : public dataRepository::Group
/// Tuning coefficient for the initial well pressure
real64 m_initialPressureCoefficient;
- /// Rate sign. +1 for injector, -1 for producer
- real64 m_rateSign;
-
- /// Total rate table
- TableFunction const * m_targetTotalRateTable;
+ // Current constrint
+ WellConstraintBase * m_currentConstraint;
- /// Phase rate table
- TableFunction const * m_targetPhaseRateTable;
+ // Minimum and maximum BHP and WHP constraints
+ MinimumBHPConstraint * m_minBHPConstraint;
+ MaximumBHPConstraint * m_maxBHPConstraint;
+ MinimumWHPConstraint * m_minWHPConstraint;
- /// Mass rate table
- TableFunction const * m_targetMassRateTable;
+ // BHP constraint used when WHP constraint is active
+ MinimumBHPConstraint * m_minBHPConstraintForWHP;
+ ProductionConstraint< LiquidRateConstraint > * m_maxLiquidConstraintForWHP;
- /// BHP table
- TableFunction const * m_targetBHPTable;
-
- /// Status table
- TableFunction const * m_statusTable;
+ // Lists of rate constraints
+ std::vector< WellConstraintBase * > m_productionRateConstraintList;
+ std::vector< WellConstraintBase * > m_injectionRateConstraintList;
/// Well status
WellControls::Status m_wellStatus;
+ /// Well open flag
+ bool m_wellOpen;
+
+ /// Well status table name
+ string m_statusTableName;
+
+ /// Status table
+ TableFunction const * m_statusTable;
/// Region average pressure used in volume rate constraint calculations
real64 m_regionAveragePressure;
@@ -522,13 +1082,44 @@ class WellControls : public dataRepository::Group
/// Region average temperature used in volume rate constraint calculations
real64 m_regionAverageTemperature;
+ integer m_estimateSolution;
+ integer m_enableIsoThermalEstimator;
+ bool m_thermalEffectsEnabled;
+
+ WellNewtonSolver m_wellNewtonSolver;
+
+
+ /// @brief Well DofManager
+ /// @details This DofManager is used to store the DOF numbers for the estimator
+ /// @note This DofManager is used in the assembly of the estimators linear system
+ DofManager m_estimatorDoFManager;
+ bool m_dofManagerInitialized;
+
+ /// flag to write detailed segment properties
+ integer m_writeSegDebug;
+
+
+ integer m_numTimeStepCuts;
+ integer m_currentNewtonIteration;
+
+ std::map< std::string, WellPropWriter > m_wellPropWriter;
+ std::map< std::string, WellPropWriter > m_wellPropWriter_eot;
+
+ integer m_numTimesteps;
+ bool m_wellDebugInit;
+
+
};
-ENUM_STRINGS( WellControls::Type,
+
+// Use local aliases to avoid accidental macro expansion of the tokens 'Type' or 'Control'
+using WellControls_Type = WellControls::Type;
+ENUM_STRINGS( WellControls_Type,
"producer",
"injector" );
-ENUM_STRINGS( WellControls::Control,
+using WellControls_Control = WellControls::Control;
+ENUM_STRINGS( WellControls_Control,
"BHP",
"phaseVolRate",
"totalVolRate",
@@ -536,6 +1127,62 @@ ENUM_STRINGS( WellControls::Control,
"uninitialized" );
+template< typename CONSTITUTIVE >
+void WellControls::setConstitutiveName( ElementSubRegionBase & subRegion, string const & wrapperName, string const & constitutiveType ) const
+{
+ subRegion.registerWrapper< string >( wrapperName ).
+ setPlotLevel( dataRepository::PlotLevel::NOPLOT ).
+ setRestartFlags( dataRepository::RestartFlags::NO_WRITE ).
+ setSizedFromParent( 0 );
+
+ string & constitutiveName = subRegion.getReference< string >( wrapperName );
+ constitutiveName = getConstitutiveName< CONSTITUTIVE >( subRegion );
+ GEOS_ERROR_IF( constitutiveName.empty(), GEOS_FMT( "{}: {} constitutive model not found on subregion {}",
+ getDataContext(), constitutiveType, subRegion.getName() ) );
+}
+template< typename CONSTITUTIVE_BASE_TYPE >
+string WellControls::getConstitutiveName( ElementSubRegionBase const & subRegion )
+{
+ string validName;
+ dataRepository::Group const & constitutiveModels = subRegion.getConstitutiveModels();
+
+ constitutiveModels.forSubGroups< CONSTITUTIVE_BASE_TYPE >( [&]( dataRepository::Group const & model )
+ {
+ GEOS_ERROR_IF( !validName.empty(), "A valid constitutive model was already found." );
+ validName = model.getName();
+ } );
+
+ return validName;
+}
+
+/**
+ * @brief Get the Constitutive Model object
+ * @tparam BASETYPE the base type of the constitutive model.
+ * @tparam LOOKUP_TYPE the type of the key used to look up the constitutive model.
+ * @param dataGroup the data group containing the constitutive models.
+ * @param key the key used to look up the constitutive model.
+ * @return the constitutive model of type @p BASETYPE registered on the @p dataGroup with the key @p key.
+ */
+template< typename BASETYPE = constitutive::ConstitutiveBase, typename LOOKUP_TYPE >
+static BASETYPE const & getConstitutiveModel( dataRepository::Group const & dataGroup, LOOKUP_TYPE const & key )
+{
+ dataRepository::Group const & constitutiveModels = dataGroup.getGroup( ElementSubRegionBase::groupKeyStruct::constitutiveModelsString() );
+ return constitutiveModels.getGroup< BASETYPE >( key );
+}
+/**
+ * @brief Get the Constitutive Model object
+ * @tparam BASETYPE the base type of the constitutive model.
+ * @tparam LOOKUP_TYPE the type of the key used to look up the constitutive model.
+ * @param dataGroup the data group containing the constitutive models.
+ * @param key the key used to look up the constitutive model.
+ * @return the constitutive model of type @p BASETYPE registered on the @p dataGroup with the key @p key.
+ */
+template< typename BASETYPE = constitutive::ConstitutiveBase, typename LOOKUP_TYPE >
+static BASETYPE & getConstitutiveModel( dataRepository::Group & dataGroup, LOOKUP_TYPE const & key )
+{
+ dataRepository::Group & constitutiveModels = dataGroup.getGroup( ElementSubRegionBase::groupKeyStruct::constitutiveModelsString() );
+ return constitutiveModels.getGroup< BASETYPE >( key );
+}
} //namespace geos
#endif //GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_WELLCONTROLS_HPP
diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/WellFields.hpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellFields.hpp
index 5f77922f4ee..3bb07399534 100644
--- a/src/coreComponents/physicsSolvers/fluidFlow/wells/WellFields.hpp
+++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellFields.hpp
@@ -34,6 +34,21 @@ namespace fields
namespace well
{
+DECLARE_FIELD( connectionRate,
+ "wellElementConnectionRate",
+ array1d< real64 >,
+ 0,
+ LEVEL_0,
+ WRITE_AND_READ,
+ "Connection rate" );
+
+DECLARE_FIELD( connectionRate_n,
+ "wellElementConnectionRate_n",
+ array1d< real64 >,
+ 0,
+ NOPLOT,
+ WRITE_AND_READ,
+ "Connection rate at the previous converged time step" );
DECLARE_FIELD( energyPerforationFlux,
"energyPerforationFlux",
array1d< real64 >,
@@ -50,6 +65,61 @@ DECLARE_FIELD( dEnergyPerforationFlux,
NO_WRITE,
"Derivative of energy perforation flux with respect to pressure temperature and global component density (compositional only)" );
+DECLARE_FIELD( pressure,
+ "pressure",
+ array1d< real64 >,
+ 0,
+ LEVEL_0,
+ WRITE_AND_READ,
+ "Pressure" );
+
+DECLARE_FIELD( pressure_n,
+ "pressure_n",
+ array1d< real64 >,
+ 0,
+ NOPLOT,
+ WRITE_AND_READ,
+ "Pressure at the previous converged time step" );
+
+DECLARE_FIELD( temperature,
+ "temperature",
+ array1d< real64 >,
+ 0,
+ LEVEL_0,
+ WRITE_AND_READ,
+ "Temperature" );
+
+DECLARE_FIELD( temperature_n,
+ "temperature_n",
+ array1d< real64 >,
+ 0,
+ NOPLOT,
+ WRITE_AND_READ,
+ "Temperature at the previous converged time step" );
+
+DECLARE_FIELD( gravityCoefficient,
+ "gravityCoefficient",
+ array1d< real64 >,
+ 0,
+ NOPLOT,
+ WRITE_AND_READ,
+ "Gravity coefficient (dot product of gravity acceleration by gravity vector)" );
+
+DECLARE_FIELD( pressureScalingFactor,
+ "pressureScalingFactor",
+ array1d< real64 >,
+ 1,
+ NOPLOT,
+ NO_WRITE,
+ "Scaling factors for pressure" );
+
+DECLARE_FIELD( temperatureScalingFactor,
+ "temperatureScalingFactor",
+ array1d< real64 >,
+ 1,
+ NOPLOT,
+ NO_WRITE,
+ "Scaling factors for temperature" );
}
diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/WellInjectionConstraint.cpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellInjectionConstraint.cpp
new file mode 100644
index 00000000000..57145a752dc
--- /dev/null
+++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellInjectionConstraint.cpp
@@ -0,0 +1,108 @@
+/*
+ * ------------------------------------------------------------------------------------------------------------
+ * 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 WellInjectionConstraint.cpp
+ */
+
+#include "LogLevelsInfo.hpp"
+#include "WellInjectionConstraint.hpp"
+#include "WellConstants.hpp"
+#include "dataRepository/InputFlags.hpp"
+#include "functions/FunctionManager.hpp"
+
+#include "WellLiquidRateConstraint.hpp"
+#include "WellMassRateConstraint.hpp"
+#include "WellPhaseVolumeRateConstraint.hpp"
+#include "WellVolumeRateConstraint.hpp"
+
+namespace geos
+{
+using namespace dataRepository;
+
+template< typename ConstraintRateType >
+InjectionConstraint< ConstraintRateType >::InjectionConstraint( string const & name, Group * const parent )
+ : ConstraintRateType( name, parent )
+{
+ // set rate sign for injectors (base class member)
+ this->m_rateSign = 1.0;
+ classtype::registerWrapper( injectionStreamKey::injectionStreamString(), &m_injectionStream ).
+ setDefaultValue( -1 ).
+ setSizedFromParent( 0 ).
+ setInputFlag( InputFlags::OPTIONAL ).
+ setDescription( "Global component densities of the injection stream [moles/m^3 or kg/m^3]" );
+
+ InjectionConstraint< ConstraintRateType >::registerWrapper( injectionStreamKey::injectionTemperatureString(), &m_injectionTemperature ).
+ setDefaultValue( -1 ).
+ setInputFlag( InputFlags::OPTIONAL ).
+ setDescription( "Temperature of the injection stream [K]" );
+}
+template< typename ConstraintRateType >
+InjectionConstraint< ConstraintRateType >::~InjectionConstraint()
+{}
+template< typename ConstraintRateType >
+void InjectionConstraint< ConstraintRateType >::postInputInitialization()
+{
+ // Validate value and table options
+ ConstraintRateType::postInputInitialization();
+
+// Validate the injection stream and temperature
+ validateInjectionStream( );
+
+}
+template< typename ConstraintRateType >
+void InjectionConstraint< ConstraintRateType >::validateInjectionStream( )
+{
+ GEOS_THROW_IF( (m_injectionStream.empty() && m_injectionTemperature >= 0) ||
+ (!m_injectionStream.empty() && m_injectionTemperature < 0),
+ this->getName() << " " << this->getDataContext() << ": Both "
+ << injectionStreamKey::injectionStreamString() << " and " << injectionStreamKey::injectionTemperatureString()
+ << " must be specified for multiphase simulations",
+ InputError );
+
+ if( !m_injectionStream.empty())
+ {
+ real64 sum = 0.0;
+ for( localIndex ic = 0; ic < m_injectionStream.size(); ++ic )
+ {
+ GEOS_ERROR_IF( m_injectionStream[ic] < 0.0 || m_injectionStream[ic] > 1.0,
+ classtype::getWrapperDataContext( injectionStreamKey::injectionStreamString() ) << ": Invalid injection stream" );
+ sum += m_injectionStream[ic];
+ }
+ GEOS_THROW_IF( LvArray::math::abs( 1.0 - sum ) > std::numeric_limits< real64 >::epsilon(),
+ classtype::getWrapperDataContext( injectionStreamKey::injectionStreamString() ) << ": Invalid injection stream",
+ InputError );
+ }
+}
+
+// Register concrete wrapper constraint types and instantiate templates.
+template class InjectionConstraint< LiquidRateConstraint >;
+using InjectionLiquidRateConstraint = InjectionConstraint< LiquidRateConstraint >;
+REGISTER_CATALOG_ENTRY( WellConstraintBase, InjectionLiquidRateConstraint, string const &, Group * const )
+
+template class InjectionConstraint< MassRateConstraint >;
+using InjectionMassRateConstraint = InjectionConstraint< MassRateConstraint >;
+REGISTER_CATALOG_ENTRY( WellConstraintBase, InjectionMassRateConstraint, string const &, Group * const )
+
+template class InjectionConstraint< PhaseVolumeRateConstraint >;
+using InjectionPhaseVolumeRateConstraint = InjectionConstraint< PhaseVolumeRateConstraint >;
+REGISTER_CATALOG_ENTRY( WellConstraintBase, InjectionPhaseVolumeRateConstraint, string const &, Group * const )
+
+template class InjectionConstraint< VolumeRateConstraint >;
+using InjectionVolumeRateConstraint = InjectionConstraint< VolumeRateConstraint >;
+REGISTER_CATALOG_ENTRY( WellConstraintBase, InjectionVolumeRateConstraint, string const &, Group * const )
+
+
+}
diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/WellInjectionConstraint.hpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellInjectionConstraint.hpp
new file mode 100644
index 00000000000..1707f5f20e7
--- /dev/null
+++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellInjectionConstraint.hpp
@@ -0,0 +1,139 @@
+/*
+ * ------------------------------------------------------------------------------------------------------------
+ * 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 WellInjectionConstraint.hpp
+ */
+
+
+#ifndef GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_WELLINJECTIONCONSTRAINT_HPP
+#define GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_WELLINJECTIONCONSTRAINT_HPP
+
+#include "common/format/EnumStrings.hpp"
+#include "dataRepository/Group.hpp"
+#include "functions/TableFunction.hpp"
+
+namespace geos
+{
+
+using namespace dataRepository;
+/**
+ * @class InjectionConstraint
+ * @brief This class describes constraint used to control a injection well.
+ */
+
+template< typename ConstraintType >
+class InjectionConstraint : public ConstraintType
+{
+public:
+ typedef InjectionConstraint< ConstraintType > classtype;
+ /**
+ * @name Constructor / Destructor
+ */
+ ///@{
+
+ /**
+ * @brief Constructor for WellControls Objects.
+ * @param[in] name the name of this instantiation of WellControls in the repository
+ * @param[in] parent the parent group of this instantiation of WellControls
+ */
+ explicit InjectionConstraint( string const & name, dataRepository::Group * const parent );
+
+ /**
+ * @brief Default destructor.
+ */
+ ~InjectionConstraint() override;
+
+ /**
+ * @brief Deleted default constructor.
+ */
+ InjectionConstraint() = delete;
+
+ /**
+ * @brief Deleted copy constructor.
+ */
+ InjectionConstraint( InjectionConstraint const & ) = delete;
+
+ /**
+ * @brief Deleted move constructor.
+ */
+ InjectionConstraint( InjectionConstraint && ) = delete;
+
+ /**
+ * @brief Deleted assignment operator.
+ * @return a reference to a constraint object
+ */
+ InjectionConstraint & operator=( InjectionConstraint const & ) = delete;
+
+ /**
+ * @brief Deleted move operator.
+ * @return a reference to a constraint object
+ */
+ InjectionConstraint & operator=( InjectionConstraint && ) = delete;
+
+ ///@}
+
+ /**
+ * @brief name of the node manager in the object catalog
+ * @return string that contains the catalog name to generate a new Constraint object through the object catalog.
+ */
+ static string catalogName()
+ {
+ return "Injection"+ConstraintType::catalogName();
+ }
+
+ virtual string getCatalogName() const override { return catalogName(); }
+
+ struct injectionStreamKey
+ {
+ /// String key for the well injection stream
+ static constexpr char const * injectionStreamString() { return "injectionStream"; }
+ /// String key for the well injection temperature
+ static constexpr char const * injectionTemperatureString() { return "injectionTemperature"; }
+ };
+
+ /**
+ * @brief Const accessor for the composition of the injection stream
+ * @return a global component fraction vector
+ */
+ arrayView1d< real64 const > getInjectionStream() const { return m_injectionStream; }
+
+ /**
+ * @brief Const accessor for the temperature of the injection stream
+ * @return the temperature of the injection stream
+ */
+ real64 getInjectionTemperature() const { return m_injectionTemperature; }
+
+protected:
+
+ virtual void postInputInitialization() override;
+ static bool isViolated( const real64 & currentValue, const real64 & constraintValue )
+ { return currentValue > constraintValue; }
+
+ void validateInjectionStream();
+private:
+
+ /// Vector with global component fractions at the injector
+ array1d< real64 > m_injectionStream;
+
+ /// Temperature at the injector
+ real64 m_injectionTemperature;
+
+};
+
+
+} //namespace geos
+
+#endif //GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_WELLINJECTIONCONSTRAINT_HPP
diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/WellLiquidRateConstraint.cpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellLiquidRateConstraint.cpp
new file mode 100644
index 00000000000..3c84f70f1a7
--- /dev/null
+++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellLiquidRateConstraint.cpp
@@ -0,0 +1,76 @@
+/*
+ * ------------------------------------------------------------------------------------------------------------
+ * 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 WellLiquidRateConstraint.cpp
+ */
+
+#include "LogLevelsInfo.hpp"
+#include "WellLiquidRateConstraint.hpp"
+#include "WellConstants.hpp"
+#include "dataRepository/InputFlags.hpp"
+#include "functions/FunctionManager.hpp"
+
+namespace geos
+{
+
+using namespace dataRepository;
+
+
+LiquidRateConstraint::LiquidRateConstraint( string const & name, Group * const parent )
+ : WellConstraintBase( name, parent )
+{
+ this->registerWrapper( viewKeyStruct::liquidRateString(), &this->m_constraintValue ).
+ setDefaultValue( 0.0 ).
+ setInputFlag( InputFlags::OPTIONAL ).
+ setRestartFlags( RestartFlags::WRITE_AND_READ ).
+ setDescription( "Phase rate, (if useSurfaceCondSitions: [surface m^3/s]; else [reservoir m^3/s]) " );
+
+ this->registerWrapper( viewKeyStruct::phaseNamesString(), &m_phaseNames ).
+ setRTTypeName( rtTypes::CustomTypes::groupNameRefArray ).
+ setInputFlag( InputFlags::REQUIRED ).
+ setDescription( "List of fluid phase names defining the liquid" );
+}
+
+LiquidRateConstraint::~LiquidRateConstraint()
+{}
+
+void LiquidRateConstraint::postInputInitialization()
+{
+ // Validate table options
+ WellConstraintBase::postInputInitialization();
+
+ // check constraint value
+ GEOS_THROW_IF( m_constraintValue < 0,
+ getWrapperDataContext( viewKeyStruct::liquidRateString() ) << ": Target value is negative",
+ InputError );
+
+ GEOS_THROW_IF ((m_constraintValue <= 0.0 && m_constraintScheduleTableName.empty()),
+ getName() << " " << getDataContext() << ": You need to specify a liquid rate constraint. \n" <<
+ "The rate constraint can be specified using " <<
+ "either " << viewKeyStruct::liquidRateString() <<
+ " or " << WellConstraintBase::viewKeyStruct::constraintScheduleTableNameString(),
+ InputError );
+}
+
+
+bool LiquidRateConstraint::checkViolation( WellConstraintBase const & currentConstraint, real64 const & currentTime ) const
+{
+ real64 const currentValue = currentConstraint.liquidRate();
+ real64 const constraintValue = this->getConstraintValue( currentTime );
+ return ( LvArray::math::abs( currentValue ) <= LvArray::math::abs( constraintValue ) );
+}
+
+} //namespace geos
diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/WellLiquidRateConstraint.hpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellLiquidRateConstraint.hpp
new file mode 100644
index 00000000000..10ba7bffdf2
--- /dev/null
+++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellLiquidRateConstraint.hpp
@@ -0,0 +1,175 @@
+/*
+ * ------------------------------------------------------------------------------------------------------------
+ * 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 WellLiquidRateConstraint.hpp
+ */
+
+
+#ifndef GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_WELLLIQUIDRATECONSTRAINT_HPP
+#define GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_WELLLIQUIDRATECONSTRAINT_HPP
+
+#include "common/format/EnumStrings.hpp"
+
+#include "functions/TableFunction.hpp"
+#include "WellConstraintsBase.hpp"
+
+namespace geos
+{
+
+
+/**
+ * @class LiquidRateConstraint
+ * @brief This class describes a Liquid rate constraint used to control of type WellConstraintType
+ */
+
+
+class LiquidRateConstraint : public WellConstraintBase
+{
+public:
+
+
+ /**
+ * @name Constructor / Destructor
+ */
+ ///@{
+
+ /**
+ * @brief Constructor for WellControls Objects.
+ * @param[in] name the name of this instantiation of WellControls in the repository
+ * @param[in] parent the parent group of this instantiation of WellControls
+ */
+ explicit LiquidRateConstraint( string const & name, dataRepository::Group * const parent );
+
+
+ /**
+ * @brief Default destructor.
+ */
+ ~LiquidRateConstraint() override;
+
+ /**
+ * @brief Deleted default constructor.
+ */
+ LiquidRateConstraint() = delete;
+
+ /**
+ * @brief Deleted copy constructor.
+ */
+ LiquidRateConstraint( LiquidRateConstraint const & ) = delete;
+
+ /**
+ * @brief Deleted move constructor.
+ */
+ LiquidRateConstraint( LiquidRateConstraint && ) = delete;
+
+ /**
+ * @brief Deleted assignment operator.
+ * @return a reference to a constraint object
+ */
+ LiquidRateConstraint & operator=( LiquidRateConstraint const & ) = delete;
+
+ /**
+ * @brief Deleted move operator.
+ * @return a reference to a constraint object
+ */
+ LiquidRateConstraint & operator=( LiquidRateConstraint && ) = delete;
+
+ /**
+ * @brief name of the node manager in the object catalog
+ * @return string that contains the catalog name to generate a new Constraint object through the object catalog.
+ */
+ static string catalogName()
+ {
+ return "LiquidRateConstraint";
+ }
+ ///@}
+
+ /**
+ * @name Getters / Setters
+ */
+ ///@{
+ /**
+ * @brief Get the target phase name
+ * @return the target phase name
+ */
+ const string_array & getPhaseNames() const { return m_phaseNames; }
+
+ /**
+ * @brief Set phases associated with liquid constraint
+ * @param array of phase names
+ */
+ void setPhaseNames( const string_array & phaseNames ) { m_phaseNames=phaseNames; }
+
+ /**
+ * @brief Get the phase indices
+ * @return array of phase indices
+ */
+ const array1d< integer > & getPhaseIndices() const { return m_phaseIndices; }
+
+ ///@}
+ /**
+ * @brief Struct to serve as a container for variable strings and keys.
+ * @struct viewKeyStruct
+ */
+ struct viewKeyStruct
+ {
+ /// String key for the liquid rate
+ static constexpr char const * liquidRateString() { return "liquidRate"; }
+ /// String key for the phases names
+ static constexpr char const * phaseNamesString() { return "phaseNames"; }
+ };
+
+ // Temp interface - tjb
+ virtual ConstraintTypeId getControl() const override { return ConstraintTypeId::LIQUIDRATE; };
+
+ virtual bool checkViolation( WellConstraintBase const & currentConstraint, real64 const & currentTime ) const override;
+
+ /**
+ * @brief Validate Liquid type is consistent with fluidmodel
+ */
+ template< typename T >
+ void validateLiquidType( T const & fluidModel );
+
+protected:
+
+ virtual void postInputInitialization() override;
+
+protected:
+
+ /// Name of the targeted phase
+ string_array m_phaseNames;
+ ///Indices of the phases defining the fluid
+ array1d< integer > m_phaseIndices;
+
+};
+
+template< typename T >
+void LiquidRateConstraint::validateLiquidType( T const & fluidModel )
+{
+ m_phaseIndices.resize( m_phaseNames.size());
+ for( size_t ip =0; ipgetWrapper< string >( viewKeyStruct::discretizationString() ).
+ setInputFlag( InputFlags::FALSE );
+
+ registerWrapper( viewKeyStruct::isThermalString(), &m_isThermal ).
+ setApplyDefaultValue( 0 ).
+ setInputFlag( InputFlags::OPTIONAL ).
+ setDescription( "Flag indicating whether the problem is thermal or not." );
+
+
+ this->registerWrapper( viewKeyStruct::useMassFlagString(), &m_useMass ).
+ setApplyDefaultValue( 0 ).
+ setInputFlag( InputFlags::OPTIONAL ).
+ setDescription( "Use mass formulation instead of molar" );
+
+ this->registerWrapper( viewKeyStruct::useTotalMassEquationString(), &m_useTotalMassEquation ).
+ setApplyDefaultValue( 1 ).
+ setInputFlag( InputFlags::OPTIONAL ).
+ setDescription( "Use total mass equation" );
+
+ this->registerWrapper( viewKeyStruct::allowLocalCompDensChoppingString(), &m_allowCompDensChopping ).
+ setSizedFromParent( 0 ).
+ setInputFlag( InputFlags::OPTIONAL ).
+ setApplyDefaultValue( 1 ).
+ setDescription( "Flag indicating whether local (cell-wise) chopping of negative compositions is allowed" );
+
+ this->registerWrapper( viewKeyStruct::timeStepFromTablesFlagString(), &m_timeStepFromTables ).
+ setApplyDefaultValue( 0 ).
+ setInputFlag( dataRepository::InputFlags::OPTIONAL ).
+ setDescription ( "Choose time step to honor rates/bhp tables time intervals" );
+
+}
+Group * WellManager::createChild( string const & childKey, string const & childName )
+{
+ static std::set< string > const childTypes = {
+ keys::compositionalMultiphaseWell,
+ keys::singlePhaseWell,
+ PhysicsSolverBase::groupKeyStruct::linearSolverParametersString(),
+ PhysicsSolverBase::groupKeyStruct::nonlinearSolverParametersString(),
+ };
+ GEOS_ERROR_IF( childTypes.count( childKey ) == 0,
+ CatalogInterface::unknownTypeError( childKey, getDataContext(), childTypes ),
+ getDataContext() );
+ if( childKey == keys::compositionalMultiphaseWell )
+ {
+ setCompositional( true );
+ return ®isterGroup< CompositionalMultiphaseWell >( childName );
+ }
+ else if( childKey == keys::singlePhaseWell )
+ {
+ setCompositional( false );
+ return ®isterGroup< SinglePhaseWell >( childName );
+ }
+ else
+ {
+ PhysicsSolverBase::createChild( childKey, childName );
+ return nullptr;
+ }
+}
+
+void WellManager::expandObjectCatalogs()
+{
+ createChild( keys::compositionalMultiphaseWell, keys::compositionalMultiphaseWell );
+ createChild( keys::singlePhaseWell, keys::singlePhaseWell );
+}
+
+void WellManager::registerDataOnMesh( Group & meshBodies )
+{
+
+ //std::string const & flowSolverName = getParent().getName();//getGroup< CompositionalMultiphaseBase >().getName();
+ // CompositionalMultiphaseBase const & flowSolver = getParent().getGroup< CompositionalMultiphaseBase >( getFlowSolverName() );
+
+ forDiscretizationOnMeshTargets( meshBodies, [&] ( string const &,
+ MeshLevel & mesh,
+ string_array const & regionNames )
+ {
+
+ ElementRegionManager & elemManager = mesh.getElemManager();
+
+ elemManager.forElementSubRegions< WellElementSubRegion >( regionNames,
+ [&]( localIndex const,
+ WellElementSubRegion & subRegion )
+ {
+
+ WellControls & well = getWellControls( subRegion );
+ //well.setFlowSolverName( flowSolver.getName() );
+ well.setThermal( isThermal() );
+ well.registerWellDataOnMesh( subRegion );
+ m_numFluidPhases = well.numFluidPhases();
+ m_numFluidComponents = well.numFluidComponents();
+
+ } );
+ } );
+ // 1. Set key dimensions of the problem
+ // Empty check needed to avoid errors when running in schema generation mode.
+
+ // 1 pressure + NC compositions + 1 connectionRate + temp if thermal
+ m_numDofPerWellElement = isThermal() ? m_numFluidComponents + 3 : m_numFluidComponents + 2;
+ // 1 pressure + NC compositions + temp if thermal
+ m_numDofPerResElement = isThermal() ? m_numFluidComponents + 2 : m_numFluidComponents + 1;
+
+}
+
+WellControls & WellManager::getWell( WellElementSubRegion const & subRegion )
+{
+ return this->getGroup< WellControls >( subRegion.getWellControlsName());
+}
+WellControls & WellManager::getWell( std::string const & wellControlsName )
+{
+ return this->getGroup< WellControls >( wellControlsName );
+}
+
+WellControls const & WellManager::getWell( std::string const & wellControlsName ) const
+{
+ return this->getGroup< WellControls >( wellControlsName );
+}
+void WellManager::implicitStepSetup( real64 const & time_n,
+ real64 const & dt,
+ DomainPartition & domain )
+{
+
+ forDiscretizationOnMeshTargets ( domain.getMeshBodies(), [&] ( string const & meshBodyName,
+ MeshLevel & mesh,
+ string_array const & regionNames )
+ {
+
+ ElementRegionManager & elemManager = mesh.getElemManager();
+
+ elemManager.forElementRegions< WellElementRegion >( regionNames,
+ [&]( localIndex const,
+ WellElementRegion & region )
+ {
+ WellControls & well = getWell( region.getWellControlsName() );
+ if( well.estimateSolution() )
+ {
+ well.setupWellDofs( domain, region, meshBodyName, mesh );
+ }
+
+ } )
+ ;
+ } );
+
+ forDiscretizationOnMeshTargets ( domain.getMeshBodies(), [&] ( string const &,
+ MeshLevel & mesh,
+ string_array const & regionNames )
+ {
+
+ ElementRegionManager & elemManager = mesh.getElemManager();
+
+ elemManager.forElementSubRegions< WellElementSubRegion >( regionNames,
+ [&]( localIndex const,
+ WellElementSubRegion & subRegion )
+ {
+ WellControls & well = getWell( subRegion );
+ well.implicitStepSetup( time_n, dt, elemManager, subRegion );
+ } )
+ ;
+ } );
+}
+real64
+WellManager::setNextDt( real64 const & currentTime, const real64 & currentDt, geos::DomainPartition & domain )
+{
+
+ real64 nextDt = PhysicsSolverBase::setNextDt( currentTime, currentDt, domain );
+
+ if( m_timeStepFromTables )
+ {
+ real64 nextDt_orig = nextDt;
+ real64 nextDtLocal = nextDt;
+ forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &,
+ MeshLevel & mesh,
+ string_array const & regionNames )
+ {
+ mesh.getElemManager().forElementSubRegions< WellElementSubRegion >( regionNames, [&]( localIndex const,
+ WellElementSubRegion & subRegion )
+ {
+
+ WellControls & wellControls = getWellControls( subRegion );
+ real64 nextDtWell = wellControls.setNextDt( currentTime, nextDt, subRegion );
+ if( nextDtWell < nextDtLocal )
+ {
+ nextDtLocal = nextDtWell;
+ }
+ } );
+ } );
+ // get the minimum across all ranks
+ nextDt = MpiWrapper::min< real64 >( nextDtLocal );
+ if( getLogLevel() > 0 && nextDt < nextDt_orig )
+ GEOS_LOG_RANK_0( GEOS_FMT( "{}: next time step based on tables coordinates = {}", getName(), nextDt ));
+ }
+
+ return nextDt;
+}
+localIndex WellManager::numDofPerWellElement() const
+{
+ return m_numDofPerWellElement;
+}
+
+localIndex WellManager::numDofPerResElement() const
+{
+ return m_numDofPerResElement;
+}
+integer WellManager::isThermal() const
+{
+ return m_isThermal;
+}
+
+string WellManager::wellElementDofName() const
+{
+ return viewKeyStruct::dofFieldString();
+}
+
+string WellManager::resElementDofName() const
+{
+ if( isCompositional() )
+ return CompositionalMultiphaseBase::viewKeyStruct::elemDofFieldString();
+ else
+ return SinglePhaseBase::viewKeyStruct::elemDofFieldString();
+}
+
+localIndex WellManager::numFluidComponents() const
+{
+ return m_numFluidComponents;
+}
+
+localIndex WellManager::numFluidPhases() const
+{
+ return m_numFluidPhases;
+}
+WellControls & WellManager::getWellControls( WellElementSubRegion const & subRegion )
+{
+ return this->getGroup< WellControls >( subRegion.getWellControlsName());
+}
+
+WellControls const & WellManager::getWellControls( WellElementSubRegion const & subRegion ) const
+{
+ return this->getGroup< WellControls >( subRegion.getWellControlsName());
+}
+
+CompositionalMultiphaseWell & WellManager::getCompositionalMultiphaseWell( WellElementSubRegion const & subRegion )
+{
+ return this->getGroup< CompositionalMultiphaseWell >( subRegion.getWellControlsName());
+}
+
+CompositionalMultiphaseWell const & WellManager::getCompositionalMultiphaseWell( WellElementSubRegion const & subRegion ) const
+{
+ return this->getGroup< CompositionalMultiphaseWell >( subRegion.getWellControlsName());
+}
+void WellManager::initializePostSubGroups()
+{
+#if 0
+ GEOS_MARK_FUNCTION;
+ // Validate constitutive models
+ if( isCompositional() )
+ {
+ DomainPartition & domain = this->getGroupByPath< DomainPartition >( "/Problem/domain" );
+ constitutive::ConstitutiveManager const & cm = domain.getConstitutiveManager();
+ CompositionalMultiphaseBase const & flowSolver = getParent().getGroup< CompositionalMultiphaseBase >( getFlowSolverName() );
+ string const referenceFluidName = flowSolver.referenceFluidModelName();
+ constitutive::MultiFluidBase const & referenceFluid = cm.getConstitutiveRelation< constitutive::MultiFluidBase >( m_referenceFluidModelName );
+
+ forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &,
+ MeshLevel const & mesh,
+ string_array const & regionNames )
+ {
+
+ mesh.getElemManager().forElementSubRegions< WellElementSubRegion >( regionNames, [&]( localIndex const,
+ WellElementSubRegion const & subRegion )
+ {
+ string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString() );
+ constitutive::MultiFluidBase const & fluid = getConstitutiveModel< constitutive::MultiFluidBase >( subRegion, fluidName );
+ WellControls const & wellControls = getWellControls( subRegion );
+ wellControls.validateFluidModel( fluid, referenceFluid );
+ } );
+
+ } );
+ }
+ else
+ {
+ // Single phase validation can be added here in the future
+ }
+#endif
+}
+
+void WellManager::setupDofs( DomainPartition const & domain,
+ DofManager & dofManager ) const
+{
+ map< std::pair< string, string >, string_array > meshTargets;
+ forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const & meshBodyName,
+ MeshLevel const & meshLevel,
+ string_array const & regionNames )
+ {
+ string_array regions;
+ ElementRegionManager const & elementRegionManager = meshLevel.getElemManager();
+ elementRegionManager.forElementRegions< WellElementRegion >( regionNames,
+ [&]( localIndex const,
+ WellElementRegion const & region )
+ {
+ regions.emplace_back( region.getName() );
+ } );
+ auto const key = std::make_pair( meshBodyName, meshLevel.getName());
+ meshTargets[key] = std::move( regions );
+ } );
+
+ dofManager.addField( wellElementDofName(),
+ FieldLocation::Elem,
+ numDofPerWellElement(),
+ meshTargets );
+
+ dofManager.addCoupling( wellElementDofName(),
+ wellElementDofName(),
+ DofManager::Connector::Node );
+}
+
+void WellManager::assembleSystem( real64 const time,
+ real64 const dt,
+ DomainPartition & domain,
+ DofManager const & dofManager,
+ CRSMatrixView< real64, globalIndex const > const & localMatrix,
+ arrayView1d< real64 > const & localRhs )
+{
+
+
+ // selects constraints one of 2 ways
+ // wellEstimator flag set to 0 => orginal logic rates are computed during update state and constraints are selected every newton
+ // iteration
+ // wellEstimator flag > 0 => well esitmator solved for each constraint and then selects the constraint
+ // => estimator solve only performed first "wellEstimator" iterations
+ NonlinearSolverParameters const & nonlinearParams = getNonlinearSolverParameters();
+ IterationsStatistics const & iterationsStatistics = getIterationStats();
+ //selectWellConstraint( time, dt, solverStatistics.m_numNewtonIterations, domain );
+
+ forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const & meshBodyName,
+ MeshLevel & meshLevel,
+ string_array const & regionNames )
+ {
+ GEOS_UNUSED_VAR( meshBodyName );
+ ElementRegionManager & elementRegionManager = meshLevel.getElemManager();
+ elementRegionManager.forElementRegions< WellElementRegion >( regionNames,
+ [&]( localIndex const,
+ WellElementRegion & region )
+ {
+ WellElementSubRegion & subRegion = region.getGroup( ElementRegionBase::viewKeyStruct::elementSubRegions() )
+ .getGroup< WellElementSubRegion >( region.getSubRegionName() );
+ WellControls & wellControls = getWellControls( subRegion );
+ wellControls.selectWellConstraint( time,
+ dt,
+ iterationsStatistics.getNumTimeSteps(),
+ nonlinearParams.m_numNewtonIterations,
+ domain,
+ meshLevel,
+ elementRegionManager,
+ subRegion,
+ dofManager );
+
+ // assemble the accumulation term in the mass balance equations
+ wellControls.assembleWellAccumulationTerms( time, dt, subRegion, dofManager, localMatrix, localRhs );
+ if( wellControls.isWellOpen() )
+ {
+ // assemble the pressure relations between well elements
+ wellControls.assembleWellPressureRelations( time, dt, subRegion, dofManager, localMatrix, localRhs );
+ // assemble well constraint terms
+ wellControls.assembleWellConstraintTerms( time, dt, subRegion, dofManager, localMatrix.toViewConstSizes(), localRhs );
+ // compute the perforation rates (later assembled by the coupled solver)
+ wellControls.computeWellPerforationRates( time, dt, elementRegionManager, subRegion );
+ // assemble the flux terms in the mass balance equations
+ wellControls.assembleWellFluxTerms( time, dt, subRegion, dofManager, localMatrix, localRhs );
+ }
+ } );
+ } );
+
+}
+
+
+void WellManager::resetStateToBeginningOfStep( DomainPartition & domain )
+{
+ forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &,
+ MeshLevel & mesh,
+ string_array const & regionNames )
+ {
+
+ ElementRegionManager & elemManager = mesh.getElemManager();
+
+ elemManager.forElementSubRegions< WellElementSubRegion >( regionNames,
+ [&]( localIndex const,
+ WellElementSubRegion & subRegion )
+ {
+ WellControls & wellControls = getWellControls( subRegion );
+ wellControls.resetStateToBeginningOfStep( elemManager, subRegion );
+
+
+ } );
+ } );
+}
+
+void WellManager::implicitStepComplete( real64 const & time,
+ real64 const & dt,
+ DomainPartition & domain )
+{
+ forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &,
+ MeshLevel & mesh,
+ string_array const & regionNames )
+ {
+
+ ElementRegionManager & elemManager = mesh.getElemManager();
+
+ elemManager.forElementSubRegions< WellElementSubRegion >( regionNames,
+ [&]( localIndex const,
+ WellElementSubRegion & subRegion )
+ {
+ WellControls & wellControls = getWellControls( subRegion );
+ wellControls.implicitStepComplete( time, dt, subRegion );
+ } );
+ } );
+}
+
+void WellManager::postRestartInitialization()
+{
+#if 0
+ DomainPartition & domain = this->getGroupByPath< DomainPartition >( "/Problem/domain" );
+ forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &,
+ MeshLevel & mesh,
+ string_array const & regionNames )
+ {
+ // loop over the wells
+ mesh.getElemManager().forElementSubRegions< WellElementSubRegion >( regionNames, [&]( localIndex const,
+ WellElementSubRegion & subRegion )
+
+ {
+ WellControls & wellControls = getWell( subRegion );
+ wellControls.postRestartInitialization( );
+
+ } );
+ } );
+#endif
+}
+void WellManager::initializePostInitialConditionsPreSubGroups()
+{
+ PhysicsSolverBase::initializePostInitialConditionsPreSubGroups();
+ DomainPartition & domain = this->getGroupByPath< DomainPartition >( "/Problem/domain" );
+ forDiscretizationOnMeshTargets ( domain.getMeshBodies(), [&] ( string const &,
+ MeshLevel & mesh,
+ string_array const & regionNames )
+ {
+
+ // loop over the wells
+ mesh.getElemManager().forElementSubRegions< WellElementSubRegion >( regionNames, [&]( localIndex const,
+ WellElementSubRegion & subRegion )
+ {
+ // reconstruct local connectivity needed for flux calculations
+ subRegion.reconstructLocalConnectivity();
+ WellControls & wellControls = getWellControls( subRegion );
+ wellControls.initializeWellPostInitialConditionsPreSubGroups( subRegion );
+
+
+ } );
+ } );
+}
+void WellManager::setKeepVariablesConstantDuringInitStep( bool const keepVariablesConstantDuringInitStep )
+{
+ DomainPartition & domain = this->getGroupByPath< DomainPartition >( "/Problem/domain" );
+ forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &,
+ MeshLevel & mesh,
+ string_array const & regionNames )
+ {
+ // loop over the wells
+ mesh.getElemManager().forElementSubRegions< WellElementSubRegion >( regionNames, [&]( localIndex const,
+ WellElementSubRegion & subRegion )
+
+ {
+ WellControls & wellControls = getWellControls( subRegion );
+ wellControls.setKeepVariablesConstantDuringInitStep( keepVariablesConstantDuringInitStep );
+
+ } );
+ } );
+}
+void WellManager::updateState( DomainPartition & domain )
+{
+ GEOS_MARK_FUNCTION;
+
+ real64 maxPhaseVolFrac = 0.0;
+ forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &,
+ MeshLevel & mesh,
+ string_array const & regionNames )
+ {
+ ElementRegionManager & elemManager = mesh.getElemManager();
+ elemManager.forElementSubRegions< WellElementSubRegion >( regionNames, [&]( localIndex const,
+ WellElementSubRegion & subRegion )
+ {
+ WellControls & wellControls = getWellControls( subRegion );
+ if( wellControls.getWellState())
+ {
+
+ real64 const maxRegionPhaseVolFrac = wellControls.updateWellState( elemManager, subRegion );
+
+ maxPhaseVolFrac = LvArray::math::max( maxRegionPhaseVolFrac, maxPhaseVolFrac );
+ }
+ } );
+ } );
+ maxPhaseVolFrac = MpiWrapper::max( maxPhaseVolFrac );
+
+ GEOS_LOG_LEVEL_RANK_0( logInfo::Solution,
+ GEOS_FMT( " {}: Max well phase volume fraction change = {}",
+ getName(), fmt::format( "{:.{}f}", maxPhaseVolFrac, 4 ) ) );
+
+}
+
+real64
+WellManager::calculateResidualNorm( real64 const & time_n,
+ real64 const & dt,
+ DomainPartition const & domain,
+ DofManager const & dofManager,
+ arrayView1d< real64 const > const & localRhs )
+{
+ GEOS_MARK_FUNCTION;
+
+ integer numNorm = 1; // mass balance
+ array1d< real64 > localResidualNorm, wellResidalNorm;
+ array1d< real64 > localResidualNormalizer;
+
+ if( isThermal() )
+ {
+ numNorm = 2; // mass balance and energy balance
+ }
+ localResidualNorm.resize( numNorm );
+
+ localResidualNormalizer.resize( numNorm );
+
+ string const wellDofKey = dofManager.getKey( wellElementDofName() );
+
+ forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &,
+ MeshLevel const & mesh,
+ string_array const & regionNames )
+ {
+
+
+ ElementRegionManager const & elemManager = mesh.getElemManager();
+
+ elemManager.forElementSubRegions< WellElementSubRegion >( regionNames,
+ [&]( localIndex const,
+ WellElementSubRegion const & subRegion )
+ {
+
+ WellControls & wellControls = getWellControls( subRegion );
+
+ // step 1: compute the norm in the subRegion
+ if( wellControls.isWellOpen( ) )
+ {
+ wellResidalNorm = wellControls.calculateLocalWellResidualNorm( time_n,
+ dt,
+ m_nonlinearSolverParameters,
+ subRegion,
+ dofManager,
+ localRhs );
+ for( integer i=0; i localResidualNorm[i] )
+ {
+ localResidualNorm[i] = wellResidalNorm[i];
+ }
+ }
+ }
+ else
+ {
+ for( integer i=0; i const & localSolution )
+{
+ GEOS_MARK_FUNCTION;
+
+ string const wellDofKey = dofManager.getKey( wellElementDofName() );
+
+ real64 scalingFactor = 1.0;
+ real64 localScalingFactor = 1.0;
+ if( isCompositional() )
+ {
+
+
+ real64 maxDeltaPres = 0.0, maxDeltaCompDens = 0.0, maxDeltaTemp = 0.0;
+ real64 minPresScalingFactor = 1.0, minCompDensScalingFactor = 1.0, minTempScalingFactor = 1.0;
+ real64 localMaxDeltaPres = 0.0, localMaxDeltaCompDens = 0.0, localMaxDeltaTemp = 0.0;
+ real64 localMinPresScalingFactor = 1.0, localMinCompDensScalingFactor = 1.0, localMinTempScalingFactor = 1.0;
+
+ forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &,
+ MeshLevel & mesh,
+ string_array const & regionNames )
+ {
+
+ ElementRegionManager & elemManager = mesh.getElemManager();
+ elemManager.forElementSubRegions< WellElementSubRegion >( regionNames,
+ [&]( localIndex const,
+ WellElementSubRegion & subRegion )
+
+ {
+ CompositionalMultiphaseWell * wellControls = dynamic_cast< CompositionalMultiphaseWell * >(&getWellControls ( subRegion ));
+ localScalingFactor = wellControls->scalingForLocalSystemSolution( subRegion,
+ dofManager,
+ localMaxDeltaPres,
+ localMaxDeltaCompDens,
+ localMaxDeltaTemp,
+ localMinPresScalingFactor,
+ localMinCompDensScalingFactor,
+ localMinTempScalingFactor,
+ localSolution );
+ maxDeltaPres = LvArray::math::max( localMaxDeltaPres, maxDeltaPres );
+ maxDeltaCompDens = LvArray::math::max( localMaxDeltaCompDens, maxDeltaCompDens );
+ maxDeltaTemp = LvArray::math::max( localMaxDeltaTemp, maxDeltaTemp );
+ minPresScalingFactor = LvArray::math::min( localMinPresScalingFactor, minPresScalingFactor );
+ minCompDensScalingFactor = LvArray::math::min( localMinCompDensScalingFactor, minCompDensScalingFactor );
+ minTempScalingFactor = LvArray::math::min( localMinTempScalingFactor, minTempScalingFactor );
+ scalingFactor = LvArray::math::min( localScalingFactor, scalingFactor );
+
+ } );
+ } );
+
+ scalingFactor = MpiWrapper::min( scalingFactor );
+ maxDeltaPres = MpiWrapper::max( maxDeltaPres );
+ maxDeltaCompDens = MpiWrapper::max( maxDeltaCompDens );
+ minPresScalingFactor = MpiWrapper::min( minPresScalingFactor );
+ minCompDensScalingFactor = MpiWrapper::min( minCompDensScalingFactor );
+
+ string const massUnit = m_useMass ? "kg/m3" : "mol/m3";
+ GEOS_LOG_LEVEL_RANK_0( logInfo::Solution,
+ GEOS_FMT( " {}: Max well pressure change: {} Pa (before scaling)",
+ getName(), GEOS_FMT( "{:.{}f}", maxDeltaPres, 3 ) ) );
+ GEOS_LOG_LEVEL_RANK_0( logInfo::Solution,
+ GEOS_FMT( " {}: Max well component density change: {} {} (before scaling)",
+ getName(), GEOS_FMT( "{:.{}f}", maxDeltaCompDens, 3 ), massUnit ) );
+
+ if( m_isThermal )
+ {
+ maxDeltaTemp = MpiWrapper::max( maxDeltaTemp );
+ minTempScalingFactor = MpiWrapper::min( minTempScalingFactor );
+ GEOS_LOG_LEVEL_RANK_0( logInfo::Solution,
+ GEOS_FMT( " {}: Max well temperature change: {} K (before scaling)",
+ getName(), GEOS_FMT( "{:.{}f}", maxDeltaTemp, 3 ) ) );
+ }
+
+
+ GEOS_LOG_LEVEL_RANK_0( logInfo::Solution,
+ GEOS_FMT( " {}: Min well pressure scaling factor: {}",
+ getName(), minPresScalingFactor ) );
+ GEOS_LOG_LEVEL_RANK_0( logInfo::Solution,
+ GEOS_FMT( " {}: Min well component density scaling factor: {}",
+ getName(), minCompDensScalingFactor ) );
+ if( m_isThermal )
+ {
+ GEOS_LOG_LEVEL_RANK_0( logInfo::Solution,
+ GEOS_FMT( " {}: Min well temperature scaling factor: {}",
+ getName(), minTempScalingFactor ) );
+ }
+
+ }
+ else
+ {
+ // Single phase well scaling- not implemented yet
+ scalingFactor=1.0;
+ }
+ return LvArray::math::max( scalingFactor, m_minScalingFactor );
+
+}
+
+bool
+WellManager::checkSystemSolution( DomainPartition & domain,
+ DofManager const & dofManager,
+ arrayView1d< real64 const > const & localSolution,
+ real64 const scalingFactor )
+{
+ GEOS_MARK_FUNCTION;
+
+ string const wellDofKey = dofManager.getKey( wellElementDofName() );
+ integer globalCheck = 1;
+
+ forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &,
+ MeshLevel & mesh,
+ string_array const & regionNames )
+ {
+
+ ElementRegionManager & elemManager = mesh.getElemManager();
+ elemManager.forElementSubRegions< WellElementSubRegion >( regionNames,
+ [&]( localIndex const,
+ WellElementSubRegion & subRegion )
+
+ {
+
+ WellControls & wellControls = getWellControls( subRegion );
+ integer localCheck = wellControls.checkWellSystemSolution( subRegion, dofManager, localSolution, scalingFactor );
+ globalCheck = MpiWrapper::min( localCheck );
+ } );
+ } );
+ return globalCheck;
+}
+
+void
+WellManager::applySystemSolution( DofManager const & dofManager,
+ arrayView1d< real64 const > const & localSolution,
+ real64 const scalingFactor,
+ real64 const dt,
+ DomainPartition & domain )
+{
+
+
+ DofManager::CompMask pressureMask( m_numDofPerWellElement, 0, 1 );
+
+ DofManager::CompMask connRateMask( m_numDofPerWellElement, numFluidComponents()+1, numFluidComponents()+2 );
+ GEOS_UNUSED_VAR( dt );
+ // update all the fields using the global damping coefficients
+ dofManager.addVectorToField( localSolution,
+ wellElementDofName(),
+ well::pressure::key(),
+ scalingFactor,
+ pressureMask );
+
+ dofManager.addVectorToField( localSolution,
+ wellElementDofName(),
+ well::connectionRate::key(),
+ scalingFactor,
+ connRateMask );
+ if( isCompositional())
+ {
+ DofManager::CompMask componentMask( m_numDofPerWellElement, 1, numFluidComponents()+1 );
+ dofManager.addVectorToField( localSolution,
+ wellElementDofName(),
+ well::globalCompDensity::key(),
+ scalingFactor,
+ componentMask );
+
+ }
+ if( isThermal() )
+ {
+ DofManager::CompMask temperatureMask( m_numDofPerWellElement, numFluidComponents()+2, numFluidComponents()+3 );
+
+ dofManager.addVectorToField( localSolution,
+ wellElementDofName(),
+ well::temperature::key(),
+ scalingFactor,
+ temperatureMask );
+
+ }
+ // if component density chopping is allowed, some component densities may be negative after the update
+ // these negative component densities are set to zero in this function
+ if( isCompositional() && m_allowCompDensChopping )
+ {
+ chopNegativeDensities( domain );
+ }
+
+ forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &,
+ MeshLevel & mesh,
+ string_array const & regionNames )
+ {
+ stdVector< string > propNames;
+ propNames.emplace_back( well::pressure::key() );
+
+ propNames.emplace_back( well::connectionRate::key() );
+ if( isCompositional())
+ {
+ propNames.emplace_back( well::globalCompDensity::key() );
+ }
+ if( isThermal() )
+ {
+ propNames.emplace_back( well::temperature::key() );
+ }
+ // synchronize
+ FieldIdentifiers fieldsToBeSync;
+
+ fieldsToBeSync.addElementFields( propNames,
+ regionNames );
+
+
+ CommunicationTools::getInstance().synchronizeFields( fieldsToBeSync,
+ mesh,
+ domain.getNeighbors(),
+ true );
+ } );
+
+}
+
+void WellManager::chopNegativeDensities( DomainPartition & domain )
+{
+ integer const numComp = m_numFluidComponents;
+
+ forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &,
+ MeshLevel & mesh,
+ string_array const & regionNames )
+ {
+
+ ElementRegionManager & elemManager = mesh.getElemManager();
+
+ elemManager.forElementSubRegions< WellElementSubRegion >( regionNames,
+ [&]( localIndex const,
+ WellElementSubRegion & subRegion )
+ {
+ arrayView1d< integer const > const & wellElemGhostRank = subRegion.ghostRank();
+
+ arrayView2d< real64, compflow::USD_COMP > const & wellElemCompDens =
+ subRegion.getField< well::globalCompDensity >();
+
+ forAll< parallelDevicePolicy<> >( subRegion.size(), [=] GEOS_HOST_DEVICE ( localIndex const iwelem )
+ {
+ if( wellElemGhostRank[iwelem] < 0 )
+ {
+ for( integer ic = 0; ic < numComp; ++ic )
+ {
+ // we allowed for some densities to be slightly negative in CheckSystemSolution
+ // if the new density is negative, chop back to zero
+ if( wellElemCompDens[iwelem][ic] < 0 )
+ {
+ wellElemCompDens[iwelem][ic] = 0;
+ }
+ }
+ }
+ } );
+ } );
+
+ } );
+}
+
+REGISTER_CATALOG_ENTRY( PhysicsSolverBase, WellManager, string const &, Group * const )
+} // namespace geos
diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/WellManager.hpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellManager.hpp
new file mode 100644
index 00000000000..db764b24072
--- /dev/null
+++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellManager.hpp
@@ -0,0 +1,577 @@
+/*
+ * ------------------------------------------------------------------------------------------------------------
+ * 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 WellManager.hpp
+ */
+
+#ifndef GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_WELL_MANAGER_HPP_
+#define GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_WELL_MANAGER_HPP_
+
+#include "physicsSolvers/PhysicsSolverBase.hpp"
+#include "physicsSolvers/fluidFlow/SinglePhaseBase.hpp"
+#include "physicsSolvers/fluidFlow/CompositionalMultiphaseBase.hpp"
+#include "physicsSolvers/fluidFlow/wells/CompositionalMultiphaseWell.hpp"
+#include "physicsSolvers/fluidFlow/wells/SinglePhaseWell.hpp"
+namespace geos
+{
+
+class DomainPartition;
+class WellControls;
+class WellElementSubRegion;
+
+namespace dataRepository
+{
+namespace keys
+{
+static constexpr auto compositionalMultiphaseWell = "CompositionalMultiphaseWell";
+static constexpr auto singlePhaseWell = "SinglePhaseWell";
+}
+}
+/**
+ * @class WellManager
+ *
+ * Base class for well solvers.
+ * Provides some common features
+ */
+class WellManager : public PhysicsSolverBase
+{
+public:
+
+ /// String used to form the solverName used to register single-physics solvers in CoupledSolver
+ static string coupledSolverAttributePrefix() { return "well"; }
+
+ /**
+ * @brief main constructor for Group Objects
+ * @param name the name of this instantiation of Group in the repository
+ * @param parent the parent group of this instantiation of Group
+ */
+ WellManager( const string & name,
+ Group * const parent );
+
+ /// default destructor
+ virtual ~WellManager() override = default;
+
+ /// deleted default constructor
+ WellManager() = delete;
+
+ /// deleted copy constructor
+ WellManager( WellManager const & ) = delete;
+
+ /// default move constructor
+ WellManager( WellManager && ) = default;
+
+ /// deleted assignment operator
+ WellManager & operator=( WellManager const & ) = delete;
+
+ /// deleted move operator
+ WellManager & operator=( WellManager && ) = delete;
+
+ virtual Group * createChild( string const & childKey, string const & childName ) override;
+
+ /// Expand catalog for schema generation
+ virtual void expandObjectCatalogs() override;
+
+ /**
+ * @brief setter for the name of the flow solver (needed to use the flow kernels like UpdateFluid)
+ * @param name the name of the flow solver
+ */
+ void setFlowSolverName( string const & name ) { m_flowSolverName = name; }
+
+ /**
+ * @brief setter for compositional flag
+ * @param compositional the compositional flag
+ */
+ void setCompositional( bool const & isCompositional ) { m_isCompositional = isCompositional; }
+
+ /**
+ * @brief getter for compositional flag
+ * @return the compositional flag
+ */
+ bool isCompositional() const { return m_isCompositional; }
+
+
+ /**
+ * @brief getter for the name of the flow solver (used in UpdateState)
+ * @return a string containing the name of the flow solver
+ */
+ string const & getFlowSolverName() const { return m_flowSolverName; }
+
+
+ /**
+ * @brief name of the node manager in the object catalog
+ * @return string that contains the catalog name to generate a new NodeManager object through the object catalog.
+ */
+ static string catalogName() { return "WellManager"; }
+ /**
+ * @copydoc PhysicsSolverBase::getCatalogName()
+ */
+ string getCatalogName() const override { return catalogName(); }
+
+ virtual void registerDataOnMesh( Group & meshBodies ) override;
+ /**
+ * @brief Get a well solver for a given well element sub-region
+ * @param subRegion the well subRegion whose well solver is requested
+ * @return a reference to the well solver
+ */
+ WellControls & getWell( WellElementSubRegion const & subRegion );
+
+ /**
+ * @brief Get a well solver for a given well element sub-region
+ * @param wellControlsName name of well
+ * @return a reference to the well solver
+ */
+ WellControls & getWell( std::string const & wellControlsName );
+
+ /**
+ * @brief Get a well solver for a given well element sub-region
+ * @param wellControlsName name of well
+ * @return a reference to the well solver
+ */
+ WellControls const & getWell( std::string const & wellControlsName ) const;
+/**
+ * @brief get the name of DOF defined on well elements
+ * @return name of the DOF field used by derived solver type
+ */
+ string wellElementDofName() const;
+
+ struct viewKeyStruct : PhysicsSolverBase::viewKeyStruct
+ {
+ static constexpr char const * dofFieldString() { return "wellVars"; }
+ static constexpr char const * isThermalString() { return "isThermal"; }
+ static constexpr char const * useMassFlagString() {return "useMass"; }
+ /// @return string for the nextDt targetRegions wrapper
+ static constexpr char const * targetRegionsString() { return "targetRegions"; }
+ static constexpr char const * timeStepFromTablesFlagString() { return "timeStepFromTables"; }
+ static constexpr char const * useTotalMassEquationString() { return "useTotalMassEquation"; }
+ static constexpr char const * allowLocalCompDensChoppingString() { return CompositionalMultiphaseBase::viewKeyStruct::allowLocalCompDensChoppingString(); }
+
+
+ };
+
+ /**
+ * @brief getter for the number of degrees of freedom per well element
+ * @return the number of dofs
+ */
+ localIndex numDofPerWellElement() const;
+
+ /**
+ * @brief getter for the number of degrees of freedom per mesh element
+ * @return the number of dofs
+ */
+ localIndex numDofPerResElement() const;
+
+ /**
+ * @brief getter for iso/thermal switch
+ * @return True if thermal
+ */
+ integer isThermal() const;
+
+
+ /**
+ * @brief get the name of DOF defined on well elements
+ * @return name of the DOF field used by derived solver type
+ */
+ virtual string resElementDofName() const;
+
+ /**
+ * @brief const getter for the number of fluid components
+ * @return the number of fluid components
+ */
+ virtual localIndex numFluidComponents() const;
+
+ /**
+ * @brief const getter for the number of fluid phases
+ * @return the number of fluid phases
+ */
+ virtual localIndex numFluidPhases() const;
+
+ /**
+ * @brief const getter for well total mass equation usage
+ * @return true if total mass equation is used
+ */
+ integer useTotalMassEquation() const { return m_useTotalMassEquation; }
+
+ /**
+ * @brief getter for the well controls associated to this well subRegion
+ * @param subRegion the well subRegion whose controls are requested
+ * @return a reference to the controls
+ */
+ WellControls & getWellControls( WellElementSubRegion const & subRegion );
+
+ /**
+ * @brief const getter for the well controls associated to this well subRegion
+ * @param subRegion the well subRegion whose controls are requested
+ * @return a reference to the const controls
+ */
+ WellControls const & getWellControls( WellElementSubRegion const & subRegion ) const;
+
+ /**
+ * @brief getter for the compositional multiphase well associated to this well subRegion
+ * @param subRegion the well subRegion whose controls are requested
+ * @return a reference to the well
+ */
+ CompositionalMultiphaseWell & getCompositionalMultiphaseWell( WellElementSubRegion const & subRegion );
+
+ /**
+ * @brief const getter for the compositional multiphase well associated to this well subRegion
+ * @param subRegion the well subRegion whose controls are requested
+ * @return a reference to the const well
+ */
+ CompositionalMultiphaseWell const & getCompositionalMultiphaseWell( WellElementSubRegion const & subRegion ) const;
+
+ /**
+ * @brief Selects the active well constraint based on current conditions
+ * @param[in] currentTime the current time
+ * @param[in] currentDt the current time step size
+ * @param[in] coupledIterationNumber the current coupled iteration number
+ * @param[in] domain the domain object
+ * @return the prescribed time step size
+ */
+ void selectWellConstraint( real64 const & time_n,
+ real64 const & dt,
+ integer const coupledIterationNumber,
+ DomainPartition & domain );
+ /* PhysicsSolverBase interfaces */
+
+ /**
+ * @brief function to set the next time step size
+ * @param[in] currentTime the current time
+ * @param[in] currentDt the current time step size
+ * @param[in] domain the domain object
+ * @return the prescribed time step size
+ */
+ virtual real64 setNextDt( real64 const & currentTime,
+ real64 const & currentDt,
+ DomainPartition & domain ) override;
+
+ virtual void setupDofs( DomainPartition const & domain,
+ DofManager & dofManager ) const override;
+
+ /**
+ * @brief function to perform setup for implicit timestep
+ * @param time_n the time at the beginning of the step
+ * @param dt the desired timestep
+ * @param domain the domain partition
+ *
+ * This function should contain any step level initialization required to perform an implicit
+ * step.
+ *
+ * @note This function must be overridden in the derived physics solver in order to use an implict
+ * solution method such as LinearImplicitStep() or NonlinearImplicitStep().
+ */
+ virtual void
+ implicitStepSetup( real64 const & time_n,
+ real64 const & dt,
+ DomainPartition & domain ) override;
+
+
+ /**
+ * @brief function to assemble the linear system matrix and rhs
+ * @param time the time at the beginning of the step
+ * @param dt the desired timestep
+ * @param domain the domain partition
+ * @param dofManager degree-of-freedom manager associated with the linear system
+ * @param localMatrix the system matrix
+ * @param localRhs the system right-hand side vector
+ *
+ * This function assembles the residual and the jacobian of the residual wrt the primary
+ * variables. In a stand alone physics solver, this function will fill a single block in the
+ * block system. However the capability to query the block system structure for any coupled blocks
+ * may be implemented to fill in off diagonal blocks of the system to enable coupling between
+ * solvers.
+ *
+ * @note This function must be overridden in the derived physics solver in order to use an implict
+ * solution method such as LinearImplicitStep() or NonlinearImplicitStep().
+ */
+ virtual void
+ assembleSystem( real64 const time,
+ real64 const dt,
+ DomainPartition & domain,
+ DofManager const & dofManager,
+ CRSMatrixView< real64, globalIndex const > const & localMatrix,
+ arrayView1d< real64 > const & localRhs ) override;
+
+
+ virtual void
+ resetStateToBeginningOfStep( DomainPartition & domain ) override;
+
+ virtual void
+ implicitStepComplete( real64 const & time,
+ real64 const & dt,
+ DomainPartition & domain ) override;
+
+ virtual void applyBoundaryConditions( real64 const GEOS_UNUSED_PARAM( time_n ),
+ real64 const GEOS_UNUSED_PARAM( dt ),
+ DomainPartition & GEOS_UNUSED_PARAM( domain ),
+ DofManager const & GEOS_UNUSED_PARAM( dofManager ),
+ CRSMatrixView< real64, globalIndex const > const & GEOS_UNUSED_PARAM( localMatrix ),
+ arrayView1d< real64 > const & GEOS_UNUSED_PARAM( localRhs ) ) override {}
+
+ /**
+ * @brief calculate the norm of the global system residual
+ * @param time the time at the beginning of the step
+ * @param dt the desired timestep
+ * @param domain the domain partition
+ * @param dofManager degree-of-freedom manager associated with the linear system
+ * @param localRhs the system right-hand side vector
+ * @return norm of the residual
+ *
+ * This function returns the norm of global residual vector, which is suitable for comparison with
+ * a tolerance.
+ */
+ virtual real64
+ calculateResidualNorm( real64 const & time,
+ real64 const & dt,
+ DomainPartition const & domain,
+ DofManager const & dofManager,
+ arrayView1d< real64 const > const & localRhs ) override;
+ /**
+ * @brief Recompute all dependent quantities from primary variables (including constitutive models)
+ * @param domain the domain containing the mesh and fields
+ */
+ virtual void updateState( DomainPartition & domain ) override;
+
+ /**
+ * @brief Function to determine if the solution vector should be scaled back in order to maintain a known constraint.
+ * @param[in] domain The domain partition.
+ * @param[in] dofManager degree-of-freedom manager associated with the linear system
+ * @param[in] localSolution the solution vector
+ * @return The factor that should be used to scale the solution vector values when they are being applied.
+ */
+ virtual real64
+ scalingForSystemSolution( DomainPartition & domain,
+ DofManager const & dofManager,
+ arrayView1d< real64 const > const & localSolution )override;
+
+ /**
+ * @brief Function to check system solution for physical consistency and constraint violation
+ * @param domain the domain partition
+ * @param dofManager degree-of-freedom manager associated with the linear system
+ * @param localSolution the solution vector
+ * @param scalingFactor factor to scale the solution prior to application
+ * @return true if solution can be safely applied without violating physical constraints, false otherwise
+ *
+ * @note This function must be overridden in the derived physics solver in order to use an implict
+ * solution method such as LinearImplicitStep() or NonlinearImplicitStep().
+ *
+ */
+ virtual bool
+ checkSystemSolution( DomainPartition & domain,
+ DofManager const & dofManager,
+ arrayView1d< real64 const > const & localSolution,
+ real64 const scalingFactor ) override;
+
+ /**
+ * @brief Function to apply the solution vector to the state
+ * @param dofManager degree-of-freedom manager associated with the linear system
+ * @param localSolution the solution vector
+ * @param scalingFactor factor to scale the solution prior to application
+ * @param dt the timestep
+ * @param domain the domain partition
+ *
+ * This function performs 2 operations:
+ * 1) extract the solution vector for the "blockSystem" parameter, and applies the
+ * contents of the solution vector to the primary variable field data,
+ * 2) perform a synchronization of the primary field variable such that all ghosts are updated,
+ *
+ * The "scalingFactor" parameter allows for the scaled application of the solution vector. For
+ * instance, a line search may apply a negative scaling factor to remove part of the previously
+ * applied solution.
+ *
+ * @note This function must be overridden in the derived physics solver in order to use an implict
+ * solution method such as LinearImplicitStep() or NonlinearImplicitStep().
+ *
+ */
+ virtual void
+ applySystemSolution( DofManager const & dofManager,
+ arrayView1d< real64 const > const & localSolution,
+ real64 const scalingFactor,
+ real64 const dt,
+ DomainPartition & domain ) override;
+
+ /**
+ * @brief Sets all the negative component densities (if any) to zero.
+ * @param domain the physical domain object
+ */
+ void chopNegativeDensities( DomainPartition & domain );
+#if 0
+ /**
+ * @brief calculate the norm of the global system residual
+ * @param time the time at the beginning of the step
+ * @param dt the desired timestep
+ * @param domain the domain partition
+ * @param dofManager degree-of-freedom manager associated with the linear system
+ * @param localRhs the system right-hand side vector
+ * @return norm of the residual
+ *
+ * This function returns the norm of global residual vector, which is suitable for comparison with
+ * a tolerance.
+ */
+ virtual real64
+ calculateResidualNorm( real64 const & time,
+ real64 const & dt,
+ DomainPartition const & domain,
+ DofManager const & dofManager,
+ arrayView1d< real64 const > const & localRhs );
+ /**
+ * @brief Function to check system solution for physical consistency and constraint violation
+ * @param domain the domain partition
+ * @param dofManager degree-of-freedom manager associated with the linear system
+ * @param localSolution the solution vector
+ * @param scalingFactor factor to scale the solution prior to application
+ * @return true if solution can be safely applied without violating physical constraints, false otherwise
+ *
+ * @note This function must be overridden in the derived physics solver in order to use an implict
+ * solution method such as LinearImplicitStep() or NonlinearImplicitStep().
+ *
+ */
+ virtual bool
+ checkSystemSolution( DomainPartition & domain,
+ DofManager const & dofManager,
+ arrayView1d< real64 const > const & localSolution,
+ real64 const scalingFactor );
+
+ /**
+ * @brief Function to determine if the solution vector should be scaled back in order to maintain a known constraint.
+ * @param[in] domain The domain partition.
+ * @param[in] dofManager degree-of-freedom manager associated with the linear system
+ * @param[in] localSolution the solution vector
+ * @return The factor that should be used to scale the solution vector values when they are being applied.
+ */
+ virtual real64
+ scalingForSystemSolution( DomainPartition & domain,
+ DofManager const & dofManager,
+ arrayView1d< real64 const > const & localSolution );
+
+ /**
+ * @brief Function to apply the solution vector to the state
+ * @param dofManager degree-of-freedom manager associated with the linear system
+ * @param localSolution the solution vector
+ * @param scalingFactor factor to scale the solution prior to application
+ * @param dt the timestep
+ * @param domain the domain partition
+ *
+ * This function performs 2 operations:
+ * 1) extract the solution vector for the "blockSystem" parameter, and applies the
+ * contents of the solution vector to the primary variable field data,
+ * 2) perform a synchronization of the primary field variable such that all ghosts are updated,
+ *
+ * The "scalingFactor" parameter allows for the scaled application of the solution vector. For
+ * instance, a line search may apply a negative scaling factor to remove part of the previously
+ * applied solution.
+ *
+ * @note This function must be overridden in the derived physics solver in order to use an implict
+ * solution method such as LinearImplicitStep() or NonlinearImplicitStep().
+ *
+ */
+ virtual void
+ applySystemSolution( DofManager const & dofManager,
+ arrayView1d< real64 const > const & localSolution,
+ real64 const scalingFactor,
+ real64 const dt,
+ DomainPartition & domain );
+
+
+ /**
+ * @brief Recompute all dependent quantities from primary variables (including constitutive models)
+ * @param domain the domain containing the mesh and fields
+ */
+ virtual void updateState( DomainPartition & domain );
+
+ /**
+ * @brief perform cleanup for implicit timestep
+ * @param time the time at the beginning of the step
+ * @param dt the desired timestep
+ * @param domain the domain partition
+ *
+ * This function performs whatever tasks are required to complete an implicit timestep. For
+ * example, the acceptance of the solution will occur during this step, and deallocation of
+ * temporaries will be be performed in this function.
+ *
+ * @note This function must be overridden in the derived physics solver in order to use an implict
+ * solution method such as LinearImplicitStep() or NonlinearImplicitStep().
+ */
+ virtual void
+ implicitStepComplete( real64 const & time,
+ real64 const & dt,
+ DomainPartition & domain ) override;
+
+#endif
+
+ /**
+ * @brief Utility function to keep the well variables during a time step (used in
+ * poromechanics simulations)
+ * @param[in] keepVariablesConstantDuringInitStep flag to tell the solver to freeze its
+ * primary variables during a time step
+ * @detail This function is meant to be called by a specific task before/after the
+ * initialization step
+ */
+ void setKeepVariablesConstantDuringInitStep( bool const keepVariablesConstantDuringInitStep );
+
+
+protected:
+ //virtual void postInputInitialization() override;
+
+ virtual void initializePostSubGroups() override;
+
+ virtual void initializePostInitialConditionsPreSubGroups() override;
+
+ virtual void postRestartInitialization() override final;
+
+
+private:
+
+ /// name of the flow solver
+ string m_flowSolverName;
+
+ /// flag indicating whether mass or molar formulation should be used
+ integer m_useMass;
+
+ /// flag indicating whether total mass equation should be used
+ integer m_useTotalMassEquation;
+
+ /// flag indicating whether thermal formulation is used
+ integer m_isThermal;
+
+ /// flag indicating whether compositional formulation is used
+ bool m_isCompositional;
+
+
+ /// number of phases
+ integer m_numFluidPhases;
+
+ /// number of components
+ integer m_numFluidComponents;
+
+ /// number of degrees of freedom per well element
+ integer m_numDofPerWellElement;
+
+ /// number of degrees of freedom per reservoir element
+ integer m_numDofPerResElement;
+
+ /// minimum value of the scaling factor obtained by enforcing maxCompFracChange
+ real64 m_minScalingFactor;
+
+ /// flag indicating whether local (cell-wise) chopping of negative compositions is allowed
+ integer m_allowCompDensChopping;
+
+ // flag to enable time step selection base on rates/bhp tables coordinates
+ integer m_timeStepFromTables;
+};
+
+}
+
+#endif //GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_WELL_MANAGER_HPP_
diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/WellMassRateConstraint.cpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellMassRateConstraint.cpp
new file mode 100644
index 00000000000..eadffc21b42
--- /dev/null
+++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellMassRateConstraint.cpp
@@ -0,0 +1,75 @@
+/*
+ * ------------------------------------------------------------------------------------------------------------
+ * 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 WellMassRateConstraints.cpp
+ */
+
+#include "LogLevelsInfo.hpp"
+#include "WellMassRateConstraint.hpp"
+#include "WellConstants.hpp"
+#include "dataRepository/InputFlags.hpp"
+#include "functions/FunctionManager.hpp"
+
+namespace geos
+{
+
+using namespace dataRepository;
+
+MassRateConstraint::MassRateConstraint( string const & name, Group * const parent )
+ : WellConstraintBase( name, parent )
+{
+ this->setInputFlags( InputFlags::OPTIONAL_NONUNIQUE );
+
+ this->registerWrapper( viewKeyStruct::massRateString(), &this->m_constraintValue ).
+ setDefaultValue( 0.0 ).
+ setInputFlag( InputFlags::OPTIONAL ).
+ setRestartFlags( RestartFlags::WRITE_AND_READ ).
+ setDescription( "Maximum mass rate (kg/s)" );
+}
+
+MassRateConstraint::~MassRateConstraint()
+{}
+
+
+void MassRateConstraint::postInputInitialization()
+{
+ // Validate table options
+ WellConstraintBase::postInputInitialization();
+
+ // check constraint value
+ GEOS_THROW_IF( m_constraintValue < 0,
+ getWrapperDataContext( viewKeyStruct::massRateString() ) << ": Target value is negative",
+ InputError );
+
+ GEOS_THROW_IF ((m_constraintValue <= 0.0 && m_constraintScheduleTableName.empty()),
+ getName() << " " << getDataContext() << ": You need to specify a mass rate constraint. \n" <<
+ "The rate constraint can be specified using " <<
+ "either " << viewKeyStruct::massRateString() <<
+ " or " << WellConstraintBase::viewKeyStruct::constraintScheduleTableNameString(),
+ InputError );
+}
+
+
+bool MassRateConstraint::checkViolation( WellConstraintBase const & currentConstraint, real64 const & currentTime )const
+{
+ // isViolated is defined as a static method on the specific WellConstraintType (Injection/Production)
+ // Evaluate violation according to the sign set for injectors/producers
+ real64 const currentValue = currentConstraint.massRate();
+ real64 const constraintValue = this->getConstraintValue( currentTime );
+ return ( LvArray::math::abs( currentValue ) > LvArray::math::abs( constraintValue ) );
+
+}
+} //namespace geos
diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/WellMassRateConstraint.hpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellMassRateConstraint.hpp
new file mode 100644
index 00000000000..6cd6703ba36
--- /dev/null
+++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellMassRateConstraint.hpp
@@ -0,0 +1,120 @@
+/*
+ * ------------------------------------------------------------------------------------------------------------
+ * 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 WellMassRateConstraint.hpp
+ */
+
+
+#ifndef GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_WELLMASSRATECONSTRAINT_HPP
+#define GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_WELLMASSRATECONSTRAINT_HPP
+
+#include "common/format/EnumStrings.hpp"
+#include "dataRepository/Group.hpp"
+#include "functions/TableFunction.hpp"
+#include "WellConstraintsBase.hpp"
+namespace geos
+{
+
+/**
+ * @class MassRateConstraint
+ * @brief This class describes a mass rate constraint used to control a well.
+ */
+
+class MassRateConstraint : public WellConstraintBase
+{
+public:
+
+ /**
+ * @name Constructor / Destructor
+ */
+ ///@{
+
+ /**
+ * @brief Constructor for WellControls Objects.
+ * @param[in] name the name of this instantiation of WellControls in the repository
+ * @param[in] parent the parent group of this instantiation of WellControls
+ */
+ explicit MassRateConstraint( string const & name, dataRepository::Group * const parent );
+
+
+ /**
+ * @brief Default destructor.
+ */
+ ~MassRateConstraint() override;
+
+ /**
+ * @brief Deleted default constructor.
+ */
+ MassRateConstraint() = delete;
+
+ /**
+ * @brief Deleted copy constructor.
+ */
+ MassRateConstraint( MassRateConstraint const & ) = delete;
+
+ /**
+ * @brief Deleted move constructor.
+ */
+ MassRateConstraint( MassRateConstraint && ) = delete;
+
+ /**
+ * @brief Deleted assignment operator.
+ * @return a reference to a constraint object
+ */
+ MassRateConstraint & operator=( MassRateConstraint const & ) = delete;
+
+ /**
+ * @brief Deleted move operator.
+ * @return a reference to a constraint object
+ */
+ MassRateConstraint & operator=( MassRateConstraint && ) = delete;
+
+ /**
+ * @brief name of the node manager in the object catalog
+ * @return string that contains the catalog name to generate a new Constraint object through the object catalog.
+ */
+ static string catalogName()
+ {
+ return "MassRateConstraint";
+ }
+ ///@}
+
+ struct viewKeyStruct
+ {
+ /// String key for the well target rate
+ static constexpr char const * massRateString() { return "massRate"; }
+ };
+
+ /**
+ * @name Getters / Setters
+ */
+
+ // Temp interface - tjb
+ virtual ConstraintTypeId getControl() const override { return ConstraintTypeId::MASSRATE; };
+ ///@}
+
+ virtual bool checkViolation( WellConstraintBase const & currentConstraint, real64 const & currentTime ) const override;
+
+protected:
+
+ virtual void postInputInitialization() override;
+
+};
+
+
+} //namespace geos
+
+#endif //GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_WELLMASSRATECONSTRAINT_HPP
diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/WellNewtonSolver.cpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellNewtonSolver.cpp
new file mode 100644
index 00000000000..bad6991f312
--- /dev/null
+++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellNewtonSolver.cpp
@@ -0,0 +1,535 @@
+/*
+ * ------------------------------------------------------------------------------------------------------------
+ * 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 "WellNewtonSolver.hpp"
+
+#include "common/MpiWrapper.hpp"
+#include "codingUtilities/RTTypes.hpp"
+#include "common/format/EnumStrings.hpp"
+#include "dataRepository/Group.hpp"
+#include "physicsSolvers/LogLevelsInfo.hpp"
+#include "common/format/LogPart.hpp"
+#include "common/TimingMacros.hpp"
+#include "linearAlgebra/solvers/KrylovSolver.hpp"
+#include "mesh/DomainPartition.hpp"
+#include "math/interpolation/Interpolation.hpp"
+#include "common/Timer.hpp"
+#include "common/Units.hpp"
+
+
+namespace geos
+{
+
+using namespace dataRepository;
+
+WellNewtonSolver::WellNewtonSolver( string const & name,
+ Group * const parent )
+ :
+ dataRepository::Group( name, parent ),
+
+ m_dofManager( name ),
+ m_usePhysicsScaling( 1 ),
+ m_linearSolverParameters( groupKeyStruct::linearSolverParametersString(), this ),
+ m_nonlinearSolverParameters( groupKeyStruct::nonlinearSolverParametersString(), this ),
+ m_solverStatistics( groupKeyStruct::solverStatisticsString(), this ),
+ m_systemSetupTimestamp( 0 ),
+ m_activeCoupledIterations( 1 ),
+ m_enableIsoThermalEstimator( 0 )
+{
+ setInputFlags( InputFlags::OPTIONAL_NONUNIQUE );
+
+ this->registerWrapper( viewKeyStruct::activeCoupledIterationsString(), &m_activeCoupledIterations ).
+ setApplyDefaultValue( 1 ).
+ setInputFlag( InputFlags::OPTIONAL ).
+ setDescription( "Number of coupled iterations to activate estimator solve." );
+
+ registerWrapper( viewKeyStruct::enableIsoThermalEstimatorString(), &m_enableIsoThermalEstimator ).
+ setDefaultValue( 0 ).
+ setInputFlag( InputFlags::OPTIONAL ).
+ setDescription( "Estimator configuration option to disable thermal effects on initial well constraint solve and then converge solution with thermal effects enabled: \n"
+ " - If the flag is set to 1, thermal effects are enabled during the initial constraint solve. \n"
+ " - If the flag is set to 0, thermal effects are disabled during the initial constraint solve." );
+
+
+
+ registerWrapper( viewKeyStruct::writeLinearSystemString(), &m_writeLinearSystem ).
+ setApplyDefaultValue( 0 ).
+ setInputFlag( InputFlags::OPTIONAL ).
+ setRestartFlags( RestartFlags::WRITE_AND_READ ).
+ setDescription( "Write matrix, rhs, solution to screen ( = 1) or file ( = 2)." );
+
+ registerWrapper( viewKeyStruct::allowNonConvergedLinearSolverSolutionString(), &m_allowNonConvergedLinearSolverSolution ).
+ setApplyDefaultValue( 1 ).
+ setInputFlag( InputFlags::OPTIONAL ).
+ setRestartFlags( RestartFlags::WRITE_AND_READ ).
+ setDescription( "Cut time step if linear solution fail without going until max nonlinear iterations." );
+
+ registerWrapper( viewKeyStruct::usePhysicsScalingString(), &m_usePhysicsScaling ).
+ setApplyDefaultValue( 1 ).
+ setInputFlag( InputFlags::OPTIONAL ).
+ setDescription( "Enable physics-based scaling of the linear system. Default: true." );
+
+
+ registerWrapper( viewKeyStruct::writeStatisticsCSVString(), &m_writeStatisticsCSV ).
+ setApplyDefaultValue( StatsOutputType::none ).
+ setInputFlag( InputFlags::OPTIONAL ).
+ setRestartFlags( RestartFlags::NO_WRITE ).
+ setDescription( GEOS_FMT( "When set to `{}`, output iterations information to a csv\n"
+ "When set to `{}`, output convergence information to a csv\n"
+ "When set to `{}` output both convergence & iteration information to a csv.",
+ EnumStrings< StatsOutputType >::toString( StatsOutputType::iteration ),
+ EnumStrings< StatsOutputType >::toString( StatsOutputType::convergence ),
+ EnumStrings< StatsOutputType >::toString( StatsOutputType::all ) ));
+
+ addLogLevel< logInfo::Convergence >();
+ addLogLevel< logInfo::Fields >();
+ addLogLevel< logInfo::LinearSolver >();
+ addLogLevel< logInfo::ResidualNorm >();
+ addLogLevel< logInfo::Solution >();
+ addLogLevel< logInfo::TimeStep >();
+ addLogLevel< logInfo::Timers >();
+
+ registerGroup( groupKeyStruct::linearSolverParametersString(), &m_linearSolverParameters );
+ registerGroup( groupKeyStruct::nonlinearSolverParametersString(), &m_nonlinearSolverParameters );
+ registerGroup( groupKeyStruct::solverStatisticsString(), &m_solverStatistics );
+
+ m_localMatrix.setName( this->getName() + "/localMatrix" );
+ m_matrix.setDofManager( &m_dofManager );
+}
+
+void WellNewtonSolver::postInputInitialization()
+{
+ m_solverStatistics.setOutputFilesName( getName() );
+
+ m_solverStatistics.makeDir( m_writeStatisticsCSV != StatsOutputType::none );
+
+ getIterationStats().setTableName( getName() );
+ getIterationStats().setLogOutputRequest( true );
+ getIterationStats().setCSVOutputRequest( m_writeStatisticsCSV == StatsOutputType::iteration ||
+ m_writeStatisticsCSV == StatsOutputType::all );
+ getConvergenceStats().setCSVOutputRequest( m_writeStatisticsCSV == StatsOutputType::convergence ||
+ m_writeStatisticsCSV == StatsOutputType::all );
+}
+
+WellNewtonSolver::~WellNewtonSolver() = default;
+
+#if 0
+void WellNewtonSolver::initialize_postMeshGeneration()
+{
+ //ExecutableGroup::initialize_postMeshGeneration();
+ //DomainPartition const & domain = this->getGroupByPath< DomainPartition >( "/Problem/domain" );
+ //generateMeshTargetsFromTargetRegions( domain.getMeshBodies());
+}
+#endif
+void WellNewtonSolver::generateMeshTargetsFromTargetRegions( Group const & meshBodies )
+{
+ for( auto const & target : m_targetRegionNames )
+ {
+
+ stdVector< string > targetTokens = stringutilities::tokenize( target, "/" );
+
+ if( targetTokens.size()==1 ) // no MeshBody or MeshLevel specified
+ {
+ GEOS_ERROR_IF( meshBodies.numSubGroups() != 1,
+ getDataContext() << ": No MeshBody information is specified in" <<
+ " WellNewtonSolver::meshTargets, but there are multiple MeshBody objects",
+ getDataContext() );
+ MeshBody const & meshBody = meshBodies.getGroup< MeshBody >( 0 );
+ string const meshBodyName = meshBody.getName();
+
+ string const meshLevelName = ""; //tjbm_discretizationName;
+
+ string const regionName = target;
+ auto const key = std::make_pair( meshBodyName, meshLevelName );
+ m_meshTargets[key].emplace_back( regionName );
+ }
+ else if( targetTokens.size()==2 )
+ {
+ string const meshBodyName = targetTokens[0];
+ GEOS_ERROR_IF( !meshBodies.hasGroup( meshBodyName ),
+ getWrapperDataContext( viewKeyStruct::targetRegionsString() ) << ": MeshBody (" <<
+ meshBodyName << ") is specified in targetRegions, but does not exist.",
+ getWrapperDataContext( viewKeyStruct::targetRegionsString() ) );
+
+ string const meshLevelName = "";//tjbm_discretizationName;
+
+ string const regionName = targetTokens[1];
+
+
+ auto const key = std::make_pair( meshBodyName, meshLevelName );
+ m_meshTargets[key].emplace_back( regionName );
+ }
+ else
+ {
+ GEOS_ERROR( getDataContext() << ": Invalid specification of targetRegions" );
+ }
+ }
+}
+
+
+
+Group * WellNewtonSolver::createChild( string const & GEOS_UNUSED_PARAM( childKey ), string const & GEOS_UNUSED_PARAM( childName ) )
+{
+ // Unused as all children are created within the constructor
+ return nullptr;
+}
+
+WellNewtonSolver::CatalogInterface::CatalogType & WellNewtonSolver::getCatalog()
+{
+ static WellNewtonSolver::CatalogInterface::CatalogType catalog;
+ return catalog;
+}
+
+
+bool WellNewtonSolver::registerCallback( void * func, const std::type_info & funcType )
+{
+ if( std::type_index( funcType ) == std::type_index( typeid( std::function< void( CRSMatrix< real64, globalIndex >, array1d< real64 > ) > ) ) )
+ {
+ m_assemblyCallback = *reinterpret_cast< std::function< void( CRSMatrix< real64, globalIndex >, array1d< real64 > ) > * >( func );
+ return true;
+ }
+
+ return false;
+}
+
+
+
+void WellNewtonSolver::logEndOfCycleInformation( integer const cycleNumber,
+ integer const numOfSubSteps,
+ stdVector< real64 > const & subStepDts ) const
+{
+ LogPart logpart( "TIMESTEP", MpiWrapper::commRank() == 0 );
+ logpart.addEndDescription( "- Cycle ", cycleNumber );
+ logpart.addEndDescription( "- N substeps ", numOfSubSteps );
+
+ std::stringstream logMessage;
+ for( integer i = 0; i < numOfSubSteps; ++i )
+ {
+ if( i > 0 )
+ {
+ logMessage << ", ";
+ }
+ logMessage << subStepDts[i] << " " << units::getSymbol( units::Unit::Time );
+ }
+
+ if( logMessage.rdbuf()->in_avail() == 0 )
+ logMessage << "/";
+
+ logpart.addEndDescription( "- substep dts ", logMessage.str() );
+ logpart.end();
+
+ if( isLogLevelActive< logInfo::SolverExecutionDetails >( getLogLevel()))
+ getIterationStats().outputStatistics();
+}
+
+#if 0
+// tjb keep this history
+void WellNewtonSolver::implicitStepSetup( real64 const & GEOS_UNUSED_PARAM( time_n ),
+ real64 const & GEOS_UNUSED_PARAM( dt ),
+ DomainPartition & GEOS_UNUSED_PARAM( domain ) )
+{
+ // clean the solution history
+ while( m_solutionHistory.size() > 0 )
+ {
+ m_solutionHistory.eraseArray( 0 );
+ }
+}
+#endif
+void WellNewtonSolver::setupDofs( DomainPartition const & GEOS_UNUSED_PARAM( domain ),
+ DofManager & GEOS_UNUSED_PARAM( dofManager ) ) const
+{
+ GEOS_ERROR( "WellNewtonSolver::setupDofs called!. Should be overridden." );
+}
+
+void WellNewtonSolver::setSparsityPattern( DomainPartition & GEOS_UNUSED_PARAM( domain ),
+ DofManager & dofManager,
+ CRSMatrix< real64, globalIndex > & GEOS_UNUSED_PARAM( localMatrix ),
+ SparsityPattern< globalIndex > & pattern )
+{
+ dofManager.setSparsityPattern( pattern );
+}
+
+void WellNewtonSolver::setSystemSetupTimestamp( Timestamp timestamp )
+{
+ m_systemSetupTimestamp = timestamp;
+
+ std::ostringstream oss;
+ m_dofManager.printFieldInfo( oss );
+ GEOS_LOG_LEVEL( logInfo::Fields, oss.str());
+}
+
+std::unique_ptr< PreconditionerBase< LAInterface > >
+WellNewtonSolver::createPreconditioner( DomainPartition & GEOS_UNUSED_PARAM( domain ) ) const
+{
+ // By default, do not create a preconditioner, one will be created internally inside LA backend
+ return {};
+
+ // TODO: refactor interfaces to always create preconditioner externally and pass to backends
+ // return LAInterface::createPreconditioner( m_linearSolverParameters.get() );
+}
+
+
+namespace
+{
+
+/**
+ * @brief Helper for debug output of linear algebra objects (matrices and vectors)
+ * @tparam T type of LA object (must have stream insertion and .write() implemented)
+ * @param obj the object to output
+ * @param cycleNumber event cycle number
+ * @param nonlinearIteration nonlinear iteration number
+ * @param filePrefix short filename prefix (e.g. "mat")
+ * @param screenName long name for screen output (e.g. "System matrix")
+ * @param toScreen whether to print on screen
+ * @param toFile whether to write to file
+ */
+template< typename T >
+void debugOutputLAObject( T const & obj,
+ real64 const & GEOS_UNUSED_PARAM( time ),
+ integer const cycleNumber,
+ integer const nonlinearIteration,
+ string const & filePrefix,
+ string const & screenName,
+ bool const toScreen,
+ bool const toFile )
+{
+ if( toScreen )
+ {
+ GEOS_LOG_RANK_0( GEOS_FMT( "{2:=>{1}}\n{0}:\n{2:=>{1}}", screenName, screenName.size() + 1, "" ) );
+ GEOS_LOG( obj );
+ }
+
+ if( toFile )
+ {
+ string const filename = GEOS_FMT( "{}_{:06}_{:02}.mtx", filePrefix.c_str(), cycleNumber, nonlinearIteration );
+ obj.write( filename, LAIOutputFormat::MATRIX_MARKET );
+ GEOS_LOG_RANK_0( GEOS_FMT( "{} written to {}", screenName, filename ) );
+ }
+}
+
+}
+
+void WellNewtonSolver::debugOutputSystem( real64 const & time,
+ integer const cycleNumber,
+ integer const nonlinearIteration,
+ ParallelMatrix const & matrix,
+ ParallelVector const & rhs ) const
+{
+ // special case when flag value > 2
+ if( m_writeLinearSystem > 2 && cycleNumber < m_writeLinearSystem )
+ return;
+
+ debugOutputLAObject( matrix,
+ time,
+ cycleNumber,
+ nonlinearIteration,
+ getName() + "_mat",
+ "System matrix",
+ m_writeLinearSystem == 1,
+ m_writeLinearSystem >= 2 );
+
+ debugOutputLAObject( rhs,
+ time,
+ cycleNumber,
+ nonlinearIteration,
+ getName() + "_rhs",
+ "System right-hand side",
+ m_writeLinearSystem == 1,
+ m_writeLinearSystem >= 2 );
+}
+
+void WellNewtonSolver::debugOutputSolution( real64 const & time,
+ integer const cycleNumber,
+ integer const nonlinearIteration,
+ ParallelVector const & solution ) const
+{
+ // special case when flag value > 2
+ if( m_writeLinearSystem > 2 && cycleNumber < m_writeLinearSystem )
+ return;
+
+ debugOutputLAObject( solution,
+ time,
+ cycleNumber,
+ nonlinearIteration,
+ getName() + "_sol",
+ "System solution",
+ m_writeLinearSystem == 1,
+ m_writeLinearSystem >= 2 );
+}
+
+void WellNewtonSolver::updateAndWriteConvergenceStep( real64 const & time_n, real64 const & dt,
+ integer const cycleNumber, integer const iteration )
+{
+ getConvergenceStats().updateSolverStep( time_n, dt, cycleNumber, iteration );
+ getConvergenceStats().writeConvergenceStatsToTable();
+}
+
+
+void WellNewtonSolver::solveLinearSystem( DofManager const & dofManager,
+ ParallelMatrix & matrix,
+ ParallelVector & rhs,
+ ParallelVector & solution )
+{
+ GEOS_MARK_FUNCTION;
+
+ rhs.scale( -1.0 );
+ solution.zero();
+
+ LinearSolverParameters const & params = m_linearSolverParameters.get();
+ const bool isDirectSolver = (params.solverType == LinearSolverParameters::SolverType::direct);
+ const bool isSetupNeeded = !(isDirectSolver && params.direct.reuseFactorization);
+
+ matrix.setDofManager( &dofManager );
+
+ GEOS_WARNING_IF( isDirectSolver && dofManager.numGlobalDofs() > 100000,
+ "Direct solver used for large system ( > 100,000 DOFs ). "
+ "This may lead to high memory consumption and long computation times. "
+ "Consider using an iterative solver for better performance." );
+
+ // Apply physics-based scaling to the linear system if enabled
+ if( m_usePhysicsScaling )
+ {
+ Timer timer_setup( m_timers.get_inserted( "linear solver scaling" ) );
+
+ matrix.computeScalingVector( m_scaling );
+ matrix.leftRightScale( m_scaling, m_scaling );
+ rhs.pointwiseProduct( m_scaling );
+ // Assume the solution is zeroed out, thus no need to scale it
+ }
+
+ if( isDirectSolver || !m_precond )
+ {
+ if( !m_linearSolver )
+ {
+ m_linearSolver = LAInterface::createSolver( params );
+ }
+
+ if( isSetupNeeded )
+ {
+ Timer timer_setup( m_timers.get_inserted( "linear solver setup" ) );
+ m_linearSolver->setup( matrix );
+ }
+
+ {
+ Timer timer_setup( m_timers.get_inserted( "linear solver solve" ) );
+ m_linearSolver->solve( rhs, solution );
+ }
+
+ m_linearSolverResult = m_linearSolver->result();
+ }
+ else
+ {
+ {
+ Timer timer_setup( m_timers.get_inserted( "linear solver setup" ) );
+ m_precond->setup( matrix );
+ }
+ std::unique_ptr< KrylovSolver< ParallelVector > > solver = KrylovSolver< ParallelVector >::create( params, matrix, *m_precond );
+ {
+ Timer timer_setup( m_timers.get_inserted( "linear solver solve" ) );
+ solver->solve( rhs, solution );
+ }
+ m_linearSolverResult = solver->result();
+ }
+
+ getIterationStats().accumulateSolverLinearTime( m_linearSolverResult.setupTime, m_linearSolverResult.solveTime );
+
+ GEOS_LOG_LEVEL_RANK_0( logInfo::LinearSolver,
+ GEOS_FMT( " Linear solve: ( iter, res ) = ( {:3}, {:4.2e} )",
+ m_linearSolverResult.numIterations,
+ m_linearSolverResult.residualReduction ));
+
+ if( params.stopIfError )
+ {
+ GEOS_ERROR_IF( m_linearSolverResult.breakdown(),
+ getDataContext() << ": Linear solution breakdown -> simulation STOP",
+ getDataContext() );
+ }
+ else
+ {
+ GEOS_WARNING_IF( !m_linearSolverResult.success(),
+ getDataContext() << ": Linear solution failed",
+ getDataContext() );
+ }
+
+ // Unscale the solution vector if physics-based scaling was applied
+ if( m_usePhysicsScaling )
+ {
+ Timer timer_setup( m_timers.get_inserted( "linear solver scaling" ) );
+
+ solution.pointwiseProduct( m_scaling );
+ }
+}
+
+
+
+// Detect oscillations for all dofs in the solution history
+bool WellNewtonSolver::detectOscillations() const
+{
+ // grab the parameters
+ integer const oscillationCheckDepth = m_nonlinearSolverParameters.m_oscillationCheckDepth;
+ real64 const oscillationTolerance = m_nonlinearSolverParameters.m_oscillationTolerance;
+ real64 const oscillationFraction = m_nonlinearSolverParameters.m_oscillationFraction;
+
+ if( m_solutionHistory.size() < oscillationCheckDepth )
+ return false; // not enough history to check oscillations
+
+ RAJA::ReduceSum< parallelDeviceReduce, localIndex > oscillationCount( 0 );
+
+ auto const solutionHistory = m_solutionHistory.toViewConst();
+ localIndex const numDofs = m_solutionHistory[0].size();
+ localIndex const historySize = m_solutionHistory.size();
+
+ RAJA::forall< parallelDevicePolicy<> >( RAJA::TypedRangeSegment< localIndex >( 0, numDofs ),
+ [=] GEOS_HOST_DEVICE ( localIndex const dof )
+ {
+ bool oscillationDetected = true;
+ for( localIndex i = historySize - 1; i > historySize - oscillationCheckDepth; --i )
+ {
+ real64 dxCur = solutionHistory[i][dof];
+ real64 dxPrev = solutionHistory[i-1][dof];
+
+ if( LvArray::math::abs( dxCur ) < oscillationTolerance || LvArray::math::abs( dxPrev ) < oscillationTolerance )
+ {
+ oscillationDetected = false;
+ break; // solution changes are too small
+ }
+
+ real64 maxAbs = LvArray::math::max( LvArray::math::abs( dxCur ), LvArray::math::abs( dxPrev ) );
+ if( LvArray::math::abs( dxCur + dxPrev ) / maxAbs > oscillationTolerance )
+ {
+ oscillationDetected = false;
+ break; // solution changes are not oscillating
+ }
+
+ if( dxCur * dxPrev > 0 )
+ {
+ oscillationDetected = false;
+ break; // sign is not oscillating
+ }
+ }
+
+ if( oscillationDetected )
+ {
+ oscillationCount += 1;
+ }
+ } );
+
+ real64 const f = static_cast< real64 >( MpiWrapper::sum( oscillationCount.get() ) ) / MpiWrapper::sum( numDofs );
+
+ return f > oscillationFraction;
+}
+
+
+} // namespace geos
diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/WellNewtonSolver.hpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellNewtonSolver.hpp
new file mode 100644
index 00000000000..705c959f8bf
--- /dev/null
+++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellNewtonSolver.hpp
@@ -0,0 +1,871 @@
+/*
+ * ------------------------------------------------------------------------------------------------------------
+ * 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 WellNewtonSolver.hpp
+ */
+
+#ifndef GEOS_PHYSICSSOLVERS_WELLNEWTONSOLVER_HPP_
+#define GEOS_PHYSICSSOLVERS_WELLNEWTONSOLVER_HPP_
+
+#include "codingUtilities/traits.hpp"
+#include "common/DataTypes.hpp"
+#include "common/format/LogPart.hpp"
+
+#include "dataRepository/RestartFlags.hpp"
+#include "linearAlgebra/interfaces/InterfaceTypes.hpp"
+#include "linearAlgebra/utilities/LinearSolverResult.hpp"
+#include "linearAlgebra/DofManager.hpp"
+#include "mesh/MeshBody.hpp"
+#include "physicsSolvers/NonlinearSolverParameters.hpp"
+#include "physicsSolvers/LinearSolverParameters.hpp"
+#include "physicsSolvers/SolverStatistics.hpp"
+#include "physicsSolvers/LogLevelsInfo.hpp"
+#include "common/Timer.hpp"
+#include
+
+namespace geos
+{
+
+class DomainPartition;
+
+/**
+ * @class WellNewtonSolver
+ * @brief Base class for all physics solvers
+ *
+ * This class provides the base interface for all physics solvers. It provides the basic
+ * functionality for setting up and solving a linear system, as well as the interface for
+ * performing a timestep.
+ */
+class WellNewtonSolver : public dataRepository::Group
+{
+public:
+
+ /**
+ * @brief Type of the stat output
+ */
+ enum class StatsOutputType : integer
+ {
+ none, iteration, convergence, all
+ };
+
+ /**
+ * @brief Constructor for WellNewtonSolver
+ * @param name the name of this instantiation of WellNewtonSolver
+ * @param parent the parent group of this instantiation of WellNewtonSolver
+ */
+ explicit WellNewtonSolver( string const & name,
+ Group * const parent );
+
+ /**
+ * @brief Move constructor for WellNewtonSolver
+ */
+ WellNewtonSolver( WellNewtonSolver && ) = default;
+
+ /**
+ * @brief Destructor for WellNewtonSolver
+ */
+ virtual ~WellNewtonSolver() override;
+
+ /**
+ * @brief Deleted constructor
+ */
+ WellNewtonSolver() = delete;
+
+ /**
+ * @brief Deleted copy constructor
+ */
+ WellNewtonSolver( WellNewtonSolver const & ) = delete;
+
+ /**
+ * @brief Deleted copy assignment operator
+ */
+ WellNewtonSolver & operator=( WellNewtonSolver const & ) = delete;
+
+ /**
+ * @brief Deleted move assignment operator
+ */
+ WellNewtonSolver & operator=( WellNewtonSolver && ) = delete;
+
+ /**
+ * @brief name of the node manager in the object catalog
+ * @return string that contains the catalog name to generate a new NodeManager object through the object catalog.
+ */
+ static string catalogName() { return "WellNewtonSolver"; }
+
+
+
+ /**
+ * @brief Generate mesh targets from target regions
+ * @param meshBodies the group of mesh bodies
+ */
+ void generateMeshTargetsFromTargetRegions( Group const & meshBodies );
+
+
+ /**
+ *
+ * @brief Getter for system matrix
+ * @return a reference to linear system matrix of this solver
+ */
+ ParallelMatrix & getSystemMatrix() { return m_matrix; }
+
+ /**
+ * @brief Getter for system rhs vector
+ * @return a reference to linear system right-hand side of this solver
+ */
+ ParallelMatrix const & getSystemMatrix() const { return m_matrix; }
+
+ /**
+ * @brief Getter for system rhs vector
+ * @return a reference to linear system right-hand side of this solver
+ */
+ ParallelVector & getSystemRhs() { return m_rhs; }
+
+ /**
+ * @brief Getter for system rhs vector
+ * @return a reference to linear system right-hand side of this solver
+ */
+ ParallelVector const & getSystemRhs() const { return m_rhs; }
+
+ /**
+ * @brief Getter for system solution vector
+ * @return a reference to solution vector of this solver
+ */
+ ParallelVector & getSystemSolution() { return m_solution; }
+
+ /**
+ * @brief Getter for system solution vector
+ * @return a reference to solution vector of this solver
+ */
+ ParallelVector const & getSystemSolution() const { return m_solution; }
+
+ /**
+ * @brief Getter for degree-of-freedom manager
+ * @return a reference to degree-of-freedom manager of this solver
+ */
+ DofManager & getDofManager() { return m_dofManager; }
+
+ /**
+ * @brief Getter for degree-of-freedom manager
+ * @return a reference to degree-of-freedom manager of this solver
+ */
+ DofManager const & getDofManager() const { return m_dofManager; }
+
+ /**
+ * @brief Getter for local matrix
+ * @return a reference to linear system matrix of this solver
+ */
+ CRSMatrix< real64, globalIndex > & getLocalMatrix() { return m_localMatrix; }
+
+ /**
+ * @brief Getter for local matrix
+ * @return a reference to linear system matrix of this solver
+ */
+ CRSMatrixView< real64 const, globalIndex const > getLocalMatrix() const { return m_localMatrix.toViewConst(); }
+
+
+ template< typename T >
+ void setupSystem( T & well, DomainPartition & domain,
+ std::string const & meshBodyName,
+ MeshLevel const & meshLevel,
+ WellElementRegion & wellElementRegion,
+ bool const setSparsity =true );
+
+ template< typename T >
+ bool
+ solveNonlinearSystem( T & well, real64 const & time_n,
+ real64 const & stepDt,
+ integer const cycleNumber,
+ DomainPartition & domain,
+ MeshLevel & mesh,
+ ElementRegionManager & elemManager,
+ WellElementSubRegion & subRegion );
+
+
+ /**
+ * @brief Populate degree-of-freedom manager with fields relevant to this solver
+ * @param domain the domain containing the mesh and fields
+ * @param dofManager degree-of-freedom manager associated with the linear system
+ */
+ virtual void
+ setupDofs( DomainPartition const & domain,
+ DofManager & dofManager ) const;
+
+ /**
+ * @brief Set up the linear system (DOF indices and sparsity patterns)
+ * @param domain the domain containing the mesh and fields
+ * @param dofManager degree-of-freedom manager associated with the linear system
+ * @param localMatrix the system matrix
+ * @param rhs the system right-hand side vector
+ * @param solution the solution vector
+ * @param setSparsity flag to indicate if the sparsity pattern should be set
+ *
+ * @note While the function is virtual, the base class implementation should be
+ * sufficient for most single-physics solvers.
+ */
+
+
+ /**
+ * @brief Set the sparsity pattern of the linear system matrix
+ * @param domain the domain containing the mesh and fields
+ * @param dofManager degree-of-freedom manager associated with the linear system
+ * @param localMatrix the system matrix
+ * @param pattern the sparsity pattern to be filled
+ */
+ virtual void
+ setSparsityPattern( DomainPartition & domain,
+ DofManager & dofManager,
+ CRSMatrix< real64, globalIndex > & localMatrix,
+ SparsityPattern< globalIndex > & pattern );
+
+ /**
+ * @brief Create a preconditioner for this solver's linear system.
+ * @param domain the domain containing the mesh and fields
+ * @return the newly created preconditioner object
+ */
+ virtual std::unique_ptr< PreconditionerBase< LAInterface > >
+ createPreconditioner( DomainPartition & domain ) const;
+
+
+ /**
+ * @brief Output the assembled linear system for debug purposes.
+ * @param time beginning-of-step time
+ * @param cycleNumber event cycle number
+ * @param nonlinearIteration current nonlinear iteration number
+ * @param matrix system matrix
+ * @param rhs system right-hand side vector
+ */
+ void
+ debugOutputSystem( real64 const & time,
+ integer const cycleNumber,
+ integer const nonlinearIteration,
+ ParallelMatrix const & matrix,
+ ParallelVector const & rhs ) const;
+
+ /**
+ * @brief Output the linear system solution for debug purposes.
+ * @param time beginning-of-step time
+ * @param cycleNumber event cycle number
+ * @param nonlinearIteration current nonlinear iteration number
+ * @param solution system solution vector
+ */
+ void
+ debugOutputSolution( real64 const & time,
+ integer const cycleNumber,
+ integer const nonlinearIteration,
+ ParallelVector const & solution ) const;
+
+ /**
+ * @brief Update the convergence information and write then into a CSV file
+ * @param time_n the time at the beginning of the step
+ * @param dt the desired timestep
+ * @param cycleNumber event cycle number
+ * @param iteration current iteration
+ */
+ virtual void
+ updateAndWriteConvergenceStep( real64 const & time_n,
+ real64 const & dt,
+ integer const cycleNumber,
+ integer const iteration );
+
+
+
+ /**
+ * @brief function to apply a linear system solver to the assembled system.
+ * @param dofManager degree-of-freedom manager associated with the linear system
+ * @param matrix the system matrix
+ * @param rhs the system right-hand side vector
+ * @param solution the solution vector
+ *
+ * This function calls the linear solver package to perform a single linear solve on the block
+ * system. The derived physics solver is required to specify the call, as no default is provided.
+ *
+ * @note This function must be overridden in the derived physics solver in order to use an implict
+ * solution method such as LinearImplicitStep() or NonlinearImplicitStep().
+ */
+ virtual void
+ solveLinearSystem( DofManager const & dofManager,
+ ParallelMatrix & matrix,
+ ParallelVector & rhs,
+ ParallelVector & solution );
+
+
+
+ /**
+ * @brief creates a child group of of this WellNewtonSolver instantiation
+ * @param childKey the key of the child type
+ * @param childName the name of the child
+ * @return a pointer to the child group
+ */
+ virtual Group * createChild( string const & childKey, string const & childName ) override;
+
+ /**
+ * @brief Type alias for catalog interface used by this class. See CatalogInterface.
+ */
+ using CatalogInterface = dataRepository::CatalogInterface< WellNewtonSolver, string const &, Group * const >;
+
+ /**
+ * @brief Get the singleton catalog for WellNewtonSolver.
+ * @return reference to the catalog object
+ */
+ static CatalogInterface::CatalogType & getCatalog();
+
+ /**
+ * @brief Structure to hold scoped key names
+ */
+ struct viewKeyStruct
+ {
+ /// @return string for the cflFactor wrapper
+ static constexpr char const * cflFactorString() { return "cflFactor"; }
+
+ /// @return string for the initialDt wrapper
+ static constexpr char const * initialDtString() { return "initialDt"; }
+
+ /// @return string for the minDtIncreaseInterval wrapper
+ static constexpr char const * minDtIncreaseIntervalString() { return "minDtIncreaseInterval"; }
+
+ /// @return string for the discretization wrapper
+ static constexpr char const * discretizationString() { return "discretization"; }
+
+ /// @return string for the nextDt targetRegions wrapper
+ static constexpr char const * targetRegionsString() { return "targetRegions"; }
+
+ /// @return string for the writeLinearSystem wrapper
+ static constexpr char const * writeLinearSystemString() { return "writeLinearSystem"; }
+
+ /// @return string for the usePhysicsScaling wrapper
+ static constexpr char const * usePhysicsScalingString() { return "usePhysicsScaling"; }
+
+ /// @return string for the allowNonConvergedLinearSolverSolution wrapper
+ static constexpr char const * allowNonConvergedLinearSolverSolutionString() { return "allowNonConvergedLinearSolverSolution"; }
+
+ /// @return string for the writeStatistics wrapper
+ static constexpr char const * writeStatisticsCSVString() { return "writeStatistics"; }
+
+ /// @return string for the numTimestepsSinceLastDtCut wrapper
+ static constexpr char const * numTimestepsSinceLastDtCutString() { return "numTimestepsSinceLastDtCut"; }
+
+ /// string key for the esitmate well solution flag
+ static constexpr char const * activeCoupledIterationsString() { return "activeCoupledIterations"; }
+ static constexpr char const * estimateWellSolutionString() { return "estimateWellSolution"; }
+ /// string key for the enable iso thermal estimator flag
+ static constexpr char const * enableIsoThermalEstimatorString() { return "enableIsoThermalEstimator"; }
+ };
+
+ /**
+ * @brief Structure to hold scoped key names
+ */
+ struct groupKeyStruct
+ {
+ /// @return string for the linearSolverParameters wrapper
+ static constexpr char const * linearSolverParametersString() { return "LinearSolverParameters"; }
+
+ /// @return string for the nonlinearSolverParameters wrapper
+ static constexpr char const * nonlinearSolverParametersString() { return "NonlinearSolverParameters"; }
+
+ /// @return string for the solverStatistics wrapper
+ static constexpr char const * solverStatisticsString() { return "SolverStatistics"; }
+ };
+
+ /**
+ * @brief getter for the timestamp of the system setup
+ * @return the timestamp of the last time systemSetup was called
+ */
+ Timestamp getSystemSetupTimestamp() const { return m_systemSetupTimestamp; }
+
+
+ /**
+ * @brief set the timestamp of the system setup
+ * @param[in] timestamp the new timestamp of system setup
+ */
+ void setSystemSetupTimestamp( Timestamp timestamp );
+
+
+ /**
+ * @brief accessor for the linear solver parameters.
+ * @return the linear solver parameter list
+ */
+ LinearSolverParameters & getLinearSolverParameters()
+ {
+ return m_linearSolverParameters.get();
+ }
+
+ /**
+ * @brief const accessor for the linear solver parameters.
+ * @return the linear solver parameter list
+ */
+ LinearSolverParameters const & getLinearSolverParameters() const
+ {
+ return m_linearSolverParameters.get();
+ }
+
+ /**
+ * @brief accessor for the nonlinear solver parameters.
+ * @return the nonlinear solver parameter list
+ */
+ NonlinearSolverParameters & getNonlinearSolverParameters()
+ {
+ return m_nonlinearSolverParameters;
+ }
+
+ /**
+ * @brief const accessor for the nonlinear solver parameters.
+ * @return the nonlinear solver parameter list
+ */
+ NonlinearSolverParameters const & getNonlinearSolverParameters() const
+ {
+ return m_nonlinearSolverParameters;
+ }
+
+ /**
+ * @brief synchronize the nonlinear solver parameters.
+ */
+ virtual void
+ synchronizeNonlinearSolverParameters()
+ { /* empty here, overriden in CoupledSolver */ }
+
+ /**
+ * @brief Get position of a given region within solver's target region list
+ * @param regionName the region name to find
+ * @return index within target regions list
+ */
+ localIndex targetRegionIndex( string const & regionName ) const;
+
+ /**
+ * @brief return the list of target regions
+ * @return the array of region names
+ */
+ string_array const & getTargetRegionNames() const {return m_targetRegionNames;}
+
+
+
+ /**
+ * @brief function to set the value of m_assemblyCallback
+ * @param func the function to set m_assemblyCallback to
+ * @param funcType the type of the function
+ * @return true if the function was successfully set, false otherwise
+ *
+ * This is used to provide a callback function for to be called in the assembly step.
+ */
+ virtual bool registerCallback( void * func, const std::type_info & funcType ) final override;
+
+ /**
+ * @return An IterationsStatistics for the "root" solver.
+ * Otherwise return an empty IterationsStatistics
+ */
+ IterationsStatistics & getIterationStats()
+ {
+ return m_solverStatistics.m_iterationsStats;
+ }
+ /**
+ * @return An IterationsStatistics for the "root" solver.
+ * Otherwise return an empty IterationsStatistics
+ * (const version)
+ */
+ IterationsStatistics const & getIterationStats() const
+ {
+ return m_solverStatistics.m_iterationsStats;
+ }
+ /**
+ * @return A ConvergenceStatistics for all sub-solvers
+ */
+ ConvergenceStatistics & getConvergenceStats()
+ {
+ return m_solverStatistics.m_convergenceStats;
+ }
+ /**
+ * @return A ConvergenceStatistics for all sub-solvers (const version)
+ */
+ ConvergenceStatistics const & getConvergenceStats() const
+ {
+ return m_solverStatistics.m_convergenceStats;
+ }
+
+ /**
+ * @brief accessor for the solver statistics.
+ * @return reference to m_solverStatistics
+ */
+ SolverStatistics & getSolverStatistics() { return m_solverStatistics; }
+
+ /**
+ * @brief const accessor for the solver statistics.
+ * @return reference to m_solverStatistics
+ */
+ SolverStatistics const & getSolverStatistics() const { return m_solverStatistics; }
+
+
+
+ /**
+ * @brief Detect oscillations in the solution
+ * @return true if oscillations are detected, false otherwise
+ */
+ bool detectOscillations() const;
+
+
+ /**
+ * @brief Set thermal effects enable
+ * @param[in] true/false
+ */
+ void enableThermalEffects ( bool enable ) { m_thermalEffectsEnabled = enable; };
+
+ /**
+ * @brief Are thermal effects enabled
+ * @return true if thermal effects are enabled, false otherwise
+ */
+ bool thermalEffectsEnabled() const { return m_thermalEffectsEnabled; }
+
+ /**
+ * @brief Is isoThermalEstimator enabled
+ * @return true if isoThermalEstimator is enabled, false otherwise
+ */
+ bool isoThermalEstimatorEnabled() const { return m_enableIsoThermalEstimator; }
+
+ bool getNumActiveCoupledIterations() const { return m_activeCoupledIterations; }
+
+protected:
+
+ virtual void postInputInitialization() override;
+
+ /// behavior in case of linear solver failure
+ integer m_allowNonConvergedLinearSolverSolution;
+
+
+
+ /// Data structure to handle degrees of freedom
+ DofManager m_dofManager;
+
+ /// System matrix
+ ParallelMatrix m_matrix;
+
+ /// System right-hand side vector
+ ParallelVector m_rhs;
+
+ /// System solution vector
+ ParallelVector m_solution;
+
+ /// Diagonal scaling vector D (Ahat = D * A * D, bhat = D * b, x = D * xhat)
+ ParallelVector m_scaling;
+
+ /// Flag to decide whether to apply physics-based scaling to the linear system
+ integer m_usePhysicsScaling;
+
+ /// Local system matrix and rhs
+ CRSMatrix< real64, globalIndex > m_localMatrix;
+
+ /// Custom linear solver for the "native" solver type
+ std::unique_ptr< LinearSolverBase< LAInterface > > m_linearSolver;
+
+ /// Custom preconditioner for the "native" iterative solver
+ std::unique_ptr< PreconditionerBase< LAInterface > > m_precond;
+
+ /// flag for debug output of matrix, rhs, and solution
+ integer m_writeLinearSystem;
+
+ /// Parameter for outputing statistics information
+ StatsOutputType m_writeStatisticsCSV;
+
+ /// Linear solver parameters
+ LinearSolverParametersInput m_linearSolverParameters;
+
+ /// Result of the last linear solver
+ LinearSolverResult m_linearSolverResult;
+
+ /// Nonlinear solver parameters
+ NonlinearSolverParameters m_nonlinearSolverParameters;
+
+ /// Solver statistics
+ SolverStatistics m_solverStatistics;
+
+ /// Timestamp of the last call to setup system
+ Timestamp m_systemSetupTimestamp;
+
+ /// Callback function for assembly step
+ std::function< void( CRSMatrix< real64, globalIndex >, array1d< real64 > ) > m_assemblyCallback;
+
+ /// Timers for the aggregate profiling of the solver
+ stdMap< std::string, std::chrono::system_clock::duration > m_timers;
+
+ /// History of the solution vector, used for oscillation detection
+ ArrayOfArrays< real64 > m_solutionHistory;
+
+private:
+ /// List of names of regions the solver will be applied to
+ string_array m_targetRegionNames;
+
+ /// Map containing the array of target regions (value) for each MeshBody (key).
+ map< std::pair< string, string >, string_array > m_meshTargets;
+
+ /// Number of coupled iterations to activate estimator solve
+ integer m_activeCoupledIterations;
+
+ /// Flag to enable thermal effects in wellbore calculations
+ bool m_thermalEffectsEnabled;
+ integer m_enableIsoThermalEstimator;
+
+
+ /**
+ * @brief output information about the cycle to the log
+ * @param cycleNumber the current cycle number
+ * @param numOfSubSteps the number of substeps taken
+ * @param subStepDts the time step size for each substep
+ */
+ void logEndOfCycleInformation( integer const cycleNumber,
+ integer const numOfSubSteps,
+ stdVector< real64 > const & subStepDts ) const;
+};
+
+template< typename T >
+void WellNewtonSolver::setupSystem( T & well, DomainPartition & domain,
+ std::string const & meshBodyName,
+ MeshLevel const & meshLevel,
+ WellElementRegion & wellElementRegion,
+ bool const setSparsity )
+{
+ GEOS_MARK_FUNCTION;
+
+ map< std::pair< string, string >, string_array > meshTargets;
+ string_array regions;
+
+ meshTargets.clear();
+ regions.clear();
+ regions.emplace_back( wellElementRegion.getName() );
+ auto const key = std::make_pair( meshBodyName, meshLevel.getName() );
+ meshTargets[key] = std::move( regions );
+
+ m_dofManager.setDomain( domain );
+ m_dofManager.addField( well.wellElementDofName(),
+ FieldLocation::Elem,
+ well.numDofPerWellElement(),
+ meshTargets );
+
+ m_dofManager.addCoupling( well.wellElementDofName(),
+ well.wellElementDofName(),
+ DofManager::Connector::Node );
+
+ m_dofManager.reorderByRank();
+ if( setSparsity )
+ {
+ SparsityPattern< globalIndex > pattern;
+ setSparsityPattern( domain, m_dofManager, m_localMatrix, pattern );
+ m_localMatrix.assimilate< parallelDevicePolicy<> >( std::move( pattern ) );
+ }
+ m_localMatrix.setName( this->getName() + "/matrix" );
+
+ m_rhs.setName( this->getName() + "/rhs" );
+ m_rhs.create( m_dofManager.numLocalDofs(), MPI_COMM_GEOS );
+
+ m_solution.setName( this->getName() + "/solution" );
+ m_solution.create( m_dofManager.numLocalDofs(), MPI_COMM_GEOS );
+}
+
+
+template< typename T >
+bool WellNewtonSolver::solveNonlinearSystem( T & well, real64 const & time_n,
+ real64 const & stepDt,
+ integer const cycleNumber,
+ DomainPartition & domain,
+ MeshLevel & mesh,
+ ElementRegionManager & elemManager,
+ WellElementSubRegion & subRegion )
+{
+ integer const maxNewtonIter = m_nonlinearSolverParameters.m_maxIterNewton;
+ integer dtAttempt = m_nonlinearSolverParameters.m_numTimeStepAttempts;
+ integer configurationLoopIter = m_nonlinearSolverParameters.m_numConfigurationAttempts;
+ integer const minNewtonIter = m_nonlinearSolverParameters.m_minIterNewton;
+ real64 const newtonTol = m_nonlinearSolverParameters.m_newtonTol;
+
+// keep residual from previous iteration in case we need to do a line search
+
+ integer newtonIter = 0;
+ real64 scaleFactor = 1.0;
+
+ bool isNewtonConverged = false;
+
+ for( newtonIter = 0; newtonIter < maxNewtonIter; ++newtonIter )
+ {
+ if( m_nonlinearSolverParameters.getLogLevel() > 4 )
+ GEOS_LOG_LEVEL_RANK_0( logInfo::NonlinearSolver,
+ GEOS_FMT( " Well: {} Est Attempt: NewtonIter: {:2}", subRegion.getName(), stepDt, newtonIter ));
+
+ {
+ Timer timer( m_timers.get_inserted( "assemble" ) );
+
+// We sync the nonlinear convergence history. The coupled solver parameters are the one being
+// used. We want to propagate the info to subsolvers. It can be important for solvers that
+// have special treatment for specific iterations.
+ synchronizeNonlinearSolverParameters();
+
+// zero out matrix/rhs before assembly
+ m_localMatrix.zero();
+ m_rhs.zero();
+
+ arrayView1d< real64 > const localRhs = m_rhs.open();
+
+// call assemble to fill the matrix and the rhs
+ well.assembleSystem( time_n,
+ stepDt,
+ cycleNumber,
+ elemManager,
+ subRegion,
+ m_dofManager,
+ m_localMatrix.toViewConstSizes(),
+ localRhs );
+
+// apply boundary conditions to system
+ well.applyWellBoundaryConditions( time_n,
+ stepDt,
+ elemManager,
+ subRegion,
+ m_dofManager,
+ localRhs,
+ m_localMatrix.toViewConstSizes() );
+
+ m_rhs.close();
+ // sort out how this work with well estimator
+
+ well.outputSingleWellDebug( time_n, stepDt, newtonIter, mesh, subRegion,
+ m_dofManager, m_localMatrix.toViewConstSizes(), localRhs );
+
+
+ if( m_assemblyCallback )
+ {
+// Make a copy of LA objects and ship off to the callback
+ array1d< real64 > localRhsCopy( m_rhs.localSize() );
+ localRhsCopy.setValues< parallelDevicePolicy<> >( m_rhs.values() );
+ m_assemblyCallback( m_localMatrix, std::move( localRhsCopy ) );
+ }
+ }
+
+ // well.outputSingleWellDebug( time_n, stepDt, 0, newtonIter, 0,
+ // mesh, subRegion, dofManager, m_localMatrix.toViewConstSizes(), m_rhs.values() );
+ real64 residualNorm = 0;
+ {
+ Timer timer( m_timers.get_inserted( "convergence check" ) );
+
+// get residual norm
+ residualNorm = well.calculateWellResidualNorm( time_n, stepDt, m_nonlinearSolverParameters, subRegion, m_dofManager, m_rhs.values() );
+ if( m_nonlinearSolverParameters.getLogLevel() > 4 )
+ GEOS_LOG_LEVEL_RANK_0( logInfo::Convergence,
+ GEOS_FMT( " ( R ) = ( {:4.2e} )", residualNorm ) );
+ }
+ //auto iterInfo = currentIter( time_n, dt );
+ //outputSingleWellDebug( time_n, stepDt, 0, newtonIter, 0,
+ // mesh, subRegion, dofManager, m_localMatrix.toViewConstSizes(), m_rhs.values() );
+// if the residual norm is less than the Newton tolerance we denote that we have
+// converged and break from the Newton loop immediately.
+ std::cout << " Well: " << subRegion.getName() << " Est Attempt: " << dtAttempt
+ << ", ConfigurationIter: " << configurationLoopIter
+ << ", NewtonIter: " << newtonIter
+ << ", Residual Norm: " << residualNorm << std::endl;
+ if( residualNorm < newtonTol && newtonIter >= minNewtonIter )
+ {
+ isNewtonConverged = true;
+ break;
+ }
+
+// if the residual norm is above the max allowed residual norm, we break from
+// the Newton loop to avoid crashes due to Newton divergence
+ if( residualNorm > m_nonlinearSolverParameters.m_maxAllowedResidualNorm )
+ {
+ string const maxAllowedResidualNormString = NonlinearSolverParameters::viewKeysStruct::maxAllowedResidualNormString();
+ if( m_nonlinearSolverParameters.getLogLevel() > 4 )
+ GEOS_LOG_LEVEL_RANK_0( logInfo::Convergence,
+ GEOS_FMT( " The residual norm is above the {} of {}. Newton loop terminated.",
+ maxAllowedResidualNormString,
+ m_nonlinearSolverParameters.m_maxAllowedResidualNorm ) );
+ isNewtonConverged = false;
+ break;
+ }
+
+ {
+ Timer timer( m_timers.get_inserted( "linear solver total" ) );
+
+// TODO: Trilinos currently requires this, re-evaluate after moving to Tpetra-based solvers
+ if( m_precond )
+ {
+ m_precond->clear();
+ }
+
+ {
+ Timer timer_setup( m_timers.get_inserted( "linear solver create" ) );
+
+// Compose parallel LA matrix/rhs out of local LA matrix/rhs
+//
+ m_matrix.create( m_localMatrix.toViewConst(), m_dofManager.numLocalDofs(), MPI_COMM_GEOS );
+ }
+
+// Output the linear system matrix/rhs for debugging purposes
+ //string tag = "_"+std::to_string( my_ctime ); tjb
+ //debugOutputSystem( time_n, cycleNumber, newtonIter, m_matrix, m_rhs, tag );
+
+ debugOutputSystem( time_n, cycleNumber, newtonIter, m_matrix, m_rhs );
+// Solve the linear system
+ solveLinearSystem( m_dofManager, m_matrix, m_rhs, m_solution );
+
+// Increment the solver statistics for reporting purposes
+ getIterationStats().updateNonlinearIteration( m_linearSolverResult.numIterations );
+
+// Output the linear system solution for debugging purposes
+ debugOutputSolution( time_n, cycleNumber, newtonIter, m_solution );
+ //debugOutputSolution( time_n, cycleNumber, newtonIter, m_solution, tag );
+ }
+
+ {
+ Timer timer( m_timers.get_inserted( "apply solution" ) );
+
+// Compute the scaling factor for the Newton update
+ scaleFactor = well.scalingForWellSystemSolution( subRegion, m_dofManager, m_solution.values() );
+ if( m_nonlinearSolverParameters.getLogLevel() > 4 )
+ GEOS_LOG_LEVEL_RANK_0( logInfo::Solution,
+ GEOS_FMT( " {}: Global solution scaling factor = {}", getName(), scaleFactor ) );
+
+ if( !well.checkWellSystemSolution( subRegion, m_dofManager, m_solution.values(), scaleFactor ) )
+ {
+// TODO try chopping (similar to line search)
+ if( m_nonlinearSolverParameters.getLogLevel() > 4 )
+ GEOS_LOG_RANK_0( GEOS_FMT( " {}: Solution check failed. Newton loop terminated.", getName()) );
+ break;
+ }
+
+// apply the system solution to the fields/variables
+ well.applyWellSystemSolution( m_dofManager, m_solution.values(), scaleFactor, stepDt, domain, mesh, subRegion );
+ }
+
+ {
+ Timer timer( m_timers.get_inserted( "update state" ) );
+
+ // update derived variables (constitutive models)
+ well.updateWellState( elemManager, subRegion );
+ }
+
+ }
+ std::cout << "WellSolverBase::solveNewtonSystem completed with isNewtonConverged = " << isNewtonConverged << std::endl;
+ return isNewtonConverged;
+}
+
+
+/**
+ * @brief String for the stats output type
+ */
+ENUM_STRINGS( WellNewtonSolver::StatsOutputType,
+ "none",
+ "iteration",
+ "convergence",
+ "all" );
+
+} // namespace geos
+
+
+#endif /* GEOS_PHYSICSSOLVERS_WELLNEWTONSOLVER_HPP_ */
diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/WellPhaseVolumeRateConstraint.cpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellPhaseVolumeRateConstraint.cpp
new file mode 100644
index 00000000000..63f7baa7053
--- /dev/null
+++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellPhaseVolumeRateConstraint.cpp
@@ -0,0 +1,81 @@
+/*
+ * ------------------------------------------------------------------------------------------------------------
+ * 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 WellPhaseVolumeRateConstraint.cpp
+ */
+
+#include "LogLevelsInfo.hpp"
+#include "WellPhaseVolumeRateConstraint.hpp"
+#include "WellConstants.hpp"
+#include "dataRepository/InputFlags.hpp"
+#include "functions/FunctionManager.hpp"
+
+
+namespace geos
+{
+
+using namespace dataRepository;
+
+
+PhaseVolumeRateConstraint::PhaseVolumeRateConstraint( string const & name, Group * const parent )
+ : WellConstraintBase( name, parent )
+{
+ this->setInputFlags( InputFlags::OPTIONAL_NONUNIQUE );
+
+ this->registerWrapper( viewKeyStruct::phaseRateString(), &this->m_constraintValue ).
+ setDefaultValue( 0.0 ).
+ setInputFlag( InputFlags::OPTIONAL ).
+ setRestartFlags( RestartFlags::WRITE_AND_READ ).
+ setDescription( "Phase rate, (if useSurfaceConditions: [surface m^3/s]; else [reservoir m^3/s]) " );
+
+ this->registerWrapper( viewKeyStruct::phaseNameString(), &this->m_phaseName ).
+ setRTTypeName( rtTypes::CustomTypes::groupNameRef ).
+ setDefaultValue( "" ).
+ setInputFlag( InputFlags::OPTIONAL ).
+ setRestartFlags( RestartFlags::WRITE_AND_READ ).
+ setDescription( "Name of the target phase" );
+}
+
+PhaseVolumeRateConstraint::~PhaseVolumeRateConstraint()
+{}
+
+void PhaseVolumeRateConstraint::postInputInitialization()
+{
+ // Validate table options
+ WellConstraintBase::postInputInitialization();
+
+ // check constraint value
+ GEOS_THROW_IF( m_constraintValue < 0,
+ getWrapperDataContext( viewKeyStruct::phaseRateString() ) << ": Target value is negative",
+ InputError );
+
+
+ GEOS_THROW_IF ((m_constraintValue <= 0.0 && m_constraintScheduleTableName.empty()),
+ getName() << " " << getDataContext() << ": You need to specify a phase rate constraint. \n" <<
+ "The rate constraint can be specified using " <<
+ "either " << viewKeyStruct::phaseRateString() <<
+ " or " << WellConstraintBase::viewKeyStruct::constraintScheduleTableNameString(),
+ InputError );
+}
+
+bool PhaseVolumeRateConstraint::checkViolation( WellConstraintBase const & currentConstraint, real64 const & currentTime ) const
+{
+ real64 const currentValue = currentConstraint.phaseVolumeRates()[m_phaseIndex];
+ real64 const constraintValue = getConstraintValue( currentTime );
+ return ( LvArray::math::abs( currentValue ) > LvArray::math::abs( constraintValue ) );
+}
+
+} //namespace geos
diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/WellPhaseVolumeRateConstraint.hpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellPhaseVolumeRateConstraint.hpp
new file mode 100644
index 00000000000..bb7880cfc0c
--- /dev/null
+++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellPhaseVolumeRateConstraint.hpp
@@ -0,0 +1,179 @@
+/*
+ * ------------------------------------------------------------------------------------------------------------
+ * 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 WellPhaseVolumeRateConstraint.hpp
+ */
+
+
+#ifndef GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_WELLPHASEVOLUMERATECONSTRAINT_HPP
+#define GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_WELLPHASEVOLUMERATECONSTRAINT_HPP
+
+#include "common/format/EnumStrings.hpp"
+#include "dataRepository/Group.hpp"
+#include "functions/TableFunction.hpp"
+#include "WellConstraintsBase.hpp"
+#include "WellConstants.hpp"
+
+namespace geos
+{
+
+
+template< typename T >
+localIndex getPhaseIndexFromFluidModel( T const & fluidModel, std::string const & inputPhase )
+{
+ localIndex phaseIndex=-1;
+ // Find target phase index for phase rate constraint
+ for( integer ip = 0; ip < fluidModel.numFluidPhases(); ++ip )
+ {
+ if( fluidModel.phaseNames()[ip] == inputPhase )
+ {
+ phaseIndex = ip;
+ }
+ }
+ return phaseIndex;
+}
+
+/**
+ * @class PhaseVolumeRateConstraint
+ * @brief This class describes a phase rate constraint used to control a well of WellConstraintType type (Injection or Production).
+ */
+
+class PhaseVolumeRateConstraint : public WellConstraintBase
+{
+public:
+
+
+ /**
+ * @name Constructor / Destructor
+ */
+ ///@{
+
+ /**
+ * @brief Constructor for WellControls Objects.
+ * @param[in] name the name of this instantiation of WellControls in the repository
+ * @param[in] parent the parent group of this instantiation of WellControls
+ */
+ explicit PhaseVolumeRateConstraint( string const & name, dataRepository::Group * const parent );
+
+ /**
+ * @brief Default destructor.
+ */
+ ~PhaseVolumeRateConstraint() override;
+
+ /**
+ * @brief Deleted default constructor.
+ */
+ PhaseVolumeRateConstraint() = delete;
+
+ /**
+ * @brief Deleted copy constructor.
+ */
+ PhaseVolumeRateConstraint( PhaseVolumeRateConstraint const & ) = delete;
+
+ /**
+ * @brief Deleted move constructor.
+ */
+ PhaseVolumeRateConstraint( PhaseVolumeRateConstraint && ) = delete;
+
+ /**
+ * @brief Deleted assignment operator.
+ * @return a reference to a constraint object
+ */
+ PhaseVolumeRateConstraint & operator=( PhaseVolumeRateConstraint const & ) = delete;
+
+ /**
+ * @brief Deleted move operator.
+ * @return a reference to a constraint object
+ */
+ PhaseVolumeRateConstraint & operator=( PhaseVolumeRateConstraint && ) = delete;
+
+ ///@}
+
+ /**
+ * @brief name of the node manager in the object catalog
+ * @return string that contains the catalog name to generate a new Constraint object through the object catalog.
+ */
+ static string catalogName()
+ {
+ return "PhaseVolumeRateConstraint";
+ }
+
+ /**
+ * @name Getters / Setters
+ */
+ ///@{
+
+ // Temp interface - tjb
+ virtual ConstraintTypeId getControl() const override { return ConstraintTypeId::PHASEVOLRATE; };
+
+ /**
+ * @brief Get the target phase name
+ * @return the target phase name
+ */
+ const string & getPhaseName() const { return m_phaseName; }
+
+ /**
+ * @brief Get the target phase index
+ * @return the target phase index
+ */
+ const localIndex & getPhaseIndex() const { return m_phaseIndex; }
+
+ ///@}
+
+ struct viewKeyStruct
+ {
+ /// String key for the well target phase rate
+ static constexpr char const * phaseRateString() { return "phaseRate"; }
+ /// String key for the well target phase name
+ static constexpr char const * phaseNameString() { return "phaseName"; }
+ };
+
+ /**
+ * @brief Validate phase type is consistent with fluidmodel
+ */
+ template< typename T > void validatePhaseType( T const & fluidModel );
+ ///@}
+
+ virtual bool checkViolation( WellConstraintBase const & currentConstraint, real64 const & currentTime ) const override;
+protected:
+
+ virtual void postInputInitialization() override;
+
+private:
+
+ /// Name of the targeted phase
+ string m_phaseName;
+
+ /// Index of the target phase, used to impose the phase rate constraint
+ localIndex m_phaseIndex;
+
+};
+
+template< typename T >
+void PhaseVolumeRateConstraint::validatePhaseType( T const & fluidModel )
+{
+ // Find target phase index for phase rate constraint
+ m_phaseIndex = getPhaseIndexFromFluidModel( fluidModel, this->template getReference< string >( viewKeyStruct::phaseNameString()));
+
+ GEOS_THROW_IF( m_phaseIndex == -1,
+ "PhaseVolumeRateConstraint " << this->template getReference< string >( viewKeyStruct::phaseNameString()) <<
+ ": Invalid phase type for simulation fluid model",
+ InputError );
+}
+
+} //namespace geos
+
+#endif //GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_WELLPHASEVOLUMERATECONSTRAINT_HPP
diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/WellProductionConstraint.cpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellProductionConstraint.cpp
new file mode 100644
index 00000000000..4ec3ec3299d
--- /dev/null
+++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellProductionConstraint.cpp
@@ -0,0 +1,70 @@
+/*
+ * ------------------------------------------------------------------------------------------------------------
+ * 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 WellProductionConstraint.cpp
+ */
+
+#include "LogLevelsInfo.hpp"
+#include "WellProductionConstraint.hpp"
+#include "WellConstants.hpp"
+#include "dataRepository/InputFlags.hpp"
+#include "functions/FunctionManager.hpp"
+
+#include "WellLiquidRateConstraint.hpp"
+#include "WellMassRateConstraint.hpp"
+#include "WellPhaseVolumeRateConstraint.hpp"
+#include "WellVolumeRateConstraint.hpp"
+
+namespace geos
+{
+
+template< typename ConstraintRateType >
+ProductionConstraint< ConstraintRateType >::ProductionConstraint( string const & name, Group * const parent )
+ : ConstraintRateType( name, parent )
+{
+ // set rate sign for producers (base class member)
+ this->m_rateSign = -1.0;
+}
+template< typename ConstraintRateType >
+ProductionConstraint< ConstraintRateType >::~ProductionConstraint()
+{}
+
+template< typename ConstraintRateType >
+void ProductionConstraint< ConstraintRateType >::postInputInitialization()
+{
+ // Validate value and table options
+ ConstraintRateType::postInputInitialization();
+
+}
+// Register concrete wrapper constraint types and instantiate templates.
+
+template class ProductionConstraint< LiquidRateConstraint >;
+using ProductionLiquidRateConstraint = ProductionConstraint< LiquidRateConstraint >;
+REGISTER_CATALOG_ENTRY( WellConstraintBase, ProductionLiquidRateConstraint, string const &, Group * const )
+
+template class ProductionConstraint< MassRateConstraint >;
+using ProductionMassRateConstraint = ProductionConstraint< MassRateConstraint >;
+REGISTER_CATALOG_ENTRY( WellConstraintBase, ProductionMassRateConstraint, string const &, Group * const )
+
+template class ProductionConstraint< PhaseVolumeRateConstraint >;
+using ProductionPhaseVolumeRateConstraint = ProductionConstraint< PhaseVolumeRateConstraint >;
+REGISTER_CATALOG_ENTRY( WellConstraintBase, ProductionPhaseVolumeRateConstraint, string const &, Group * const )
+
+template class ProductionConstraint< VolumeRateConstraint >;
+using ProductionVolumeRateConstraint = ProductionConstraint< VolumeRateConstraint >;
+REGISTER_CATALOG_ENTRY( WellConstraintBase, ProductionVolumeRateConstraint, string const &, Group * const )
+
+} //namespace geos
diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/WellProductionConstraint.hpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellProductionConstraint.hpp
new file mode 100644
index 00000000000..ce1ca16de14
--- /dev/null
+++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellProductionConstraint.hpp
@@ -0,0 +1,106 @@
+/*
+ * ------------------------------------------------------------------------------------------------------------
+ * 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 WellProductionConstraints.hpp
+ */
+
+
+#ifndef GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_WELLPRODUCTIONCONSTRAINT_HPP
+#define GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_WELLPRODUCTIONCONSTRAINT_HPP
+
+#include "common/format/EnumStrings.hpp"
+#include "dataRepository/Group.hpp"
+#include "functions/TableFunction.hpp"
+
+namespace geos
+{
+using namespace dataRepository;
+/**
+ * @class ProductionConstraint
+ * @brief This class describes constraint used to control a production well.
+ */
+
+template< typename ConstraintType >
+class ProductionConstraint : public ConstraintType
+{
+public:
+ /**
+ * @name Constructor / Destructor
+ */
+ ///@{
+
+ /**
+ * @brief Constructor for WellControls Objects.
+ * @param[in] name the name of this instantiation of WellControls in the repository
+ * @param[in] parent the parent group of this instantiation of WellControls
+ */
+ explicit ProductionConstraint( string const & name, dataRepository::Group * const parent );
+
+ /**
+ * @brief Default destructor.
+ */
+ ~ProductionConstraint() override;
+
+ /**
+ * @brief Deleted default constructor.
+ */
+ ProductionConstraint() = delete;
+
+ /**
+ * @brief Deleted copy constructor.
+ */
+ ProductionConstraint( ProductionConstraint const & ) = delete;
+
+ /**
+ * @brief Deleted move constructor.
+ */
+ ProductionConstraint( ProductionConstraint && ) = delete;
+
+ /**
+ * @brief Deleted assignment operator.
+ * @return a reference to a constraint object
+ */
+ ProductionConstraint & operator=( ProductionConstraint const & ) = delete;
+
+ /**
+ * @brief Deleted move operator.
+ * @return a reference to a constraint object
+ */
+ ProductionConstraint & operator=( ProductionConstraint && ) = delete;
+
+ ///@}
+
+ /**
+ * @brief name of the node manager in the object catalog
+ * @return string that contains the catalog name to generate a new Constraint object through the object catalog.
+ */
+ static string catalogName()
+ {
+ return "Production"+ConstraintType::catalogName();
+ }
+ virtual string getCatalogName() const override { return catalogName(); }
+protected:
+
+ virtual void postInputInitialization() override;
+
+ static bool isViolated( const real64 & currentValue, const real64 & constraintValue )
+ { return currentValue < constraintValue; }
+};
+
+
+} //namespace geos
+
+#endif //GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_WELLPRODUCTIONCONSTRAINT_HPP
diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/WellPropWriter.hpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellPropWriter.hpp
new file mode 100644
index 00000000000..d066b83f3ce
--- /dev/null
+++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellPropWriter.hpp
@@ -0,0 +1,674 @@
+/*
+ * ------------------------------------------------------------------------------------------------------------
+ * 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 WellPropWriter.hpp
+ */
+
+#ifndef GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_WELLPROPWRITER_HPP
+#define GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_WELLPROPWRITER_HPP
+
+#include