diff --git a/inputFiles/compositionalMultiphaseFlow/soreideWhitson/1D_100cells/1D_benchmark.xml b/inputFiles/compositionalMultiphaseFlow/soreideWhitson/1D_100cells/1D_benchmark.xml
index 89b9cc7dc86..cc7e0796fa3 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/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 }">
-
-
+
+ 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..0de45dc3362 100644
--- a/src/coreComponents/integrationTests/wellsTests/testIsothermalReservoirCompositionalMultiphaseMSWells.cpp
+++ b/src/coreComponents/integrationTests/wellsTests/testIsothermalReservoirCompositionalMultiphaseMSWells.cpp
@@ -104,15 +104,20 @@ char const * xmlInput =
logLevel="2"
type="injector"
control="totalVolRate"
- referenceElevation="-0.01"
- targetBHP="1.45e7"
enableCrossflow="0"
useSurfaceConditions="1"
surfacePressure="1.45e7"
- surfaceTemperature="300.15"
- targetTotalRate="0.001"
- injectionTemperature="300.15"
- injectionStream="{ 1.0, 0.0 }"/>
+ surfaceTemperature="300.15">
+
+
+
@@ -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,7 @@ class CompositionalMultiphaseReservoirSolverTest : public ::testing::Test
solver->getSystemSolution() );
solver->implicitStepSetup( time, dt, domain );
+ solver->wellSolver()->initializeWells( domain, time );
}
static real64 constexpr time = 0.0;
diff --git a/src/coreComponents/integrationTests/wellsTests/testIsothermalReservoirCompositionalMultiphaseSSWells.cpp b/src/coreComponents/integrationTests/wellsTests/testIsothermalReservoirCompositionalMultiphaseSSWells.cpp
index 277c7a2a38c..58fe3e50a05 100644
--- a/src/coreComponents/integrationTests/wellsTests/testIsothermalReservoirCompositionalMultiphaseSSWells.cpp
+++ b/src/coreComponents/integrationTests/wellsTests/testIsothermalReservoirCompositionalMultiphaseSSWells.cpp
@@ -104,15 +104,20 @@ char const * xmlInput =
logLevel="2"
type="injector"
control="totalVolRate"
- referenceElevation="-0.01"
- targetBHP="1.45e7"
enableCrossflow="0"
useSurfaceConditions="1"
surfacePressure="1.45e7"
- surfaceTemperature="300.15"
- targetTotalRate="0.001"
- injectionTemperature="300.15"
- injectionStream="{ 1.0, 0.0 }"/>
+ surfaceTemperature="300.15">
+
+
+
@@ -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,7 @@ class CompositionalMultiphaseReservoirSolverTest : public ::testing::Test
solver->getSystemSolution() );
solver->implicitStepSetup( time, dt, domain );
+ solver->wellSolver()->initializeWells( domain, time );
}
static real64 constexpr time = 0.0;
diff --git a/src/coreComponents/integrationTests/wellsTests/testOpenClosePerf.cpp b/src/coreComponents/integrationTests/wellsTests/testOpenClosePerf.cpp
index c45c9a21e68..5502909a7c5 100644
--- a/src/coreComponents/integrationTests/wellsTests/testOpenClosePerf.cpp
+++ b/src/coreComponents/integrationTests/wellsTests/testOpenClosePerf.cpp
@@ -61,13 +61,19 @@ char const * PreXmlInput =
logLevel="1"
targetRegions="{wellRegion1}"
useMass="0">
-
+
+
+
+
diff --git a/src/coreComponents/integrationTests/wellsTests/testReservoirCompositionalMultiphaseMSWells.cpp b/src/coreComponents/integrationTests/wellsTests/testReservoirCompositionalMultiphaseMSWells.cpp
index f1ca32e9e81..8f33df720e6 100644
--- a/src/coreComponents/integrationTests/wellsTests/testReservoirCompositionalMultiphaseMSWells.cpp
+++ b/src/coreComponents/integrationTests/wellsTests/testReservoirCompositionalMultiphaseMSWells.cpp
@@ -61,21 +61,33 @@ char const * xmlInput =
logLevel="1"
targetRegions="{wellRegion1,wellRegion2}"
useMass="0">
-
-
+
+
+
+
+
+
+
+
@@ -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,7 @@ class CompositionalMultiphaseReservoirSolverTest : public ::testing::Test
solver->getSystemSolution() );
solver->implicitStepSetup( time, dt, domain );
+ solver->wellSolver()->initializeWells( domain, time );
}
static real64 constexpr time = 0.0;
diff --git a/src/coreComponents/integrationTests/wellsTests/testReservoirSinglePhaseMSWells.cpp b/src/coreComponents/integrationTests/wellsTests/testReservoirSinglePhaseMSWells.cpp
index d75ad50a957..8fc88fd94ac 100644
--- a/src/coreComponents/integrationTests/wellsTests/testReservoirSinglePhaseMSWells.cpp
+++ b/src/coreComponents/integrationTests/wellsTests/testReservoirSinglePhaseMSWells.cpp
@@ -61,18 +61,31 @@ char const * PreXmlInput =
-
-
+
+
+
+
+
+
+
+
+
@@ -357,6 +370,8 @@ class SinglePhaseReservoirSolverTest : public ::testing::Test
solver->getSystemSolution() );
solver->implicitStepSetup( TIME, DT, domain );
+
+ solver->wellSolver()->initializeWells( domain, TIME );
}
void TestAssembleCouplingTerms()
diff --git a/src/coreComponents/integrationTests/wellsTests/testReservoirThermalSinglePhaseMSWells.cpp b/src/coreComponents/integrationTests/wellsTests/testReservoirThermalSinglePhaseMSWells.cpp
index d10f1da56f6..5baf73b0430 100644
--- a/src/coreComponents/integrationTests/wellsTests/testReservoirThermalSinglePhaseMSWells.cpp
+++ b/src/coreComponents/integrationTests/wellsTests/testReservoirThermalSinglePhaseMSWells.cpp
@@ -64,22 +64,35 @@ char const * PreXmlInput =
logLevel="1"
isThermal="1"
targetRegions="{wellRegion1,wellRegion2}">
-
-
+
+
+
+
+
+
+
+
+
@@ -515,6 +528,7 @@ class SinglePhaseReservoirSolverTest : public ::testing::Test
solver->getSystemSolution() );
solver->implicitStepSetup( TIME, DT, domain );
+ solver->wellSolver()->initializeWells( domain, TIME );
}
void TestAssembleCouplingTerms()
diff --git a/src/coreComponents/integrationTests/wellsTests/testReservoirThermalSinglePhaseMSWells_RateInj.cpp b/src/coreComponents/integrationTests/wellsTests/testReservoirThermalSinglePhaseMSWells_RateInj.cpp
index 255b0201692..07ccbe5626a 100644
--- a/src/coreComponents/integrationTests/wellsTests/testReservoirThermalSinglePhaseMSWells_RateInj.cpp
+++ b/src/coreComponents/integrationTests/wellsTests/testReservoirThermalSinglePhaseMSWells_RateInj.cpp
@@ -79,19 +79,24 @@ char const * XmlInput =
isThermal="1"
writeCSV="1"
targetRegions="{ wellRegion2 }">
-
+
+ referenceElevation="-0.01"/>
+
+
@@ -597,6 +602,8 @@ class SinglePhaseReservoirSolverTest : public ::testing::Test
solver->getSystemSolution() );
solver->implicitStepSetup( TIME, DT, domain );
+
+ solver->wellSolver()->initializeWells( domain, TIME );
}
void TestAssembleCouplingTerms()
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..308e21b106e 100644
--- a/src/coreComponents/integrationTests/wellsTests/testThermalReservoirCompositionalMultiphaseMSWells.cpp
+++ b/src/coreComponents/integrationTests/wellsTests/testThermalReservoirCompositionalMultiphaseMSWells.cpp
@@ -108,15 +108,20 @@ char const * xmlInput =
logLevel="2"
type="injector"
control="totalVolRate"
- referenceElevation="-0.01"
- targetBHP="1.45e7"
enableCrossflow="0"
useSurfaceConditions="1"
surfacePressure="1.45e7"
- surfaceTemperature="300.15"
- targetTotalRate="0.001"
- injectionTemperature="300.15"
- injectionStream="{ 0.99, 0.01 }"/>
+ surfaceTemperature="300.15">
+
+
+
@@ -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,7 @@ class CompositionalMultiphaseReservoirSolverTest : public ::testing::Test
solver->getSystemSolution() );
solver->implicitStepSetup( time, dt, domain );
+ solver->wellSolver()->initializeWells( domain, time );
}
static real64 constexpr time = 0.0;
diff --git a/src/coreComponents/integrationTests/wellsTests/testThermalReservoirCompositionalMultiphaseSSWells.cpp b/src/coreComponents/integrationTests/wellsTests/testThermalReservoirCompositionalMultiphaseSSWells.cpp
index 76fa20d4cea..2e368bba5c7 100644
--- a/src/coreComponents/integrationTests/wellsTests/testThermalReservoirCompositionalMultiphaseSSWells.cpp
+++ b/src/coreComponents/integrationTests/wellsTests/testThermalReservoirCompositionalMultiphaseSSWells.cpp
@@ -109,15 +109,20 @@ char const * xmlInput =
logLevel="2"
type="injector"
control="totalVolRate"
- referenceElevation="-0.01"
- targetBHP="1.45e7"
enableCrossflow="0"
useSurfaceConditions="1"
surfacePressure="1.45e7"
- surfaceTemperature="300.15"
- targetTotalRate="0.001"
- injectionTemperature="300.15"
- injectionStream="{ 0.99, 0.01 }"/>
+ surfaceTemperature="300.15">
+
+
+
@@ -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,7 @@ class CompositionalMultiphaseReservoirSolverTest : public ::testing::Test
solver->getSystemSolution() );
solver->implicitStepSetup( time, dt, domain );
+ solver->wellSolver()->initializeWells( domain, time );
}
static real64 constexpr time = 0.0;
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..80ed8fce152 100644
--- a/src/coreComponents/physicsSolvers/fluidFlow/CMakeLists.txt
+++ b/src/coreComponents/physicsSolvers/fluidFlow/CMakeLists.txt
@@ -129,11 +129,21 @@ 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/WellVolumeRateConstraint.hpp
+ wells/WellMassRateConstraint.hpp
+ wells/WellPhaseVolumeRateConstraint.hpp
+ wells/WellLiquidRateConstraint.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 +175,16 @@ 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/WellVolumeRateConstraint.cpp
+ wells/WellMassRateConstraint.cpp
+ wells/WellPhaseVolumeRateConstraint.cpp
+ wells/WellLiquidRateConstraint.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 28def34e770..9a0c5015173 100644
--- a/src/coreComponents/physicsSolvers/fluidFlow/wells/CompositionalMultiphaseWell.cpp
+++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/CompositionalMultiphaseWell.cpp
@@ -50,6 +50,16 @@
#include "physicsSolvers/fluidFlow/kernels/compositional/FluidUpdateKernel.hpp"
#include "physicsSolvers/fluidFlow/CompositionalMultiphaseStatistics.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/kernels/CompositionalMultiphaseWellConstraintKernels.hpp"
+#include "physicsSolvers/multiphysics/CoupledReservoirAndWellKernels.hpp"
+
+
#if defined( __INTEL_COMPILER )
#pragma GCC optimize "O0"
#endif
@@ -64,15 +74,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 +132,7 @@ CompositionalMultiphaseWell::CompositionalMultiphaseWell( const string & name,
void CompositionalMultiphaseWell::postInputInitialization()
{
- WellSolverBase::postInputInitialization();
+ WellControls::postInputInitialization();
GEOS_ERROR_IF_GT_MSG( m_maxCompFracChange, 1.0,
getWrapperDataContext( viewKeyStruct::maxCompFracChangeString() ) <<
@@ -136,28 +145,21 @@ void CompositionalMultiphaseWell::postInputInitialization()
getWrapperDataContext( viewKeyStruct::maxRelativeCompDensChangeString() ) <<
": The maximum relative change in component density must be larger than 0.0" );
}
-
-void CompositionalMultiphaseWell::registerDataOnMesh( Group & meshBodies )
+void CompositionalMultiphaseWell::setConstitutiveNames( ElementSubRegionBase & subRegion ) const
+{
+ setConstitutiveName< MultiFluidBase >( subRegion, viewKeyStruct::fluidNamesString(), "multiphase fluid" );
+}
+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 +174,107 @@ 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 )
+
+ WellControls::registerWellDataOnMesh( 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::pressure >( getName() );
+ subRegion.registerField< well::pressure_n >( getName() );
+
+ subRegion.registerField< well::temperature >( getName() );
+ if( isThermal() )
{
+ subRegion.registerField< well::temperature_n >( getName() );
+ }
- ElementRegionManager & elemManager = mesh.getElemManager();
+ subRegion.registerField< well::gravityCoefficient >( getName() );
- 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 );
-
- subRegion.registerField< well::mixtureConnectionRate >( getName() );
- subRegion.registerField< well::mixtureConnectionRate_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::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::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::pressureScalingFactor >( getName() );
- subRegion.registerField< well::temperatureScalingFactor >( getName() );
- subRegion.registerField< well::globalCompDensityScalingFactor >( getName() );
-
- PerforationData & perforationData = *subRegion.getPerforationData();
- perforationData.registerField< well::compPerforationRate >( getName() ).
- reference().resizeDimension< 1 >( 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 );
- }
- WellControls & wellControls = getWellControls( subRegion );
- wellControls.registerWrapper< real64 >( viewKeyStruct::currentBHPString() );
+ subRegion.registerField< well::globalCompDensity >( getName() ).
+ reference().resizeDimension< 1 >( m_numComponents );
+ subRegion.registerField< well::globalCompDensity_n >( getName() ).
+ reference().resizeDimension< 1 >( m_numComponents );
- wellControls.registerWrapper< array1d< real64 > >( viewKeyStruct::dCurrentBHPString() ).
- setSizedFromParent( 0 ).
- reference().resizeDimension< 0 >( m_numComponents + 2 ); // dP, dT, dC
+ subRegion.registerField< well::connectionRate >( getName() );
+ subRegion.registerField< well::connectionRate_n >( getName() );
- wellControls.registerWrapper< array1d< real64 > >( viewKeyStruct::currentPhaseVolRateString() ).
- setSizedFromParent( 0 ).
- reference().resizeDimension< 0 >( m_numPhases );
+ 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 );
- wellControls.registerWrapper< array2d< real64 > >( viewKeyStruct::dCurrentPhaseVolRateString() ).
- setSizedFromParent( 0 ).
- reference().resizeDimension< 0, 1 >( m_numPhases, m_numComponents + 3 ); // dP, dT, dC, dQ
+ 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.registerWrapper< real64 >( viewKeyStruct::massDensityString() );
+ subRegion.registerField< well::totalMassDensity >( getName() );
+ subRegion.registerField< well::dTotalMassDensity >( getName() ).
+ reference().resizeDimension< 1 >( m_numComponents +2 ); // dP, dT, dC
- wellControls.registerWrapper< real64 >( viewKeyStruct::currentTotalVolRateString() );
- wellControls.registerWrapper< array1d< real64 > >( viewKeyStruct::dCurrentTotalVolRateString() ).
- setSizedFromParent( 0 ).
- reference().resizeDimension< 0 >( m_numComponents + 3 ); // dP, dT, dC dQ
+ subRegion.registerField< well::phaseVolumeFraction_n >( getName() ).
+ reference().resizeDimension< 1 >( m_numPhases );
- wellControls.registerWrapper< real64 >( viewKeyStruct::massDensityString() );
+ subRegion.registerField< well::pressureScalingFactor >( getName() );
+ subRegion.registerField< well::temperatureScalingFactor >( getName() );
+ subRegion.registerField< well::globalCompDensityScalingFactor >( getName() );
- wellControls.registerWrapper< real64 >( viewKeyStruct::currentMassRateString() );
+ PerforationData & perforationData = *subRegion.getPerforationData();
- // 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();
- }
- } );
- } );
+ perforationData.registerField< well::gravityCoefficient >( getName() );
+ perforationData.registerField< well::compPerforationRate >( getName() ).
+ reference().resizeDimension< 1 >( 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 );
+ }
+
+ m_writeCSV=1;
+ // 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 +318,18 @@ 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,99 +342,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 ),
- "WellControls " << wellControls.getName() <<
- " : Injection stream not specified for well " << subRegion.getName(),
- InputError );
- GEOS_THROW_IF( ( streamSize != m_numComponents ),
- "WellControls " << wellControls.getName() <<
- " : Injection stream for well " << subRegion.getName() << " should have " <<
- m_numComponents << " components.",
- InputError );
-
- 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 ),
- "WellControls " << wellControls.getDataContext() <<
- ": Invalid injection stream for well " << subRegion.getName(),
- InputError, wellControls.getDataContext() );
- compFracSum += compFrac;
- }
- GEOS_THROW_IF( ( compFracSum < 1.0 - std::numeric_limits< real64 >::epsilon() ) ||
- ( compFracSum > 1.0 + std::numeric_limits< real64 >::epsilon() ),
- "WellControls " << wellControls.getDataContext() <<
- ": Invalid injection stream for well " << subRegion.getName(),
- 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 {}.",
- getDataContext(), regionName, wellControls.getName() ) );
+ getDataContext(), regionName, getName() ) );
}
@@ -461,135 +376,66 @@ 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,
- "WellControls " << wellControls.getDataContext() <<
- ": Phase rate control is not available for injectors",
- InputError, wellControls.getDataContext() );
- GEOS_THROW_IF( wellControls.isProducer() && currentControl == WellControls::Control::TOTALVOLRATE,
- "WellControls " << wellControls.getDataContext() <<
- ": Total rate control is not available for producers",
- InputError, wellControls.getDataContext() );
-
- GEOS_THROW_IF( wellControls.isInjector() && targetTotalRate < 0.0,
- "WellControls " << wellControls.getDataContext() <<
- ": Target total rate cannot be negative for injectors",
- InputError, wellControls.getDataContext() );
- GEOS_THROW_IF( wellControls.isInjector() && !isZero( targetPhaseRate ),
- "WellControls " << wellControls.getDataContext() <<
- ": Target phase rate cannot be used for injectors",
- InputError, wellControls.getDataContext() );
- GEOS_THROW_IF( wellControls.isProducer() && !isZero( targetTotalRate ),
- "WellControls " << wellControls.getDataContext() <<
- ": Target total rate cannot be used for producers",
- InputError, wellControls.getDataContext() );
- GEOS_THROW_IF( wellControls.isProducer() && !isZero( targetMassRate ),
- "WellControls " << wellControls.getDataContext() <<
- ": Target mass rate cannot be used for producers",
- InputError, wellControls.getDataContext() );
- GEOS_THROW_IF( !m_useMass && !isZero( targetMassRate ),
- "WellControls " << wellControls.getDataContext() <<
- ": 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,
- "WellControls " << wellControls.getDataContext() <<
- ": Target phase rate cannot be negative for producers",
- InputError, wellControls.getDataContext() );
- GEOS_THROW_IF( wellControls.isProducer() && !isZero( targetTotalRate ),
- "WellControls " << wellControls.getDataContext() <<
- ": 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 )
+ // tjb
+ forSubGroups< InjectionConstraint< PhaseVolumeRateConstraint >, ProductionConstraint< PhaseVolumeRateConstraint > >( [&]( auto & constraint )
{
- if( fluid.phaseNames()[ip] == wellControls.getTargetPhaseName() )
- {
- m_targetPhaseIndex = ip;
- }
- }
- GEOS_THROW_IF( wellControls.isProducer() && m_targetPhaseIndex == -1,
- "WellControls " << wellControls.getDataContext() <<
- ": Phase " << wellControls.getTargetPhaseName() << " not found for well control " << wellControls.getName(),
- InputError, wellControls.getDataContext() );
+ constraint.validatePhaseType( fluid );
+ } );
+
+
+
}
void CompositionalMultiphaseWell::initializePostSubGroups()
{
- WellSolverBase::initializePostSubGroups();
+ WellControls::initializePostSubGroups();
- DomainPartition & domain = this->getGroupByPath< DomainPartition >( "/Problem/domain" );
+ //DomainPartition & domain = this->getGroupByPath< DomainPartition >( "/Problem/domain" );
- validateConstitutiveModels( domain );
+ // tjbvalidateConstitutiveModels( 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 );
+}
+void CompositionalMultiphaseWell::postRestartInitialization( )
+{
+
+ // setup fluid separator
+ constitutive::MultiFluidBase & fluidSeparator = getMultiFluidSeparator();
+ fluidSeparator.allocateConstitutiveData( *this, 1 );
+ fluidSeparator.resize( 1 );
+
}
-void CompositionalMultiphaseWell::createSeparator()
+void CompositionalMultiphaseWell::createSeparator( 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 )
- {
- 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 ));
- } );
- } );
+ 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 ));
+
}
void CompositionalMultiphaseWell::updateGlobalComponentFraction( WellElementSubRegion & subRegion ) const
{
@@ -611,70 +457,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;
@@ -684,250 +501,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 {} ",
- getDataContext(), wellControls.getName(), wellControls.referenceReservoirRegion() ) );
+ // 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 {} ",
- getDataContext(), wellControls.getName(), wellControls.referenceReservoirRegion() ));
- }
- // 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;
@@ -954,24 +722,134 @@ 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,
- subRegion,
- fluid )
-: isothermalCompositionalMultiphaseBaseKernels::
- PhaseVolumeFractionKernelFactory::
- createAndLaunch< parallelDevicePolicy<> >( m_numComponents,
+ 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::
+ PhaseVolumeFractionKernelFactory::
+ createAndLaunch< parallelDevicePolicy<> >( m_numComponents,
m_numPhases,
subRegion,
fluid );
@@ -1002,185 +880,273 @@ 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
- // update volumetric rates for the well constraints
- // note: this must be called before updateFluidModel
- updateVolRatesForConstraint( elemManager, subRegion );
+ updateSeparator( elemManager, subRegion ); // Calculate fluid properties at control conditions
- // update densities, phase fractions, phase volume fractions
+ updateVolRatesForConstraint( subRegion ); // remove tjb ??
- updateFluidModel( subRegion ); // Calculate fluid properties;
- real64 maxPhaseVolChange = updatePhaseVolumeFraction( subRegion );
- updateTotalMassDensity( subRegion );
- // update the current BHP pressure
- updateBHPForConstraint( subRegion );
+ maxPhaseVolChange = updatePhaseVolumeFraction( subRegion );
+ updateTotalMassDensity( subRegion );
+
+ // 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 >( 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;
+
+
+ }
+
+
+ }
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 )
+ {
+ 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 )
+ {
+ 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 );
+ GEOS_LOG_RANK_0( "tjb shut wells "<< subRegion.getName());
+ }
+ else
+ {
+ setWellState( true );
+ // setup if restart
+ if( getCurrentConstraint() == nullptr )
+ {
+ updateSubRegionState( elemManager, subRegion );
+ if( isProducer() )
+ {
+ forSubGroups< MinimumBHPConstraint, ProductionConstraint< VolumeRateConstraint >, ProductionConstraint< MassRateConstraint >,
+ ProductionConstraint< PhaseVolumeRateConstraint > >( [&](
+ auto
+ & constraint )
+ {
+ if( ConstraintTypeId( getControl()) == constraint.getControl() )
+ {
+ setCurrentConstraint( &constraint );
+ }
+ } );
+ }
+ else
+ {
+ forSubGroups< MaximumBHPConstraint, InjectionConstraint< VolumeRateConstraint >, InjectionConstraint< MassRateConstraint >, InjectionConstraint< PhaseVolumeRateConstraint > >( [&](
+ auto
+ &
+ constraint )
+ {
+ if( ConstraintTypeId( getControl()) == constraint.getControl() )
+ {
+ setCurrentConstraint( &constraint );
+ }
+ } );
+ }
+ }
+
+ }
}
-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 );
@@ -1190,62 +1156,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 );
@@ -1256,126 +1215,128 @@ 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;
-
integer numNorm = 1; // mass balance
array1d< real64 > localResidualNorm;
array1d< real64 > localResidualNormalizer;
@@ -1391,85 +1352,127 @@ 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();
+ string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString() );
+ MultiFluidBase const & fluid = subRegion.getConstitutiveModel< MultiFluidBase >( fluidName );
- elemManager.forElementSubRegions< WellElementSubRegion >( regionNames,
- [&]( localIndex const,
- WellElementSubRegion const & subRegion )
+ if( isWellOpen( ) )
+ {
+ // step 1: compute the norm in the subRegion
+ if( isThermal() )
{
-
-
- 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
-
- if( isThermal() )
+ real64 subRegionResidualNorm[2]{};
+
+ 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 >( 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] )
{
- if( subRegionResidualNorm[i] > localResidualNorm[i] )
- {
- localResidualNorm[i] = subRegionResidualNorm[i];
- }
+ localResidualNorm[i] = subRegionResidualNorm[i];
}
-
}
- else
+
+ }
+ else
+ {
+ 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
+
+ if( subRegionResidualNorm[0] > localResidualNorm[0] )
{
- 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[0] = subRegionResidualNorm[0];
}
- } );
- } );
+ }
+ }
+ else
+ {
+ for( integer i=0; i 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() );
+
+
+
+ //string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString() );
+ //MultiFluidBase const & fluid = subRegion.getConstitutiveModel< MultiFluidBase >( fluidName );
+
+ if( isWellOpen( ) )
+ {
+ localResidualNorm = calculateLocalWellResidualNorm( time_n,
+ dt,
+ nonlinearSolverParameters,
+ subRegion,
+ dofManager,
+ localRhs );
+
+ }
+ else
+ {
+ for( integer i=0; i const & localSolution )
+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;
- 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 )
- {
- mesh.getElemManager().forElementSubRegions( regionNames,
- [&]( localIndex const,
- ElementSubRegionBase & subRegion )
- {
- 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 >();
- const integer temperatureOffset = m_numComponents+2;
- auto const subRegionData =
- m_isThermal
+ 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 );
+ 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 ) );
+ }
+
+ 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() );
- scalingFactor = MpiWrapper::min( scalingFactor );
- maxDeltaPres = MpiWrapper::max( maxDeltaPres );
- maxDeltaCompDens = MpiWrapper::max( maxDeltaCompDens );
- minPresScalingFactor = MpiWrapper::min( minPresScalingFactor );
- minCompDensScalingFactor = MpiWrapper::min( minCompDensScalingFactor );
+ 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)",
@@ -1596,7 +1651,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 ) );
@@ -1610,94 +1664,90 @@ CompositionalMultiphaseWell::scalingForSystemSolution( DomainPartition & domain,
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 );
@@ -1709,103 +1759,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 =
+ // ( wellControls.isInjector() ) && wellControls.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 );
@@ -1813,513 +1936,517 @@ 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 1
// 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 )
+#endif
+ std::cout << getTargetRegionNames()[0] << " Well after applyWellSystemSolution: " << std::endl;
+ // 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();
- elemManager.forElementSubRegions< WellElementSubRegion >( regionNames,
- [&]( localIndex const,
- WellElementSubRegion & subRegion )
- {
- arrayView1d< integer const > const & wellElemGhostRank = subRegion.ghostRank();
+ arrayView1d< integer const > const & wellElemGhostRank = subRegion.ghostRank();
- arrayView2d< real64, compflow::USD_COMP > const & wellElemCompDens =
- subRegion.getField< well::globalCompDensity >();
+ arrayView2d< real64, compflow::USD_COMP > const & wellElemCompDens =
+ subRegion.getField< fields::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 );
+
+ 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( 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.
- ElementRegionManager & elemManager = mesh.getElemManager();
+ if( !subRegion.isLocallyOwned() || !( getWellStatus() == WellControls::Status::OPEN ))
+ {
+ return;
+ }
- elemManager.forElementSubRegions< WellElementSubRegion >( regionNames,
- [&]( localIndex const,
- WellElementSubRegion & subRegion )
+ if( isProducer() )
+ {
+ forSubGroups< MinimumBHPConstraint, ProductionConstraint< PhaseVolumeRateConstraint >, ProductionConstraint< MassRateConstraint >, ProductionConstraint< VolumeRateConstraint >,
+ ProductionConstraint< LiquidRateConstraint >
+ >( [&]( auto & constraint )
{
- // 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() )
+ // 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 );
+ // 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 );
+ }
- WellControls & wellControls = getWellControls( subRegion );
- if( wellControls.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 );
-
- 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 );
- 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 );
- 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 );
+ 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 );
- 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();
- string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString() );
- MultiFluidBase const & fluid = getConstitutiveModel< MultiFluidBase >( subRegion, fluidName );
- fluid.saveConvergedState();
+ validateWellConstraints( time_n, dt, subRegion );
- validateWellConstraints( time_n, dt, subRegion );
+ updateSubRegionState( elemManager, subRegion );
+ }
- updateSubRegionState( elemManager, subRegion );
- }
- } )
- ;
- } );
}
void CompositionalMultiphaseWell::implicitStepComplete( real64 const & time_n,
real64 const & dt,
- DomainPartition & domain )
+ WellElementSubRegion const & subRegion )
{
- WellSolverBase::implicitStepComplete( time_n, dt, domain );
-
- if( getLogLevel() > 0 )
- {
- printRates( time_n, dt, domain );
- }
+ printRates( time_n, dt, subRegion );
}
void CompositionalMultiphaseWell::printRates( real64 const & time_n,
real64 const & dt,
- DomainPartition & domain )
+ WellElementSubRegion const & subRegion )
{
- forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &,
- MeshLevel & mesh,
- string_array const & regionNames )
- {
- ElementRegionManager & elemManager = mesh.getElemManager();
+ integer const numPhase = m_numPhases;
+ integer const numComp = m_numComponents;
+ integer const numPerf = subRegion.getPerforationData()->size();
- elemManager.forElementSubRegions< WellElementSubRegion >( regionNames,
- [&]( localIndex const,
- WellElementSubRegion & subRegion )
- {
- integer const numPhase = m_numPhases;
- integer const numComp = m_numComponents;
- integer const numPerf = subRegion.getPerforationData()->size();
- // 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 >();
+ 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 )
- {
- for( integer iperf = 0; iperf < numPerf; iperf++ )
- {
- compRate[ic] += compPerfRate[iperf][ic];
- }
- }
- } );
- for( integer ic = 0; ic < numComp; ++ic )
+ // 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] = MpiWrapper::sum( compRate[ic] );
+ compRate[ic] += compPerfRate[iperf][ic];
}
}
+ } );
+ for( integer ic = 0; ic < numComp; ++ic )
+ {
+ compRate[ic] = MpiWrapper::sum( compRate[ic] );
+ }
+ }
- // the rank that owns the reference well element is responsible for the calculations below.
- if( !subRegion.isLocallyOwned() )
- {
- return;
- }
+ // the rank that owns the reference well element is responsible for the calculations below.
+ if( !subRegion.isLocallyOwned() )
+ {
+ return;
+ }
+
+ string const wellControlsName = getName();
- string const wellControlsName = wellControls.getName();
+ // 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;
+ }
- // format: time,total_rate,total_vol_rate,phase0_vol_rate,phase1_vol_rate,...
- std::ofstream outputFile;
- if( m_writeCSV > 0 )
+ 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.open( m_ratesOutputDir + "/" + wellControlsName + ".csv", std::ios_base::app );
- outputFile << time_n << "," << dt;
+ outputFile << ",0.0";
}
-
- if( wellControls.getWellStatus() == WellControls::Status::CLOSED )
+ for( integer ic = 0; ic < numComp; ++ic )
{
- 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;
+ outputFile << ",0.0";
}
+ outputFile << std::endl;
+ outputFile.close();
+ }
+ return;
+ }
+
+ localIndex const iwelemRef = subRegion.getTopWellElementIndex();
+ string const massUnit = m_useMass ? "kg" : "mol";
+
+ // subRegion data
- 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 )
+ 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 )
{
- 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();
- }
- } );
- } );
+ 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..f76050f7bf7 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;
+
+ /**
+ * @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
*/
/**@{*/
+ /**
+ * @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,18 @@ 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
*/
void chopNegativeDensities( DomainPartition & domain );
- struct viewKeyStruct : WellSolverBase::viewKeyStruct
+ void chopNegativeDensities( WellElementSubRegion & subRegion );
+
+
+ struct viewKeyStruct : WellControls::viewKeyStruct
{
- static constexpr char const * dofFieldString() { return "compositionalWellVars"; }
+ static constexpr char const * dofFieldString() { return "wellVars"; }
// inputs
@@ -271,36 +341,7 @@ 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;
@@ -312,30 +353,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 +380,15 @@ 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;
+private:
virtual void setConstitutiveNames( ElementSubRegionBase & subRegion ) const override;
-
/// flag indicating whether mass or molar formulation should be used
integer m_useMass;
@@ -395,11 +416,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/SinglePhaseWell.cpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/SinglePhaseWell.cpp
index 54716e387d4..8e501bccc0e 100644
--- a/src/coreComponents/physicsSolvers/fluidFlow/wells/SinglePhaseWell.cpp
+++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/SinglePhaseWell.cpp
@@ -37,14 +37,13 @@
#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"
namespace geos
{
@@ -55,7 +54,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 +67,58 @@ 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 );
- // loop over the wells
- forDiscretizationOnMeshTargets( meshBodies, [&] ( string const &,
- MeshLevel & mesh,
- string_array const & regionNames )
+ setConstitutiveNames ( subRegion );
+ WellControls::registerDataOnMesh( subRegion );
+ //DomainPartition const & domain = this->getGroupByPath< DomainPartition >( "/Problem/domain" );
+ //ConstitutiveManager const & cm = domain.getConstitutiveManager();
+ 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() );
-
- wellControls.registerWrapper< array1d< real64 > >( viewKeyStruct::dCurrentBHPString() ).
- setSizedFromParent( 0 ).
- reference().resizeDimension< 0 >( 2 + isThermal() ); // dP, dT , dQ
+ 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< real64 >( viewKeyStruct::currentVolRateString() );
- wellControls.registerWrapper< array1d< real64 > >( viewKeyStruct::dCurrentVolRateString() ).
- setSizedFromParent( 0 ).
- reference().resizeDimension< 0 >( 2 + isThermal() ); // dP, dT, dQ
+ // dP, dT, dQ
+ m_writeCSV=1;
+ // 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 {}.",
- getDataContext(), regionName, wellControls.getName() ) );
+ getDataContext(), regionName, getName() ) );
}
}
- 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,
- "WellControls " << wellControls.getDataContext() <<
- ": 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) ),
- "WellControls " << wellControls.getDataContext() <<
- ": Target total rate cannot be negative",
- InputError, wellControls.getDataContext() );
- GEOS_THROW_IF( !isZero( targetPhaseRate ),
- "WellControls " << wellControls.getDataContext() <<
- ": 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 )
@@ -213,45 +226,33 @@ void SinglePhaseWell::updateBHPForConstraint( WellElementSubRegion & subRegion )
// 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 >( SinglePhaseWell::viewKeyStruct::currentBHPString() );
- arrayView1d< real64 > const & dCurrentBHP =
- wellControls.getReference< array1d< real64 > >( SinglePhaseWell::viewKeyStruct::dCurrentBHPString() );
-
- geos::internal::kernelLaunchSelectorThermalSwitch( isThermal(), [&] ( auto ISTHERMAL )
+ getReference< real64 >( WellControls::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 ) );
}
-void SinglePhaseWell::updateVolRateForConstraint( ElementRegionManager const & elemManager, WellElementSubRegion & subRegion )
+void SinglePhaseWell::calculateReferenceElementRates( WellElementSubRegion & subRegion )
{
GEOS_MARK_FUNCTION;
@@ -265,81 +266,124 @@ 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 >();
+
+ // control data
+
+
// 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;
+ // tjb compute mass
+ } );
+
+
+}
+
+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() );
- GEOS_ERROR_IF ( !region.hasWrapper( SinglePhaseStatistics::regionStatisticsName()),
+ 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 {} ",
- getDataContext(), wellControls.getName(), wellControls.referenceReservoirRegion() ) );
+ getDataContext(), getName(), getReferenceReservoirRegion() ) );
- SinglePhaseStatistics::RegionStatistics const & stats = region.getReference< SinglePhaseStatistics::RegionStatistics >( SinglePhaseStatistics::regionStatisticsName() );
+ SinglePhaseStatistics::RegionStatistics const & stats = region.getReference< SinglePhaseStatistics::RegionStatistics >(
+ SinglePhaseStatistics::regionStatisticsName() );
+ setRegionAveragePressure( stats.averagePressure );
+ setRegionAverageTemperature( stats.averageTemperature );
GEOS_ERROR_IF( stats.averagePressure <= 0.0,
- GEOS_FMT(
- "{}: No region average quantities computed. WellControl {} referenceReservoirRegion field requires SinglePhaseStatistics to be configured for region {} ",
- getDataContext(), wellControls.getName(), wellControls.referenceReservoirRegion() ));
- wellControls.setRegionAveragePressure( stats.averagePressure );
- wellControls.setRegionAverageTemperature( stats.averageTemperature );
+ GEOS_FMT( "{}: No region average quantities computed. WellControl {} referenceReservoirRegion field requires CompositionalMultiphaseStatistics to be configured for region {} ",
+ getDataContext(), getName(), getReferenceReservoirRegion() ));
+
}
// 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 +391,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,230 +409,258 @@ 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
+real64 SinglePhaseWell::updateSubRegionState( ElementRegionManager const & elemManager, WellElementSubRegion & subRegion )
{
- 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 )
+ if( getWellState())
{
- 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 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 );
+ // update density in the well elements
+ updateFluidModel( subRegion );
+ updateSeparator( elemManager, subRegion ); // Calculate fluid properties at control conditions
- // update the current BHP
- updateBHPForConstraint( subRegion );
+ // Calculate the reference element rates
+ calculateReferenceElementRates( subRegion );
+ // update the current BHP
+ updateBHPForConstraint( subRegion );
- // note: the perforation rates are updated separately
- return 0.0; // change in phasevolume fraction doesnt apply
-}
-void SinglePhaseWell::initializeWells( DomainPartition & domain, real64 const & time_n )
+ }
+ return 0.0; // change in phasevolume fraction doesnt apply
+}
+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 )
+ 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 >();
+
+ // 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 ) )
{
- ElementRegionManager & elemManager = meshLevel.getElemManager();
+ setWellState( true );
+ if( getCurrentConstraint() == nullptr )
+ {
+ if( isProducer() )
+ {
+ forSubGroups< MinimumBHPConstraint, ProductionConstraint< VolumeRateConstraint >, ProductionConstraint< MassRateConstraint >,
+ ProductionConstraint< PhaseVolumeRateConstraint > >( [&]( auto & constraint )
+ {
+ if( ConstraintTypeId( getControl()) == constraint.getControl() )
+ {
+ setCurrentConstraint( &constraint );
+ setControl( static_cast< WellControls::Control >(constraint.getControl()) ); // tjb old
+ }
+ } );
+ }
+ 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
+ }
+ } );
+ }
+ }
- elemManager.forElementSubRegions< WellElementSubRegion >( regionNames,
- [&]( localIndex const,
- WellElementSubRegion & subRegion )
+ 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 >(
+ WellControls::viewKeyStruct::currentVolRateString() ));
+ //constraint->setMassRate( wellControls.getReference< real64 >( WellControls::viewKeyStruct::currentMassRateString() ));
+ // 7) Copy well / fluid dofs to "prop"_n variables
+ saveState( subRegion );
+ }
+ else if( !hasNonZeroRate )
+ {
+ setWellState( false );
+ GEOS_LOG_RANK_0( "tjb shut wells "<< subRegion.getName());
+ }
+ else
+ {
+ setWellState( true );
+ // setup for restart
+ 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 )
+ updateSubRegionState( elemManager, subRegion );
+ 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 );
+ }
+ } );
}
+ else
+ {
+ forSubGroups< MaximumBHPConstraint, InjectionConstraint< VolumeRateConstraint >, InjectionConstraint< MassRateConstraint >, InjectionConstraint< PhaseVolumeRateConstraint > >( [&](
+ auto
+ &
+ constraint )
+ {
+ if( ConstraintTypeId( getControl()) == constraint.getControl() )
+ {
+ setCurrentConstraint( &constraint );
+ }
+ } );
+ }
+ }
- } );
+ }
- } );
}
-void SinglePhaseWell::shutDownWell( real64 const time_n,
- DomainPartition const & domain,
+
+void SinglePhaseWell::shutDownWell( WellElementSubRegion & subRegion,
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 )
- {
- ElementRegionManager const & elemManager = mesh.getElemManager();
- elemManager.forElementSubRegions< WellElementSubRegion >( regionNames,
- [&]( localIndex const,
- WellElementSubRegion const & subRegion )
- {
-
- // if the well is open, we don't have to do anything, so we just return
- WellControls const & wellControls = getWellControls( subRegion );
- if( wellControls.isWellOpen( ) )
- {
- return;
- }
+ if( isWellOpen( ) )
+ {
+ return;
+ }
- globalIndex const rankOffset = dofManager.rankOffset();
+ 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< 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 >();
+ 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 )
- {
- if( ghostRank[ei] >= 0 )
- {
- return;
- }
+ forAll< parallelDevicePolicy<> >( subRegion.size(), [=] GEOS_HOST_DEVICE ( localIndex const ei )
+ {
+ if( ghostRank[ei] >= 0 )
+ {
+ return;
+ }
- 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;
+ 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;
- } );
- } );
} );
+
}
+real64 SinglePhaseWell::updateWellState( ElementRegionManager const & elemManager, WellElementSubRegion & subRegion )
+{
+ GEOS_MARK_FUNCTION;
+ updateSubRegionState( elemManager, subRegion );
+ return 0.0;
+}
+#if 0
void SinglePhaseWell::assembleSystem( real64 const time,
real64 const dt,
DomainPartition & domain,
@@ -598,11 +670,38 @@ void SinglePhaseWell::assembleSystem( real64 const time,
{
string const wellDofKey = dofManager.getKey( wellElementDofName());
+
+ // 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();
+ selectWellConstraint( time, dt, nonlinearParams.m_numNewtonIterations, domain );
+
+
// assemble the accumulation term in the mass balance equations
assembleAccumulationTerms( time, dt, domain, dofManager, localMatrix, localRhs );
// then assemble the pressure relations between well elements
- assemblePressureRelations( time, dt, domain, dofManager, localMatrix, localRhs );
+ //assemblePressureRelations( time, dt, domain, dofManager, localMatrix, localRhs );
+ {
+ forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &,
+ MeshLevel & mesh,
+ string_array const & regionNames )
+ {
+ ElementRegionManager & elementRegionManager = mesh.getElemManager();
+ elementRegionManager.forElementRegions< WellElementRegion >( regionNames,
+ [&]( localIndex const,
+ WellElementRegion & region )
+ {
+ WellElementSubRegion & subRegion = region.getGroup( ElementRegionBase::viewKeyStruct::elementSubRegions() )
+ .getGroup< WellElementSubRegion >( region.getSubRegionName() );
+
+ assembleWellConstraintTerms( time, dt, subRegion, dofManager, localMatrix.toViewConstSizes(), localRhs );
+ } );
+ } );
+ }
// then compute the perforation rates (later assembled by the coupled solver)
computePerforationRates( time, dt, domain );
@@ -614,298 +713,333 @@ void SinglePhaseWell::assembleSystem( real64 const time,
// then apply a special treatment to the wells that are shut
shutDownWell( time, domain, dofManager, localMatrix, localRhs );
}
+#endif
-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::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_n );
- GEOS_UNUSED_VAR( dt );
+ GEOS_UNUSED_VAR( time );
- // loop over the wells
- forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &,
- MeshLevel const & mesh,
- string_array const & regionNames )
+ // get a reference to the degree-of-freedom numbers
+ 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 );
+ }
- ElementRegionManager const & elemManager = mesh.getElemManager();
+}
- elemManager.forElementSubRegions< WellElementSubRegion >( regionNames,
- [&]( localIndex const,
- WellElementSubRegion const & subRegion )
- {
- WellControls const & wellControls = getWellControls( subRegion );
- // get a reference to the degree-of-freedom numbers
- string const wellDofKey = dofManager.getKey( wellElementDofName() );
+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;
+
- 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
+ // the rank that owns the reference well element is responsible for the calculations below.
+
+ if( !subRegion.isLocallyOwned() || !( getWellStatus() == WellControls::Status::OPEN ))
+ {
+ return;
+ }
+ {
+ // tjb wellControls.forSubGroups< BHPConstraint, MassConstraint, VolumeRateConstraint >( [&]( auto & constraint )
+ forSubGroups< BHPConstraint, InjectionConstraint< VolumeRateConstraint >, ProductionConstraint< VolumeRateConstraint > >( [&]( auto & constraint )
+ {
+ if( constraint.getName() == getCurrentConstraint()->getName())
{
- singlePhaseWellKernels::
- FaceBasedAssemblyKernelFactory::
- createAndLaunch< parallelDevicePolicy<> >( dt,
- dofManager.rankOffset(),
- wellDofKey,
- wellControls,
- subRegion,
- localMatrix,
- localRhs );
+ // found limiting constraint
+
+ // fluid data
+ constitutive::SingleFluidBase & fluidSeparator = getSingleFluidSeparator();
+ integer isThermal = fluidSeparator.isThermal();
+
+ 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 );
+ // 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::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
-}
-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 +1048,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 +1154,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 +1163,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 +1179,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 +1210,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 +1249,195 @@ 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 )
+
+ // 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() )
{
- ElementRegionManager & elemManager = mesh.getElemManager();
+ 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 );
- 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() )
- {
- 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 );
+ validateWellConstraints( time_n, dt, subRegion );
- SingleFluidBase const & fluid =
- getConstitutiveModel< SingleFluidBase >( subRegion, subRegion.getReference< string >( viewKeyStruct::fluidNamesString() ) );
- fluid.saveConvergedState();
+ updateSubRegionState( elemManager, subRegion );
- validateWellConstraints( time, dt, subRegion );
-
- updateSubRegionState( elemManager, subRegion );
- } );
- } );
}
void SinglePhaseWell::implicitStepComplete( real64 const & time_n,
real64 const & dt,
- DomainPartition & domain )
+ WellElementSubRegion const & subRegion )
{
- WellSolverBase::implicitStepComplete( time_n, dt, domain );
-
- if( getLogLevel() > 0 )
- {
- printRates( time_n, dt, domain );
- }
+ printRates( time_n, dt, subRegion );
}
void SinglePhaseWell::printRates( real64 const & time_n,
- real64 const & GEOS_UNUSED_PARAM( dt ),
- DomainPartition & domain )
+ real64 const & dt,
+ WellElementSubRegion const & subRegion )
{
- forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &,
- MeshLevel & mesh,
- string_array const & regionNames )
- {
-
- ElementRegionManager & elemManager = mesh.getElemManager();
-
- elemManager.forElementSubRegions< WellElementSubRegion >( regionNames,
- [&]( localIndex const,
- WellElementSubRegion & subRegion )
- {
-
- // the rank that owns the reference well element is responsible for the calculations below.
- if( !subRegion.isLocallyOwned() )
- {
- return;
- }
- localIndex const iwelemRef = subRegion.getTopWellElementIndex();
+ GEOS_UNUSED_VAR( dt ); // FIX THIS tjb
+ // the rank that owns the reference well element is responsible for the calculations below.
+ if( !subRegion.isLocallyOwned() )
+ {
+ return;
+ }
- // subRegion data
+ localIndex const iwelemRef = subRegion.getTopWellElementIndex();
- arrayView1d< real64 const > const & connRate =
- subRegion.getField< well::connectionRate >();
+ // subRegion data
- // control data
+ arrayView1d< real64 const > const & connRate =
+ subRegion.getField< well::connectionRate >();
- WellControls const & wellControls = getWellControls( subRegion );
- string const wellControlsName = wellControls.getName();
+ // control data
- // 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( !wellControls.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;
- }
+ string const wellControlsName = getName();
- integer const useSurfaceConditions = wellControls.useSurfaceConditions();
+ // 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;
+ }
- real64 const & currentBHP =
- wellControls.getReference< real64 >( SinglePhaseWell::viewKeyStruct::currentBHPString() );
- real64 const & currentTotalVolRate =
- wellControls.getReference< real64 >( SinglePhaseWell::viewKeyStruct::currentVolRateString() );
+ 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;
+ }
- // bring everything back to host, capture the scalars by reference
- forAll< serialPolicy >( 1, [&useSurfaceConditions,
- ¤tBHP,
- connRate,
- ¤tTotalVolRate,
- &iwelemRef,
- &wellControlsName,
- &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: {} 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();
- }
- } );
- } );
+ 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();
+ }
} );
}
-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..a77226289dc 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,53 @@ 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
+ * @brief Update separator model state
+ * @param elemManager the element region manager
+ * @param subRegion the well subRegion containing the separator
*/
- virtual void computePerforationRates( real64 const & time_n,
- real64 const & dt, DomainPartition & domain ) override;
+ void updateSeparator( ElementRegionManager const & elemManager, WellElementSubRegion & subRegion );
/**
- * @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
- */
- virtual real64 updateSubRegionState( ElementRegionManager const & elemManager, WellElementSubRegion & subRegion ) override;
+ * @brief Recompute all dependent quantities from primary variables (including constitutive
+ * models)
- /**
- * @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
+ * @param
+ * @param subRegion the well subRegion containing the well elements and their associated
*/
- 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 real64 updateWellState( ElementRegionManager const & elemManager, WellElementSubRegion & subRegion ) override;
- /**
- * @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 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"; }
};
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 +303,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 +312,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..5ea4fc74d42
--- /dev/null
+++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellConstraintsBase.cpp
@@ -0,0 +1,141 @@
+/*
+ * ------------------------------------------------------------------------------------------------------------
+ * 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." );
+
+}
+
+
+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..8668c44d3a8
--- /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 "constraintValue"; }
+
+ /// 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 d0d20deb642..e634f36d5cd 100644
--- a/src/coreComponents/physicsSolvers/fluidFlow/wells/WellControls.cpp
+++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellControls.cpp
@@ -22,8 +22,11 @@
#include "WellConstants.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,26 +35,32 @@ 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 )
{
setInputFlags( InputFlags::OPTIONAL_NONUNIQUE );
@@ -59,61 +68,26 @@ WellControls::WellControls( string const & name, Group * const parent )
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( 0 ).
+ setInputFlag( dataRepository::InputFlags::OPTIONAL ).
+ setDescription( "When set to 1, write the rates 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 +129,13 @@ 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 ).
+ this->registerWrapper( viewKeyStruct::estimateWellSolutionString(), &m_estimateSolution ).
+ setApplyDefaultValue( 0 ).
setInputFlag( InputFlags::OPTIONAL ).
- setDescription( "Name of the BHP table when the rate is a time dependent function" );
+ setDescription( "Flag to esitmate well solution prior to coupled reservoir and well solve." );
- 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 ).
- setInputFlag( InputFlags::OPTIONAL ).
- setDescription( "Name of the phase rate table when the rate is a time dependent function" );
-
- registerWrapper( viewKeyStruct::targetMassRateTableNameString(), &m_targetMassRateTableName ).
- setRTTypeName( rtTypes::CustomTypes::groupNameRef ).
- setInputFlag( InputFlags::OPTIONAL ).
- setDescription( "Name of the mass rate table when the rate is a time dependent function" );
-
- 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 +144,85 @@ 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;
-}
-
-void WellControls::switchToTotalRateControl( real64 const & val )
-{
- m_currentControl = Control::TOTALVOLRATE;
- m_targetTotalRate = val;
-}
+ //Group * baseChild = Group::createChild( childKey, childName );
+ //if( baseChild != nullptr )
+ //{
+ // return baseChild;
+ //}
+ //GEOS_LOG_RANK_0( GEOS_FMT( "{}: adding {} {}", getName(), childKey, childName ) );
+ ////const auto childTypes = { viewKeyStruct::perforationString() };
+ //GEOS_ERROR_IF( childKey != viewKeyStruct::perforationString(),
+ // CatalogInterface::unknownTypeError( childKey, getDataContext(), childTypes ) );
+
+ 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::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;
-void WellControls::switchToPhaseRateControl( real64 const & val )
-{
- m_currentControl = Control::PHASEVOLRATE;
- m_targetPhaseRate = val;
+ }
+ 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::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 +247,28 @@ 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() );
+ 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
@@ -250,119 +282,27 @@ void WellControls::postInputInitialization()
m_currentControl = m_inputControl;
}
- // 1.a) check target BHP
- GEOS_THROW_IF( m_targetBHP < 0,
- getWrapperDataContext( viewKeyStruct::targetBHPString() ) <<
- ": Target bottom-hole pressure is negative",
- InputError, getWrapperDataContext( viewKeyStruct::targetBHPString() ) );
-
- // 1.b) check target rates
- GEOS_THROW_IF( m_targetTotalRate < 0,
- getWrapperDataContext( viewKeyStruct::targetTotalRateString() ) << ": Target rate is negative",
- InputError, getWrapperDataContext( viewKeyStruct::targetTotalRateString() ) );
-
- GEOS_THROW_IF( m_targetPhaseRate < 0,
- getWrapperDataContext( viewKeyStruct::targetPhaseRateString() ) << ": Target oil rate is negative",
- InputError, getWrapperDataContext( viewKeyStruct::targetPhaseRateString() ) );
-
- GEOS_THROW_IF( m_targetMassRate < 0,
- getWrapperDataContext( viewKeyStruct::targetMassRateString() ) << ": Target mass rate is negative",
- InputError, getWrapperDataContext( viewKeyStruct::targetMassRateString() ) );
-
- GEOS_THROW_IF( (m_injectionStream.empty() && m_injectionTemperature >= 0) ||
- (!m_injectionStream.empty() && m_injectionTemperature < 0),
- "WellControls " << getDataContext() << ": 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,
- getWrapperDataContext( viewKeyStruct::injectionStreamString() ) << ": Invalid injection stream",
- getWrapperDataContext( viewKeyStruct::injectionStreamString() ) );
- sum += m_injectionStream[ic];
- }
- GEOS_THROW_IF( LvArray::math::abs( 1.0 - sum ) > std::numeric_limits< real64 >::epsilon(),
- getWrapperDataContext( viewKeyStruct::injectionStreamString() ) << ": Invalid injection stream",
- InputError, getWrapperDataContext( viewKeyStruct::injectionStreamString() ) );
- }
// 3) check the flag for surface / reservoir conditions
GEOS_THROW_IF( m_useSurfaceConditions != 0 && m_useSurfaceConditions != 1,
getWrapperDataContext( viewKeyStruct::useSurfaceConditionsString() ) << ": 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())),
- "WellControls " << getDataContext() << ": 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 " <<
- "either " << viewKeyStruct::targetTotalRateString() <<
- " or " << viewKeyStruct::targetTotalRateTableNameString()<<
- "The mass rate constraint can be specified using " <<
- "either " << viewKeyStruct::targetMassRateString() <<
- " or " << viewKeyStruct::targetMassRateTableNameString(),
- InputError, getDataContext() );
+ // tjb add more constraint validation
+ // 1) liquid rate - phase names consistent with fluild model
+ // 2) at least one bhp and one rate constraint defined
+ // 3) constraint type and well type compatibility
- // 5) check whether redundant information has been provided
- GEOS_THROW_IF( ((m_targetPhaseRate > 0.0 && !m_targetPhaseRateTableName.empty())),
- "WellControls " << getDataContext() << ": 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_targetMassRate > 0.0 && m_useSurfaceConditions==0)),
+ // "WellControls " << getDataContext() << ": Option only valid if useSurfaceConditions set to 1",
+ // InputError );
- GEOS_THROW_IF( ((m_targetTotalRate > 0.0 && !m_targetTotalRateTableName.empty())),
- "WellControls " << getDataContext() << ": 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())),
- "WellControls " << getDataContext() << ": 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())),
- "WellControls " << getDataContext() << ": 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)),
- "WellControls " << getDataContext() << ": 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())),
- "WellControls " << getDataContext() << ": 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 ));
- }
+ // 8) Make sure that the initial pressure coefficient is positive
+ GEOS_THROW_IF( m_initialPressureCoefficient < 0,
+ getWrapperDataContext( viewKeyStruct::initialPressureCoefficientString() ) <<
+ ": This tuning coefficient is negative",
+ InputError );
// 6.2) Check incoherent information
@@ -378,79 +318,6 @@ void WellControls::postInputInitialization()
<< EnumStrings< Control >::toString( Control::MASSRATE ),
InputError, getDataContext() );
- // 8) Make sure that the initial pressure coefficient is positive
- GEOS_THROW_IF( m_initialPressureCoefficient < 0,
- 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,
- "WellControls " << getDataContext() << ": 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,
- "WellControls " << getDataContext() << ": 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,
- "WellControls " << getDataContext() << ": 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,
- "WellControls " << getDataContext() << ": 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())
{
@@ -475,37 +342,107 @@ 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;
+ break;
+ }
+ }
+ }
+ else
+ {
+ std::vector< WellConstraintBase * > const constraints = getInjRateConstraints();
+ for( auto const & constraint : constraints )
+ {
+ if( isZero( constraint->getConstraintValue( currentTime ) ) )
+ {
+ m_wellStatus = WellControls::Status::CLOSED;
+ break;
+ }
+ }
}
+
if( m_statusTable->evaluate( ¤tTime ) < LvArray::NumericLimits< real64 >::epsilon )
{
m_wellStatus = WellControls::Status::CLOSED;
}
}
}
+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 );
}
@@ -523,4 +460,618 @@ 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 = m_estimateSolution && (coupledIterationNumber < m_wellNewtonSolver.getNumActiveCoupledIterations() );
+
+
+
+ if( getWellState())
+ {
+ if( useEstimator )
+ {
+ // Estimate well solution prior to coupled solve
+ evaluateConstraints( time_n,
+ dt,
+ cycleNumber,
+ coupledIterationNumber,
+ domain,
+ meshLevel,
+ elemManager,
+ subRegion,
+ dofManager );
+ }
+ else
+ {
+ // Evaluate well constraints based on current solution
+ 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();
+ // Solve minimum bhp constraint first
+ 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;
+ // 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() ));
+ }
+ GEOS_LOG_RANK_IF ( getLogLevel() > 4 && subRegion.isLocallyOwned(),
+ " Well " << subRegion.getName() << " Limiting Constraint " << limitingConstraint->getName() << " " << limitingConstraint->bottomHolePressure() << " " <<
+ limitingConstraint->phaseVolumeRates() << " " <<
+ limitingConstraint->totalVolumeRate() << " " << limitingConstraint->massRate());
+ }
+ }
+ constraintList.erase( std::find( constraintList.begin(), constraintList.end(), limitingConstraint ) );
+
+ // Check current against other constraints
+ for( auto & constraint : constraintList )
+ {
+
+ if( limitingConstraint->getName() != constraint->getName())
+ {
+ if( constraint->checkViolation( *limitingConstraint, time_n ) )
+ {
+ limitingConstraint = constraint;
+ setControl( static_cast< WellControls::Control >(constraint->getControl()) ); // tjb old
+ setCurrentConstraint( constraint );
+ 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() ));
+ }
+ GEOS_LOG_RANK_IF ( subRegion.isLocallyOwned(),
+ " Well " << subRegion.getName() << " Control switch " << constraint->getName() << " " << constraint->getConstraintValue( time_n ) );
+ }
+ }
+ }
+ 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 )
+ {
+ { // remove from list and add BHP constraint
+ auto it = std::find( constraintList.begin(), constraintList.end(), limitingConstraint );
+ if( it != constraintList.end() )
+ {
+ constraintList.erase( it );
+ }
+ if( getMinBHPConstraint()->isConstraintActive() )
+ {
+ constraintList.push_back( getMinBHPConstraint() );
+ }
+ constraintList.insert( constraintList.begin(), limitingConstraint );
+ }
+ }
+ // 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 );
+
+ for( auto const & constraint : constraintList )
+ {
+ GEOS_LOG_RANK_IF ( getLogLevel() > 4 && subRegion.isLocallyOwned(),
+ " Well " << subRegion.getName() << " Constraint " << constraint->getName() << " active " << constraint->isConstraintActive() <<
+ " value " << constraint->getConstraintValue( time_n ) );
+ if( 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() ));
+ }
+
+ }
+ }
+ solveConstraint ( limitingConstraint, time_n,
+ dt,
+ cycleNumber,
+ coupledIterationNumber,
+ domain,
+ mesh,
+ elemManager,
+ subRegion,
+ dofManager );
+ if( isCompositional())
+ {
+ limitingConstraint->setPhaseVolumeRates ( getReference< array1d< real64 > >(
+ viewKeyStruct::currentPhaseVolRateString() ) );
+ limitingConstraint->setTotalVolumeRate ( getReference< real64 >(
+ viewKeyStruct::currentTotalVolRateString() ));
+ limitingConstraint->setMassRate( getReference< real64 >( viewKeyStruct::currentMassRateString() ));
+ }
+ else
+ {
+ limitingConstraint->setBHP ( getReference< real64 >( viewKeyStruct::currentBHPString() ));
+ limitingConstraint->setTotalVolumeRate( getReference< real64 >( viewKeyStruct::currentVolRateString() ));
+ }
+ 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 );
+
+ // Store computed well quantities for this constraint
+ 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() ));
+ }
+ 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..eafd4c67a85 100644
--- a/src/coreComponents/physicsSolvers/fluidFlow/wells/WellControls.hpp
+++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellControls.hpp
@@ -20,12 +20,23 @@
#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 "constitutive/fluid/multifluid/MultiFluidBase.hpp"
+#include "constitutive/fluid/singlefluid/SingleFluidBase.hpp"
+#include "physicsSolvers/fluidFlow/wells/WellNewtonSolver.hpp"
+
namespace geos
{
namespace dataRepository
@@ -41,12 +52,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 +134,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
*/
- void switchToMassRateControl( real64 const & val );
+ string_array const & getTargetRegionNames() const {return m_targetRegionNames;}
+ /**
+ * @brief Get the control type for the well.
+ * @return the Control enum enforced at the well
+ */
+ 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 +547,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 +563,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 +606,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 +632,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 +693,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 +716,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 +758,54 @@ class WellControls : public dataRepository::Group
* @return a Status
*/
WellControls::Status getWellStatus () const { return m_wellStatus; }
+
+
+
///@}
+
+ 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 +814,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 +824,151 @@ 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 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 * massDensityString() { return "massDensity";}
+
+ static constexpr char const * currentBHPString() { return "currentBHP"; }
+
+ 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 );
- 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; };
+
+ /**
+ * @brief Getters for constraint lists
+ */
+ 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 );
+
+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 +981,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 +999,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,41 +1008,58 @@ 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;
+ // Current constrint
+ WellConstraintBase * m_currentConstraint;
+
+ // Minimum and maximum BHP and WHP constraints
+ MinimumBHPConstraint * m_minBHPConstraint;
+ MaximumBHPConstraint * m_maxBHPConstraint;
- /// Total rate table
- TableFunction const * m_targetTotalRateTable;
+ // Lists of rate constraints
+ std::vector< WellConstraintBase * > m_productionRateConstraintList;
+ std::vector< WellConstraintBase * > m_injectionRateConstraintList;
- /// Phase rate table
- TableFunction const * m_targetPhaseRateTable;
+ /// Well status
+ WellControls::Status m_wellStatus;
- /// Mass rate table
- TableFunction const * m_targetMassRateTable;
+ /// Well open flag
+ bool m_wellOpen;
- /// BHP table
- TableFunction const * m_targetBHPTable;
+ /// Well status table name
+ string m_statusTableName;
/// Status table
TableFunction const * m_statusTable;
- /// Well status
- WellControls::Status m_wellStatus;
-
-
/// Region average pressure used in volume rate constraint calculations
real64 m_regionAveragePressure;
/// 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;
};
-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 +1067,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..45ef0b58a98
--- /dev/null
+++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellLiquidRateConstraint.hpp
@@ -0,0 +1,170 @@
+/*
+ * ------------------------------------------------------------------------------------------------------------
+ * 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;
+
+protected:
+
+ virtual void postInputInitialization() override;
+
+ template< typename T >
+ void validateLiquidType( T const & fluidModel )
+ {
+ m_phaseIndices.resize( m_phaseNames.size());
+ for( size_t ip =0; ip m_phaseIndices;
+
+};
+
+
+
+} //namespace geos
+
+#endif //GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_WELLLiquidRateConstraint_HPP
diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/WellManager.cpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellManager.cpp
new file mode 100644
index 00000000000..88df4c4d110
--- /dev/null
+++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellManager.cpp
@@ -0,0 +1,904 @@
+/*
+ * ------------------------------------------------------------------------------------------------------------
+ * 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.cpp
+ */
+
+#include "WellManager.hpp"
+
+#include "mesh/DomainPartition.hpp"
+#include "mesh/PerforationFields.hpp"
+#include "mesh/WellElementRegion.hpp"
+#include "mesh/WellElementSubRegion.hpp"
+#include "physicsSolvers/LogLevelsInfo.hpp"
+#include "physicsSolvers/fluidFlow/wells/LogLevelsInfo.hpp"
+#include "physicsSolvers/fluidFlow/wells/CompositionalMultiphaseWell.hpp"
+#include "physicsSolvers/fluidFlow/wells/CompositionalMultiphaseWellFields.hpp"
+#include "physicsSolvers/fluidFlow/wells/SinglePhaseWell.hpp"
+#include "physicsSolvers/fluidFlow/wells/WellControls.hpp"
+
+#include "physicsSolvers/fluidFlow/wells/WellFields.hpp"
+#include "fileIO/Outputs/OutputBase.hpp"
+#include "functions/FunctionManager.hpp"
+namespace geos
+{
+
+using namespace dataRepository;
+using namespace fields;
+
+WellManager::WellManager( string const & name,
+ Group * const parent )
+ : PhysicsSolverBase( name, parent ),
+ m_useMass( false ),
+ m_useTotalMassEquation( 1 ),
+ m_isThermal( 0 ),
+ m_isCompositional( true ),
+ m_minScalingFactor( 0.01 ),
+ m_allowCompDensChopping( 1 )
+{
+ this->getWrapper< 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.registerWellDataOnMesh( subRegion );
+ well.setThermal( isThermal() );
+ 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;
+ //tjb
+ 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( true ) // tjb 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..bf615a0ee37
--- /dev/null
+++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellNewtonSolver.hpp
@@ -0,0 +1,865 @@
+/*
+ * ------------------------------------------------------------------------------------------------------------
+ * 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();
+
+ 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 );
+ }
+
+ }
+ 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/WellSolverBase.cpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellSolverBase.cpp
index a76a66c2c72..e0850cf43c9 100644
--- a/src/coreComponents/physicsSolvers/fluidFlow/wells/WellSolverBase.cpp
+++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellSolverBase.cpp
@@ -36,7 +36,7 @@ using namespace fields;
WellSolverBase::WellSolverBase( string const & name,
Group * const parent )
- : PhysicsSolverBase( name, parent ),
+ : WellControls( name, parent ),
m_numPhases( 0 ),
m_numComponents( 0 ),
m_numDofPerWellElement( 0 ),
@@ -44,6 +44,7 @@ WellSolverBase::WellSolverBase( string const & name,
m_isThermal( 0 ),
m_ratesOutputDir( joinPath( OutputBase::getOutputDirectory(), name + "_rates" ) ),
m_keepVariablesConstantDuringInitStep( false )
+
{
registerWrapper( viewKeyStruct::isThermalString(), &m_isThermal ).
setApplyDefaultValue( 0 ).
@@ -63,40 +64,41 @@ WellSolverBase::WellSolverBase( string const & name,
setInputFlag( dataRepository::InputFlags::OPTIONAL ).
setDescription( "Choose time step to honor rates/bhp tables time intervals" );
+
addLogLevel< logInfo::WellControl >();
}
Group * WellSolverBase::createChild( string const & childKey, string const & childName )
{
+ Group * baseChild = WellControls::createChild( childKey, childName );
+ if( baseChild != nullptr )
+ {
+ return baseChild;
+ }
static std::set< string > const childTypes = {
- keys::wellControls,
+ //keys::wellControls,
PhysicsSolverBase::groupKeyStruct::linearSolverParametersString(),
PhysicsSolverBase::groupKeyStruct::nonlinearSolverParametersString(),
};
GEOS_ERROR_IF( childTypes.count( childKey ) == 0,
CatalogInterface::unknownTypeError( childKey, getDataContext(), childTypes ),
getDataContext() );
- if( childKey == keys::wellControls )
- {
- return ®isterGroup< WellControls >( childName );
- }
- else
- {
- PhysicsSolverBase::createChild( childKey, childName );
- return nullptr;
- }
+
+ PhysicsSolverBase::createChild( childKey, childName );
+ return nullptr;
+
}
void WellSolverBase::expandObjectCatalogs()
{
- createChild( keys::wellControls, keys::wellControls );
+ //createChild( keys::wellControls, keys::wellControls );
}
WellSolverBase::~WellSolverBase() = default;
void WellSolverBase::postInputInitialization()
{
- PhysicsSolverBase::postInputInitialization();
+ WellControls::postInputInitialization();
// 1. Set key dimensions of the problem
m_numDofPerWellElement = m_isThermal ? m_numComponents + 2 : m_numComponents + 1; // 1 pressure connectionRate + temp if thermal
@@ -207,95 +209,67 @@ void WellSolverBase::setupDofs( DomainPartition const & domain,
DofManager::Connector::Node );
}
-void WellSolverBase::setPerforationStatus( real64 const & time_n, DomainPartition & domain )
+
+
+void WellSolverBase::selectWellConstraint( real64 const & time_n,
+ real64 const & dt,
+ const integer coupledIterationNumber,
+ DomainPartition & domain )
{
- FunctionManager & functionManager = FunctionManager::getInstance();
+ GEOS_MARK_FUNCTION;
+ GEOS_UNUSED_VAR( dt );
+ GEOS_UNUSED_VAR( coupledIterationNumber );
- // Set well element/perf status
- forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &,
- MeshLevel & mesh,
- string_array const & regionNames )
+ forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const & meshBodyName,
+ MeshLevel & meshLevel,
+ string_array const & regionNames )
{
-
- ElementRegionManager & elemManager = mesh.getElemManager();
- elemManager.forElementSubRegions< WellElementSubRegion >( regionNames,
- [&]( localIndex const,
- WellElementSubRegion & subRegion )
+ 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 );
-
- // 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 )
+ if( !wellControls.getWellState() )
{
- 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 );
+ wellControls.setWellState( 1 );
- // 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;
- }
+ initializeWell( domain, meshLevel, subRegion, time_n );
}
}
- // 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 ? wellControls.setWellStatus( time_n, WellControls::Status::OPEN ) : wellControls.setWellStatus( time_n, WellControls::Status::CLOSED );
-
- // Set local well element status array
- for( integer i=0; i const & localMatrix,
arrayView1d< real64 > const & localRhs )
{
- string const wellDofKey = dofManager.getKey( wellElementDofName());
+
+
+ // 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();
+ selectWellConstraint( time, dt, nonlinearParams.m_numNewtonIterations, domain );
+
+
// assemble the accumulation term in the mass balance equations
assembleAccumulationTerms( time, dt, domain, dofManager, localMatrix, localRhs );
// then assemble the pressure relations between well elements
- assemblePressureRelations( time, dt, domain, dofManager, localMatrix, localRhs );
+ //assemblePressureRelations( time, dt, domain, dofManager, localMatrix, localRhs );
+
+ forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &,
+ MeshLevel & mesh,
+ string_array const & regionNames )
+ {
+ ElementRegionManager & elementRegionManager = mesh.getElemManager();
+ elementRegionManager.forElementRegions< WellElementRegion >( regionNames,
+ [&]( localIndex const,
+ WellElementRegion & region )
+ {
+ WellElementSubRegion & subRegion = region.getGroup( ElementRegionBase::viewKeyStruct::elementSubRegions() )
+ .getGroup< WellElementSubRegion >( region.getSubRegionName() );
+ assembleWellConstraintTerms( time, dt, subRegion, dofManager, localMatrix.toViewConstSizes(), localRhs );
+ } );
+ } );
+
// then compute the perforation rates (later assembled by the coupled solver)
computePerforationRates( time, dt, domain );
@@ -388,8 +388,15 @@ void WellSolverBase::precomputeData( DomainPartition & domain )
wellElemGravCoef[iwelem] = LvArray::tensorOps::AiBi< 3 >( wellElemLocation[iwelem], gravVector );
} );
+ wellControls.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
- wellControls.setReferenceGravityCoef( refElev * gravVector[2] );
+ wellControls.setReferenceGravityCoef( refElev * gravVector[2] ); // tjb remove
} );
} );
}
diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/WellSolverBase.hpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellSolverBase.hpp
index 04fe58112b4..0f999eacb3e 100644
--- a/src/coreComponents/physicsSolvers/fluidFlow/wells/WellSolverBase.hpp
+++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellSolverBase.hpp
@@ -20,13 +20,13 @@
#ifndef GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_WELLSOLVERBASE_HPP_
#define GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_WELLSOLVERBASE_HPP_
-#include "physicsSolvers/PhysicsSolverBase.hpp"
-
+#include "physicsSolvers/fluidFlow/wells/WellControls.hpp"
+#include "dataRepository/Group.hpp"
namespace geos
{
class DomainPartition;
-class WellControls;
+
class WellElementSubRegion;
/**
@@ -35,7 +35,7 @@ class WellElementSubRegion;
* Base class for well solvers.
* Provides some common features
*/
-class WellSolverBase : public PhysicsSolverBase
+class WellSolverBase : public WellControls
{
public:
@@ -60,7 +60,7 @@ class WellSolverBase : public PhysicsSolverBase
WellSolverBase( WellSolverBase const & ) = delete;
/// default move constructor
- WellSolverBase( WellSolverBase && ) = default;
+ WellSolverBase( WellSolverBase && ) = delete;
/// deleted assignment operator
WellSolverBase & operator=( WellSolverBase const & ) = delete;
@@ -128,6 +128,12 @@ class WellSolverBase : public PhysicsSolverBase
*/
virtual localIndex numFluidPhases() const = 0;
+ /**
+ * @brief getter for the well associated to this subRegion
+ * @param subRegion the well subRegion whose controls are requested
+ * @return a reference to the well
+ */
+ WellSolverBase & getWell( WellElementSubRegion const & subRegion );
/**
* @brief getter for the well controls associated to this well subRegion
* @param subRegion the well subRegion whose controls are requested
@@ -149,27 +155,63 @@ class WellSolverBase : public PhysicsSolverBase
* @param domain the domain
*/
void setPerforationStatus( real64 const & time_n, DomainPartition & domain );
-
+ void setPerforationStatus( real64 const & time_n, WellElementSubRegion & subRegion );
/**
* @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 void registerDataOnMesh( Group & meshBodies ) override;
+ virtual real64
+ calculateWellResidualNorm( real64 const & GEOS_UNUSED_PARAM( time_n ),
+ real64 const & GEOS_UNUSED_PARAM( dt ),
+ WellElementSubRegion const & GEOS_UNUSED_PARAM( subRegion ),
+ DofManager const & GEOS_UNUSED_PARAM( dofManager ),
+ arrayView1d< real64 const > const & GEOS_UNUSED_PARAM( localRhs ) ) = 0;
+
+ virtual real64
+ scalingForWellSystemSolution( ElementSubRegionBase & GEOS_UNUSED_PARAM( subRegion ),
+ DofManager const & GEOS_UNUSED_PARAM( dofManager ),
+ arrayView1d< real64 const > const & GEOS_UNUSED_PARAM( localSolution ) ) = 0;
+
+ virtual bool
+ checkWellSystemSolution( ElementSubRegionBase & GEOS_UNUSED_PARAM( subRegion ),
+ DofManager const & GEOS_UNUSED_PARAM( dofManager ),
+ arrayView1d< real64 const > const & GEOS_UNUSED_PARAM( localSolution ),
+ real64 const GEOS_UNUSED_PARAM( scalingFactor ) ) = 0;
+ virtual void
+ applyWellSystemSolution( DofManager const & GEOS_UNUSED_PARAM( dofManager ),
+ arrayView1d< real64 const > const & GEOS_UNUSED_PARAM( localSolution ),
+ real64 const GEOS_UNUSED_PARAM( scalingFactor ),
+ real64 const GEOS_UNUSED_PARAM( dt ),
+ DomainPartition & GEOS_UNUSED_PARAM( domain ),
+ MeshLevel & GEOS_UNUSED_PARAM( mesh ),
+ WellElementSubRegion & GEOS_UNUSED_PARAM( subRegion ) ) = 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
+ */
+ virtual real64 setNextDt( real64 const & currentTime,
+ real64 const & currentDt,
+ DomainPartition & domain ) override;
virtual void setupDofs( DomainPartition const & domain,
DofManager & dofManager ) const override;
- virtual void implicitStepSetup( real64 const & time_n,
- real64 const & dt,
- DomainPartition & domain ) override;
virtual void implicitStepComplete( real64 const & GEOS_UNUSED_PARAM( time_n ),
real64 const & GEOS_UNUSED_PARAM( dt ),
DomainPartition & GEOS_UNUSED_PARAM( domain ) ) override {}
+
virtual void applyBoundaryConditions( real64 const GEOS_UNUSED_PARAM( time_n ),
real64 const GEOS_UNUSED_PARAM( dt ),
DomainPartition & GEOS_UNUSED_PARAM( domain ),
@@ -177,17 +219,23 @@ class WellSolverBase : public PhysicsSolverBase
CRSMatrixView< real64, globalIndex const > const & GEOS_UNUSED_PARAM( localMatrix ),
arrayView1d< real64 > const & GEOS_UNUSED_PARAM( localRhs ) ) override {}
+ /**
+ * @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 );
/**@}*/
/**
- * @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
+ * @copydoc PhysicsSolverBase::assembleSystem()
*/
virtual void assembleSystem( real64 const time,
real64 const dt,
@@ -197,14 +245,15 @@ class WellSolverBase : public PhysicsSolverBase
arrayView1d< real64 > const & localRhs ) override;
/**
- * @brief assembles the flux terms for all connections between well elements
+ * @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 domain the physical domain object
+ * @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 assembleFluxTerms( real64 const & time_n,
real64 const & dt,
DomainPartition & domain,
@@ -226,35 +275,37 @@ class WellSolverBase : public PhysicsSolverBase
CRSMatrixView< real64, globalIndex const > const & localMatrix,
arrayView1d< real64 > const & localRhs ) = 0;
+
+
/**
- * @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 ) = 0;
-
+ virtual real64 updateWellState( ElementRegionManager const & elemManager, WellElementSubRegion & subRegion ) = 0;
/**
- * @brief Recompute all dependent quantities from primary variables (including constitutive models)
+ * @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 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 Initialize all the primary and secondary variables in all the wells
+ * @param domain the domain containing the well manager to access individual wells
+ */
+ virtual void initializeWells( DomainPartition & domain, real64 const & time_n ) = 0;
+
+ /**
+ * @brief Recompute all dependent quantities from primary variables (including constitutive
+ * models)
+ * @param elemManager the element region manager
+ * fields
*/
virtual real64 updateSubRegionState( ElementRegionManager const & elemManager, WellElementSubRegion & subRegion ) = 0;
+
/**
* @brief Recompute the perforation rates for all the wells
* @param domain the domain containing the mesh and fields
@@ -263,21 +314,14 @@ class WellSolverBase : public PhysicsSolverBase
real64 const & dt,
DomainPartition & domain ) = 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
- */
- virtual real64 setNextDt( real64 const & currentTime,
- real64 const & currentDt,
- DomainPartition & domain ) override;
/**
- * @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
+ * @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; }
@@ -287,6 +331,8 @@ class WellSolverBase : public PhysicsSolverBase
static constexpr char const * isThermalString() { return "isThermal"; }
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"; }
static constexpr char const * fluidNamesString() { return "fluidNames"; }
};
@@ -307,11 +353,6 @@ class WellSolverBase : public PhysicsSolverBase
virtual void initializePostSubGroups() 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
- */
- virtual void initializeWells( DomainPartition & domain, real64 const & time_n ) = 0;
/**
* @brief Make sure that the well constraints are compatible
@@ -327,6 +368,8 @@ class WellSolverBase : public PhysicsSolverBase
real64 const & dt,
DomainPartition & domain ) = 0;
+
+
/// name of the flow solver
string m_flowSolverName;
@@ -357,6 +400,10 @@ class WellSolverBase : public PhysicsSolverBase
/// name of the fluid constitutive model used as a reference for component/phase description
string m_referenceFluidModelName;
+
+ /// flag to use the estimator
+ integer m_estimateSolution;
+
};
}
diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/WellSolverBaseFields.hpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellSolverBaseFields.hpp
index b9dfebc799b..1c4b639f4f7 100644
--- a/src/coreComponents/physicsSolvers/fluidFlow/wells/WellSolverBaseFields.hpp
+++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellSolverBaseFields.hpp
@@ -31,65 +31,7 @@ namespace fields
{
namespace well
-{
-
-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/WellVolumeRateConstraint.cpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellVolumeRateConstraint.cpp
new file mode 100644
index 00000000000..e675af7459e
--- /dev/null
+++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellVolumeRateConstraint.cpp
@@ -0,0 +1,73 @@
+/*
+ * ------------------------------------------------------------------------------------------------------------
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ * Copyright (c) 2016-2024 Lawrence Livermore National Security LLC
+ * Copyright (c) 2018-2024 TotalEnergies
+ * Copyright (c) 2018-2024 The Board of Trustees of the Leland Stanford Junior University
+ * Copyright (c) 2023-2024 Chevron
+ * Copyright (c) 2019- GEOS/GEOSX Contributors
+ * All rights reserved
+ *
+ * See top level LICENSE, COPYRIGHT, CONTRIBUTORS, NOTICE, and ACKNOWLEDGEMENTS files for details.
+ * ------------------------------------------------------------------------------------------------------------
+ */
+
+/*
+ * @file WellVolumeRateConstraint.cpp
+ */
+
+#include "LogLevelsInfo.hpp"
+#include "WellVolumeRateConstraint.hpp"
+#include "WellConstants.hpp"
+#include "dataRepository/InputFlags.hpp"
+#include "functions/FunctionManager.hpp"
+
+namespace geos
+{
+
+using namespace dataRepository;
+
+VolumeRateConstraint::VolumeRateConstraint( string const & name, Group * const parent )
+ : WellConstraintBase( name, parent )
+{
+ this->setInputFlags( InputFlags::OPTIONAL_NONUNIQUE );
+
+ this->registerWrapper( viewKeyStruct::volumeRateString(), &this->m_constraintValue ).
+ setDefaultValue( 0.0 ).
+ setInputFlag( InputFlags::OPTIONAL ).
+ setRestartFlags( RestartFlags::WRITE_AND_READ ).
+ setDescription( "Volumetric rate (if useSurfaceConditions: [surface m^3/s]; else [reservoir m^3/s])" );
+
+}
+
+VolumeRateConstraint::~VolumeRateConstraint()
+{}
+
+
+void VolumeRateConstraint::postInputInitialization()
+{
+ // Validate table options
+ WellConstraintBase::postInputInitialization();
+
+ // check constraint value
+ GEOS_THROW_IF( m_constraintValue < 0,
+ getWrapperDataContext( viewKeyStruct::volumeRateString() ) << ": Target value is negative",
+ InputError );
+
+ GEOS_THROW_IF ((m_constraintValue <= 0.0 && m_constraintScheduleTableName.empty()),
+ getName() << " " << getDataContext() << ": You need to specify a volume rate constraint. \n" <<
+ "The rate constraint can be specified using " <<
+ "either " << viewKeyStruct::volumeRateString() <<
+ " or " << WellConstraintBase::viewKeyStruct::constraintScheduleTableNameString(),
+ InputError );
+}
+
+bool VolumeRateConstraint::checkViolation( WellConstraintBase const & currentConstraint, real64 const & currentTime ) const
+{
+ real64 const currentValue = currentConstraint.totalVolumeRate();
+ 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/WellVolumeRateConstraint.hpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellVolumeRateConstraint.hpp
new file mode 100644
index 00000000000..1ed14597ca3
--- /dev/null
+++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellVolumeRateConstraint.hpp
@@ -0,0 +1,124 @@
+/*
+ * ------------------------------------------------------------------------------------------------------------
+ * 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 WellVolumeRateConstraints.hpp
+ */
+
+
+#ifndef GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_WELLTOTALVOLRATECONSTRAINTS_HPP
+#define GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_WELLTOTALVOLRATECONSTRAINTS_HPP
+
+#include "common/format/EnumStrings.hpp"
+#include "dataRepository/Group.hpp"
+#include "functions/TableFunction.hpp"
+#include "WellConstraintsBase.hpp"
+namespace geos
+{
+
+
+
+/**
+ * @class VolumeRateConstraint
+ * @brief This class describes a volume rate constraint used to control a well.
+ */
+
+class VolumeRateConstraint : 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 VolumeRateConstraint( string const & name, dataRepository::Group * const parent );
+
+
+ /**
+ * @brief Default destructor.
+ */
+ ~VolumeRateConstraint() override;
+
+ /**
+ * @brief Deleted default constructor.
+ */
+ VolumeRateConstraint() = delete;
+
+ /**
+ * @brief Deleted copy constructor.
+ */
+ VolumeRateConstraint( VolumeRateConstraint const & ) = delete;
+
+ /**
+ * @brief Deleted move constructor.
+ */
+ VolumeRateConstraint( VolumeRateConstraint && ) = delete;
+
+ /**
+ * @brief Deleted assignment operator.
+ * @return a reference to a constraint object
+ */
+ VolumeRateConstraint & operator=( VolumeRateConstraint const & ) = delete;
+
+ /**
+ * @brief Deleted move operator.
+ * @return a reference to a constraint object
+ */
+ VolumeRateConstraint & operator=( VolumeRateConstraint && ) = 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 "VolumeRateConstraint";
+ }
+ ///@}
+ /**
+ * @brief Struct to serve as a container for variable strings and keys.
+ * @struct viewKeyStruct
+ */
+ struct viewKeyStruct
+ {
+ /// String key for the volume rate
+ static constexpr char const * volumeRateString() { return "volumeRate"; }
+ };
+ /**
+ * @name Getters / Setters
+ */
+ ///@{
+
+ // Temp interface - tjb
+ virtual ConstraintTypeId getControl() const override { return ConstraintTypeId::TOTALVOLRATE; };
+ ///@}
+
+ virtual bool checkViolation( WellConstraintBase const & currentConstraint, real64 const & currentTime ) const override;
+protected:
+
+ virtual void postInputInitialization() override;
+
+};
+
+
+} //namespace geos
+
+#endif //GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_WELLCONSTRAINT_HPP
diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/CompositionalMultiphaseWellConstraintKernels.hpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/CompositionalMultiphaseWellConstraintKernels.hpp
new file mode 100644
index 00000000000..229813ee52a
--- /dev/null
+++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/CompositionalMultiphaseWellConstraintKernels.hpp
@@ -0,0 +1,555 @@
+/*
+ * ------------------------------------------------------------------------------------------------------------
+ * 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 CompositionalMultiphaseWellConstraintKernels.hpp
+ */
+
+#ifndef GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_WELLCONSTRAINTKERNELS_HPP
+#define GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_WELLCONSTRAINTKERNELS_HPP
+
+#include "codingUtilities/Utilities.hpp"
+#include "constitutive/fluid/multifluid/MultiFluidBase.hpp"
+#include "constitutive/fluid/multifluid/MultiFluidFields.hpp"
+
+
+#include "physicsSolvers/fluidFlow/wells/WellControls.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"
+namespace geos
+{
+
+namespace wellConstraintKernels
+{
+
+/******************************** ControlEquationHelper ********************************/
+//template< integer NC, integer IS_THERMAL, typname S, typename T >
+//struct ConstraintHelper< NC, IS_THERMAL > {};
+
+template< integer NC, integer IS_THERMAL, typename CONSTRAINT = BHPConstraint >
+struct ConstraintHelper
+{
+ static void assembleConstraintEquation( real64 const & time_n,
+ WellControls & wellControls,
+ BHPConstraint & constraint,
+ WellElementSubRegion const & subRegion,
+ string const & wellDofKey,
+ localIndex const & rankOffset,
+ CRSMatrixView< real64, globalIndex const > const & localMatrix,
+ arrayView1d< real64 > const & localRhs )
+ {
+ // subRegion data
+ localIndex const iwelemRef = subRegion.getTopWellElementIndex();
+ arrayView1d< globalIndex const > const & wellElemDofNumber = subRegion.getReference< array1d< globalIndex > >( wellDofKey );
+ arrayView1d< real64 const > const & pres = subRegion.getField< fields::well::pressure >();
+ arrayView1d< real64 const > const & totalMassDens = subRegion.getField< fields::well::totalMassDensity >();
+ arrayView2d< real64 const > const & dTotalMassDens = subRegion.getField< fields::well::dTotalMassDensity >();
+ arrayView1d< real64 const > const wellElemGravCoef = subRegion.getField< fields::well::gravityCoefficient >();
+
+ // setup row/column indices for constraint equation
+ using COFFSET_WJ = compositionalMultiphaseWellKernels::ColOffset_WellJac< NC, IS_THERMAL >;
+ using WJ_ROFFSET = compositionalMultiphaseWellKernels::RowOffset_WellJac< NC, IS_THERMAL >;
+ using Deriv = constitutive::multifluid::DerivativeOffset;
+
+ localIndex const eqnRowIndex = wellElemDofNumber[iwelemRef] + WJ_ROFFSET::CONTROL - rankOffset;
+ globalIndex dofColIndices[COFFSET_WJ::nDer]{};
+ for( integer ic = 0; ic < COFFSET_WJ::nDer; ++ic )
+ {
+ dofColIndices[ ic ] = wellElemDofNumber[iwelemRef] + ic;
+ }
+
+ // constraint data
+ real64 const & targetBHP = constraint.getConstraintValue( time_n );
+ real64 const & refGravCoef = constraint.getReferenceGravityCoef();
+
+ // current constraint value
+ real64 const & currentBHP =
+ wellControls.getReference< real64 >( CompositionalMultiphaseWell::viewKeyStruct::currentBHPString() );
+
+ // residual
+ real64 controlEqn = currentBHP - targetBHP;
+
+ // setup Jacobian terms
+ real64 dControlEqn[NC+2+IS_THERMAL]{};
+
+ // bring everything back to host, capture the scalars by reference
+ forAll< serialPolicy >( 1, [pres,
+ totalMassDens,
+ dTotalMassDens,
+ wellElemGravCoef,
+ &dControlEqn,
+ &iwelemRef,
+ &refGravCoef] ( localIndex const )
+ {
+ real64 const diffGravCoef = refGravCoef - wellElemGravCoef[iwelemRef];
+ dControlEqn[COFFSET_WJ::dP] = 1 + dTotalMassDens[iwelemRef][Deriv::dP] * diffGravCoef;
+ for( integer ic = 0; ic < NC; ++ic )
+ {
+ dControlEqn[COFFSET_WJ::dC+ic] = dTotalMassDens[iwelemRef][Deriv::dC+ic] * diffGravCoef;
+ }
+ if constexpr ( IS_THERMAL )
+ {
+ dControlEqn[COFFSET_WJ::dT] = dTotalMassDens[iwelemRef][Deriv::dT] * diffGravCoef;
+ }
+ } );
+
+ // add solver matrices
+ localRhs[eqnRowIndex] += controlEqn;
+ localMatrix.addToRowBinarySearchUnsorted< serialAtomic >( eqnRowIndex,
+ dofColIndices,
+ dControlEqn,
+ COFFSET_WJ::nDer );
+ }
+
+
+ template< template< typename U > class T, typename U=PhaseVolumeRateConstraint >
+ static void assembleConstraintEquation( real64 const & time_n,
+ WellControls & wellControls,
+ T< PhaseVolumeRateConstraint > & constraint,
+ WellElementSubRegion const & subRegion,
+ string const & wellDofKey,
+ localIndex const & rankOffset,
+ CRSMatrixView< real64, globalIndex const > const & localMatrix,
+ arrayView1d< real64 > const & localRhs )
+ {
+ // subRegion data
+
+ localIndex const iwelemRef = subRegion.getTopWellElementIndex();
+ arrayView1d< globalIndex const > const & wellElemDofNumber = subRegion.getReference< array1d< globalIndex > >( wellDofKey );
+ arrayView1d< real64 const > const & connRate = subRegion.getField< fields::well::connectionRate >();
+ arrayView2d< real64 const, compflow::USD_COMP > const & compFrac = subRegion.getField< fields::well::globalCompFraction >();
+ arrayView3d< real64 const, compflow::USD_COMP_DC > const & dCompFrac_dCompDens = subRegion.getField< fields::well::dGlobalCompFraction_dGlobalCompDensity >();
+
+ // setup row/column indices for constraint equation
+ using COFFSET_WJ = compositionalMultiphaseWellKernels::ColOffset_WellJac< NC, IS_THERMAL >;
+ using WJ_ROFFSET = compositionalMultiphaseWellKernels::RowOffset_WellJac< NC, IS_THERMAL >;
+ using Deriv = constitutive::multifluid::DerivativeOffset;
+
+ localIndex const eqnRowIndex = wellElemDofNumber[iwelemRef] + WJ_ROFFSET::CONTROL - rankOffset;
+ globalIndex dofColIndices[COFFSET_WJ::nDer]{};
+ for( integer ic = 0; ic < COFFSET_WJ::nDer; ++ic )
+ {
+ dofColIndices[ ic ] = wellElemDofNumber[iwelemRef] + ic;
+ }
+
+ // fluid data
+ constitutive::MultiFluidBase & fluidSeparator = wellControls.getMultiFluidSeparator();
+
+ arrayView3d< real64 const, constitutive::multifluid::USD_PHASE > const & phaseFrac = fluidSeparator.phaseFraction();
+ arrayView4d< real64 const, constitutive::multifluid::USD_PHASE_DC > const & dPhaseFrac = fluidSeparator.dPhaseFraction();
+ arrayView3d< real64 const, constitutive::multifluid::USD_PHASE > const & phaseDens = fluidSeparator.phaseDensity();
+ arrayView4d< real64 const, constitutive::multifluid::USD_PHASE_DC > const & dPhaseDens = fluidSeparator.dPhaseDensity();
+
+ // constraint data
+ integer ip = getPhaseIndexFromFluidModel( fluidSeparator, constraint.getPhaseName());
+ real64 const & targetPhaseRate = constraint.getConstraintValue( time_n );
+
+ // current constraint value
+ arrayView1d< real64 > const & currentPhaseVolRate =
+ wellControls.getReference< array1d< real64 > >( CompositionalMultiphaseWell::viewKeyStruct::currentPhaseVolRateString() );
+ integer const useSurfaceConditions = wellControls.useSurfaceConditions();
+
+ // residual
+ real64 controlEqn = currentPhaseVolRate[ip] - targetPhaseRate;
+
+ // setup Jacobian terms
+ real64 dControlEqn[NC+2+IS_THERMAL]{};
+
+ // bring everything back to host, capture the scalars by reference
+ forAll< serialPolicy >( 1, [&ip,
+ connRate,
+ phaseDens,
+ dPhaseDens,
+ phaseFrac,
+ dPhaseFrac,
+ compFrac,
+ dCompFrac_dCompDens,
+ &dControlEqn,
+ &useSurfaceConditions,
+ &iwelemRef] ( localIndex const )
+ {
+ // skip the rest of this function if phase ip is absent
+ bool const phaseExists = (phaseFrac[iwelemRef][0][ip] > 0);
+ if( phaseExists )
+ {
+ stackArray1d< real64, NC > work( NC );
+ real64 const currentTotalRate = connRate[iwelemRef];
+
+ 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;
+
+ // divide the total mass/molar rate by the (phase density * phase fraction) to get the phase volumetric rate
+ dControlEqn[COFFSET_WJ::dP] = ( useSurfaceConditions == 0 ) * currentTotalRate * dPhaseFracTimesPhaseDensInv_dPres;
+ dControlEqn[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;
+ dControlEqn[COFFSET_WJ::dT] = ( useSurfaceConditions == 0 ) * currentTotalRate * dPhaseFracTimesPhaseDensInv_dTemp;
+ }
+
+ for( integer ic = 0; ic < NC; ++ic )
+ {
+ dControlEqn[COFFSET_WJ::dC+ic] = -phaseFracTimesPhaseDensInv * dPhaseDens[iwelemRef][0][ip][Deriv::dC+ic] * phaseDensInv;
+ dControlEqn[COFFSET_WJ::dC+ic] += dPhaseFrac[iwelemRef][0][ip][Deriv::dC+ic] * phaseDensInv;
+ dControlEqn[COFFSET_WJ::dC+ic] *= currentTotalRate;
+ }
+ applyChainRuleInPlace( NC, dCompFrac_dCompDens[iwelemRef], &dControlEqn[COFFSET_WJ::dC], work.data() );
+
+ }
+ } );
+
+ // add solver matrices
+ localRhs[eqnRowIndex] += controlEqn;
+ localMatrix.addToRowBinarySearchUnsorted< serialAtomic >( eqnRowIndex,
+ dofColIndices,
+ dControlEqn,
+ COFFSET_WJ::nDer );
+ }
+
+ template< template< typename U > class T, typename U=LiquidRateConstraint >
+ static void assembleConstraintEquation( real64 const & time_n,
+ WellControls & wellControls,
+ T< LiquidRateConstraint > & constraint,
+ WellElementSubRegion const & subRegion,
+ string const & wellDofKey,
+ localIndex const & rankOffset,
+ CRSMatrixView< real64, globalIndex const > const & localMatrix,
+ arrayView1d< real64 > const & localRhs )
+ {
+ // subRegion data
+
+ localIndex const iwelemRef = subRegion.getTopWellElementIndex();
+ arrayView1d< globalIndex const > const & wellElemDofNumber = subRegion.getReference< array1d< globalIndex > >( wellDofKey );
+ arrayView1d< real64 const > const & connRate = subRegion.getField< fields::well::connectionRate >();
+ arrayView2d< real64 const, compflow::USD_COMP > const & compFrac = subRegion.getField< fields::well::globalCompFraction >();
+ arrayView3d< real64 const, compflow::USD_COMP_DC > const & dCompFrac_dCompDens = subRegion.getField< fields::well::dGlobalCompFraction_dGlobalCompDensity >();
+
+ // setup row/column indices for constraint equation
+ using COFFSET_WJ = compositionalMultiphaseWellKernels::ColOffset_WellJac< NC, IS_THERMAL >;
+ using WJ_ROFFSET = compositionalMultiphaseWellKernels::RowOffset_WellJac< NC, IS_THERMAL >;
+ using Deriv = constitutive::multifluid::DerivativeOffset;
+
+ localIndex const eqnRowIndex = wellElemDofNumber[iwelemRef] + WJ_ROFFSET::CONTROL - rankOffset;
+ globalIndex dofColIndices[COFFSET_WJ::nDer]{};
+ for( integer ic = 0; ic < COFFSET_WJ::nDer; ++ic )
+ {
+ dofColIndices[ ic ] = wellElemDofNumber[iwelemRef] + ic;
+ }
+
+ // fluid data
+ constitutive::MultiFluidBase & fluidSeparator = wellControls.getMultiFluidSeparator();
+
+ arrayView3d< real64 const, constitutive::multifluid::USD_PHASE > const & phaseFrac = fluidSeparator.phaseFraction();
+ arrayView4d< real64 const, constitutive::multifluid::USD_PHASE_DC > const & dPhaseFrac = fluidSeparator.dPhaseFraction();
+ arrayView3d< real64 const, constitutive::multifluid::USD_PHASE > const & phaseDens = fluidSeparator.phaseDensity();
+ arrayView4d< real64 const, constitutive::multifluid::USD_PHASE_DC > const & dPhaseDens = fluidSeparator.dPhaseDensity();
+
+ // constraint data
+ real64 const & targetPhaseRate = constraint.getConstraintValue( time_n );
+ const array1d< int > & phaseIndices = constraint.getPhaseIndices();
+
+
+ // current constraint value
+ arrayView1d< real64 > const & currentPhaseVolRate =
+ wellControls.getReference< array1d< real64 > >( CompositionalMultiphaseWell::viewKeyStruct::currentPhaseVolRateString() );
+ integer const useSurfaceConditions = wellControls.useSurfaceConditions();
+
+ // residual
+ real64 controlEqn = -targetPhaseRate;
+ for( integer ip= 0; ip < phaseIndices.size(); ++ip )
+ {
+ controlEqn += currentPhaseVolRate[phaseIndices[ip]];
+ }
+
+ // setup Jacobian terms
+ real64 dControlEqn[NC+2+IS_THERMAL]{};
+
+ // bring everything back to host, capture the scalars by reference
+ forAll< serialPolicy >( 1, [phaseIndices,
+ connRate,
+ phaseDens,
+ dPhaseDens,
+ phaseFrac,
+ dPhaseFrac,
+ compFrac,
+ dCompFrac_dCompDens,
+ &dControlEqn,
+ &useSurfaceConditions,
+ &iwelemRef] ( localIndex const )
+ {
+
+ stackArray1d< real64, NC > work( NC );
+ for( integer i= 0; i < phaseIndices.size(); ++i )
+ {
+ integer ip = phaseIndices[i];
+ bool const phaseExists = (phaseFrac[iwelemRef][0][ip] > 0);
+ // skip the rest of this function if phase ip is absent
+ if( phaseExists )
+ {
+
+ real64 const currentTotalRate = connRate[iwelemRef];
+
+ 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;
+
+ // divide the total mass/molar rate by the (phase density * phase fraction) to get the phase volumetric rate
+ dControlEqn[COFFSET_WJ::dP] += ( useSurfaceConditions == 0 ) * currentTotalRate * dPhaseFracTimesPhaseDensInv_dPres;
+ dControlEqn[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;
+ dControlEqn[COFFSET_WJ::dT] += ( useSurfaceConditions == 0 ) * currentTotalRate * dPhaseFracTimesPhaseDensInv_dTemp;
+ }
+
+ for( integer ic = 0; ic < NC; ++ic )
+ {
+ real64 temp = -phaseFracTimesPhaseDensInv * dPhaseDens[iwelemRef][0][ip][Deriv::dC+ic] * phaseDensInv;
+ temp += dPhaseFrac[iwelemRef][0][ip][Deriv::dC+ic] * phaseDensInv;
+ temp *= currentTotalRate;
+ dControlEqn[COFFSET_WJ::dC+ic] += temp;
+ }
+ }
+ }
+ applyChainRuleInPlace( NC, dCompFrac_dCompDens[iwelemRef], &dControlEqn[COFFSET_WJ::dC], work.data() );
+ } );
+
+ // add solver matrices
+ localRhs[eqnRowIndex] += controlEqn;
+ localMatrix.addToRowBinarySearchUnsorted< serialAtomic >( eqnRowIndex,
+ dofColIndices,
+ dControlEqn,
+ COFFSET_WJ::nDer );
+ }
+ template< template< typename U > class T, typename U=VolumeRateConstraint >
+ static void assembleConstraintEquation( real64 const & time_n,
+ WellControls & wellControls,
+ T< VolumeRateConstraint > & constraint,
+ WellElementSubRegion const & subRegion,
+ string const & wellDofKey,
+ localIndex const & rankOffset,
+ CRSMatrixView< real64, globalIndex const > const & localMatrix,
+ arrayView1d< real64 > const & localRhs )
+ {
+ // subRegion data
+
+ localIndex const iwelemRef = subRegion.getTopWellElementIndex();
+ arrayView1d< globalIndex const > const & wellElemDofNumber = subRegion.getReference< array1d< globalIndex > >( wellDofKey );
+ arrayView1d< real64 const > const & connRate = subRegion.getField< fields::well::connectionRate >();
+ arrayView2d< real64 const, compflow::USD_COMP > const & compFrac = subRegion.getField< fields::well::globalCompFraction >();
+ arrayView3d< real64 const, compflow::USD_COMP_DC > const & dCompFrac_dCompDens = subRegion.getField< fields::well::dGlobalCompFraction_dGlobalCompDensity >();
+
+ // setup row/column indices for constraint equation
+ using COFFSET_WJ = compositionalMultiphaseWellKernels::ColOffset_WellJac< NC, IS_THERMAL >;
+ using WJ_ROFFSET = compositionalMultiphaseWellKernels::RowOffset_WellJac< NC, IS_THERMAL >;
+ using Deriv = constitutive::multifluid::DerivativeOffset;
+
+ localIndex const eqnRowIndex = wellElemDofNumber[iwelemRef] + WJ_ROFFSET::CONTROL - rankOffset;
+ globalIndex dofColIndices[COFFSET_WJ::nDer]{};
+ for( integer ic = 0; ic < COFFSET_WJ::nDer; ++ic )
+ {
+ dofColIndices[ ic ] = wellElemDofNumber[iwelemRef] + ic;
+ }
+
+ // fluid data
+ constitutive::MultiFluidBase & fluidSeparator = wellControls.getMultiFluidSeparator();
+
+ arrayView2d< real64 const, constitutive::multifluid::USD_FLUID > const & totalDens = fluidSeparator.totalDensity();
+ arrayView3d< real64 const, constitutive::multifluid::USD_FLUID_DC > const & dTotalDens = fluidSeparator.dTotalDensity();
+
+ // constraint data
+ real64 const & targetTotalVolRate = constraint.getConstraintValue( time_n );
+
+ // current constraint value
+ real64 const & currentTotalVolRate =
+ wellControls.getReference< real64 >( CompositionalMultiphaseWell::viewKeyStruct::currentTotalVolRateString() );
+ integer const useSurfaceConditions = wellControls.useSurfaceConditions();
+
+ // residual
+ real64 controlEqn = currentTotalVolRate - targetTotalVolRate;
+
+ // setup Jacobian terms
+ real64 dControlEqn[NC+2+IS_THERMAL]{};
+
+ // bring everything back to host, capture the scalars by reference
+ forAll< serialPolicy >( 1, [connRate,
+ totalDens,
+ dTotalDens,
+ compFrac,
+ dCompFrac_dCompDens,
+ &dControlEqn,
+ &useSurfaceConditions,
+ &iwelemRef] ( localIndex const )
+ {
+ stackArray1d< real64, NC > work( NC );
+
+ real64 const currentTotalRate = connRate[iwelemRef];
+
+ // compute the inverse of the total density and derivatives
+
+ real64 const totalDensInv = 1.0 / totalDens[iwelemRef][0];
+
+ stackArray1d< real64, NC > dTotalDensInv_dCompDens( NC );
+ for( integer ic = 0; ic < NC; ++ic )
+ {
+ dTotalDensInv_dCompDens[ic] = -dTotalDens[iwelemRef][0][Deriv::dC+ic] * totalDensInv * totalDensInv;
+ }
+ applyChainRuleInPlace( NC, 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
+
+ // Compute derivatives dP dT
+ real64 const dTotalDensInv_dPres = -dTotalDens[iwelemRef][0][Deriv::dP] * totalDensInv * totalDensInv;
+ dControlEqn[COFFSET_WJ::dP] = ( useSurfaceConditions == 0 ) * currentTotalRate * dTotalDensInv_dPres;
+ if constexpr ( IS_THERMAL )
+ {
+ dControlEqn[COFFSET_WJ::dT] = ( useSurfaceConditions == 0 ) * currentTotalRate * -dTotalDens[iwelemRef][0][Deriv::dT] * totalDensInv * totalDensInv;
+ }
+
+ dControlEqn[COFFSET_WJ::dQ] = totalDensInv;
+ for( integer ic = 0; ic < NC; ++ic )
+ {
+ dControlEqn[COFFSET_WJ::dC+ic] = currentTotalRate * dTotalDensInv_dCompDens[ic];
+ }
+
+ } );
+
+ // add solver matrices
+ localRhs[eqnRowIndex] += controlEqn;
+ localMatrix.addToRowBinarySearchUnsorted< serialAtomic >( eqnRowIndex,
+ dofColIndices,
+ dControlEqn,
+ COFFSET_WJ::nDer );
+ }
+ template< template< typename U > class T, typename U=MassRateConstraint >
+ static void assembleConstraintEquation( real64 const & time_n,
+ WellControls & wellControls,
+ T< MassRateConstraint > & constraint,
+ WellElementSubRegion const & subRegion,
+ string const & wellDofKey,
+ localIndex const & rankOffset,
+ CRSMatrixView< real64, globalIndex const > const & localMatrix,
+ arrayView1d< real64 > const & localRhs )
+ {
+ // subRegion data
+
+ localIndex const iwelemRef = subRegion.getTopWellElementIndex();
+ arrayView1d< globalIndex const > const & wellElemDofNumber = subRegion.getReference< array1d< globalIndex > >( wellDofKey );
+ arrayView1d< real64 const > const & connRate = subRegion.getField< fields::well::connectionRate >();
+ arrayView2d< real64 const, compflow::USD_COMP > const & compFrac = subRegion.getField< fields::well::globalCompFraction >();
+ arrayView3d< real64 const, compflow::USD_COMP_DC > const & dCompFrac_dCompDens = subRegion.getField< fields::well::dGlobalCompFraction_dGlobalCompDensity >();
+
+ // setup row/column indices for constraint equation
+ using COFFSET_WJ = compositionalMultiphaseWellKernels::ColOffset_WellJac< NC, IS_THERMAL >;
+ using WJ_ROFFSET = compositionalMultiphaseWellKernels::RowOffset_WellJac< NC, IS_THERMAL >;
+ using Deriv = constitutive::multifluid::DerivativeOffset;
+
+ localIndex const eqnRowIndex = wellElemDofNumber[iwelemRef] + WJ_ROFFSET::CONTROL - rankOffset;
+ globalIndex dofColIndices[COFFSET_WJ::nDer]{};
+ for( integer ic = 0; ic < COFFSET_WJ::nDer; ++ic )
+ {
+ dofColIndices[ ic ] = wellElemDofNumber[iwelemRef] + ic;
+ }
+
+ // fluid data
+ constitutive::MultiFluidBase & fluidSeparator = wellControls.getMultiFluidSeparator();
+ arrayView2d< real64 const, constitutive::multifluid::USD_FLUID > const & totalDens = fluidSeparator.totalDensity();
+ arrayView3d< real64 const, constitutive::multifluid::USD_FLUID_DC > const & dTotalDens = fluidSeparator.dTotalDensity();
+
+ // constraint data
+ real64 const & targetMassRate = constraint.getConstraintValue( time_n );
+
+ // current constraint value
+ real64 const & massDensity =
+ wellControls.getReference< real64 >( CompositionalMultiphaseWell::viewKeyStruct::massDensityString() );
+
+ // fix to use stored massrate
+ real64 const & currentTotalVolRate =
+ wellControls.getReference< real64 >( CompositionalMultiphaseWell::viewKeyStruct::currentTotalVolRateString() );
+ integer const useSurfaceConditions = wellControls.useSurfaceConditions();
+
+ // residual
+ real64 controlEqn = massDensity*currentTotalVolRate - targetMassRate;
+
+ // setup Jacobian terms
+ real64 dControlEqn[NC+2+IS_THERMAL]{};
+
+ // bring everything back to host, capture the scalars by reference
+ forAll< serialPolicy >( 1, [connRate,
+ massDensity,
+ totalDens,
+ dTotalDens,
+ compFrac,
+ dCompFrac_dCompDens,
+ &dControlEqn,
+ &useSurfaceConditions,
+ &iwelemRef] ( localIndex const )
+ {
+ stackArray1d< real64, NC > work( NC );
+
+ real64 const currentTotalRate = connRate[iwelemRef];
+
+ // compute the inverse of the total density and derivatives
+
+ real64 const totalDensInv = 1.0 / totalDens[iwelemRef][0];
+
+ stackArray1d< real64, NC > dTotalDensInv_dCompDens( NC );
+ for( integer ic = 0; ic < NC; ++ic )
+ {
+ dTotalDensInv_dCompDens[ic] = -dTotalDens[iwelemRef][0][Deriv::dC+ic] * totalDensInv * totalDensInv;
+ }
+ applyChainRuleInPlace( NC, 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
+
+ // Compute derivatives dP dT
+ real64 const dTotalDensInv_dPres = -dTotalDens[iwelemRef][0][Deriv::dP] * totalDensInv * totalDensInv;
+ dControlEqn[COFFSET_WJ::dP] = ( useSurfaceConditions == 0 )*massDensity * currentTotalRate * dTotalDensInv_dPres;
+ if constexpr ( IS_THERMAL )
+ {
+ dControlEqn[COFFSET_WJ::dT] = ( useSurfaceConditions == 0 ) * massDensity* currentTotalRate * -dTotalDens[iwelemRef][0][Deriv::dT] * totalDensInv * totalDensInv;
+ }
+
+ dControlEqn[COFFSET_WJ::dQ] = massDensity*totalDensInv;
+ for( integer ic = 0; ic < NC; ++ic )
+ {
+ dControlEqn[COFFSET_WJ::dC+ic] = massDensity* currentTotalRate * dTotalDensInv_dCompDens[ic];
+ }
+
+ } );
+
+ // add solver matrices
+ localRhs[eqnRowIndex] += controlEqn;
+ localMatrix.addToRowBinarySearchUnsorted< serialAtomic >( eqnRowIndex,
+ dofColIndices,
+ dControlEqn,
+ COFFSET_WJ::nDer );
+ }
+};
+
+
+} // end namespace wellConstraintKernels
+
+} // end namespace geos
+
+#endif //GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_WELLCONSTRAINTKERNELS_HPP
diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/CompositionalMultiphaseWellKernels.cpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/CompositionalMultiphaseWellKernels.cpp
index ee04ffa8c73..a0c71194971 100644
--- a/src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/CompositionalMultiphaseWellKernels.cpp
+++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/CompositionalMultiphaseWellKernels.cpp
@@ -31,214 +31,6 @@ namespace compositionalMultiphaseWellKernels
using namespace constitutive;
-/******************************** ControlEquationHelper ********************************/
-
-GEOS_HOST_DEVICE
-inline
-void
-ControlEquationHelper::
- switchControl( bool const isProducer,
- WellControls::Control const & inputControl,
- WellControls::Control const & currentControl,
- integer const phasePhaseIndex,
- real64 const & targetBHP,
- real64 const & targetPhaseRate,
- real64 const & targetTotalRate,
- real64 const & targetMassRate,
- real64 const & currentBHP,
- arrayView1d< real64 const > const & currentPhaseVolRate,
- real64 const & currentTotalVolRate,
- real64 const & currentMassRate,
- WellControls::Control & newControl )
-{
- // if isViable is true at the end of the following checks, no need to switch
- bool controlIsViable = false;
-
- // The limiting flow rates are treated as upper limits, while the pressure limits
- // are treated as lower limits in production wells and upper limits in injectors.
- // The well changes its mode of control whenever the existing control mode would
- // violate one of these limits.
-
- // Currently, the available constraints are:
- // - Producer: BHP, PHASEVOLRATE
- // - Injector: BHP, TOTALVOLRATE, MASSRATE
-
- // TODO: support GRAT, WRAT, LIQUID for producers and check if any of the active constraint is violated
-
- // BHP control
- if( currentControl == WellControls::Control::BHP )
- {
- // the control is viable if the reference oil rate is below the max rate for producers
- if( isProducer )
- {
- controlIsViable = ( LvArray::math::abs( currentPhaseVolRate[phasePhaseIndex] ) <= LvArray::math::abs( targetPhaseRate ) );
- }
- // the control is viable if the reference total rate is below the max rate for injectors
- else if( inputControl == WellControls::Control::MASSRATE )
- {
- controlIsViable = ( LvArray::math::abs( currentMassRate ) <= LvArray::math::abs( targetMassRate ) );
- }
- else
- {
- controlIsViable = ( LvArray::math::abs( currentTotalVolRate ) <= LvArray::math::abs( targetTotalRate ) );
- }
- }
- else // rate control
- {
- // the control is viable if the reference pressure is below/above the max/min pressure
- if( isProducer )
- {
- // targetBHP specifies a min pressure here
- controlIsViable = ( currentBHP >= targetBHP );
- }
- else
- {
- // targetBHP specifies a max pressure here
- controlIsViable = ( currentBHP <= targetBHP );
- }
- }
-
- if( controlIsViable )
- {
- newControl = currentControl;
- }
- else
- {
- if( isProducer )
- {
- newControl = ( currentControl == WellControls::Control::BHP )
- ? WellControls::Control::PHASEVOLRATE
- : WellControls::Control::BHP;
- }
- else
- {
- if( isZero( targetMassRate ) )
- {
- newControl = ( currentControl == WellControls::Control::BHP )
- ? WellControls::Control::TOTALVOLRATE
- : WellControls::Control::BHP;
- }
- else
- {
- newControl = ( currentControl == WellControls::Control::BHP )
- ? WellControls::Control::MASSRATE
- : WellControls::Control::BHP;
- }
- }
- }
-}
-
-template< integer NC, integer IS_THERMAL >
-GEOS_HOST_DEVICE
-inline
-void
-ControlEquationHelper::
- compute( globalIndex const rankOffset,
- WellControls::Control const currentControl,
- integer const targetPhaseIndex,
- real64 const & targetBHP,
- real64 const & targetPhaseRate,
- real64 const & targetTotalRate,
- real64 const & targetMassRate,
- real64 const & currentBHP,
- arrayView1d< real64 const > const & dCurrentBHP,
- arrayView1d< real64 const > const & currentPhaseVolRate,
- arrayView2d< real64 const > const & dCurrentPhaseVolRate,
-
- real64 const & currentTotalVolRate,
- arrayView1d< real64 const > const & dCurrentTotalVolRate,
- real64 const & massDensity,
- globalIndex const dofNumber,
- CRSMatrixView< real64, globalIndex const > const & localMatrix,
- arrayView1d< real64 > const & localRhs )
-{
-
- using COFFSET_WJ = compositionalMultiphaseWellKernels::ColOffset_WellJac< NC, IS_THERMAL >;
- using Deriv = multifluid::DerivativeOffset;
-
- localIndex const eqnRowIndex = dofNumber + ROFFSET::CONTROL - rankOffset;
- globalIndex dofColIndices[COFFSET_WJ::nDer]{};
- for( integer ic = 0; ic < COFFSET_WJ::nDer; ++ic )
- {
- dofColIndices[ ic ] = dofNumber + ic;
- }
-
- real64 controlEqn = 0;
- real64 dControlEqn[NC+2+IS_THERMAL]{};
-
- // Note: We assume in the computation of currentBHP that the reference elevation
- // is in the top well element. This is enforced by a check in the solver.
- // If we wanted to allow the reference elevation to be outside the top
- // well element, it would make more sense to check the BHP constraint in
- // the well element that contains the reference elevation.
-
- // BHP control
- if( currentControl == WellControls::Control::BHP )
- {
- // control equation is a difference between current BHP and target BHP
- controlEqn = currentBHP - targetBHP;
- dControlEqn[COFFSET_WJ::dP] = dCurrentBHP[Deriv::dP];
- for( integer ic = 0; ic < NC; ++ic )
- {
- dControlEqn[COFFSET_WJ::dC+ic] = dCurrentBHP[Deriv::dC+ic];
- }
- if constexpr ( IS_THERMAL )
-
- dControlEqn[COFFSET_WJ::dT] = dCurrentBHP[Deriv::dT];
-
- }
- // Oil volumetric rate control
- else if( currentControl == WellControls::Control::PHASEVOLRATE )
- {
- controlEqn = currentPhaseVolRate[targetPhaseIndex] - targetPhaseRate;
- dControlEqn[COFFSET_WJ::dP] = dCurrentPhaseVolRate[targetPhaseIndex][COFFSET_WJ::dP];
- dControlEqn[COFFSET_WJ::dQ] = dCurrentPhaseVolRate[targetPhaseIndex][COFFSET_WJ::dQ];
- for( integer ic = 0; ic < NC; ++ic )
- {
- dControlEqn[COFFSET_WJ::dC+ic] = dCurrentPhaseVolRate[targetPhaseIndex][COFFSET_WJ::dC+ic];
- }
- if constexpr ( IS_THERMAL )
- dControlEqn[COFFSET_WJ::dT] = dCurrentPhaseVolRate[targetPhaseIndex][COFFSET_WJ::dT];
- }
- // Total volumetric rate control
- else if( currentControl == WellControls::Control::TOTALVOLRATE )
- {
- controlEqn = currentTotalVolRate - targetTotalRate;
- dControlEqn[COFFSET_WJ::dP] = dCurrentTotalVolRate[COFFSET_WJ::dP];
- dControlEqn[COFFSET_WJ::dQ] = dCurrentTotalVolRate[COFFSET_WJ::dQ];
- for( integer ic = 0; ic < NC; ++ic )
- {
- dControlEqn[COFFSET_WJ::dC+ic] = dCurrentTotalVolRate[COFFSET_WJ::dC+ic];
- }
- if constexpr ( IS_THERMAL )
- dControlEqn[COFFSET_WJ::dT] = dCurrentTotalVolRate[COFFSET_WJ::dT];
- }
- // Total mass rate control
- else if( currentControl == WellControls::Control::MASSRATE )
- {
- controlEqn = massDensity*currentTotalVolRate - targetMassRate;
- dControlEqn[COFFSET_WJ::dP] = massDensity*dCurrentTotalVolRate[COFFSET_WJ::dP];
- dControlEqn[COFFSET_WJ::dQ] = massDensity*dCurrentTotalVolRate[COFFSET_WJ::dQ];
- for( integer ic = 0; ic < NC; ++ic )
- {
- dControlEqn[COFFSET_WJ::dC+ic] = massDensity*dCurrentTotalVolRate[COFFSET_WJ::dC+ic];
- }
- if constexpr ( IS_THERMAL )
- dControlEqn[COFFSET_WJ::dT] = massDensity*dCurrentTotalVolRate[COFFSET_WJ::dT];
- }
- else
- {
- GEOS_ERROR( "This constraint is not supported in CompositionalMultiphaseWell" );
- }
- localRhs[eqnRowIndex] += controlEqn;
-
- localMatrix.addToRowBinarySearchUnsorted< serialAtomic >( eqnRowIndex,
- dofColIndices,
- dControlEqn,
- COFFSET_WJ::nDer );
-
-
-}
/******************************** PressureRelationKernel ********************************/
@@ -300,11 +92,6 @@ void
PressureRelationKernel::
launch( localIndex const size,
globalIndex const rankOffset,
- bool const isLocallyOwned,
- localIndex const iwelemControl,
- integer const targetPhaseIndex,
- WellControls const & wellControls,
- real64 const & time,
arrayView1d< integer const > const elemStatus,
arrayView1d< globalIndex const > const & wellElemDofNumber,
arrayView1d< real64 const > const & wellElemGravCoef,
@@ -317,36 +104,6 @@ PressureRelationKernel::
arrayView1d< real64 > const & localRhs )
{
using COFFSET_WJ = compositionalMultiphaseWellKernels::ColOffset_WellJac< NC, IS_THERMAL >;
- // static well control data
- bool const isProducer = wellControls.isProducer();
- WellControls::Control const currentControl = wellControls.getControl();
- WellControls::Control const inputControl = wellControls.getInputControl();
- real64 const targetBHP = wellControls.getTargetBHP( time );
- real64 const targetTotalRate = wellControls.getTargetTotalRate( time );
- real64 const targetPhaseRate = wellControls.getTargetPhaseRate( time );
- real64 const targetMassRate = wellControls.getTargetMassRate( time );
-
- // dynamic well control data
- real64 const & currentBHP =
- wellControls.getReference< real64 >( CompositionalMultiphaseWell::viewKeyStruct::currentBHPString() );
- arrayView1d< real64 const > const & dCurrentBHP =
- wellControls.getReference< array1d< real64 > >( CompositionalMultiphaseWell::viewKeyStruct::dCurrentBHPString() );
-
- arrayView1d< real64 const > const & currentPhaseVolRate =
- wellControls.getReference< array1d< real64 > >( CompositionalMultiphaseWell::viewKeyStruct::currentPhaseVolRateString() );
- arrayView2d< real64 const > const & dCurrentPhaseVolRate =
- wellControls.getReference< array2d< real64 > >( CompositionalMultiphaseWell::viewKeyStruct::dCurrentPhaseVolRateString() );
-
- real64 const & currentTotalVolRate =
- wellControls.getReference< real64 >( CompositionalMultiphaseWell::viewKeyStruct::currentTotalVolRateString() );
- arrayView1d< real64 const > const & dCurrentTotalVolRate =
- wellControls.getReference< array1d< real64 > >( CompositionalMultiphaseWell::viewKeyStruct::dCurrentTotalVolRateString() );
-
- real64 const & currentMassRate =
- wellControls.getReference< real64 >( CompositionalMultiphaseWell::viewKeyStruct::currentMassRateString() );
-
- real64 const & massDensity =
- wellControls.getReference< real64 >( CompositionalMultiphaseWell::viewKeyStruct::massDensityString() );
RAJA::ReduceMax< parallelDeviceReduce, localIndex > switchControl( 0 );
@@ -360,48 +117,7 @@ PressureRelationKernel::
localIndex const iwelemNext = nextWellElemIndex[iwelem];
- if( iwelemNext < 0 && isLocallyOwned ) // if iwelemNext < 0, form control equation
- {
-
- WellControls::Control newControl = currentControl;
- ControlEquationHelper::switchControl( isProducer,
- inputControl,
- currentControl,
- targetPhaseIndex,
- targetBHP,
- targetPhaseRate,
- targetTotalRate,
- targetMassRate,
- currentBHP,
- currentPhaseVolRate,
- currentTotalVolRate,
- currentMassRate,
- newControl );
- if( currentControl != newControl )
- {
- switchControl.max( 1 );
- }
- ControlEquationHelper::compute< NC, IS_THERMAL >( rankOffset,
- newControl,
- targetPhaseIndex,
- targetBHP,
- targetPhaseRate,
- targetTotalRate,
- targetMassRate,
- currentBHP,
- dCurrentBHP,
- currentPhaseVolRate,
- dCurrentPhaseVolRate,
- currentTotalVolRate,
- dCurrentTotalVolRate,
- massDensity,
- wellElemDofNumber[iwelemControl],
- localMatrix,
- localRhs );
- // TODO: for consistency, we should assemble here, not in compute...
-
- }
- else if( iwelemNext >= 0 ) // if iwelemNext >= 0, form momentum equation
+ if( iwelemNext >= 0 ) // if iwelemNext >= 0, form momentum equation
{
real64 localPresRel = 0;
@@ -455,11 +171,6 @@ PressureRelationKernel::
void PressureRelationKernel:: \
launch< NC, IS_THERMAL >( localIndex const size, \
globalIndex const rankOffset, \
- bool const isLocallyOwned, \
- localIndex const iwelemControl, \
- integer const targetPhaseIndex, \
- WellControls const & wellControls, \
- real64 const & time, \
arrayView1d< integer const > const elemStatus, \
arrayView1d< globalIndex const > const & wellElemDofNumber, \
arrayView1d< real64 const > const & wellElemGravCoef, \
@@ -716,62 +427,97 @@ CompDensInitializationKernel::
void
RateInitializationKernel::
launch( localIndex const subRegionSize,
- integer const targetPhaseIndex,
WellControls const & wellControls,
real64 const & time,
arrayView3d< real64 const, multifluid::USD_PHASE > const & phaseDens,
arrayView2d< real64 const, multifluid::USD_FLUID > const & totalDens,
arrayView1d< real64 > const & connRate )
{
- WellControls::Control const control = wellControls.getControl();
- bool const isProducer = wellControls.isProducer();
- real64 const targetTotalRate = wellControls.getTargetTotalRate( time );
- real64 const targetPhaseRate = wellControls.getTargetPhaseRate( time );
- real64 const targetMassRate = wellControls.getTargetMassRate( time );
-
- // Estimate the connection rates
- forAll< parallelDevicePolicy<> >( subRegionSize, [=] GEOS_HOST_DEVICE ( localIndex const iwelem )
+ if( wellControls.isProducer() )
{
- if( control == WellControls::Control::BHP )
+ // Use use defined control type to set initial connection rates
+ WellConstraintBase const * constraint = wellControls.getCurrentConstraint();
+ real64 const constraintVal = constraint->getConstraintValue( time );
+ ConstraintTypeId const controlType = constraint->getControl();
+ if( controlType == ConstraintTypeId::PHASEVOLRATE )
{
- // if BHP constraint set rate below the absolute max rate
- // with the appropriate sign (negative for prod, positive for inj)
- if( isProducer )
+ integer const targetPhaseIndex = wellControls.getConstraintPhaseIndex();
+
+ forAll< parallelDevicePolicy<> >( subRegionSize, [=] GEOS_HOST_DEVICE ( localIndex const iwelem )
{
- connRate[iwelem] = LvArray::math::max( 0.1 * targetPhaseRate * phaseDens[iwelem][0][targetPhaseIndex], -1e3 );
- }
- else
+ connRate[iwelem] = constraintVal * phaseDens[iwelem][0][targetPhaseIndex];
+ } );
+ }
+ else if( controlType == ConstraintTypeId::TOTALVOLRATE )
+ {
+ forAll< parallelDevicePolicy<> >( subRegionSize, [=] GEOS_HOST_DEVICE ( localIndex const iwelem )
+ {
+ connRate[iwelem] = LvArray::math::max( 0.1 * constraintVal * totalDens[iwelem][0], -1e3 );
+ } );
+ }
+ else if( controlType == ConstraintTypeId::MASSRATE )
+ {
+ forAll< parallelDevicePolicy<> >( subRegionSize, [=] GEOS_HOST_DEVICE ( localIndex const iwelem )
+ {
+ connRate[iwelem] = constraintVal;
+ } );
+ }
+ else if( controlType == ConstraintTypeId::BHP )
+ {
+ // this assumes phase control present
+ integer const targetPhaseIndex = wellControls.getConstraintPhaseIndex();
+ std::vector< WellConstraintBase * > const constraints = wellControls.getProdRateConstraints();
+ // Use first rate constraint to set initial connection rates
+ real64 const rateVal = constraints[0]->getConstraintValue( time );
+ forAll< parallelDevicePolicy<> >( subRegionSize, [&] GEOS_HOST_DEVICE ( localIndex const iwelem )
{
- if( isZero( targetMassRate ) )
- {
- connRate[iwelem] = LvArray::math::min( 0.1 * targetTotalRate * totalDens[iwelem][0], 1e3 );
- }
- else
- {
- connRate[iwelem] = targetMassRate;
- }
- }
+ connRate[iwelem] = LvArray::math::max( 0.1 * rateVal * phaseDens[iwelem][0][targetPhaseIndex], -1e3 );
+ } );
}
- else if( control == WellControls::Control::MASSRATE )
+ }
+ else
+ {
+ // Use use defined control type to set initial connection rates
+ WellConstraintBase const * constraint = wellControls.getCurrentConstraint();
+ real64 const constraintVal = constraint->getConstraintValue( time );
+ ConstraintTypeId const controlType = constraint->getControl();
+ if( controlType == ConstraintTypeId::PHASEVOLRATE )
{
- connRate[iwelem] = targetMassRate;
+ integer const targetPhaseIndex = wellControls.getConstraintPhaseIndex();
+
+ forAll< parallelDevicePolicy<> >( subRegionSize, [=] GEOS_HOST_DEVICE ( localIndex const iwelem )
+ {
+ connRate[iwelem] = LvArray::math::max( 0.1 * constraintVal * phaseDens[iwelem][0][targetPhaseIndex], 1e3 );
+ } );
}
- else
+ else if( controlType == ConstraintTypeId::TOTALVOLRATE )
{
- if( isProducer )
+ forAll< parallelDevicePolicy<> >( subRegionSize, [=] GEOS_HOST_DEVICE ( localIndex const iwelem )
{
- connRate[iwelem] = targetPhaseRate * phaseDens[iwelem][0][targetPhaseIndex];
- }
- else
+ connRate[iwelem] = constraintVal * totalDens[iwelem][0];
+ } );
+ }
+ else if( controlType == ConstraintTypeId::MASSRATE )
+ {
+ forAll< parallelDevicePolicy<> >( subRegionSize, [=] GEOS_HOST_DEVICE ( localIndex const iwelem )
{
- connRate[iwelem] = targetTotalRate * totalDens[iwelem][0];
- }
+ connRate[iwelem] = constraintVal;
+ } );
}
- } );
+ else if( controlType == ConstraintTypeId::BHP )
+ {
+ std::vector< WellConstraintBase * > const constraints = wellControls.getInjRateConstraints();
+ // Use first rate constraint to set initial connection rates
+ real64 const rateVal = constraints[0]->getConstraintValue( time );
+ forAll< parallelDevicePolicy<> >( subRegionSize, [=] GEOS_HOST_DEVICE ( localIndex const iwelem )
+ {
+ connRate[iwelem] = LvArray::math::min( 0.1 * rateVal * totalDens[iwelem][0], 1e3 );
+ } );
+ }
+ }
}
-
} // end namespace compositionalMultiphaseWellKernels
} // end namespace geos
diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/CompositionalMultiphaseWellKernels.hpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/CompositionalMultiphaseWellKernels.hpp
index a8123abbb12..c81c2582537 100644
--- a/src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/CompositionalMultiphaseWellKernels.hpp
+++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/CompositionalMultiphaseWellKernels.hpp
@@ -39,7 +39,8 @@
#include "physicsSolvers/fluidFlow/kernels/compositional/SolutionCheckKernel.hpp"
#include "physicsSolvers/fluidFlow/wells/CompositionalMultiphaseWellFields.hpp"
#include "physicsSolvers/fluidFlow/wells/WellControls.hpp"
-#include "physicsSolvers/fluidFlow/wells/WellSolverBaseFields.hpp"
+#include "physicsSolvers/fluidFlow/wells/WellFields.hpp"
+#include "physicsSolvers/fluidFlow/wells/WellPhaseVolumeRateConstraint.hpp"
namespace geos
{
@@ -134,19 +135,19 @@ struct ControlEquationHelper
inline
static
void
- switchControl( bool const isProducer,
- WellControls::Control const & inputControl,
- WellControls::Control const & currentControl,
- integer const phasePhaseIndex,
- real64 const & targetBHP,
- real64 const & targetPhaseRate,
- real64 const & targetTotalRate,
- real64 const & targetMassRate,
- real64 const & currentBHP,
- arrayView1d< real64 const > const & currentPhaseVolRate,
- real64 const & currentTotalVolRate,
- real64 const & currentMassRate,
- WellControls::Control & newControl );
+ selectLimitingConstraint( bool const isProducer,
+ WellControls::Control const & inputControl,
+ WellControls::Control const & currentControl,
+ integer const phasePhaseIndex,
+ real64 const & targetBHP,
+ real64 const & targetPhaseRate,
+ real64 const & targetTotalRate,
+ real64 const & targetMassRate,
+ real64 const & currentBHP,
+ arrayView1d< real64 const > const & currentPhaseVolRate,
+ real64 const & currentTotalVolRate,
+ real64 const & currentMassRate,
+ WellControls::Control & newControl );
template< integer NC, integer IS_THERMAL >
GEOS_HOST_DEVICE
@@ -160,6 +161,7 @@ struct ControlEquationHelper
real64 const & targetTotalRate,
real64 const & targetMassRate,
real64 const & currentBHP,
+ real64 const & targetValue,
arrayView1d< real64 const > const & dCurrentBHP,
arrayView1d< real64 const > const & currentPhaseVolRate,
arrayView2d< real64 const > const & dCurrentPhaseVolRate,
@@ -200,11 +202,6 @@ struct PressureRelationKernel
static void
launch( localIndex const size,
globalIndex const rankOffset,
- bool const isLocallyOwned,
- localIndex const iwelemControl,
- integer const targetPhaseIndex,
- WellControls const & wellControls,
- real64 const & time,
arrayView1d< integer const > const elemStatus,
arrayView1d< globalIndex const > const & wellElemDofNumber,
arrayView1d< real64 const > const & wellElemGravCoef,
@@ -323,7 +320,6 @@ struct RateInitializationKernel
static void
launch( localIndex const subRegionSize,
- integer const targetPhaseIndex,
WellControls const & wellControls,
real64 const & currentTime,
arrayView3d< real64 const, constitutive::multifluid::USD_PHASE > const & phaseDens,
@@ -505,7 +501,6 @@ class ResidualNormKernel : public physicsSolverBaseKernels::ResidualNormKernelBa
arrayView1d< localIndex const > const & ghostRank,
integer const numComp,
integer const numDof,
- integer const targetPhaseIndex,
WellElementSubRegion const & subRegion,
constitutive::MultiFluidBase const & fluid,
WellControls const & wellControls,
@@ -519,20 +514,42 @@ class ResidualNormKernel : public physicsSolverBaseKernels::ResidualNormKernelBa
minNormalizer ),
m_numComp( numComp ),
m_numDof( numDof ),
- m_targetPhaseIndex( targetPhaseIndex ),
m_dt( dt ),
m_isLocallyOwned( subRegion.isLocallyOwned() ),
m_iwelemControl( subRegion.getTopWellElementIndex() ),
m_isProducer( wellControls.isProducer() ),
m_currentControl( wellControls.getControl() ),
- m_targetBHP( wellControls.getTargetBHP( time ) ),
- m_targetTotalRate( wellControls.getTargetTotalRate( time ) ),
- m_targetPhaseRate( wellControls.getTargetPhaseRate( time ) ),
- m_targetMassRate( wellControls.getTargetMassRate( time ) ),
m_volume( subRegion.getElementVolume() ),
m_phaseDens_n( fluid.phaseDensity_n() ),
m_totalDens_n( fluid.totalDensity_n() )
- {}
+ {
+ if( m_isProducer )
+ {
+ if( wellControls.getMinBHPConstraint()->isConstraintActive())
+ m_targetBHP = wellControls.getMinBHPConstraint()->getConstraintValue( time );
+ // Note this assumes that there is only one rate constraint
+ // This is a normalizer for the balance equations. The normalizaer should be the current rate not the constraint value!!
+ // This is one of the reasons for restricting constraint type for a production well
+ // another pr will remove fix this (so the cause for difference results is isolated to one change)
+ m_targetPhaseIndex = wellControls.getConstraintPhaseIndex( );
+ m_constraintValue = wellControls.getProdRateConstraints()[0]->getConstraintValue( time );
+
+ }
+ else
+ {
+ m_targetBHP = wellControls.getMaxBHPConstraint()->getConstraintValue( time );
+
+ // tjbNote this assumes that there is only one rate constraint
+ // This is a normalizer for the balance equations. The normalizaer should be the current rate not the constraint value!!
+ // This is one of the reasons for restricting constraint type for a production well
+ // another pr will remove fix this (so the cause for difference results is isolated to one change)
+ m_targetPhaseIndex = -1;
+ m_constraintValue = wellControls.getInjRateConstraints()[0]->getConstraintValue( time );
+
+ }
+
+
+ }
GEOS_HOST_DEVICE
virtual void computeLinf( localIndex const iwelem,
@@ -561,17 +578,17 @@ class ResidualNormKernel : public physicsSolverBaseKernels::ResidualNormKernelBa
else if( m_currentControl == WellControls::Control::TOTALVOLRATE )
{
// the residual entry is in volume / time units
- normalizer = LvArray::math::max( LvArray::math::abs( m_targetTotalRate ), m_minNormalizer );
+ normalizer = LvArray::math::max( LvArray::math::abs( m_constraintValue ), m_minNormalizer );
}
else if( m_currentControl == WellControls::Control::PHASEVOLRATE )
{
// the residual entry is in volume / time units
- normalizer = LvArray::math::max( LvArray::math::abs( m_targetPhaseRate ), m_minNormalizer );
+ normalizer = LvArray::math::max( LvArray::math::abs( m_constraintValue ), m_minNormalizer );
}
else if( m_currentControl == WellControls::Control::MASSRATE )
{
// the residual entry is in volume / time units
- normalizer = LvArray::math::max( LvArray::math::abs( m_targetMassRate ), m_minNormalizer );
+ normalizer = LvArray::math::max( LvArray::math::abs( m_constraintValue ), m_minNormalizer );
}
}
// for the pressure difference equation, always normalize by the BHP
@@ -586,18 +603,18 @@ class ResidualNormKernel : public physicsSolverBaseKernels::ResidualNormKernelBa
if( m_isProducer ) // only PHASEVOLRATE is supported for now
{
// the residual is in mass units
- normalizer = m_dt * LvArray::math::abs( m_targetPhaseRate ) * m_phaseDens_n[iwelem][0][m_targetPhaseIndex];
+ normalizer = m_dt * LvArray::math::abs( m_constraintValue ) * m_phaseDens_n[iwelem][0][m_targetPhaseIndex];
}
else // Type::INJECTOR, only TOTALVOLRATE is supported for now
{
if( m_currentControl == WellControls::Control::MASSRATE )
{
- normalizer = m_dt * LvArray::math::abs( m_targetMassRate );
+ normalizer = m_dt * LvArray::math::abs( m_constraintValue );
}
else
{
// the residual is in mass units
- normalizer = m_dt * LvArray::math::abs( m_targetTotalRate ) * m_totalDens_n[iwelem][0];
+ normalizer = m_dt * LvArray::math::abs( m_constraintValue ) * m_totalDens_n[iwelem][0];
}
}
@@ -611,17 +628,17 @@ class ResidualNormKernel : public physicsSolverBaseKernels::ResidualNormKernelBa
if( m_isProducer ) // only PHASEVOLRATE is supported for now
{
// the residual is in volume units
- normalizer = m_dt * LvArray::math::abs( m_targetPhaseRate );
+ normalizer = m_dt * LvArray::math::abs( m_constraintValue );
}
else // Type::INJECTOR, only TOTALVOLRATE is supported for now
{
if( m_currentControl == WellControls::Control::MASSRATE )
{
- normalizer = m_dt * LvArray::math::abs( m_targetMassRate/ m_totalDens_n[iwelem][0] );
+ normalizer = m_dt * LvArray::math::abs( m_constraintValue/ m_totalDens_n[iwelem][0] );
}
else
{
- normalizer = m_dt * LvArray::math::abs( m_targetTotalRate );
+ normalizer = m_dt * LvArray::math::abs( m_constraintValue );
}
}
@@ -658,7 +675,7 @@ class ResidualNormKernel : public physicsSolverBaseKernels::ResidualNormKernelBa
integer const m_numDof;
/// Index of the target phase
- integer const m_targetPhaseIndex;
+ integer m_targetPhaseIndex;
/// Time step size
real64 const m_dt;
@@ -674,10 +691,9 @@ class ResidualNormKernel : public physicsSolverBaseKernels::ResidualNormKernelBa
/// Controls
WellControls::Control const m_currentControl;
- real64 const m_targetBHP;
- real64 const m_targetTotalRate;
- real64 const m_targetPhaseRate;
- real64 const m_targetMassRate;
+ real64 m_constraintValue;
+ real64 m_targetBHP;
+
/// View on the volume
arrayView1d< real64 const > const m_volume;
@@ -700,7 +716,6 @@ class ResidualNormKernelFactory
* @tparam POLICY the policy used in the RAJA kernel
* @param[in] numComp number of fluid components
* @param[in] numDof number of dofs per well element
- * @param[in] targetPhaseIndex the index of the target phase (for phase volume control)
* @param[in] rankOffset the offset of my MPI rank
* @param[in] dofKey the string key to retrieve the degress of freedom numbers
* @param[in] localResidual the residual vector on my MPI rank
@@ -715,7 +730,6 @@ class ResidualNormKernelFactory
static void
createAndLaunch( integer const numComp,
integer const numDof,
- integer const targetPhaseIndex,
globalIndex const rankOffset,
string const & dofKey,
arrayView1d< real64 const > const & localResidual,
@@ -731,7 +745,7 @@ class ResidualNormKernelFactory
arrayView1d< integer const > const ghostRank = subRegion.ghostRank();
ResidualNormKernel kernel( rankOffset, localResidual, dofNumber, ghostRank,
- numComp, numDof, targetPhaseIndex, subRegion, fluid, wellControls, time, dt, minNormalizer );
+ numComp, numDof, subRegion, fluid, wellControls, time, dt, minNormalizer );
ResidualNormKernel::launchLinf< POLICY >( subRegion.size(), kernel, residualNorm );
}
@@ -830,6 +844,7 @@ class ElementBasedAssemblyKernel
* @param[inout] localRhs the local right-hand side vector
*/
ElementBasedAssemblyKernel( localIndex const numPhases,
+ integer const thermalEffectsEnabled,
integer const isProducer,
globalIndex const rankOffset,
string const dofKey,
@@ -839,6 +854,7 @@ class ElementBasedAssemblyKernel
arrayView1d< real64 > const & localRhs,
BitFlags< isothermalCompositionalMultiphaseBaseKernels::KernelFlags > const kernelFlags )
: m_numPhases( numPhases ),
+ m_thermalEffectsEnabled( thermalEffectsEnabled ),
m_isProducer( isProducer ),
m_rankOffset( rankOffset ),
m_iwelemControl( subRegion.getTopWellElementIndex() ),
@@ -1120,7 +1136,20 @@ class ElementBasedAssemblyKernel
if constexpr ( IS_THERMAL)
{
- if( ei == m_iwelemControl && !m_isProducer )
+ if( !m_thermalEffectsEnabled )
+ {
+ for( integer i=0; i < numComp+1+IS_THERMAL; i++ )
+ {
+ stack.localJacobian[numRows-1][i] = 0.0;
+ }
+ // constant Temperature
+ for( integer i=0; i < numComp+1+IS_THERMAL; i++ )
+ stack.localJacobian[i][numRows-1] = 0.0;
+ stack.localJacobian[numRows-1][numRows-1] = 1.0;
+
+ stack.localResidual[numRows-1]=0.0;
+ }
+ else if( ei == m_iwelemControl && !m_isProducer )
{
// For top segment energy balance eqn replaced with T(n+1) - T = 0
// No other energy balance derivatives
@@ -1197,6 +1226,8 @@ class ElementBasedAssemblyKernel
/// Number of fluid phases
integer const m_numPhases;
+ /// Flag indicating whether thermal effects are enabled
+ bool const m_thermalEffectsEnabled;
/// Well type
integer const m_isProducer;
@@ -1276,6 +1307,7 @@ class ElementBasedAssemblyKernelFactory
static void
createAndLaunch( localIndex const numComps,
localIndex const numPhases,
+ integer const & thermalEffectsEnabled,
integer const isProducer,
globalIndex const rankOffset,
BitFlags< isothermalCompositionalMultiphaseBaseKernels::KernelFlags > kernelFlags,
@@ -1292,7 +1324,7 @@ class ElementBasedAssemblyKernelFactory
integer constexpr istherm = IS_THERMAL();
ElementBasedAssemblyKernel< NUM_COMP, istherm >
- kernel( numPhases, isProducer, rankOffset, dofKey, subRegion, fluid, localMatrix, localRhs, kernelFlags );
+ kernel( numPhases, thermalEffectsEnabled, isProducer, rankOffset, dofKey, subRegion, fluid, localMatrix, localRhs, kernelFlags );
ElementBasedAssemblyKernel< NUM_COMP, istherm >::template
launch< POLICY, ElementBasedAssemblyKernel< NUM_COMP, istherm > >( subRegion.size(), kernel );
} );
@@ -1359,7 +1391,7 @@ class FaceBasedAssemblyKernel
m_wellElemDofNumber ( subRegion.getReference< array1d< globalIndex > >( wellDofKey ) ),
m_nextWellElemIndex ( subRegion.getReference< array1d< localIndex > >( WellElementSubRegion::viewKeyStruct::nextWellElementIndexString()) ),
m_elemStatus( subRegion.getLocalWellElementStatus() ),
- m_connRate ( subRegion.getField< fields::well::mixtureConnectionRate >() ),
+ m_connRate ( subRegion.getField< fields::well::connectionRate >() ),
m_wellElemCompFrac ( subRegion.getField< fields::well::globalCompFraction >() ),
m_dWellElemCompFrac_dCompDens ( subRegion.getField< fields::well::dGlobalCompFraction_dGlobalCompDensity >() ),
m_localMatrix( localMatrix ),
diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/PerforationFluxKernels.hpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/PerforationFluxKernels.hpp
index 18a48ee1e63..1d37e5acb04 100644
--- a/src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/PerforationFluxKernels.hpp
+++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/PerforationFluxKernels.hpp
@@ -332,7 +332,6 @@ class PerforationFluxKernel
// increment component fluxes
for( integer ic = 0; ic < NC; ++ic )
{
- // Note this needs to be uncommented out
m_compPerfRate[iperf][ic] += flux * m_resPhaseCompFrac[er][esr][ei][0][ip][ic];
dCompFrac[CP_Deriv::dP] = m_dResPhaseCompFrac[er][esr][ei][0][ip][ic][Deriv::dP];
if constexpr (IS_THERMAL)
@@ -353,10 +352,6 @@ class PerforationFluxKernel
m_dCompPerfRate[iperf][TAG::WELL][ic][jc] += dFlux[TAG::WELL][jc] * m_resPhaseCompFrac[er][esr][ei][0][ip][ic];
}
}
- if constexpr ( IS_THERMAL )
- {
- fluxKernelOp( iwelem, er, esr, ei, ip, potDiff, flux, dFlux );
- }
} // end resevoir is upstream phase loop
@@ -480,11 +475,12 @@ class PerforationFluxKernel
m_dCompPerfRate[iperf][TAG::WELL][ic][CP_Deriv::dC+jc] += m_dWellElemCompFrac_dCompDens[iwelem][ic][jc] * flux;
}
}
- if constexpr ( IS_THERMAL )
- {
- fluxKernelOp( iwelem, er, esr, ei, -1, potDiff, flux, dFlux );
- }
+
} // end upstream
+ if constexpr ( IS_THERMAL )
+ {
+ fluxKernelOp( iwelem, er, esr, ei, potDiff, dPotDiff, flux, dFlux );
+ }
}
/**
* @brief Performs the kernel launch
@@ -569,7 +565,7 @@ class PerforationFluxKernelFactory
string const flowSolverName,
PerforationData * const perforationData,
ElementSubRegionBase const & subRegion,
- ElementRegionManager & elemManager,
+ ElementRegionManager const & elemManager,
bool const isInjector,
bool const isCrossflowEnabled )
{
@@ -607,9 +603,21 @@ class PerforationFluxKernel : public isothermalPerforationFluxKernels::Perforati
public:
using Base = isothermalPerforationFluxKernels::PerforationFluxKernel< NC, NP, IS_THERMAL >;
- using Base::m_resPhaseCompFrac;
- using Base::m_dResCompFrac_dCompDens;
using Base::m_dWellElemCompFrac_dCompDens;
+ using Base::m_resPres;
+ using Base::m_resPhaseVolFrac;
+ using Base::m_dResPhaseVolFrac;
+ using Base::m_dResCompFrac_dCompDens;
+ using Base::m_resPhaseDens;
+ using Base::m_dResPhaseDens;
+ using Base::m_resPhaseVisc;
+ using Base::m_dResPhaseVisc;
+ using Base::m_resPhaseCompFrac;
+ using Base::m_dResPhaseCompFrac;
+ using Base::m_resPhaseRelPerm;
+ using Base::m_dResPhaseRelPerm_dPhaseVolFrac;
+ using Base::m_isInjector;
+ using Base::m_isCrossflowEnabled;
/// Compile time value for the number of components
static constexpr integer numComp = NC;
@@ -631,6 +639,8 @@ class PerforationFluxKernel : public isothermalPerforationFluxKernels::Perforati
using ThermalMultiFluidAccessors =
StencilMaterialAccessors< MultiFluidBase,
+ fields::multifluid::phaseFraction,
+ fields::multifluid::dPhaseFraction,
fields::multifluid::phaseEnthalpy,
fields::multifluid::dPhaseEnthalpy >;
@@ -645,7 +655,7 @@ class PerforationFluxKernel : public isothermalPerforationFluxKernels::Perforati
using ElementViewConst = ElementRegionManager::ElementViewConst< VIEWTYPE >;
PerforationFluxKernel ( PerforationData * const perforationData,
- ElementSubRegionBase const & subRegion,
+ ElementSubRegionBase & subRegion,
MultiFluidBase const & wellFluid,
CompFlowAccessors const & compFlowAccessors,
MultiFluidAccessors const & multiFluidAccessors,
@@ -668,6 +678,8 @@ class PerforationFluxKernel : public isothermalPerforationFluxKernels::Perforati
m_energyPerfFlux( perforationData->getField< fields::well::energyPerforationFlux >() ),
m_dEnergyPerfFlux( perforationData->getField< fields::well::dEnergyPerforationFlux >() ),
m_temp( thermalCompFlowAccessors.get( fields::flow::temperature {} ) ),
+ m_resPhaseFraction( thermalMultiFluidAccessors.get( fields::multifluid::phaseFraction {} ) ),
+ m_dResPhaseFraction( thermalMultiFluidAccessors.get( fields::multifluid::dPhaseFraction {} )),
m_resPhaseEnthalpy( thermalMultiFluidAccessors.get( fields::multifluid::phaseEnthalpy {} ) ),
m_dResPhaseEnthalpy( thermalMultiFluidAccessors.get( fields::multifluid::dPhaseEnthalpy {} ) )
{}
@@ -690,38 +702,121 @@ class PerforationFluxKernel : public isothermalPerforationFluxKernels::Perforati
}
}
- Base::computeFlux ( iperf, [&]( localIndex const iwelem, localIndex const er, localIndex const esr, localIndex const ei, localIndex const ip,
- real64 const potDiff, real64 const flux, real64 const (&dFlux)[2][CP_Deriv::nDer] )
+ Base::computeFlux ( iperf, [&]( localIndex const iwelem, localIndex const er, localIndex const esr, localIndex const ei
+ , real64 const potDiff, real64 const dPotDiff[2][CP_Deriv::nDer],
+ real64 const flux, real64 const (&dFlux)[2][CP_Deriv::nDer] )
{
+ real64 dMob[CP_Deriv::nDer]{};
+ GEOS_UNUSED_VAR( iwelem );
if( potDiff >= 0 ) // ** reservoir cell is upstream **
{
+ real64 eflux = 0.0;
+ // loop over phases, compute and upwind phase flux
+ // and sum contributions to each component's perforation rate
+ for( integer ip = 0; ip < NP; ++ip )
+ {
+ // skip the rest of the calculation if the phase is absent
+ // or if crossflow is disabled for injectors
+ real64 const resPhaseVolFrac = m_resPhaseVolFrac[er][esr][ei][ip];
+ bool const phaseExists = (resPhaseVolFrac > 0 );
+ if( !phaseExists || (m_isInjector && !m_isCrossflowEnabled) )
+ {
+ continue;
+ }
+
+ // here, we have to recompute the reservoir phase mobility (not including density)
+
+ // density
+ real64 const resDens = m_resPhaseDens[er][esr][ei][0][ip];
+ real64 dDens[CP_Deriv::nDer]{};
+ dDens[CP_Deriv::dP] = m_dResPhaseDens[er][esr][ei][0][ip][Deriv::dP];
+ dDens[CP_Deriv::dT] = m_dResPhaseDens[er][esr][ei][0][ip][Deriv::dT];
+ applyChainRule( NC, m_dResCompFrac_dCompDens[er][esr][ei],
+ m_dResPhaseDens[er][esr][ei][0][ip],
+ &dDens[CP_Deriv::dC],
+ Deriv::dC );
+ // viscosity
+ real64 const resVisc = m_resPhaseVisc[er][esr][ei][0][ip];
+ real64 dVisc[CP_Deriv::nDer]{};
+ dVisc[CP_Deriv::dP] = m_dResPhaseVisc[er][esr][ei][0][ip][Deriv::dP];
+ dVisc[CP_Deriv::dT] = m_dResPhaseVisc[er][esr][ei][0][ip][Deriv::dT];
+ applyChainRule( NC, m_dResCompFrac_dCompDens[er][esr][ei],
+ m_dResPhaseVisc[er][esr][ei][0][ip],
+ &dVisc[CP_Deriv::dC],
+ Deriv::dC );
+ // enthalpy
+ real64 const resEnthalpy = m_resPhaseEnthalpy[er][esr][ei][0][ip];
+ real64 dResEnthalpy[CP_Deriv::nDer]{};
+ dResEnthalpy[CP_Deriv::dP] = m_dResPhaseEnthalpy[er][esr][ei][0][ip][Deriv::dP];
+ dResEnthalpy[CP_Deriv::dT] = m_dResPhaseEnthalpy[er][esr][ei][0][ip][Deriv::dT];
+
+ applyChainRule( NC, m_dResCompFrac_dCompDens[er][esr][ei],
+ m_dResPhaseEnthalpy[er][esr][ei][0][ip],
+ &dResEnthalpy[CP_Deriv::dC],
+ Deriv::dC );
- real64 const res_enthalpy = m_resPhaseEnthalpy[er][esr][ei][0][ip];
+ // relative permeability
+ real64 const resRelPerm = m_resPhaseRelPerm[er][esr][ei][0][ip];
+ real64 dRelPerm[CP_Deriv::nDer]{};
+ for( integer jc = 0; jc < CP_Deriv::nDer; ++jc )
+ {
+ dRelPerm[jc]=0;
+ }
+ for( integer jp = 0; jp < NP; ++jp )
+ {
+ real64 const dResRelPerm_dS = m_dResPhaseRelPerm_dPhaseVolFrac[er][esr][ei][0][ip][jp];
+ dRelPerm[CP_Deriv::dP] += dResRelPerm_dS * m_dResPhaseVolFrac[er][esr][ei][jp][Deriv::dP];
+ dRelPerm[CP_Deriv::dT] += dResRelPerm_dS * m_dResPhaseVolFrac[er][esr][ei][jp][Deriv::dT];
+ for( integer jc = 0; jc < NC; ++jc )
+ {
+ dRelPerm[CP_Deriv::dC+jc] += dResRelPerm_dS * m_dResPhaseVolFrac[er][esr][ei][jp][Deriv::dC+jc];
+ }
+ }
- m_energyPerfFlux[iperf] += flux * res_enthalpy;
+ // compute the reservoir phase mobility, including phase density and enthalpy
+ real64 const resPhaseMob = resDens * resRelPerm / resVisc;
+ real64 const resPhaseMobE = resEnthalpy * resPhaseMob;
- // energy equation derivatives WRT res P & T
- m_dEnergyPerfFlux[iperf][TAG::RES][CP_Deriv::dP] += dFlux[TAG::RES][CP_Deriv::dP] * res_enthalpy +
- flux * m_dResPhaseEnthalpy[er][esr][ei][0][ip][Deriv::dP];
- m_dEnergyPerfFlux[iperf][TAG::RES][CP_Deriv::dT] += dFlux[TAG::RES][CP_Deriv::dT] * res_enthalpy +
- flux * m_dResPhaseEnthalpy[er][esr][ei][0][ip][Deriv::dT];
- // energy equation derivatives WRT well P
- m_dEnergyPerfFlux[iperf][TAG::WELL][CP_Deriv::dP] += dFlux[TAG::WELL][CP_Deriv::dP] * res_enthalpy;
- m_dEnergyPerfFlux[iperf][TAG::WELL][CP_Deriv::dT] += dFlux[TAG::WELL][CP_Deriv::dT] * res_enthalpy;
+ // Handles all dependencies
+ for( integer jc = 0; jc < CP_Deriv::nDer; ++jc )
+ {
+ dMob[jc] = resPhaseMob*dResEnthalpy[jc] +
+ resPhaseMobE*(dRelPerm[jc]/(resRelPerm+0.000000001) + dDens[jc] / (resDens +0.000000001) - dVisc[jc]/(resVisc+0.000000001));
+ }
+ // compute the phase flux and derivatives using upstream cell mobility
+ eflux = resPhaseMobE * potDiff;
+ real64 dEFlux[2][CP_Deriv::nDer]{};
+ // Handles all dependencies
+ for( integer jc = 0; jc < CP_Deriv::nDer; ++jc )
+ {
+ dEFlux[TAG::RES][jc] = dMob[jc] * potDiff + resPhaseMobE * dPotDiff[TAG::RES][jc];
+ m_dEnergyPerfFlux[iperf][TAG::WELL][jc] = resPhaseMobE * dPotDiff[TAG::WELL][jc];
+ }
+ m_energyPerfFlux[iperf] += resPhaseVolFrac * eflux;
+ // energy equation derivatives WRT res P & T
+ m_dEnergyPerfFlux[iperf][TAG::RES][CP_Deriv::dP] += dEFlux[TAG::RES][CP_Deriv::dP] * resPhaseVolFrac +
+ eflux * m_dResPhaseFraction[er][esr][ei][0][ip][Deriv::dP];
+ m_dEnergyPerfFlux[iperf][TAG::RES][CP_Deriv::dT] += dEFlux[TAG::RES][CP_Deriv::dT] * resPhaseVolFrac +
+ eflux *m_dResPhaseFraction[er][esr][ei][0][ip][Deriv::dT];
+ // energy equation derivatives WRT well P - //tjb
+ m_dEnergyPerfFlux[iperf][TAG::WELL][CP_Deriv::dP] += dEFlux[TAG::WELL][CP_Deriv::dP];
+ m_dEnergyPerfFlux[iperf][TAG::WELL][CP_Deriv::dT] += dEFlux[TAG::WELL][CP_Deriv::dT];
+ real64 dProp_dC[numComp]{};
+ applyChainRule( NC,
+ m_dResCompFrac_dCompDens[er][esr][ei],
+ m_dResPhaseFraction[er][esr][ei][0][ip],
+ dProp_dC,
+ Deriv::dC );
+ for( integer jc = 0; jc < NC; ++jc )
+ {
+ real64 const resPhaseCompFrac = m_resPhaseCompFrac[er][esr][ei][0][ip][jc];
+ m_dEnergyPerfFlux[iperf][TAG::RES][CP_Deriv::dC+jc] += eflux * dProp_dC[jc] +
+ resPhaseCompFrac * dFlux[TAG::RES][CP_Deriv::dC+jc];
+ }
- // energy equation derivatives WRT reservoir dens
- real64 dProp_dC[numComp]{};
- applyChainRule( NC,
- m_dResCompFrac_dCompDens[er][esr][ei],
- m_dResPhaseEnthalpy[er][esr][ei][0][ip],
- dProp_dC,
- Deriv::dC );
+ } // end resevoir is upstream phase loop
- for( integer jc = 0; jc < NC; ++jc )
- {
- m_dEnergyPerfFlux[iperf][TAG::RES][CP_Deriv::dC+jc] += flux * dProp_dC[jc];
- }
}
else // ** reservoir cell is downstream
{
@@ -814,6 +909,8 @@ class PerforationFluxKernel : public isothermalPerforationFluxKernels::Perforati
ElementViewConst< arrayView1d< real64 const > > const m_temp;
/// Views on phase enthalpies
+ ElementViewConst< arrayView3d< real64 const, multifluid::USD_PHASE > > const m_resPhaseFraction;
+ ElementViewConst< arrayView4d< real64 const, multifluid::USD_PHASE_DC > > const m_dResPhaseFraction;
ElementViewConst< arrayView3d< real64 const, multifluid::USD_PHASE > > const m_resPhaseEnthalpy;
ElementViewConst< arrayView4d< real64 const, multifluid::USD_PHASE_DC > > const m_dResPhaseEnthalpy;
@@ -846,9 +943,9 @@ class PerforationFluxKernelFactory
integer const numPhases,
string const flowSolverName,
PerforationData * const perforationData,
- ElementSubRegionBase const & subRegion,
+ ElementSubRegionBase & subRegion,
MultiFluidBase const & fluid,
- ElementRegionManager & elemManager,
+ ElementRegionManager const & elemManager,
bool const isInjector,
bool const isCrossflowEnabled )
{
diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/SinglePhaseWellConstraintKernels.hpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/SinglePhaseWellConstraintKernels.hpp
new file mode 100644
index 00000000000..d779d44d791
--- /dev/null
+++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/SinglePhaseWellConstraintKernels.hpp
@@ -0,0 +1,195 @@
+/*
+ * ------------------------------------------------------------------------------------------------------------
+ * 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 SinglePhaseWellConstraintKernels.hpp
+ */
+
+#ifndef GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_SINGLEPHASEWELLCONSTRAINTKERNELS_HPP
+#define GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_SINGLEPHASEWELLCONSTRAINTKERNELS_HPP
+
+#include "codingUtilities/Utilities.hpp"
+#include "constitutive/fluid/singlefluid/SingleFluidBase.hpp"
+#include "constitutive/fluid/singlefluid/SingleFluidFields.hpp"
+
+
+#include "physicsSolvers/fluidFlow/wells/WellControls.hpp"
+#include "physicsSolvers/fluidFlow/wells/WellBHPConstraints.hpp"
+#include "physicsSolvers/fluidFlow/wells/WellVolumeRateConstraint.hpp"
+
+
+namespace geos
+{
+
+namespace singlePhaseWellConstraintKernels
+{
+
+/******************************** ControlEquationHelper ********************************/
+
+
+template< integer IS_THERMAL >
+struct ConstraintHelper
+{
+ static void assembleConstraintEquation( real64 const & time_n,
+ WellControls & wellControls,
+ BHPConstraint & constraint,
+ WellElementSubRegion const & subRegion,
+ string const & wellDofKey,
+ localIndex const & rankOffset,
+ CRSMatrixView< real64, globalIndex const > const & localMatrix,
+ arrayView1d< real64 > const & localRhs )
+ {
+ // subRegion data
+ localIndex const iwelemRef = subRegion.getTopWellElementIndex();
+ arrayView1d< globalIndex const > const & wellElemDofNumber = subRegion.getReference< array1d< globalIndex > >( wellDofKey );
+ arrayView1d< real64 const > const & pres = subRegion.getField< fields::well::pressure >();
+
+ constitutive::SingleFluidBase & fluidSeparator = wellControls.getSingleFluidSeparator();
+ arrayView2d< real64 const, constitutive::singlefluid::USD_FLUID > const & density = fluidSeparator.density();
+ arrayView3d< real64 const, constitutive::singlefluid::USD_FLUID_DER > const & dDensity = fluidSeparator.dDensity();
+
+ arrayView1d< real64 const > const wellElemGravCoef = subRegion.getField< fields::well::gravityCoefficient >();
+
+ // setup row/column indices for constraint equation
+ using ROFFSET_WJ = singlePhaseWellKernels::RowOffset_WellJac< IS_THERMAL >;
+ using COFFSET_WJ = singlePhaseWellKernels::ColOffset_WellJac< IS_THERMAL >;
+ using Deriv = constitutive::singlefluid::DerivativeOffsetC< IS_THERMAL >;
+
+ localIndex const eqnRowIndex = wellElemDofNumber[iwelemRef] + ROFFSET_WJ::CONTROL - rankOffset;
+ globalIndex dofColIndices[COFFSET_WJ::nDer]{};
+ for( integer i = 0; i < COFFSET_WJ::nDer; ++i )
+ {
+ dofColIndices[ i ] = wellElemDofNumber[iwelemRef] + i;
+ }
+ // constraint data
+ real64 const & targetBHP = constraint.getConstraintValue( time_n );
+ real64 const & refGravCoef = constraint.getReferenceGravityCoef();
+
+ // current constraint value
+ real64 const & currentBHP =
+ wellControls.getReference< real64 >( SinglePhaseWell::viewKeyStruct::currentBHPString() );
+
+ // residual
+ real64 controlEqn = currentBHP - targetBHP;
+
+ // setup Jacobian terms
+ real64 dControlEqn[2+IS_THERMAL]{};
+
+ // bring everything back to host, capture the scalars by reference
+ forAll< serialPolicy >( 1, [pres,
+ density,
+ dDensity,
+ wellElemGravCoef,
+ &dControlEqn,
+ &iwelemRef,
+ &refGravCoef] ( localIndex const )
+ {
+ real64 const diffGravCoef = refGravCoef - wellElemGravCoef[iwelemRef];
+ dControlEqn[COFFSET_WJ::dP] = 1.0 + dDensity[iwelemRef][0][Deriv::dP] *diffGravCoef;
+ if constexpr ( IS_THERMAL )
+ {
+ dControlEqn[COFFSET_WJ::dT] = dDensity[iwelemRef][0][Deriv::dT] * diffGravCoef;
+ }
+ } );
+
+ // add solver matrices
+ localRhs[eqnRowIndex] += controlEqn;
+ localMatrix.addToRowBinarySearchUnsorted< serialAtomic >( eqnRowIndex,
+ dofColIndices,
+ dControlEqn,
+ COFFSET_WJ::nDer );
+ }
+ template< template< typename U > class T, typename U=VolumeRateConstraint >
+ static void assembleConstraintEquation( real64 const & time_n,
+ WellControls & wellControls,
+ T< VolumeRateConstraint > & constraint,
+ WellElementSubRegion const & subRegion,
+ string const & wellDofKey,
+ localIndex const & rankOffset,
+ CRSMatrixView< real64, globalIndex const > const & localMatrix,
+ arrayView1d< real64 > const & localRhs )
+ {
+ // subRegion data
+
+ localIndex const iwelemRef = subRegion.getTopWellElementIndex();
+ arrayView1d< globalIndex const > const & wellElemDofNumber = subRegion.getReference< array1d< globalIndex > >( wellDofKey );
+
+
+ // setup row/column indices for constraint equation
+ using ROFFSET_WJ = singlePhaseWellKernels::RowOffset_WellJac< IS_THERMAL >;
+ using COFFSET_WJ = singlePhaseWellKernels::ColOffset_WellJac< IS_THERMAL >;
+ using Deriv = constitutive::singlefluid::DerivativeOffsetC< IS_THERMAL >;
+
+ localIndex const eqnRowIndex = wellElemDofNumber[iwelemRef] + ROFFSET_WJ::CONTROL - rankOffset;
+ globalIndex dofColIndices[COFFSET_WJ::nDer]{};
+ for( integer i = 0; i < COFFSET_WJ::nDer; ++i )
+ {
+ dofColIndices[ i ] = wellElemDofNumber[iwelemRef] + i;
+ }
+
+ // fluid data
+ constitutive::SingleFluidBase & fluidSeparator = wellControls.getSingleFluidSeparator();
+ arrayView2d< real64 const, constitutive::singlefluid::USD_FLUID > const & density = fluidSeparator.density();
+ arrayView3d< real64 const, constitutive::singlefluid::USD_FLUID_DER > const & dDensity = fluidSeparator.dDensity();
+
+ // constraint data
+ real64 const & targetVolRate = constraint.getConstraintValue( time_n );
+
+ // current constraint value
+ real64 & currentVolRate =
+ wellControls.getReference< real64 >( WellControls::viewKeyStruct::currentVolRateString() );
+
+ integer const useSurfaceConditions = wellControls.useSurfaceConditions();
+
+ // residual
+ real64 controlEqn = currentVolRate - targetVolRate;
+
+ // setup Jacobian terms
+ real64 dControlEqn[2+IS_THERMAL]{};
+
+ // bring everything back to host, capture the scalars by reference
+ forAll< serialPolicy >( 1, [currentVolRate,
+ density,
+ dDensity,
+ &dControlEqn,
+ &useSurfaceConditions,
+ &iwelemRef] ( localIndex const )
+ {
+ // compute the inverse of the total density and derivatives
+ real64 const densInv = 1.0 / density[iwelemRef][0];
+
+ dControlEqn[COFFSET_WJ::dP] = -( useSurfaceConditions == 0 ) * dDensity[iwelemRef][0][Deriv::dP] * currentVolRate * densInv;
+ dControlEqn[COFFSET_WJ::dQ] = densInv;
+ if constexpr ( IS_THERMAL )
+ {
+ dControlEqn[COFFSET_WJ::dT] = -( useSurfaceConditions == 0 ) * dDensity[iwelemRef][0][Deriv::dT] * currentVolRate * densInv;
+ }
+
+ } );
+
+ // add solver matrices
+ localRhs[eqnRowIndex] += controlEqn;
+ localMatrix.addToRowBinarySearchUnsorted< serialAtomic >( eqnRowIndex,
+ dofColIndices,
+ dControlEqn,
+ COFFSET_WJ::nDer );
+ }
+};
+
+} // end namespace wellConstraintKernels
+
+} // end namespace geos
+
+#endif //GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_WELLCONSTRAINTKERNELS_HPP
diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/SinglePhaseWellKernels.cpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/SinglePhaseWellKernels.cpp
index ddd707cf9b1..35bb78a54c4 100644
--- a/src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/SinglePhaseWellKernels.cpp
+++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/SinglePhaseWellKernels.cpp
@@ -23,6 +23,7 @@
#include "physicsSolvers/fluidFlow/wells/SinglePhaseWell.hpp"
#include "physicsSolvers/fluidFlow/wells/SinglePhaseWellFields.hpp"
#include "constitutive/fluid/singlefluid/SingleFluidLayouts.hpp"
+
namespace geos
{
@@ -262,14 +263,10 @@ FluxKernel::
#define INST_PressureRelationKernel( IS_THERMAL ) \
template \
- localIndex \
+ void \
PressureRelationKernel:: \
launch< IS_THERMAL >( localIndex const size, \
globalIndex const rankOffset, \
- bool const isLocallyOwned, \
- localIndex const iwelemControl, \
- WellControls const & wellControls, \
- real64 const & timeAtEndOfStep, \
arrayView1d< globalIndex const > const & wellElemDofNumber, \
arrayView1d< real64 const > const & wellElemGravCoef, \
arrayView1d< localIndex const > const & nextWellElemIndex, \
@@ -283,14 +280,10 @@ INST_PressureRelationKernel( 0 );
INST_PressureRelationKernel( 1 );
template< integer IS_THERMAL >
-localIndex
+void
PressureRelationKernel::
launch( localIndex const size,
globalIndex const rankOffset,
- bool const isLocallyOwned,
- localIndex const iwelemControl,
- WellControls const & wellControls,
- real64 const & time,
arrayView1d< globalIndex const > const & wellElemDofNumber,
arrayView1d< real64 const > const & wellElemGravCoef,
arrayView1d< localIndex const > const & nextWellElemIndex,
@@ -303,23 +296,6 @@ PressureRelationKernel::
using Deriv = constitutive::singlefluid::DerivativeOffset;
using COFFSET_WJ = singlePhaseWellKernels::ColOffset_WellJac< IS_THERMAL >;
// static well control data
- bool const isProducer = wellControls.isProducer();
- WellControls::Control const currentControl = wellControls.getControl();
- real64 const targetBHP = wellControls.getTargetBHP( time );
- real64 const targetRate = wellControls.getTargetTotalRate( time );
-
- // dynamic well control data
- real64 const & currentBHP =
- wellControls.getReference< real64 >( SinglePhaseWell::viewKeyStruct::currentBHPString() );
- arrayView1d< real64 const > const & dCurrentBHP =
- wellControls.getReference< array1d< real64 > >( SinglePhaseWell::viewKeyStruct::dCurrentBHPString() );
-
- real64 const & currentVolRate =
- wellControls.getReference< real64 >( SinglePhaseWell::viewKeyStruct::currentVolRateString() );
- arrayView1d< real64 const > const & dCurrentVolRate =
- wellControls.getReference< array1d< real64 > >( SinglePhaseWell::viewKeyStruct::dCurrentVolRateString() );
-
- RAJA::ReduceMax< parallelDeviceReduce, localIndex > switchControl( 0 );
// loop over the well elements to compute the pressure relations between well elements
forAll< parallelDevicePolicy<> >( size, [=] GEOS_HOST_DEVICE ( localIndex const iwelem )
@@ -327,34 +303,7 @@ PressureRelationKernel::
localIndex const iwelemNext = nextWellElemIndex[iwelem];
- if( iwelemNext < 0 && isLocallyOwned ) // if iwelemNext < 0, form control equation
- {
- WellControls::Control newControl = currentControl;
- ControlEquationHelper::switchControl( isProducer,
- currentControl,
- targetBHP,
- targetRate,
- currentBHP,
- currentVolRate,
- newControl );
- if( currentControl != newControl )
- {
- switchControl.max( 1 );
- }
-
- ControlEquationHelper::compute< IS_THERMAL >( rankOffset,
- newControl,
- targetBHP,
- targetRate,
- currentBHP,
- dCurrentBHP,
- currentVolRate,
- dCurrentVolRate,
- wellElemDofNumber[iwelemControl],
- localMatrix,
- localRhs );
- }
- else if( iwelemNext >= 0 ) // if iwelemNext >= 0, form momentum equation
+ if( iwelemNext >= 0 ) // if iwelemNext >= 0, form momentum equation
{
// local working variables and arrays
@@ -406,7 +355,6 @@ PressureRelationKernel::
}
}
} );
- return switchControl.get();
}
/******************************** AccumulationKernel ********************************/
@@ -599,10 +547,22 @@ RateInitializationKernel::
arrayView2d< real64 const, constitutive::singlefluid::USD_FLUID > const & wellElemDens,
arrayView1d< real64 > const & connRate )
{
- real64 const targetRate = wellControls.getTargetTotalRate( currentTime );
+
WellControls::Control const control = wellControls.getControl();
bool const isProducer = wellControls.isProducer();
-
+ real64 constraintVal;
+ if( isProducer )
+ {
+ std::vector< WellConstraintBase * > const constraints = wellControls.getProdRateConstraints();
+ // Use first rate constraint to set initial connection rates
+ constraintVal = constraints[0]->getConstraintValue( currentTime );
+ }
+ else
+ {
+ std::vector< WellConstraintBase * > const constraints = wellControls.getInjRateConstraints();
+ // Use first rate constraint to set initial connection rates
+ constraintVal = constraints[0]->getConstraintValue( currentTime );;
+ }
// Estimate the connection rates
forAll< parallelDevicePolicy<> >( subRegionSize, [=] GEOS_HOST_DEVICE ( localIndex const iwelem )
{
@@ -612,16 +572,16 @@ RateInitializationKernel::
// with the appropriate sign (negative for prod, positive for inj)
if( isProducer )
{
- connRate[iwelem] = LvArray::math::max( 0.1 * targetRate * wellElemDens[iwelem][0], -1e3 );
+ connRate[iwelem] = LvArray::math::max( 0.1 * constraintVal * wellElemDens[iwelem][0], -1e3 );
}
else
{
- connRate[iwelem] = LvArray::math::min( 0.1 * targetRate * wellElemDens[iwelem][0], 1e3 );
+ connRate[iwelem] = LvArray::math::min( 0.1 * constraintVal * wellElemDens[iwelem][0], 1e3 );
}
}
else
{
- connRate[iwelem] = targetRate * wellElemDens[iwelem][0];
+ connRate[iwelem] = constraintVal * wellElemDens[iwelem][0];
}
} );
}
diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/SinglePhaseWellKernels.hpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/SinglePhaseWellKernels.hpp
index 5221162c73c..8365d25c946 100644
--- a/src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/SinglePhaseWellKernels.hpp
+++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/SinglePhaseWellKernels.hpp
@@ -20,18 +20,22 @@
#ifndef GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_SINGLEPHASEWELLKERNELS_HPP
#define GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_SINGLEPHASEWELLKERNELS_HPP
+#include "common/DataTypes.hpp"
+#include "common/GEOS_RAJA_Interface.hpp"
+#include "physicsSolvers/PhysicsSolverBaseKernels.hpp"
+
#include "constitutive/fluid/singlefluid/SingleFluidFields.hpp"
#include "constitutive/fluid/singlefluid/SingleFluidBase.hpp"
#include "constitutive/fluid/singlefluid/SingleFluidLayouts.hpp"
#include "constitutive/fluid/singlefluid/SingleFluidLayouts.hpp"
-#include "common/DataTypes.hpp"
-#include "common/GEOS_RAJA_Interface.hpp"
+
#include "mesh/ElementRegionManager.hpp"
#include "physicsSolvers/fluidFlow/FlowSolverBaseFields.hpp"
#include "physicsSolvers/fluidFlow/StencilAccessors.hpp"
#include "physicsSolvers/fluidFlow/wells/WellControls.hpp"
+#include "physicsSolvers/fluidFlow/wells/WellFields.hpp"
#include "physicsSolvers/fluidFlow/wells/SinglePhaseWellFields.hpp"
-#include "physicsSolvers/PhysicsSolverBaseKernels.hpp"
+
#include "physicsSolvers/KernelLaunchSelectors.hpp"
namespace geos
@@ -185,13 +189,9 @@ struct PressureRelationKernel
using TAG = singlePhaseWellKernels::ElemTag;
template< integer IS_THERMAL >
- static localIndex
+ static void
launch( localIndex const size,
globalIndex const rankOffset,
- bool const isLocallyOwned,
- localIndex const iwelemControl,
- WellControls const & wellControls,
- real64 const & time,
arrayView1d< globalIndex const > const & wellElemDofNumber,
arrayView1d< real64 const > const & wellElemGravCoef,
arrayView1d< localIndex const > const & nextWellElemIndex,
@@ -811,12 +811,19 @@ class ResidualNormKernel : public physicsSolverBaseKernels::ResidualNormKernelBa
m_isLocallyOwned( subRegion.isLocallyOwned() ),
m_iwelemControl( subRegion.getTopWellElementIndex() ),
m_currentControl( wellControls.getControl() ),
- m_targetBHP( wellControls.getTargetBHP( time ) ),
- m_targetRate( wellControls.getTargetTotalRate( time ) ),
- m_targetMassRate( wellControls.getTargetMassRate( time ) ),
+ m_constraintValue ( wellControls.getCurrentConstraint()->getConstraintValue( time )),
m_volume( subRegion.getElementVolume() ),
m_density_n( fluid.density_n() )
- {}
+ {
+ if( wellControls.isProducer() )
+ {
+ m_targetBHP = wellControls.getMinBHPConstraint()->getConstraintValue( time );
+ }
+ else
+ {
+ m_targetBHP = wellControls.getMaxBHPConstraint()->getConstraintValue( time );
+ }
+ }
GEOS_HOST_DEVICE
virtual void computeLinf( localIndex const iwelem,
@@ -838,12 +845,12 @@ class ResidualNormKernel : public physicsSolverBaseKernels::ResidualNormKernelBa
else if( m_currentControl == WellControls::Control::TOTALVOLRATE )
{
// this residual entry is in volume / time units
- normalizer = LvArray::math::max( LvArray::math::abs( m_targetRate ), m_minNormalizer );
+ normalizer = LvArray::math::max( LvArray::math::abs( m_constraintValue ), m_minNormalizer );
}
else if( m_currentControl == WellControls::Control::MASSRATE )
{
// the residual entry is in volume / time units
- normalizer = LvArray::math::max( LvArray::math::abs( m_targetMassRate ), m_minNormalizer );
+ normalizer = LvArray::math::max( LvArray::math::abs( m_constraintValue ), m_minNormalizer );
}
}
// for the pressure difference equation, always normalize by the BHP
@@ -856,7 +863,7 @@ class ResidualNormKernel : public physicsSolverBaseKernels::ResidualNormKernelBa
else // SinglePhaseWell::RowOffset::MASSBAL
{
// this residual entry is in mass units
- normalizer = m_dt * LvArray::math::abs( m_targetRate ) * m_density_n[iwelem][0];
+ normalizer = m_dt * LvArray::math::abs( m_constraintValue ) * m_density_n[iwelem][0];
// to make sure that everything still works well if the rate is zero, we add this check
normalizer = LvArray::math::max( normalizer, m_volume[iwelem] * m_density_n[iwelem][0] );
@@ -894,9 +901,9 @@ class ResidualNormKernel : public physicsSolverBaseKernels::ResidualNormKernelBa
/// Controls
WellControls::Control const m_currentControl;
- real64 const m_targetBHP;
- real64 const m_targetRate;
- real64 const m_targetMassRate;
+ real64 const m_constraintValue;
+ real64 m_targetBHP;
+
/// View on the volume
arrayView1d< real64 const > const m_volume;
diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/ThermalCompositionalMultiphaseWellKernels.hpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/ThermalCompositionalMultiphaseWellKernels.hpp
index 54b7819e8bf..a8b5b91032f 100644
--- a/src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/ThermalCompositionalMultiphaseWellKernels.hpp
+++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/ThermalCompositionalMultiphaseWellKernels.hpp
@@ -22,6 +22,8 @@
#include "physicsSolvers/fluidFlow/wells/kernels/CompositionalMultiphaseWellKernels.hpp"
#include "physicsSolvers/PhysicsSolverBaseKernels.hpp"
+#include "physicsSolvers/fluidFlow/wells/WellConstraintsBase.hpp"
+#include "physicsSolvers/fluidFlow/wells/WellPhaseVolumeRateConstraint.hpp"
namespace geos
{
@@ -80,7 +82,7 @@ class TotalMassDensityKernel : public compositionalMultiphaseWellKernels::TotalM
arraySlice2d< real64 const, multifluid::USD_PHASE_DC - 2 > dPhaseMassDens = m_dPhaseMassDens[ei][0];
real64 & dTotalMassDens_dT = m_dTotalMassDens[ei][Deriv::dT];
- dTotalMassDens_dT=0.0;
+
// Call the base compute the compute the total mass density and derivatives
return Base::compute( ei, [&]( localIndex const ip )
{
@@ -161,7 +163,7 @@ class ResidualNormKernel : public physicsSolverBaseKernels::ResidualNormKernelBa
arrayView1d< real64 const > const & localResidual,
arrayView1d< globalIndex const > const & dofNumber,
arrayView1d< localIndex const > const & ghostRank,
- integer const targetPhaseIndex,
+
WellElementSubRegion const & subRegion,
MultiFluidBase const & fluid,
WellControls const & wellControls,
@@ -174,22 +176,39 @@ class ResidualNormKernel : public physicsSolverBaseKernels::ResidualNormKernelBa
ghostRank,
minNormalizer ),
m_numPhases( fluid.numFluidPhases()),
- m_targetPhaseIndex( targetPhaseIndex ),
m_dt( dt ),
m_isLocallyOwned( subRegion.isLocallyOwned() ),
m_iwelemControl( subRegion.getTopWellElementIndex() ),
m_isProducer( wellControls.isProducer() ),
m_currentControl( wellControls.getControl() ),
- m_targetBHP( wellControls.getTargetBHP( time ) ),
- m_targetTotalRate( wellControls.getTargetTotalRate( time ) ),
- m_targetPhaseRate( wellControls.getTargetPhaseRate( time ) ),
- m_targetMassRate( wellControls.getTargetMassRate( time ) ),
m_volume( subRegion.getElementVolume() ),
m_phaseDens_n( fluid.phaseDensity_n() ),
m_totalDens_n( fluid.totalDensity_n() ),
m_phaseVolFraction_n( subRegion.getField< fields::well::phaseVolumeFraction_n >()),
m_phaseInternalEnergy_n( fluid.phaseInternalEnergy_n() )
- {}
+ {
+ if( m_isProducer )
+ {
+ // tjb This needs to be fixed should use current constraint rate for normalization
+ if( wellControls.getMinBHPConstraint()->isConstraintActive() )
+ {
+ m_targetBHP = wellControls.getMinBHPConstraint()->getConstraintValue( time );
+ }
+ if( m_currentControl == WellControls::Control::PHASEVOLRATE )
+ {
+ m_targetPhaseIndex = wellControls.getConstraintPhaseIndex();
+ m_constraintValue = wellControls.getProdRateConstraints()[0]->getConstraintValue( time );
+ }
+ }
+ else
+ {
+ m_targetBHP = wellControls.getMaxBHPConstraint()->getConstraintValue( time );
+ m_targetPhaseIndex = -1;
+ m_constraintValue = wellControls.getInjRateConstraints()[0]->getConstraintValue( time );
+
+ }
+
+ }
GEOS_HOST_DEVICE
@@ -232,17 +251,17 @@ class ResidualNormKernel : public physicsSolverBaseKernels::ResidualNormKernelBa
else if( m_currentControl == WellControls::Control::TOTALVOLRATE )
{
// the residual entry is in volume / time units
- normalizer = LvArray::math::max( LvArray::math::abs( m_targetTotalRate ), m_minNormalizer );
+ normalizer = LvArray::math::max( LvArray::math::abs( m_constraintValue ), m_minNormalizer );
}
else if( m_currentControl == WellControls::Control::PHASEVOLRATE )
{
// the residual entry is in volume / time units
- normalizer = LvArray::math::max( LvArray::math::abs( m_targetPhaseRate ), m_minNormalizer );
+ normalizer = LvArray::math::max( LvArray::math::abs( m_constraintValue ), m_minNormalizer );
}
else if( m_currentControl == WellControls::Control::MASSRATE )
{
// the residual entry is in volume / time units
- normalizer = LvArray::math::max( LvArray::math::abs( m_targetMassRate ), m_minNormalizer );
+ normalizer = LvArray::math::max( LvArray::math::abs( m_constraintValue ), m_minNormalizer );
}
}
// for the pressure difference equation, always normalize by the BHP
@@ -257,18 +276,18 @@ class ResidualNormKernel : public physicsSolverBaseKernels::ResidualNormKernelBa
if( m_isProducer ) // only PHASEVOLRATE is supported for now
{
// the residual is in mass units
- normalizer = m_dt * LvArray::math::abs( m_targetPhaseRate ) * m_phaseDens_n[iwelem][0][m_targetPhaseIndex];
+ normalizer = m_dt * LvArray::math::abs( m_constraintValue ) * m_phaseDens_n[iwelem][0][m_targetPhaseIndex];
}
else // Type::INJECTOR, only TOTALVOLRATE is supported for now
{
if( m_currentControl == WellControls::Control::MASSRATE )
{
- normalizer = m_dt * LvArray::math::abs( m_targetMassRate );
+ normalizer = m_dt * LvArray::math::abs( m_constraintValue );
}
else
{
// the residual is in mass units
- normalizer = m_dt * LvArray::math::abs( m_targetTotalRate ) * m_totalDens_n[iwelem][0];
+ normalizer = m_dt * LvArray::math::abs( m_constraintValue ) * m_totalDens_n[iwelem][0];
}
}
@@ -282,17 +301,17 @@ class ResidualNormKernel : public physicsSolverBaseKernels::ResidualNormKernelBa
if( m_isProducer ) // only PHASEVOLRATE is supported for now
{
// the residual is in volume units
- normalizer = m_dt * LvArray::math::abs( m_targetPhaseRate );
+ normalizer = m_dt * LvArray::math::abs( m_constraintValue );
}
else // Type::INJECTOR, only TOTALVOLRATE is supported for now
{
if( m_currentControl == WellControls::Control::MASSRATE )
{
- normalizer = m_dt * LvArray::math::abs( m_targetMassRate/ m_totalDens_n[iwelem][0] );
+ normalizer = m_dt * LvArray::math::abs( m_constraintValue/ m_totalDens_n[iwelem][0] );
}
else
{
- normalizer = m_dt * LvArray::math::abs( m_targetTotalRate );
+ normalizer = m_dt * LvArray::math::abs( m_constraintValue );
}
}
@@ -339,7 +358,7 @@ class ResidualNormKernel : public physicsSolverBaseKernels::ResidualNormKernelBa
integer const m_numPhases;
/// Index of the target phase
- integer const m_targetPhaseIndex;
+ integer m_targetPhaseIndex;
/// Time step size
real64 const m_dt;
@@ -355,10 +374,9 @@ class ResidualNormKernel : public physicsSolverBaseKernels::ResidualNormKernelBa
/// Controls
WellControls::Control const m_currentControl;
- real64 const m_targetBHP;
- real64 const m_targetTotalRate;
- real64 const m_targetPhaseRate;
- real64 const m_targetMassRate;
+ real64 m_constraintValue;
+ real64 m_targetBHP;
+
/// View on the volume
arrayView1d< real64 const > const m_volume;
@@ -383,7 +401,6 @@ class ResidualNormKernelFactory
* @tparam POLICY the policy used in the RAJA kernel
* @param[in] numComp number of fluid components
* @param[in] numDof number of dofs per well element
- * @param[in] targetPhaseIndex the index of the target phase (for phase volume control)
* @param[in] rankOffset the offset of my MPI rank
* @param[in] dofKey the string key to retrieve the degress of freedom numbers
* @param[in] localResidual the residual vector on my MPI rank
@@ -397,7 +414,6 @@ class ResidualNormKernelFactory
template< typename POLICY >
static void
createAndLaunch( integer const numComp,
- integer const targetPhaseIndex,
globalIndex const rankOffset,
string const & dofKey,
arrayView1d< real64 const > const & localResidual,
@@ -418,7 +434,7 @@ class ResidualNormKernelFactory
arrayView1d< integer const > const ghostRank = subRegion.ghostRank();
kernelType kernel( rankOffset, localResidual, dofNumber, ghostRank,
- targetPhaseIndex, subRegion, fluid, wellControls, time, dt, minNormalizer );
+ subRegion, fluid, wellControls, time, dt, minNormalizer );
kernelType::template launchLinf< POLICY >( subRegion.size(), kernel, residualNorm );
} );
}
@@ -476,6 +492,7 @@ class ElementBasedAssemblyKernel : public compositionalMultiphaseWellKernels::El
* @param[inout] localRhs the local right-hand side vector
*/
ElementBasedAssemblyKernel( localIndex const numPhases,
+ bool const thermalEffectsEnabled,
integer const isProducer,
globalIndex const rankOffset,
string const dofKey,
@@ -484,7 +501,7 @@ class ElementBasedAssemblyKernel : public compositionalMultiphaseWellKernels::El
CRSMatrixView< real64, globalIndex const > const & localMatrix,
arrayView1d< real64 > const & localRhs,
BitFlags< isothermalCompositionalMultiphaseBaseKernels::KernelFlags > const kernelFlags )
- : Base( numPhases, isProducer, rankOffset, dofKey, subRegion, fluid, localMatrix, localRhs, kernelFlags ),
+ : Base( numPhases, thermalEffectsEnabled, isProducer, rankOffset, dofKey, subRegion, fluid, localMatrix, localRhs, kernelFlags ),
m_phaseInternalEnergy_n( fluid.phaseInternalEnergy_n()),
m_phaseInternalEnergy( fluid.phaseInternalEnergy()),
m_dPhaseInternalEnergy( fluid.dPhaseInternalEnergy())
@@ -636,6 +653,7 @@ class ElementBasedAssemblyKernelFactory
static void
createAndLaunch( localIndex const numComps,
localIndex const numPhases,
+ integer const thermalEffectsEnabled,
integer const isProducer,
globalIndex const rankOffset,
BitFlags< isothermalCompositionalMultiphaseBaseKernels::KernelFlags > kernelFlags,
@@ -645,13 +663,12 @@ class ElementBasedAssemblyKernelFactory
CRSMatrixView< real64, globalIndex const > const & localMatrix,
arrayView1d< real64 > const & localRhs )
{
- isothermalCompositionalMultiphaseBaseKernels::
- internal::kernelLaunchSelectorCompSwitch( numComps, [&]( auto NC )
+ isothermalCompositionalMultiphaseBaseKernels::internal::kernelLaunchSelectorCompSwitch( numComps, [&]( auto NC )
{
localIndex constexpr NUM_COMP = NC();
ElementBasedAssemblyKernel< NUM_COMP >
- kernel( numPhases, isProducer, rankOffset, dofKey, subRegion, fluid, localMatrix, localRhs, kernelFlags );
+ kernel( numPhases, thermalEffectsEnabled, isProducer, rankOffset, dofKey, subRegion, fluid, localMatrix, localRhs, kernelFlags );
ElementBasedAssemblyKernel< NUM_COMP >::template
launch< POLICY, ElementBasedAssemblyKernel< NUM_COMP > >( subRegion.size(), kernel );
} );
@@ -731,6 +748,7 @@ class FaceBasedAssemblyKernel : public compositionalMultiphaseWellKernels::FaceB
, localRhs
, kernelFlags ),
m_numPhases ( fluid.numFluidPhases()),
+ m_thermalEffectsEnabled( wellControls.thermalEffectsEnabled() ),
m_globalWellElementIndex( subRegion.getGlobalWellElementIndex() ),
m_phaseFraction( fluid.phaseFraction()),
m_dPhaseFraction( fluid.dPhaseFraction()),
@@ -777,7 +795,8 @@ class FaceBasedAssemblyKernel : public compositionalMultiphaseWellKernels::FaceB
void complete( localIndex const iwelem, StackVariables & stack ) const
{
Base::complete ( iwelem, stack );
-
+ // tjb iso return;
+ if( !m_thermalEffectsEnabled ) return;
using namespace compositionalMultiphaseUtilities;
if( stack.numConnectedElems ==1 )
{
@@ -939,7 +958,7 @@ class FaceBasedAssemblyKernel : public compositionalMultiphaseWellKernels::FaceB
stack.localEnergyFlux[0] = -m_dt * eflux * currentConnRate;
stack.localEnergyFluxJacobian_dQ[0][0] = -m_dt * eflux_dq;
}
- else if( ( iwelemNext < 0 && m_isProducer ) || currentConnRate < 0 ) // exit connection, producer
+ else if( ( iwelemNext < 0 && m_isProducer ) ) // exit connection, producer
{
real64 eflux=0;
real64 eflux_dq=0;
@@ -966,9 +985,9 @@ class FaceBasedAssemblyKernel : public compositionalMultiphaseWellKernels::FaceB
for( integer dof=0; dof < CP_Deriv::nDer; dof++ )
{
- stack.localEnergyFluxJacobian[0][dof] *= -m_dt*currentConnRate;
+ stack.localEnergyFluxJacobian[0][dof] *= -m_dt*currentConnRate;
}
- stack.localEnergyFlux[0] = -m_dt * eflux * currentConnRate;
+ stack.localEnergyFlux[0] = -m_dt * eflux * currentConnRate;
stack.localEnergyFluxJacobian_dQ[0][0] = -m_dt*eflux_dq;
}
else
@@ -1047,6 +1066,8 @@ class FaceBasedAssemblyKernel : public compositionalMultiphaseWellKernels::FaceB
protected:
/// Number of phases
integer const m_numPhases;
+ /// Flag specifying whether thermal effects are enabled
+ bool const m_thermalEffectsEnabled;
/// Global index of local element
arrayView1d< globalIndex const > m_globalWellElementIndex;
diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/ThermalSinglePhaseWellKernels.hpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/ThermalSinglePhaseWellKernels.hpp
index f736826561c..b19ca90845d 100644
--- a/src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/ThermalSinglePhaseWellKernels.hpp
+++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/ThermalSinglePhaseWellKernels.hpp
@@ -522,14 +522,21 @@ class ResidualNormKernel : public physicsSolverBaseKernels::ResidualNormKernelBa
m_iwelemControl( subRegion.getTopWellElementIndex() ),
m_isProducer( wellControls.isProducer() ),
m_currentControl( wellControls.getControl() ),
- m_targetBHP( wellControls.getTargetBHP( time ) ),
- m_targetRate( wellControls.getTargetTotalRate( time ) ),
- m_targetMassRate( wellControls.getTargetMassRate( time ) ),
+ m_constraintValue ( wellControls.getCurrentConstraint()->getConstraintValue( time )),
m_volume( subRegion.getElementVolume() ),
m_density_n( fluid.density_n() ),
m_internalEnergy_n( fluid.internalEnergy_n() )
- {}
+ {
+ if( wellControls.isProducer() )
+ {
+ m_targetBHP = wellControls.getMinBHPConstraint()->getConstraintValue( time );
+ }
+ else
+ {
+ m_targetBHP = wellControls.getMaxBHPConstraint()->getConstraintValue( time );
+ }
+ }
GEOS_HOST_DEVICE
@@ -568,12 +575,12 @@ class ResidualNormKernel : public physicsSolverBaseKernels::ResidualNormKernelBa
else if( m_currentControl == WellControls::Control::TOTALVOLRATE )
{
// the residual entry is in volume / time units
- normalizer = LvArray::math::max( LvArray::math::abs( m_targetRate ), m_minNormalizer );
+ normalizer = LvArray::math::max( LvArray::math::abs( m_constraintValue ), m_minNormalizer );
}
else if( m_currentControl == WellControls::Control::MASSRATE )
{
// the residual entry is in volume / time units
- normalizer = LvArray::math::max( LvArray::math::abs( m_targetMassRate ), m_minNormalizer );
+ normalizer = LvArray::math::max( LvArray::math::abs( m_constraintValue ), m_minNormalizer );
}
}
// for the pressure difference equation, always normalize by the BHP
@@ -587,7 +594,7 @@ class ResidualNormKernel : public physicsSolverBaseKernels::ResidualNormKernelBa
{
// this residual entry is in mass units
- normalizer = m_dt * LvArray::math::abs( m_targetRate ) * m_density_n[iwelem][0];
+ normalizer = m_dt * LvArray::math::abs( m_constraintValue ) * m_density_n[iwelem][0];
// to make sure that everything still works well if the rate is zero, we add this check
normalizer = LvArray::math::max( normalizer, m_volume[iwelem] * m_density_n[iwelem][0] );
@@ -640,9 +647,8 @@ class ResidualNormKernel : public physicsSolverBaseKernels::ResidualNormKernelBa
/// Controls
WellControls::Control const m_currentControl;
- real64 const m_targetBHP;
- real64 const m_targetRate;
- real64 const m_targetMassRate;
+ real64 const m_constraintValue;
+ real64 m_targetBHP;
/// View on the volume
arrayView1d< real64 const > const m_volume;
diff --git a/src/coreComponents/physicsSolvers/multiphysics/CompositionalMultiphaseReservoirAndWells.cpp b/src/coreComponents/physicsSolvers/multiphysics/CompositionalMultiphaseReservoirAndWells.cpp
index 425bccfa72c..ab99ec2ca8a 100644
--- a/src/coreComponents/physicsSolvers/multiphysics/CompositionalMultiphaseReservoirAndWells.cpp
+++ b/src/coreComponents/physicsSolvers/multiphysics/CompositionalMultiphaseReservoirAndWells.cpp
@@ -147,9 +147,8 @@ initializePreSubGroups()
CompositionalMultiphaseBase const * const flowSolver = this->flowSolver();
Base::wellSolver()->setFlowSolverName( flowSolver->getName() );
-
bool const useMassFlow = flowSolver->getReference< integer >( CompositionalMultiphaseBase::viewKeyStruct::useMassFlagString() );
- bool const useMassWell = Base::wellSolver()->template getReference< integer >( CompositionalMultiphaseWell::viewKeyStruct::useMassFlagString() );
+ bool const useMassWell = Base::wellSolver()->template getReference< integer >( WellManager::viewKeyStruct::useMassFlagString() );
GEOS_THROW_IF( useMassFlow != useMassWell,
GEOS_FMT( "{}: the input flag {} must be the same in the flow and well solvers, respectively '{}' and '{}'",
this->getDataContext(), CompositionalMultiphaseBase::viewKeyStruct::useMassFlagString(),
@@ -157,12 +156,27 @@ initializePreSubGroups()
InputError, this->getDataContext(), Base::reservoirSolver()->getDataContext(), Base::wellSolver()->getDataContext() );
bool const isThermalFlow = flowSolver->getReference< integer >( CompositionalMultiphaseBase::viewKeyStruct::isThermalString() );
- bool const isThermalWell = Base::wellSolver()->template getReference< integer >( CompositionalMultiphaseWell::viewKeyStruct::isThermalString() );
+ bool const isThermalWell = Base::wellSolver()->template getReference< integer >( WellManager::viewKeyStruct::isThermalString() );
GEOS_THROW_IF( isThermalFlow != isThermalWell,
GEOS_FMT( "{}: the input flag {} must be the same in the flow and well solvers, respectively '{}' and '{}'",
this->getDataContext(), CompositionalMultiphaseBase::viewKeyStruct::isThermalString(),
Base::reservoirSolver()->getDataContext(), Base::wellSolver()->getDataContext() ),
InputError, this->getDataContext(), Base::reservoirSolver()->getDataContext(), Base::wellSolver()->getDataContext() );
+ DomainPartition & domain = this->template getGroupByPath< DomainPartition >( "/Problem/domain" );
+
+ this->template forDiscretizationOnMeshTargets<>( domain.getMeshBodies(), [&] ( string const &,
+ MeshLevel & mesh,
+ string_array const & regionNames )
+ {
+ ElementRegionManager & elemManager = mesh.getElemManager();
+ elemManager.forElementSubRegions< WellElementSubRegion >( regionNames, [&]( localIndex const,
+ WellElementSubRegion const & subRegion )
+ {
+ WellControls & wellControls = Base::wellSolver()->getWellControls( subRegion );
+ wellControls.setFlowSolverName( flowSolver->getName() );
+
+ } );
+ } );
}
template< typename RESERVOIR_SOLVER >
@@ -344,6 +358,7 @@ assembleCouplingTerms( real64 const time_n,
coupledReservoirAndWellKernels::
ThermalCompositionalMultiPhaseFluxKernelFactory::
createAndLaunch< parallelDevicePolicy<> >( numComps,
+ wellControls.thermalEffectsEnabled( ),
wellControls.isProducer(),
dt,
rankOffset,
diff --git a/src/coreComponents/physicsSolvers/multiphysics/CompositionalMultiphaseReservoirAndWells.hpp b/src/coreComponents/physicsSolvers/multiphysics/CompositionalMultiphaseReservoirAndWells.hpp
index b2b1f7a4800..b99975f2ba4 100644
--- a/src/coreComponents/physicsSolvers/multiphysics/CompositionalMultiphaseReservoirAndWells.hpp
+++ b/src/coreComponents/physicsSolvers/multiphysics/CompositionalMultiphaseReservoirAndWells.hpp
@@ -23,7 +23,7 @@
#include "physicsSolvers/multiphysics/CoupledReservoirAndWellsBase.hpp"
#include "physicsSolvers/fluidFlow/CompositionalMultiphaseBase.hpp"
-#include "physicsSolvers/fluidFlow/wells/CompositionalMultiphaseWell.hpp"
+#include "physicsSolvers/fluidFlow/wells/WellManager.hpp"
namespace geos
{
@@ -31,12 +31,12 @@ namespace geos
/// @tparam RESERVOIR_SOLVER compositional flow or compositional poromechanics solver
template< typename RESERVOIR_SOLVER = CompositionalMultiphaseBase >
class CompositionalMultiphaseReservoirAndWells : public CoupledReservoirAndWellsBase< RESERVOIR_SOLVER,
- CompositionalMultiphaseWell >
+ WellManager >
{
public:
using Base = CoupledReservoirAndWellsBase< RESERVOIR_SOLVER,
- CompositionalMultiphaseWell >;
+ WellManager >;
using Base::getLogLevel;
using Base::m_solvers;
using Base::m_linearSolverParameters;
diff --git a/src/coreComponents/physicsSolvers/multiphysics/CoupledReservoirAndWellKernels.hpp b/src/coreComponents/physicsSolvers/multiphysics/CoupledReservoirAndWellKernels.hpp
index 5d106df3156..05592b5725a 100644
--- a/src/coreComponents/physicsSolvers/multiphysics/CoupledReservoirAndWellKernels.hpp
+++ b/src/coreComponents/physicsSolvers/multiphysics/CoupledReservoirAndWellKernels.hpp
@@ -329,7 +329,263 @@ class IsothermalCompositionalMultiPhaseFluxKernelFactory
}
};
+/**
+ * @class FaceBasedAssemblyKernel
+ * @tparam NUM_COMP number of fluid components
+ * @brief Define the interface for the assembly kernel in charge of flux terms
+ */
+template< integer NC, integer IS_THERMAL >
+class IsothermalCompositionalMultiPhaseWellFluxKernel
+{
+public:
+
+ /// Compile time value for the number of components
+ static constexpr integer numComp = NC;
+ static constexpr integer resNumDOF = NC+1+IS_THERMAL;
+
+ // Well jacobian column and row indicies
+ using WJ_COFFSET = compositionalMultiphaseWellKernels::ColOffset_WellJac< NC, IS_THERMAL >;
+ using WJ_ROFFSET = compositionalMultiphaseWellKernels::RowOffset_WellJac< NC, IS_THERMAL >;
+
+ using ROFFSET = compositionalMultiphaseWellKernels::RowOffset;
+ using COFFSET = compositionalMultiphaseWellKernels::ColOffset;
+
+ using CP_Deriv = multifluid::DerivativeOffsetC< NC, IS_THERMAL >;
+
+ using TAG = compositionalMultiphaseWellKernels::SubRegionTag;
+
+
+
+ /// Compute time value for the number of degrees of freedom
+ static constexpr integer numDof = WJ_COFFSET::nDer;
+
+ /// Compile time value for the number of equations except volume and momentum
+ static constexpr integer numEqn = WJ_ROFFSET::nEqn - 2;
+
+ /**
+ * @brief Constructor for the kernel interface
+ * @param[in] rankOffset the offset of my MPI rank
+ * @param[in] stencilWrapper reference to the stencil wrapper
+ * @param[in] dofNumberAccessor
+ * @param[in] compFlowAccessors
+ * @param[in] multiFluidAccessors
+ * @param[in] capPressureAccessors
+ * @param[in] permeabilityAccessors
+ * @param[in] dt time step size
+ * @param[inout] localMatrix the local CRS matrix
+ * @param[inout] localRhs the local right-hand side vector
+ * @param[in] kernelFlags flags packed together
+ */
+ IsothermalCompositionalMultiPhaseWellFluxKernel( real64 const dt,
+ globalIndex const rankOffset,
+ string const wellDofKey,
+ WellElementSubRegion const & subRegion,
+ PerforationData const * const perforationData,
+ MultiFluidBase const & fluid,
+ arrayView1d< real64 > const & localRhs,
+ CRSMatrixView< real64, globalIndex const > const & localMatrix,
+ BitFlags< isothermalCompositionalMultiphaseBaseKernels::KernelFlags > kernelFlags )
+ :
+ m_dt( dt ),
+ m_numPhases ( fluid.numFluidPhases()),
+ m_rankOffset( rankOffset ),
+ m_compPerfRate( perforationData->getField< fields::well::compPerforationRate >() ),
+ m_dCompPerfRate( perforationData->getField< fields::well::dCompPerforationRate >() ),
+ m_perfWellElemIndex( perforationData->getField< fields::perforation::wellElementIndex >() ),
+ m_wellElemDofNumber( subRegion.getReference< array1d< globalIndex > >( wellDofKey ) ),
+ m_localRhs( localRhs ),
+ m_localMatrix( localMatrix ),
+ m_useTotalMassEquation ( kernelFlags.isSet( isothermalCompositionalMultiphaseBaseKernels::KernelFlags::TotalMassEquation ) )
+ { }
+
+
+ /**
+ * @brief Compute the local flux contributions to the residual and Jacobian
+ * @tparam FUNC the type of the function that can be used to customize the computation of the phase fluxes
+ * @param[in] ie the element index
+ * @param[inout] stack the stack variables
+ * @param[in] compFluxKernelOp the function used to customize the computation of the component fluxes
+ */
+
+ template< typename FUNC = NoOpFunc >
+ GEOS_HOST_DEVICE
+ inline
+ void computeFlux( localIndex const iperf,
+ FUNC && compFluxKernelOp = NoOpFunc{} ) const
+ {
+
+ using namespace compositionalMultiphaseUtilities;
+ // local working variables and arrays
+ stackArray1d< localIndex, numComp > eqnRowIndices( numComp );
+ stackArray1d< globalIndex, resNumDOF > dofColIndices( resNumDOF );
+
+ stackArray1d< real64, numComp > localPerf( numComp );
+ stackArray2d< real64, numComp *resNumDOF > localPerfJacobian( numComp, resNumDOF );
+
+ // get the reservoir (sub)region and element indices
+ //localIndex const er = m_resElementRegion[iperf];
+ //localIndex const esr = m_resElementSubRegion[iperf];
+ //localIndex const ei = m_resElementIndex[iperf];
+
+ // get the well element index for this perforation
+ localIndex const iwelem = m_perfWellElemIndex[iperf];
+ //globalIndex const resOffset = m_resElemDofNumber[er][esr][ei];
+ globalIndex const wellElemOffset = m_wellElemDofNumber[iwelem];
+
+ for( integer ic = 0; ic < numComp; ++ic )
+ {
+ eqnRowIndices[ ic] = LvArray::integerConversion< localIndex >( wellElemOffset - m_rankOffset ) + WJ_ROFFSET::MASSBAL + ic;
+ }
+ for( integer jdof = 0; jdof < NC+1; ++jdof )
+ {
+ dofColIndices[ jdof] = wellElemOffset + WJ_COFFSET::dP + jdof;
+ }
+ // For temp its different
+ if constexpr ( IS_THERMAL )
+ {
+ dofColIndices[ NC+1 ] = wellElemOffset + WJ_COFFSET::dT;
+ }
+ // populate local flux vector and derivatives
+
+ for( integer ic = 0; ic < numComp; ++ic )
+ {
+ localPerf[ic] = -m_dt * m_compPerfRate[iperf][ic];
+ }
+ for( integer ic = 0; ic < numComp; ++ic )
+ {
+ localIndex localDofIndexPres = 0;
+
+ localPerfJacobian[ic][localDofIndexPres] = -m_dt * m_dCompPerfRate[iperf][TAG::WELL ][ic][CP_Deriv::dP];
+ for( integer jc = 0; jc < numComp; ++jc )
+ {
+ localIndex const localDofIndexComp = localDofIndexPres + jc + 1;
+ localPerfJacobian[ic][localDofIndexComp] = -m_dt * m_dCompPerfRate[iperf][TAG::WELL ][ic][CP_Deriv::dC+jc];
+ }
+ if constexpr ( IS_THERMAL )
+ {
+ localIndex localDofIndexTemp = localDofIndexPres + NC + 1;
+ localPerfJacobian[ic][localDofIndexTemp] = -m_dt * m_dCompPerfRate[iperf][TAG::WELL ][ic][CP_Deriv::dT];
+ }
+ }
+
+ if( m_useTotalMassEquation )
+ {
+ stackArray1d< real64, resNumDOF > work( resNumDOF );
+ shiftBlockRowsAheadByOneAndReplaceFirstRowWithColumnSum( numComp, numComp, resNumDOF, 1, localPerfJacobian, work );
+
+ // Apply equation/variable change transformation(s)
+ shiftBlockElementsAheadByOneAndReplaceFirstElementWithSum( numComp, numComp, 1, localPerf );
+ }
+
+ for( localIndex i = 0; i < localPerf.size(); ++i )
+ {
+ if( eqnRowIndices[i] >= 0 && eqnRowIndices[i] < m_localMatrix.numRows() )
+ {
+ m_localMatrix.addToRowBinarySearchUnsorted< parallelDeviceAtomic >( eqnRowIndices[i],
+ dofColIndices.data(),
+ localPerfJacobian[i].dataIfContiguous(),
+ resNumDOF );
+ RAJA::atomicAdd( parallelDeviceAtomic{}, &m_localRhs[eqnRowIndices[i]], localPerf[i] );
+ }
+ }
+
+ compFluxKernelOp( wellElemOffset, iwelem, dofColIndices );
+
+ }
+
+
+/**
+ * @brief Performs the kernel launch
+ * @tparam POLICY the policy used in the RAJA kernels
+ * @tparam KERNEL_TYPE the kernel type
+ * @param[in] numElements the number of elements
+ * @param[inout] kernelComponent the kernel component providing access to setup/compute/complete functions and stack
+ * variables
+ */
+ template< typename POLICY, typename KERNEL_TYPE >
+ static void
+ launch( localIndex const numElements,
+ KERNEL_TYPE const & kernelComponent )
+ {
+ GEOS_MARK_FUNCTION;
+ forAll< POLICY >( numElements, [=] GEOS_HOST_DEVICE ( localIndex const ie )
+ {
+ kernelComponent.computeFlux( ie );
+
+ } );
+ }
+
+protected:
+
+/// Time step size
+ real64 const m_dt;
+
+/// Number of phases
+ integer const m_numPhases;
+
+ globalIndex const m_rankOffset;
+// Perfoation variables
+ arrayView2d< real64 const > const m_compPerfRate;
+ arrayView4d< real64 const > const m_dCompPerfRate;
+ arrayView1d< localIndex const > const m_perfWellElemIndex;
+
+// Element region, subregion, index
+ arrayView1d< globalIndex const > const m_wellElemDofNumber;
+
+// RHS and Jacobian
+ arrayView1d< real64 > const m_localRhs;
+ CRSMatrixView< real64, globalIndex const > m_localMatrix;
+
+ integer const m_useTotalMassEquation;
+};
+
+/**
+ * @class FaceBasedAssemblyKernelFactory
+ */
+class IsothermalCompositionalMultiPhaseWellFluxKernelFactory
+{
+public:
+
+ /**
+ * @brief Create a new kernel and launch
+ * @tparam POLICY the policy used in the RAJA kernel
+ * @param[in] numComps the number of fluid components
+ * @param[in] dt time step size
+ * @param[in] rankOffset the offset of my MPI rank
+ * @param[in] useTotalMassEquation flag specifying whether to replace one component bal eqn with total mass eqn
+ * @param[in] dofKey string to get the element degrees of freedom numbers
+ * @param[in] wellControls object holding well control/constraint information
+ * @param[in] subregion well subregion
+ * @param[inout] localMatrix the local CRS matrix
+ * @param[inout] localRhs the local right-hand side vector
+ */
+ template< typename POLICY >
+ static void
+ createAndLaunch( integer const numComps,
+ real64 const dt,
+ globalIndex const rankOffset,
+ string const wellDofKey,
+ WellElementSubRegion const & subRegion,
+ PerforationData const * const perforationData,
+ MultiFluidBase const & fluid,
+ arrayView1d< real64 > const & localRhs,
+ CRSMatrixView< real64, globalIndex const > const & localMatrix,
+ BitFlags< isothermalCompositionalMultiphaseBaseKernels::KernelFlags > kernelFlags )
+ {
+ isothermalCompositionalMultiphaseBaseKernels::internal::kernelLaunchSelectorCompSwitch( numComps, [&]( auto NC )
+ {
+ integer constexpr NUM_COMP = NC();
+
+ using kernelType = IsothermalCompositionalMultiPhaseWellFluxKernel< NUM_COMP, 0 >;
+ kernelType kernel( dt, rankOffset, wellDofKey, subRegion, perforationData,
+ fluid, localRhs, localMatrix, kernelFlags );
+ kernelType::template launch< POLICY >( perforationData->size(), kernel );
+ } );
+
+ }
+};
+/********************************************************/
/**
* @class FaceBasedAssemblyKernel
* @tparam NUM_COMP number of fluid components
@@ -383,6 +639,7 @@ class ThermalCompositionalMultiPhaseFluxKernel : public IsothermalCompositionalM
* @param[in] kernelFlags flags packed together
*/
ThermalCompositionalMultiPhaseFluxKernel( real64 const dt,
+ bool const thermalEffectsEnabled,
integer const isProducer,
globalIndex const rankOffset,
string const wellDofKey,
@@ -407,6 +664,7 @@ class ThermalCompositionalMultiPhaseFluxKernel : public IsothermalCompositionalM
detectCrossflow,
numCrossFlowPerforations,
kernelFlags ),
+ m_thermalEffectsEnabled( thermalEffectsEnabled ),
m_isProducer( isProducer ),
m_globalWellElementIndex( subRegion.getGlobalWellElementIndex() ),
m_energyPerfFlux( perforationData->getField< fields::well::energyPerforationFlux >()),
@@ -432,6 +690,8 @@ class ThermalCompositionalMultiPhaseFluxKernel : public IsothermalCompositionalM
stackArray1d< globalIndex, 2*resNumDOF > & dofColIndices,
localIndex const iwelem )
{
+ if( !m_thermalEffectsEnabled )
+ return;
// No energy equation if top element and Injector
// Top element defined by global index == 0
// Assumption is global index == 0 is top segment with fixed temp BC
@@ -441,7 +701,7 @@ class ThermalCompositionalMultiPhaseFluxKernel : public IsothermalCompositionalM
return;
}
// local working variables and arrays
- stackArray1d< localIndex, 2* numComp > eqnRowIndices( 2 );
+ stackArray1d< localIndex, 2* numComp > eqnRowIndices( 2* numComp );
stackArray1d< real64, 2 * numComp > localPerf( 2 );
stackArray2d< real64, 2 * resNumDOF * 2 * numComp > localPerfJacobian( 2, 2 * resNumDOF );
@@ -510,6 +770,8 @@ class ThermalCompositionalMultiPhaseFluxKernel : public IsothermalCompositionalM
}
protected:
+ /// Flag specifying whether thermal effects are enabled
+ bool const m_thermalEffectsEnabled;
/// Well type
integer const m_isProducer;
@@ -545,6 +807,7 @@ class ThermalCompositionalMultiPhaseFluxKernelFactory
template< typename POLICY >
static void
createAndLaunch( integer const numComps,
+ integer const thermalEffectsEnabled,
integer const isProducer,
real64 const dt,
globalIndex const rankOffset,
@@ -565,7 +828,7 @@ class ThermalCompositionalMultiPhaseFluxKernelFactory
integer constexpr NUM_COMP = NC();
using kernelType = ThermalCompositionalMultiPhaseFluxKernel< NUM_COMP, 1 >;
- kernelType kernel( dt, isProducer, rankOffset, wellDofKey, subRegion, resDofNumber, perforationData,
+ kernelType kernel( dt, thermalEffectsEnabled, isProducer, rankOffset, wellDofKey, subRegion, resDofNumber, perforationData,
fluid, localRhs, localMatrix, detectCrossflow, numCrossFlowPerforations, kernelFlags );
kernelType::template launch< POLICY >( perforationData->size(), kernel );
} );
@@ -573,6 +836,234 @@ class ThermalCompositionalMultiPhaseFluxKernelFactory
}
};
+/********************************************************/
+/**
+ * @class FaceBasedAssemblyKernel
+ * @tparam NUM_COMP number of fluid components
+ * @brief Define the interface for the assembly kernel in charge of flux terms
+ */
+template< integer NC, integer IS_THERMAL >
+class ThermalCompositionalMultiPhaseWellFluxKernel : public IsothermalCompositionalMultiPhaseWellFluxKernel< NC, IS_THERMAL >
+{
+public:
+ using Base = IsothermalCompositionalMultiPhaseWellFluxKernel< NC, IS_THERMAL >;
+ /// Compile time value for the number of components
+ static constexpr integer numComp = NC;
+ static constexpr integer resNumDOF = NC+1+IS_THERMAL;
+
+ // Well jacobian column and row indicies
+ using WJ_COFFSET = compositionalMultiphaseWellKernels::ColOffset_WellJac< NC, IS_THERMAL >;
+ using WJ_ROFFSET = compositionalMultiphaseWellKernels::RowOffset_WellJac< NC, IS_THERMAL >;
+
+ using ROFFSET = compositionalMultiphaseWellKernels::RowOffset;
+ using COFFSET = compositionalMultiphaseWellKernels::ColOffset;
+
+ using CP_Deriv = multifluid::DerivativeOffsetC< NC, IS_THERMAL >;
+
+ using TAG = compositionalMultiphaseWellKernels::SubRegionTag;
+
+ using Base::m_dt;
+ using Base::m_localRhs;
+ using Base::m_localMatrix;
+ using Base::m_rankOffset;
+
+
+
+ /// Compute time value for the number of degrees of freedom
+ static constexpr integer numDof = WJ_COFFSET::nDer;
+
+ /// Compile time value for the number of equations except volume and momentum
+ static constexpr integer numEqn = WJ_ROFFSET::nEqn - 2;
+
+ /**
+ * @brief Constructor for the kernel interface
+ * @param[in] rankOffset the offset of my MPI rank
+ * @param[in] stencilWrapper reference to the stencil wrapper
+ * @param[in] dofNumberAccessor
+ * @param[in] compFlowAccessors
+ * @param[in] multiFluidAccessors
+ * @param[in] capPressureAccessors
+ * @param[in] permeabilityAccessors
+ * @param[in] dt time step size
+ * @param[inout] localMatrix the local CRS matrix
+ * @param[inout] localRhs the local right-hand side vector
+ * @param[in] kernelFlags flags packed together
+ */
+ ThermalCompositionalMultiPhaseWellFluxKernel( real64 const dt,
+ bool const thermalEffectsEnabled,
+ integer const isProducer,
+ globalIndex const rankOffset,
+ string const wellDofKey,
+ WellElementSubRegion const & subRegion,
+ PerforationData const * const perforationData,
+ MultiFluidBase const & fluid,
+ arrayView1d< real64 > const & localRhs,
+ CRSMatrixView< real64, globalIndex const > const & localMatrix,
+ BitFlags< isothermalCompositionalMultiphaseBaseKernels::KernelFlags > kernelFlags )
+ : Base( dt,
+ rankOffset,
+ wellDofKey,
+ subRegion,
+ perforationData,
+ fluid,
+ localRhs,
+ localMatrix,
+ kernelFlags ),
+ m_thermalEffectsEnabled( thermalEffectsEnabled ),
+ m_isProducer( isProducer ),
+ m_globalWellElementIndex( subRegion.getGlobalWellElementIndex() ),
+ m_energyPerfFlux( perforationData->getField< fields::well::energyPerforationFlux >()),
+ m_dEnergyPerfFlux( perforationData->getField< fields::well::dEnergyPerforationFlux >())
+
+ { }
+
+
+ /**
+ * @brief Compute the local flux contributions to the residual and Jacobian
+ * @tparam FUNC the type of the function that can be used to customize the computation of the phase fluxes
+ * @param[in] ie the element index
+ * @param[inout] stack the stack variables
+ * @param[in] compFluxKernelOp the function used to customize the computation of the component fluxes
+ */
+
+ GEOS_HOST_DEVICE
+ inline
+ void computeFlux( localIndex const iperf ) const
+ {
+ Base::computeFlux( iperf, [&] ( globalIndex const & wellElemOffset,
+ localIndex const iwelem,
+ stackArray1d< globalIndex, resNumDOF > & dofColIndices )
+ {
+ GEOS_UNUSED_VAR( dofColIndices );
+ if( !m_thermalEffectsEnabled ) // tjb iso
+ return;
+ // No energy equation if top element and Injector
+ // Top element defined by global index == 0
+ // Assumption is global index == 0 is top segment with fixed temp BC
+ if( !m_isProducer )
+ {
+ if( m_globalWellElementIndex[iwelem] == 0 )
+ return;
+ }
+ // local working variables and arrays
+ localIndex eqnRowIndices = LvArray::integerConversion< localIndex >( wellElemOffset - m_rankOffset ) + WJ_ROFFSET::ENERGYBAL;
+
+ stackArray2d< real64, resNumDOF > localPerfJacobian( 1, resNumDOF );
+ // populate local flux vector and derivatives
+
+ real64 localPerf = -m_dt * m_energyPerfFlux[iperf];
+
+ // std::cout << "Local perf: " << iperf << " " << localPerf << std::endl;
+ localIndex localDofIndexPres = 0;
+ localPerfJacobian [0][localDofIndexPres] = -m_dt * m_dEnergyPerfFlux[iperf][TAG::WELL][CP_Deriv::dP];
+
+ // populate local flux vector and derivatives
+ for( integer ic = 0; ic < numComp; ++ic )
+ {
+ localIndex const localDofIndexComp = localDofIndexPres + ic + 1;
+ localPerfJacobian [0][localDofIndexComp] = -m_dt * m_dEnergyPerfFlux[iperf][TAG::WELL ][CP_Deriv::dC+ic];
+ }
+ localPerfJacobian [0][localDofIndexPres+NC+1] = -m_dt * m_dEnergyPerfFlux[iperf][TAG::WELL ][CP_Deriv::dT];
+
+ if( eqnRowIndices >= 0 && eqnRowIndices < m_localMatrix.numRows() )
+ {
+ // tjb iso
+ m_localMatrix.template addToRowBinarySearchUnsorted< parallelDeviceAtomic >( eqnRowIndices,
+ dofColIndices.data(),
+ localPerfJacobian[0].dataIfContiguous(),
+ resNumDOF );
+ RAJA::atomicAdd( parallelDeviceAtomic{}, &m_localRhs[eqnRowIndices], localPerf );
+ }
+ } );
+
+
+ }
+
+
+ /**
+ * @brief Performs the kernel launch
+ * @tparam POLICY the policy used in the RAJA kernels
+ * @tparam KERNEL_TYPE the kernel type
+ * @param[in] numElements the number of elements
+ * @param[inout] kernelComponent the kernel component providing access to setup/compute/complete functions and stack
+ * variables
+ */
+ template< typename POLICY, typename KERNEL_TYPE >
+ static void
+ launch( localIndex const numElements,
+ KERNEL_TYPE const & kernelComponent )
+ {
+ GEOS_MARK_FUNCTION;
+ forAll< POLICY >( numElements, [=] GEOS_HOST_DEVICE ( localIndex const ie )
+ {
+ kernelComponent.computeFlux( ie );
+
+ } );
+ }
+
+protected:
+ /// Thermal effects enabled
+ bool const m_thermalEffectsEnabled;
+ /// Well type
+ integer const m_isProducer;
+
+ /// Global index of local element
+ arrayView1d< globalIndex const > m_globalWellElementIndex;
+
+ /// Views on energy flux
+ arrayView1d< real64 const > const m_energyPerfFlux;
+ arrayView3d< real64 const > const m_dEnergyPerfFlux;
+};
+
+/**
+ * @class ThermalCompositionalMultiPhaseFluxWellKernelFactory
+ */
+class ThermalCompositionalMultiPhaseWellFluxKernelFactory
+{
+public:
+
+ /**
+ * @brief Create a new kernel and launch
+ * @tparam POLICY the policy used in the RAJA kernel
+ * @param[in] numComps the number of fluid components
+ * @param[in] dt time step size
+ * @param[in] rankOffset the offset of my MPI rank
+ * @param[in] useTotalMassEquation flag specifying whether to replace one component bal eqn with total mass eqn
+ * @param[in] dofKey string to get the element degrees of freedom numbers
+ * @param[in] wellControls object holding well control/constraint information
+ * @param[in] subregion well subregion
+ * @param[inout] localMatrix the local CRS matrix
+ * @param[inout] localRhs the local right-hand side vector
+ */
+ template< typename POLICY >
+ static void
+ createAndLaunch( integer const numComps,
+ WellControls const & wellControls,
+ integer const isProducer,
+ real64 const dt,
+ globalIndex const rankOffset,
+ string const wellDofKey,
+ WellElementSubRegion const & subRegion,
+ PerforationData const * const perforationData,
+ MultiFluidBase const & fluid,
+ BitFlags< isothermalCompositionalMultiphaseBaseKernels::KernelFlags > kernelFlags,
+ arrayView1d< real64 > const & localRhs,
+ CRSMatrixView< real64, globalIndex const > const & localMatrix
+ )
+ {
+ isothermalCompositionalMultiphaseBaseKernels::internal::kernelLaunchSelectorCompSwitch( numComps, [&]( auto NC )
+ {
+ integer constexpr NUM_COMP = NC();
+
+ using kernelType = ThermalCompositionalMultiPhaseWellFluxKernel< NUM_COMP, 1 >;
+ kernelType kernel( dt, wellControls.thermalEffectsEnabled(), isProducer, rankOffset, wellDofKey, subRegion, perforationData,
+ fluid, localRhs, localMatrix, kernelFlags );
+ kernelType::template launch< POLICY >( perforationData->size(), kernel );
+ } );
+
+ }
+};
+
} // end namespace coupledReservoirAndWellKernels
} // end namespace geos
diff --git a/src/coreComponents/physicsSolvers/multiphysics/CoupledReservoirAndWellsBase.cpp b/src/coreComponents/physicsSolvers/multiphysics/CoupledReservoirAndWellsBase.cpp
index fbb8e7fb935..d2fb522266a 100644
--- a/src/coreComponents/physicsSolvers/multiphysics/CoupledReservoirAndWellsBase.cpp
+++ b/src/coreComponents/physicsSolvers/multiphysics/CoupledReservoirAndWellsBase.cpp
@@ -116,7 +116,7 @@ addCouplingNumNonzeros( PhysicsSolverBase const * const solver,
}
bool validateWellPerforations( PhysicsSolverBase const * const reservoirSolver,
- WellSolverBase const * const wellSolver,
+ WellManager const * const wellSolver,
DomainPartition const & domain )
{
std::pair< string, string > badPerforation;
diff --git a/src/coreComponents/physicsSolvers/multiphysics/CoupledReservoirAndWellsBase.hpp b/src/coreComponents/physicsSolvers/multiphysics/CoupledReservoirAndWellsBase.hpp
index 32a67843aec..c7a1f3ea030 100644
--- a/src/coreComponents/physicsSolvers/multiphysics/CoupledReservoirAndWellsBase.hpp
+++ b/src/coreComponents/physicsSolvers/multiphysics/CoupledReservoirAndWellsBase.hpp
@@ -29,7 +29,7 @@
#include "mesh/PerforationFields.hpp"
#include "mesh/DomainPartition.hpp"
#include "physicsSolvers/fluidFlow/wells/WellControls.hpp"
-#include "physicsSolvers/fluidFlow/wells/WellSolverBase.hpp"
+#include "physicsSolvers/fluidFlow/wells/WellManager.hpp"
namespace geos
{
@@ -65,7 +65,7 @@ addCouplingNumNonzeros( PhysicsSolverBase const * const solver,
* @param domain the physical domain object
*/
bool validateWellPerforations( PhysicsSolverBase const * const reservoirSolver,
- WellSolverBase const * const wellSolver,
+ WellManager const * const wellSolver,
DomainPartition const & domain );
}
diff --git a/src/coreComponents/physicsSolvers/multiphysics/SinglePhaseReservoirAndWells.cpp b/src/coreComponents/physicsSolvers/multiphysics/SinglePhaseReservoirAndWells.cpp
index 46cf3257088..5b3d2f05188 100644
--- a/src/coreComponents/physicsSolvers/multiphysics/SinglePhaseReservoirAndWells.cpp
+++ b/src/coreComponents/physicsSolvers/multiphysics/SinglePhaseReservoirAndWells.cpp
@@ -126,6 +126,29 @@ initializePreSubGroups()
Base::initializePreSubGroups();
SinglePhaseBase const * const flowSolver = this->flowSolver();
Base::wellSolver()->setFlowSolverName( flowSolver->getName() );
+
+ bool const isThermalFlow = flowSolver->getReference< integer >( SinglePhaseBase::viewKeyStruct::isThermalString() );
+ bool const isThermalWell = Base::wellSolver()->template getReference< integer >( WellManager::viewKeyStruct::isThermalString() );
+ GEOS_THROW_IF( isThermalFlow != isThermalWell,
+ GEOS_FMT( "{}: the input flag {} must be the same in the flow and well solvers, respectively '{}' and '{}'",
+ this->getDataContext(), SinglePhaseBase::viewKeyStruct::isThermalString(),
+ Base::reservoirSolver()->getDataContext(), Base::wellSolver()->getDataContext() ),
+ InputError, this->getDataContext(), Base::reservoirSolver()->getDataContext(), Base::wellSolver()->getDataContext() );
+ DomainPartition & domain = this->template getGroupByPath< DomainPartition >( "/Problem/domain" );
+
+ this->template forDiscretizationOnMeshTargets<>( domain.getMeshBodies(), [&] ( string const &,
+ MeshLevel & mesh,
+ string_array const & regionNames )
+ {
+ ElementRegionManager & elemManager = mesh.getElemManager();
+ elemManager.forElementSubRegions< WellElementSubRegion >( regionNames, [&]( localIndex const,
+ WellElementSubRegion const & subRegion )
+ {
+ WellControls & wellControls = Base::wellSolver()->getWellControls( subRegion );
+ wellControls.setFlowSolverName( flowSolver->getName() );
+
+ } );
+ } );
}
template< typename RESERVOIR_SOLVER >
diff --git a/src/coreComponents/physicsSolvers/multiphysics/SinglePhaseReservoirAndWells.hpp b/src/coreComponents/physicsSolvers/multiphysics/SinglePhaseReservoirAndWells.hpp
index 0adeee58026..98da0ab2737 100644
--- a/src/coreComponents/physicsSolvers/multiphysics/SinglePhaseReservoirAndWells.hpp
+++ b/src/coreComponents/physicsSolvers/multiphysics/SinglePhaseReservoirAndWells.hpp
@@ -23,7 +23,7 @@
#include "physicsSolvers/multiphysics/CoupledReservoirAndWellsBase.hpp"
#include "physicsSolvers/fluidFlow/SinglePhaseBase.hpp"
-#include "physicsSolvers/fluidFlow/wells/SinglePhaseWell.hpp"
+#include "physicsSolvers/fluidFlow/wells/WellManager.hpp"
namespace geos
{
@@ -31,12 +31,12 @@ namespace geos
/// @tparam RESERVOIR_SOLVER single-phase flow or single-phase poromechanics solver
template< typename RESERVOIR_SOLVER = SinglePhaseBase >
class SinglePhaseReservoirAndWells : public CoupledReservoirAndWellsBase< RESERVOIR_SOLVER,
- SinglePhaseWell >
+ WellManager >
{
public:
using Base = CoupledReservoirAndWellsBase< RESERVOIR_SOLVER,
- SinglePhaseWell >;
+ WellManager >;
using Base::m_solvers;
using Base::m_linearSolverParameters;
diff --git a/src/coreComponents/schema/schema.xsd b/src/coreComponents/schema/schema.xsd
index 12808396834..ef1a6aa78f1 100644
--- a/src/coreComponents/schema/schema.xsd
+++ b/src/coreComponents/schema/schema.xsd
@@ -222,18 +222,10 @@
-
-
-
-
-
-
-
-
@@ -363,10 +355,6 @@
-
-
-
-
@@ -511,10 +499,6 @@
-
-
-
-
@@ -543,6 +527,10 @@
+
+
+
+
@@ -1604,26 +1592,10 @@ stress - traction is applied to the faces as specified by the inner product of i
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
@@ -1632,18 +1604,6 @@ stress - traction is applied to the faces as specified by the inner product of i
-
-
-
-
-
-
-
-
-
-
-
-
@@ -2760,12 +2720,6 @@ Information output from lower logLevels is added with the desired log level
-
-
-
-
-
-
@@ -2802,12 +2756,6 @@ Information output from lower logLevels is added with the desired log level
-
-
-
-
-
-
@@ -2815,6 +2763,16 @@ Information output from lower logLevels is added with the desired log level
+
+
+
+
+
+
+
+
+
+
@@ -3654,144 +3612,6 @@ When set to `all` output both convergence & iteration information to a csv.-->
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
@@ -4187,7 +4007,7 @@ Information output from lower logLevels is added with the desired log level
-
+
@@ -4216,7 +4036,7 @@ When set to `all` output both convergence & iteration information to a csv.-->
-
+
@@ -5697,52 +5517,6 @@ Local- Add jump stabilization on interior of macro elements-->
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
@@ -6228,6 +6002,411 @@ When set to `all` output both convergence & iteration information to a csv.-->
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/coreComponents/schema/schema.xsd.other b/src/coreComponents/schema/schema.xsd.other
index e77cc9d197a..e6101f33339 100644
--- a/src/coreComponents/schema/schema.xsd.other
+++ b/src/coreComponents/schema/schema.xsd.other
@@ -351,15 +351,11 @@ A field can represent a physical variable. (pressure, temperature, global compos
-
-
-
-
@@ -526,7 +522,7 @@ A field can represent a physical variable. (pressure, temperature, global compos
-
+
@@ -570,7 +566,6 @@ A field can represent a physical variable. (pressure, temperature, global compos
-
@@ -607,7 +602,6 @@ A field can represent a physical variable. (pressure, temperature, global compos
-
@@ -615,6 +609,7 @@ A field can represent a physical variable. (pressure, temperature, global compos
+
@@ -867,27 +862,6 @@ A field can represent a physical variable. (pressure, temperature, global compos
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
@@ -1342,18 +1316,6 @@ A field can represent a physical variable. (pressure, temperature, global compos
-
-
-
-
-
-
-
-
-
-
-
-
@@ -1497,6 +1459,63 @@ A field can represent a physical variable. (pressure, temperature, global compos
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -1590,7 +1609,7 @@ A field can represent a physical variable. (pressure, temperature, global compos
-
+
@@ -3504,7 +3523,7 @@ A field can represent a physical variable. (pressure, temperature, global compos
-
+
@@ -3532,7 +3551,7 @@ A field can represent a physical variable. (pressure, temperature, global compos
-
+
@@ -3551,11 +3570,11 @@ A field can represent a physical variable. (pressure, temperature, global compos
-
+
-
+
@@ -3565,7 +3584,7 @@ A field can represent a physical variable. (pressure, temperature, global compos
-
+
@@ -3575,11 +3594,11 @@ A field can represent a physical variable. (pressure, temperature, global compos
-
+
-
+
@@ -3589,7 +3608,7 @@ A field can represent a physical variable. (pressure, temperature, global compos
-
+
@@ -3599,7 +3618,7 @@ A field can represent a physical variable. (pressure, temperature, global compos
-
+
@@ -3609,7 +3628,7 @@ A field can represent a physical variable. (pressure, temperature, global compos
-
+
@@ -3639,7 +3658,7 @@ A field can represent a physical variable. (pressure, temperature, global compos
-
+
@@ -3667,7 +3686,7 @@ A field can represent a physical variable. (pressure, temperature, global compos
-
+
@@ -3681,7 +3700,7 @@ A field can represent a physical variable. (pressure, temperature, global compos
-
+
@@ -3693,7 +3712,7 @@ A field can represent a physical variable. (pressure, temperature, global compos
-
+
@@ -3701,11 +3720,11 @@ A field can represent a physical variable. (pressure, temperature, global compos
-
+
-
+
@@ -3728,7 +3747,7 @@ A field can represent a physical variable. (pressure, temperature, global compos
-
+
@@ -3754,7 +3773,7 @@ A field can represent a physical variable. (pressure, temperature, global compos
-
+
@@ -3775,7 +3794,7 @@ A field can represent a physical variable. (pressure, temperature, global compos
-
+
@@ -3805,7 +3824,7 @@ A field can represent a physical variable. (pressure, temperature, global compos
-
+
@@ -3819,7 +3838,7 @@ A field can represent a physical variable. (pressure, temperature, global compos
-
+
@@ -3850,7 +3869,7 @@ A field can represent a physical variable. (pressure, temperature, global compos
-
+
@@ -3895,7 +3914,7 @@ A field can represent a physical variable. (pressure, temperature, global compos
-
+