diff --git a/inputFiles/compositionalMultiphaseFlow/soreideWhitson/1D_100cells/1D_benchmark.xml b/inputFiles/compositionalMultiphaseFlow/soreideWhitson/1D_100cells/1D_benchmark.xml
index 89b9cc7dc86..1218d8156a2 100644
--- a/inputFiles/compositionalMultiphaseFlow/soreideWhitson/1D_100cells/1D_benchmark.xml
+++ b/inputFiles/compositionalMultiphaseFlow/soreideWhitson/1D_100cells/1D_benchmark.xml
@@ -1,294 +1,390 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ 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..d7df3eba0ce 100644
--- a/inputFiles/compositionalMultiphaseFlow/soreideWhitson/1D_100cells/1D_smoke.xml
+++ b/inputFiles/compositionalMultiphaseFlow/soreideWhitson/1D_100cells/1D_smoke.xml
@@ -1,294 +1,390 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ 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..4d4ff61234c 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">
+
+
+
-
+
-
+
+ name="fluidTPFA"/>
@@ -80,13 +83,16 @@
solidModelName="nullSolid"
porosityModelName="rockPorosity"
permeabilityModelName="rockPerm"/>
+
+
+
@@ -94,9 +100,8 @@
-
+ wettingNonWettingRelPermTableNames="{ waterRelativePermeabilityTable, gasRelativePermeabilityTable }"/>
+
-
-
-
+
+
+
+
-
@@ -152,11 +157,13 @@
name="initCO2CompFracTable"
coordinates="{ -3238.2, -2506.13 }"
values="{ 0.000001, 0.000001 }"/>
+
-
@@ -165,37 +172,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..e585c6e4238 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="totalVolRate">
+
+
+
+ control="massRate">
+
+
+
+ control="massRate">
+
+
+
-
-
-
-
+
+ name="fluidTPFA"/>
-
-
-
+
+
+
-
-
-
+ wettingNonWettingRelPermTableNames="{ waterRelativePermeabilityTable, gasRelativePermeabilityTable }"/>
-
-
-
-
-
-
-
-
+
+
+
-
@@ -204,11 +199,13 @@
name="initCO2CompFracTable"
coordinates="{ -3238.2, -2506.13 }"
values="{ 0.000001, 0.000001 }"/>
+
-
@@ -217,43 +214,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..83ca125931e 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">
+
+
+
-
+
-
+
+ name="fluidTPFA"/>
@@ -80,26 +83,27 @@
solidModelName="nullSolid"
porosityModelName="rockPorosity"
permeabilityModelName="rockPerm"/>
+
+
+
-
-
+
-
-
-
-
-
+
+
+
+
-
@@ -157,59 +159,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..6067d36b757 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">
+
+
+
-
+
-
+
+ name="fluidTPFA"/>
@@ -84,28 +87,27 @@
solidModelName="nullSolid"
porosityModelName="rockPorosity"
permeabilityModelName="rockPerm"/>
+
+
+
-
-
-
-
+
-
-
+
+
+
+
-
@@ -161,10 +163,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..742d818218b 100644
--- a/inputFiles/compositionalMultiphaseWell/benchmarks/Egg/deadOilEgg_base_direct.xml
+++ b/inputFiles/compositionalMultiphaseWell/benchmarks/Egg/deadOilEgg_base_direct.xml
@@ -1,7 +1,6 @@
-
+ control="BHP">
+
+
+
-
-
-
+ control="BHP">
+
+
+
+ control="BHP">
+
+
+
+ control="BHP">
+
+
+
+ control="totalVolRate">
+
+
+
+ control="totalVolRate">
+
+
+
+ control="totalVolRate">
+
+
+
+ control="totalVolRate">
+
+
+
+ control="totalVolRate">
+
+
+
+ control="totalVolRate">
+
+
+
+ control="totalVolRate">
+
+
+
+ control="totalVolRate">
+
+
+
-
-
-
-
-
-
-
-
@@ -279,7 +327,6 @@
materialList="{ fluid }"/>
-
-
-
-
-
@@ -373,11 +416,8 @@
-
-
-
-
-
diff --git a/inputFiles/compositionalMultiphaseWell/benchmarks/Egg/deadOilEgg_base_iterative.xml b/inputFiles/compositionalMultiphaseWell/benchmarks/Egg/deadOilEgg_base_iterative.xml
index 615688bef78..fa47c6541e9 100644
--- a/inputFiles/compositionalMultiphaseWell/benchmarks/Egg/deadOilEgg_base_iterative.xml
+++ b/inputFiles/compositionalMultiphaseWell/benchmarks/Egg/deadOilEgg_base_iterative.xml
@@ -1,7 +1,6 @@
-
+ control="BHP">
+
+
+
-
-
-
+ control="BHP">
+
+
+
+ control="BHP">
+
+
+
+ control="BHP">
+
+
+
+ control="totalVolRate">
+
+
+
+ control="totalVolRate">
+
+
+
+ control="totalVolRate">
+
+
+
+ control="totalVolRate">
+
+
+
+ control="totalVolRate">
+
+
+
+ control="totalVolRate">
+
+
+
+ control="totalVolRate">
+
+
+
+ control="totalVolRate">
+
+
+
-
-
-
-
-
-
-
-
@@ -281,7 +329,6 @@
materialList="{ fluid }"/>
-
-
-
-
-
@@ -375,11 +418,8 @@
-
-
-
-
-
diff --git a/inputFiles/compositionalMultiphaseWell/black_oil_wells_saturated_3d.xml b/inputFiles/compositionalMultiphaseWell/black_oil_wells_saturated_3d.xml
index 7ebac080cbc..14479ff4132 100644
--- a/inputFiles/compositionalMultiphaseWell/black_oil_wells_saturated_3d.xml
+++ b/inputFiles/compositionalMultiphaseWell/black_oil_wells_saturated_3d.xml
@@ -2,7 +2,6 @@
-
-
+
+ control="BHP">
+
+
+
+ control="BHP">
+
+
+
-
-
-
-
+
-
+
-
+
-
@@ -148,12 +153,14 @@
name="region"
cellBlocks="{ * }"
materialList="{ fluid, rock, relperm, cappres }"/>
+
+
+ materialList="{ fluid }"/>
@@ -163,7 +170,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 }"/>
-
+
-
+
-
+
-
+
-
diff --git a/inputFiles/compositionalMultiphaseWell/black_oil_wells_saturated_3d_stone2.xml b/inputFiles/compositionalMultiphaseWell/black_oil_wells_saturated_3d_stone2.xml
index ce669961545..5df4a8cf991 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 @@
-
-
+
+ control="BHP">
+
+
+
+ control="BHP">
+
+
+
-
-
-
-
-
-
-
+
+
+
+
+
+
-
-
+
-
+
-
+
-
@@ -147,12 +153,14 @@
name="region"
cellBlocks="{ * }"
materialList="{ fluid, rock, relperm, cappres }"/>
+
+
+ materialList="{ fluid }"/>
@@ -162,7 +170,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 }"/>
-
+
-
+
-
+
-
+
-
diff --git a/inputFiles/compositionalMultiphaseWell/black_oil_wells_unsaturated_3d.xml b/inputFiles/compositionalMultiphaseWell/black_oil_wells_unsaturated_3d.xml
index 323b9a144c6..b2e5a4c7d91 100644
--- a/inputFiles/compositionalMultiphaseWell/black_oil_wells_unsaturated_3d.xml
+++ b/inputFiles/compositionalMultiphaseWell/black_oil_wells_unsaturated_3d.xml
@@ -2,7 +2,6 @@
-
-
+
+
+ control="BHP">
+
+
+
+ control="BHP">
+
+
+
-
-
-
-
-
+
-
-
+
-
+
-
@@ -148,12 +154,14 @@
name="region"
cellBlocks="{ * }"
materialList="{ fluid, rock, relperm }"/>
+
+
+ materialList="{ fluid }"/>
@@ -163,7 +171,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"/>
+
-
+
-
+
-
+ 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..464449a53f4 100644
--- a/inputFiles/compositionalMultiphaseWell/black_oil_wells_unsaturated_3d_stone2.xml
+++ b/inputFiles/compositionalMultiphaseWell/black_oil_wells_unsaturated_3d_stone2.xml
@@ -1,260 +1,267 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/inputFiles/compositionalMultiphaseWell/compositional_multiphase_wells_1d.xml b/inputFiles/compositionalMultiphaseWell/compositional_multiphase_wells_1d.xml
index bea2c48815d..2ba87aa5909 100644
--- a/inputFiles/compositionalMultiphaseWell/compositional_multiphase_wells_1d.xml
+++ b/inputFiles/compositionalMultiphaseWell/compositional_multiphase_wells_1d.xml
@@ -30,20 +30,30 @@
+ control="BHP">
+
+
+
+ control="totalVolRate">
+
+
+
@@ -58,7 +68,6 @@
ny="{ 1 }"
nz="{ 1 }"
cellBlockNames="{ cb1 }">
-
-
+ name="fluidTPFA"/>
@@ -175,7 +182,6 @@
-
-
-
+ control="BHP">
+
+
+
+ control="phaseVolRate">
+
+
+
+ control="totalVolRate">
+
+
+
-
-
@@ -69,7 +81,6 @@
ny="{ 20 }"
nz="{ 1 }"
cellBlockNames="{ cb1 }">
-
-
-
+ name="fluidTPFA"/>
@@ -223,7 +231,6 @@
-
-
+ control="BHP">
+
+
+
+ control="phaseVolRate">
+
+
+
+ control="totalVolRate">
+
+
+
@@ -78,7 +93,6 @@
ny="{ 20 }"
nz="{ 1 }"
cellBlockNames="{ cb1 }">
-
-
-
+ name="fluidTPFA"/>
@@ -223,7 +234,6 @@
-
-
-
-
+
+ control="BHP">
+
+
+
+ control="BHP">
+
+
+
+ control="totalVolRate">
+
+
+
@@ -77,7 +92,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..e5f85ebfbdd 100644
--- a/inputFiles/compositionalMultiphaseWell/dome_soreide_whitson_base.xml
+++ b/inputFiles/compositionalMultiphaseWell/dome_soreide_whitson_base.xml
@@ -1,287 +1,339 @@
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/inputFiles/compositionalMultiphaseWell/isothm_mass_inj_table.xml b/inputFiles/compositionalMultiphaseWell/isothm_mass_inj_table.xml
index 14076fab917..cf1631e6f94 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">
+
+
+
+
@@ -83,10 +85,8 @@
-
-
@@ -94,8 +94,6 @@
name="sink"
xMin="{ 89.99, 89.99, -0.01 }"
xMax="{ 101.01, 101.01, 1.01 }"/>
-
-
-
-
@@ -143,25 +142,28 @@
name="region"
cellBlocks="{ cb }"
materialList="{ fluid, rock, relperm }"/>
+
-
+
+
+
@@ -181,11 +183,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..7c198945edd 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">
+
+
+
+
@@ -82,10 +85,8 @@
-
-
@@ -93,8 +94,6 @@
name="sink"
xMin="{ 89.99, 89.99, -0.01 }"
xMax="{ 101.01, 101.01, 1.01 }"/>
-
-
-
-
-
@@ -148,25 +149,28 @@
name="region"
cellBlocks="{ cb }"
materialList="{ fluid, rock, relperm }"/>
+
-
+
+
+
@@ -186,11 +190,9 @@
phaseMinVolumeFraction="{ 0.0, 0.0 }"
phaseRelPermExponent="{ 1.5, 1.5 }"
phaseRelPermMaxValue="{ 0.9, 0.9 }"/>
-
-
+
+
+
-
diff --git a/inputFiles/compositionalMultiphaseWell/simpleCo2InjTutorial_base.xml b/inputFiles/compositionalMultiphaseWell/simpleCo2InjTutorial_base.xml
index 705b4934fb9..5d4f887ad1e 100644
--- a/inputFiles/compositionalMultiphaseWell/simpleCo2InjTutorial_base.xml
+++ b/inputFiles/compositionalMultiphaseWell/simpleCo2InjTutorial_base.xml
@@ -1,8 +1,6 @@
-
-
+ useSurfaceConditions="1"
+ surfacePressure="101325"
+ surfaceTemperature="288.71"
+ control="totalVolRate">
+
+
+
-
-
-
+
-
-
-
+
-
-
-
-
-
-
-
-
@@ -185,42 +177,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..9e87e03c62f 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..ad4a7cc4368 100644
--- a/inputFiles/compositionalMultiphaseWell/staircase_co2_wells_3d.xml
+++ b/inputFiles/compositionalMultiphaseWell/staircase_co2_wells_3d.xml
@@ -33,28 +33,36 @@
useMass="1">
+ control="phaseVolRate">
+
+
+
+ control="totalVolRate">
+
+
+
-
-
-
+ 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 +223,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..f5d6b2a7340 100644
--- a/inputFiles/compositionalMultiphaseWell/staircase_co2_wells_hybrid_3d.xml
+++ b/inputFiles/compositionalMultiphaseWell/staircase_co2_wells_hybrid_3d.xml
@@ -38,25 +38,34 @@
+ control="BHP">
+
+
+
+ control="totalVolRate">
+
+
+
-
-
-
-
+
diff --git a/inputFiles/poromechanics/PoroElastic_staircase_co2_3d_fim.xml b/inputFiles/poromechanics/PoroElastic_staircase_co2_3d_fim.xml
index 0ca07929f8e..5e1a987f1cc 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 }"/>
@@ -55,34 +54,42 @@
useMass="1">
+ control="BHP">
+
+
+
+ control="totalVolRate">
+
+
+
-
-
+
+
-
+
+
-
+
-
-
-
diff --git a/inputFiles/poromechanics/PoroElastic_staircase_co2_3d_sequential.xml b/inputFiles/poromechanics/PoroElastic_staircase_co2_3d_sequential.xml
index e978595a49f..6a336f2fda3 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 }"/>
@@ -62,34 +61,42 @@
useMass="1">
+ control="BHP">
+
+
+
+ control="totalVolRate">
+
+
+
-
-
+
+
-
+
+
-
+
-
-
diff --git a/inputFiles/poromechanics/PoroElastic_staircase_singlephase_3d_fim.xml b/inputFiles/poromechanics/PoroElastic_staircase_singlephase_3d_fim.xml
index 95f2090ada1..29c208416b7 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 }"/>
+ control="BHP">
+
+
+
+ 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..5f5b8cbc6ba 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 }"/>
+ control="BHP">
+
+
+
+ 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..3660699ecaf 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">
+
+
+
@@ -88,47 +95,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..5557c612313 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">
+
+
+
@@ -97,47 +104,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..369356badf7 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">
+
+
+
+ control="BHP">
+
+
+
-
+
+
-
-
-
+
-
+
-
+ solverType="direct"/>
+ targetRegions="{ Region, Fault }"
+ discretization="FE1"/>
-
+ targetRegions="{ Region, Fault }"/>
+ control="BHP">
+
+
+
+ control="BHP">
+
+
+
-
+
+
-
-
-
+
@@ -5,10 +7,9 @@
-
-
+ logLevel="1"/>
+ targetRegions="{ Region, Fault }"
+ discretization="FE1"/>
-
+ targetRegions="{ Region, Fault }"/>
+ targetRegions="{ wellRegion1, wellRegion2 }"
+ writeCSV="1">
+ logLevel="2"
+ type="producer">
+
+
+
+ logLevel="2"
+ type="injector">
+
+
+
-
+
+
-
-
-
+
@@ -5,10 +7,9 @@
-
+ targetRegions="{ Region, Fault }"
+ discretization="FE1"/>
-
+ targetRegions="{ Region, Fault }"/>
+ targetRegions="{ wellRegion1, wellRegion2 }"
+ writeCSV="1">
+ logLevel="2"
+ type="producer">
+
+
+
+ logLevel="2"
+ 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..bd55d92c512 100644
--- a/inputFiles/singlePhaseWell/compressible_single_phase_wells_1d.xml
+++ b/inputFiles/singlePhaseWell/compressible_single_phase_wells_1d.xml
@@ -27,17 +27,27 @@
+ control="BHP">
+
+
+
+ control="totalVolRate">
+
+
+
@@ -52,7 +62,6 @@
ny="{ 1 }"
nz="{ 1 }"
cellBlockNames="{ cb1 }">
-
-
+ control="BHP">
+
+
+
+ control="totalVolRate">
+
+
+
@@ -52,7 +62,6 @@
ny="{ 1 }"
nz="{ 1 }"
cellBlockNames="{ cb1 }">
-
+ distanceFromHead="1.45"
+ skinFactor="1"/>
-
+ targetRegions="{ wellRegion1, wellRegion2 }">
+ control="BHP">
+
+
+
+ control="totalVolRate">
+
+
+
@@ -53,7 +63,6 @@
ny="{ 1 }"
nz="{ 1 }"
cellBlockNames="{ cb1 }">
-
-
+ targetRegions="{ wellRegion1, wellRegion2, wellRegion3 }">
+ control="BHP">
+
+
+
+ control="BHP">
+
+
+
+ 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..911c36553c7 100644
--- a/inputFiles/singlePhaseWell/incompressible_single_phase_wells_hybrid_2d.xml
+++ b/inputFiles/singlePhaseWell/incompressible_single_phase_wells_hybrid_2d.xml
@@ -28,24 +28,39 @@
+ control="BHP">
+
+
+
+ control="BHP">
+
+
+
+ control="totalVolRate">
+
+
+
@@ -60,7 +75,6 @@
ny="{ 20 }"
nz="{ 1 }"
cellBlockNames="{ cb1 }">
-
-
-
+ targetRegions="{ wellRegion1, wellRegion2, wellRegion3 }">
+ control="BHP">
+
+
+
+ control="BHP">
+
+
+
+ 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..c8cb5ca9a9f 100644
--- a/inputFiles/singlePhaseWell/staircase_single_phase_wells_3d.xml
+++ b/inputFiles/singlePhaseWell/staircase_single_phase_wells_3d.xml
@@ -16,7 +16,7 @@
@@ -27,17 +27,27 @@
+ control="totalVolRate">
+
+
+
+ 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..c9c874c8a07 100644
--- a/inputFiles/singlePhaseWell/staircase_single_phase_wells_hybrid_3d.xml
+++ b/inputFiles/singlePhaseWell/staircase_single_phase_wells_hybrid_3d.xml
@@ -28,19 +28,29 @@
+ control="BHP">
+
+
+
+ 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..83cf41e02f6 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">
+
+
+
@@ -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..bf01638a399 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">
+
+
+
@@ -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..3ed0c30607e 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">
-
-
+
+
+
+
+
+
+
+
@@ -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..bd711598535 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">
+
+
+
@@ -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..055682b1863 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">
+
+
+
@@ -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/physicsSolvers/PhysicsSolverBase.cpp b/src/coreComponents/physicsSolvers/PhysicsSolverBase.cpp
index 7448c5ec46d..117d916c546 100644
--- a/src/coreComponents/physicsSolvers/PhysicsSolverBase.cpp
+++ b/src/coreComponents/physicsSolvers/PhysicsSolverBase.cpp
@@ -1310,7 +1310,8 @@ void PhysicsSolverBase::debugOutputSystem( real64 const & time,
integer const cycleNumber,
integer const nonlinearIteration,
ParallelMatrix const & matrix,
- ParallelVector const & rhs ) const
+ ParallelVector const & rhs,
+ string const & tag ) const
{
// special case when flag value > 2
if( m_writeLinearSystem > 2 && cycleNumber < m_writeLinearSystem )
@@ -1320,7 +1321,7 @@ void PhysicsSolverBase::debugOutputSystem( real64 const & time,
time,
cycleNumber,
nonlinearIteration,
- getName() + "_mat",
+ getName() + "_mat"+tag,
"System matrix",
m_writeLinearSystem == 1,
m_writeLinearSystem >= 2 );
@@ -1329,7 +1330,7 @@ void PhysicsSolverBase::debugOutputSystem( real64 const & time,
time,
cycleNumber,
nonlinearIteration,
- getName() + "_rhs",
+ getName() + "_rhs"+tag,
"System right-hand side",
m_writeLinearSystem == 1,
m_writeLinearSystem >= 2 );
@@ -1338,7 +1339,8 @@ void PhysicsSolverBase::debugOutputSystem( real64 const & time,
void PhysicsSolverBase::debugOutputSolution( real64 const & time,
integer const cycleNumber,
integer const nonlinearIteration,
- ParallelVector const & solution ) const
+ ParallelVector const & solution,
+ string const & tag ) const
{
// special case when flag value > 2
if( m_writeLinearSystem > 2 && cycleNumber < m_writeLinearSystem )
@@ -1348,7 +1350,7 @@ void PhysicsSolverBase::debugOutputSolution( real64 const & time,
time,
cycleNumber,
nonlinearIteration,
- getName() + "_sol",
+ getName() + "_sol"+tag,
"System solution",
m_writeLinearSystem == 1,
m_writeLinearSystem >= 2 );
diff --git a/src/coreComponents/physicsSolvers/PhysicsSolverBase.hpp b/src/coreComponents/physicsSolvers/PhysicsSolverBase.hpp
index b02c106a751..1362e98450f 100644
--- a/src/coreComponents/physicsSolvers/PhysicsSolverBase.hpp
+++ b/src/coreComponents/physicsSolvers/PhysicsSolverBase.hpp
@@ -484,13 +484,15 @@ class PhysicsSolverBase : public ExecutableGroup
* @param nonlinearIteration current nonlinear iteration number
* @param matrix system matrix
* @param rhs system right-hand side vector
+ * @param tag option to tag the output
*/
void
debugOutputSystem( real64 const & time,
integer const cycleNumber,
integer const nonlinearIteration,
ParallelMatrix const & matrix,
- ParallelVector const & rhs ) const;
+ ParallelVector const & rhs,
+ string const & tag="" ) const;
/**
* @brief Output the linear system solution for debug purposes.
@@ -503,7 +505,9 @@ class PhysicsSolverBase : public ExecutableGroup
debugOutputSolution( real64 const & time,
integer const cycleNumber,
integer const nonlinearIteration,
- ParallelVector const & solution ) const;
+ ParallelVector const & solution,
+ string const & tag ="" ) const;
+
/**
* @brief Update the convergence information and write then into a CSV file
diff --git a/src/coreComponents/physicsSolvers/fluidFlow/CMakeLists.txt b/src/coreComponents/physicsSolvers/fluidFlow/CMakeLists.txt
index 28ffa57e587..c7b51561083 100644
--- a/src/coreComponents/physicsSolvers/fluidFlow/CMakeLists.txt
+++ b/src/coreComponents/physicsSolvers/fluidFlow/CMakeLists.txt
@@ -116,11 +116,21 @@ set( fluidFlowSolvers_headers
wells/SinglePhaseWellFields.hpp
wells/WellConstants.hpp
wells/WellControls.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/WellSolverBase.hpp
wells/WellSolverBaseFields.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 )
@@ -150,6 +160,14 @@ set( fluidFlowSolvers_sources
wells/SinglePhaseWell.cpp
wells/kernels/SinglePhaseWellKernels.cpp
wells/WellControls.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/WellSolverBase.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 14537f0d999..6a8fe95f2ef 100644
--- a/src/coreComponents/physicsSolvers/fluidFlow/wells/CompositionalMultiphaseWell.cpp
+++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/CompositionalMultiphaseWell.cpp
@@ -50,6 +50,25 @@
#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"
+
+// tjb wrong place
+#include "physicsSolvers/multiphysics/CoupledReservoirAndWellKernels.hpp"
+#include "common/MpiWrapper.hpp"
+#include "physicsSolvers/fluidFlow/StencilAccessors.hpp"
+#include "physicsSolvers/fluidFlow/wells/WellControls.hpp"
+#include "mainInterface/ProblemManager.hpp"
+#include "events/EventManager.hpp"
+#include "physicsSolvers/PhysicsSolverManager.hpp"
+#include "physicsSolvers/multiphysics/CompositionalMultiphaseReservoirAndWells.hpp"
+#include "physicsSolvers/fluidFlow/CompositionalMultiphaseFVM.hpp"
+#include "physicsSolvers/fluidFlow/wells/LogLevelsInfo.hpp"
#if defined( __INTEL_COMPILER )
#pragma GCC optimize "O0"
#endif
@@ -72,7 +91,8 @@ CompositionalMultiphaseWell::CompositionalMultiphaseWell( const string & name,
m_maxAbsolutePresChange( -1 ), // disabled by default
m_minScalingFactor( 0.01 ),
m_allowCompDensChopping( 1 ),
- m_targetPhaseIndex( -1 )
+ m_targetPhaseIndex( -1 ),
+ m_wellDebugInit( false )
{
this->registerWrapper( viewKeyStruct::useMassFlagString(), &m_useMass ).
setApplyDefaultValue( 0 ).
@@ -237,24 +257,13 @@ void CompositionalMultiphaseWell::registerDataOnMesh( Group & meshBodies )
WellControls & wellControls = getWellControls( subRegion );
wellControls.registerWrapper< real64 >( viewKeyStruct::currentBHPString() );
- wellControls.registerWrapper< array1d< real64 > >( viewKeyStruct::dCurrentBHPString() ).
- setSizedFromParent( 0 ).
- reference().resizeDimension< 0 >( m_numComponents + 2 ); // dP, dT, dC
-
wellControls.registerWrapper< array1d< real64 > >( viewKeyStruct::currentPhaseVolRateString() ).
setSizedFromParent( 0 ).
reference().resizeDimension< 0 >( m_numPhases );
- wellControls.registerWrapper< array2d< real64 > >( viewKeyStruct::dCurrentPhaseVolRateString() ).
- setSizedFromParent( 0 ).
- reference().resizeDimension< 0, 1 >( m_numPhases, m_numComponents + 3 ); // dP, dT, dC, dQ
-
wellControls.registerWrapper< real64 >( viewKeyStruct::massDensityString() );
wellControls.registerWrapper< real64 >( viewKeyStruct::currentTotalVolRateString() );
- wellControls.registerWrapper< array1d< real64 > >( viewKeyStruct::dCurrentTotalVolRateString() ).
- setSizedFromParent( 0 ).
- reference().resizeDimension< 0 >( m_numComponents + 3 ); // dP, dT, dC dQ
wellControls.registerWrapper< real64 >( viewKeyStruct::massDensityString() );
@@ -387,49 +396,14 @@ void CompositionalMultiphaseWell::validateConstitutiveModels( DomainPartition co
} );
}
-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 );
- 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 );
- }
-}
void CompositionalMultiphaseWell::validateWellConstraints( real64 const & time_n,
real64 const & GEOS_UNUSED_PARAM( dt ),
WellElementSubRegion const & subRegion,
ElementRegionManager const & elemManager )
{
+ GEOS_UNUSED_VAR( time_n );
WellControls & wellControls = getWellControls( subRegion );
if( !wellControls.useSurfaceConditions() )
{
@@ -469,63 +443,14 @@ 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 );
- GEOS_THROW_IF( wellControls.isProducer() && currentControl == WellControls::Control::TOTALVOLRATE,
- "WellControls " << wellControls.getDataContext() <<
- ": Total rate control is not available for producers",
- InputError );
-
- GEOS_THROW_IF( wellControls.isInjector() && targetTotalRate < 0.0,
- "WellControls " << wellControls.getDataContext() <<
- ": Target total rate cannot be negative for injectors",
- InputError );
- GEOS_THROW_IF( wellControls.isInjector() && !isZero( targetPhaseRate ),
- "WellControls " << wellControls.getDataContext() <<
- ": Target phase rate cannot be used for injectors",
- InputError );
- GEOS_THROW_IF( wellControls.isProducer() && !isZero( targetTotalRate ),
- "WellControls " << wellControls.getDataContext() <<
- ": Target total rate cannot be used for producers",
- InputError );
- GEOS_THROW_IF( wellControls.isProducer() && !isZero( targetMassRate ),
- "WellControls " << wellControls.getDataContext() <<
- ": Target mass rate cannot be used for producers",
- InputError );
- GEOS_THROW_IF( !m_useMass && !isZero( targetMassRate ),
- "WellControls " << wellControls.getDataContext() <<
- ": Target mass rate cannot with useMass=0",
- InputError );
-
- // 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 );
- GEOS_THROW_IF( wellControls.isProducer() && !isZero( targetTotalRate ),
- "WellControls " << wellControls.getDataContext() <<
- ": Target total rate cannot be used for producers",
- InputError );
-
- // Find target phase index for phase rate constraint
- for( integer ip = 0; ip < fluid.numFluidPhases(); ++ip )
- {
- if( fluid.phaseNames()[ip] == wellControls.getTargetPhaseName() )
- {
- m_targetPhaseIndex = ip;
- }
- }
- GEOS_THROW_IF( wellControls.isProducer() && m_targetPhaseIndex == -1,
- "WellControls " << wellControls.getDataContext() <<
- ": Phase " << wellControls.getTargetPhaseName() << " not found for well control " << wellControls.getName(),
- InputError );
+ // tjb
+ wellControls.forSubGroups< InjectionConstraint< PhaseVolumeRateConstraint >, ProductionConstraint< PhaseVolumeRateConstraint > >( [&]( auto & constraint )
+ {
+ constraint.validatePhaseType( fluid );
+ } );
+
+
+
}
void CompositionalMultiphaseWell::initializePostSubGroups()
@@ -536,18 +461,491 @@ void CompositionalMultiphaseWell::initializePostSubGroups()
validateConstitutiveModels( domain );
+}
+
+void CompositionalMultiphaseWell::outputWellDebug( real64 const time,
+ real64 const dt,
+ integer num_timesteps,
+ integer num_timestep_cuts,
+ integer current_newton_iteration,
+ DomainPartition & domain,
+ DofManager const & dofManager,
+ CRSMatrixView< real64, globalIndex const > const & localMatrix,
+ arrayView1d< real64 > const & localRhs )
+{
+ // tjbreturn;
+ GEOS_UNUSED_VAR( time );
+ GEOS_UNUSED_VAR( dofManager );
+ GEOS_UNUSED_VAR( localMatrix );
+ GEOS_UNUSED_VAR( localRhs );
forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &,
MeshLevel & mesh,
string_array const & regionNames )
{
- mesh.getElemManager().forElementSubRegions< WellElementSubRegion >( regionNames, [&]( localIndex const,
- WellElementSubRegion & subRegion )
+ mesh.getElemManager().forElementSubRegions< WellElementSubRegion >( regionNames,
+ [&]( localIndex const,
+ WellElementSubRegion & subRegion )
{
- validateInjectionStreams( subRegion );
+
+ if( m_writeSegDebug > 1 )
+ {
+ CompositionalMultiphaseBase const & flowSolver = getParent().getGroup< CompositionalMultiphaseBase >( getFlowSolverName() );
+ auto solver_names = getParent().getSubGroupsNames();
+ //integer n = solver_names.size();
+ // Bit of a hack, cases with > 3 solvers we need to find the base solver for wells
+ // Assume that solver definition order follows coupledreswell, res, and then well
+ //std::string coupled_solver_name = solver_names[n-3];
+
+ //GeosxState & gs = getGlobalState();
+
+ //CompositionalMultiphaseReservoirAndWells< CompositionalMultiphaseBase > * solver =
+ // &(gs.getProblemManager().getPhysicsSolverManager().getGroup< geos::CompositionalMultiphaseReservoirAndWells<
+ // geos::CompositionalMultiphaseBase > >( coupled_solver_name ));
+
+ EventManager const & event = getGroupByPath< EventManager >( "/Problem/Events" );
+ real64 const & ctime = event.getReference< real64 >( EventManager::viewKeyStruct::timeString() );
+ //real64 const dt = event.getReference< real64 >( EventManager::viewKeyStruct::dtString() );
+ integer const & cycle = event.getReference< integer >( EventManager::viewKeyStruct::cycleString() );
+ integer const & subevent = event.getReference< integer >( EventManager::viewKeyStruct::currentSubEventString() );
+
+
+ // std::cout << "tjbtime1 " << ctime << " " << m_globalNumTimeSteps << " " << dt << " " << cycle << " " << subevent
+ // << " " << m_numTimeStepCuts << " " << m_currentNewtonIteration << std::endl;
+ if( true ) // need to fix for restarts cycle >= m_writeSegDebug )
+ {
+ //SolverStatistics & solver_stat = solver->getSolverStatistics();
+ //integer num_timesteps = solver_stat.getReference< integer >( SolverStatistics::viewKeyStruct::numTimeStepsString());
+ //integer current_newton_iteration = solver_stat.getReference< integer >(
+ // SolverStatistics::viewKeyStruct::numCurrentNonlinearIterationsString());
+ //integer num_timestep_cuts = solver_stat.getReference< integer >( SolverStatistics::viewKeyStruct::numTimeStepCutsString());
+ //std::cout << "tjbtime2 " << ctime << " " << m_globalNumTimeSteps << " " << dt << " " << cycle << " " << subevent
+ //<< " " << m_numTimeStepCuts << " " << m_currentNewtonIteration << std::endl;
+ string & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString() );
+ fluidName = getConstitutiveName< MultiFluidBase >( subRegion );
+ WellControls & wellControls = getWellControls( subRegion );
+
+
+ MultiFluidBase const & fluid = subRegion.getConstitutiveModel< MultiFluidBase >( fluidName );
+ PerforationData & perforationData = *subRegion.getPerforationData();
+ using CompFlowAccessors =
+ StencilAccessors< fields::flow::pressure,
+ fields::flow::temperature,
+ fields::flow::phaseVolumeFraction,
+
+
+
+ fields::flow::dPhaseVolumeFraction,
+ fields::flow::globalCompDensity,
+ fields::flow::dGlobalCompFraction_dGlobalCompDensity >;
+ string const flowSolverName = flowSolver.getName();
+ CompFlowAccessors compFlowAccessors( mesh.getElemManager(), flowSolver.getName() );
+
+ using MultiFluidAccessors =
+ StencilMaterialAccessors< MultiFluidBase,
+ fields::multifluid::phaseEnthalpy,
+ fields::multifluid::phaseDensity,
+ fields::multifluid::phaseViscosity,
+ fields::multifluid::dPhaseDensity,
+ fields::multifluid::phaseViscosity,
+ fields::multifluid::phaseInternalEnergy,
+ fields::multifluid::dPhaseViscosity,
+ fields::multifluid::phaseCompFraction,
+ fields::multifluid::dPhaseCompFraction >;
+ MultiFluidAccessors multiFluidAccessors( mesh.getElemManager(), flowSolver.getName() );
+
+ using RelPermAccessors =
+ StencilMaterialAccessors< RelativePermeabilityBase,
+ fields::relperm::phaseRelPerm,
+ fields::relperm::dPhaseRelPerm_dPhaseVolFraction >;
+
+ RelPermAccessors relPermAccessors( mesh.getElemManager(), flowSolver.getName() );
+
+
+ string const srn = subRegion.getName();
+
+ std::vector< string > cp_der {"dP", "dT"};
+ for( integer i=0; i());
+ if( isThermal() )
+ {
+ m_wellPropWriter[srn].registerSegProp( "Temperature", subRegion.getField< fields::well::temperature >());
+ }
+ m_wellPropWriter[srn].registerSegComponentProp( "ComponentDensity", subRegion.getField< fields::well::globalCompDensity >());
+
+ m_wellPropWriter[srn].registerSegProp( "TotalRate", subRegion.getField< fields::well::mixtureConnectionRate >());
+
+ m_wellPropWriter[srn].registerSegProp( "MassDensity", subRegion.getField< fields::well::totalMassDensity >());
+
+ m_wellPropWriter[srn].registerSegComponentProp( "CompFraction", subRegion.getField< fields::well::globalCompFraction >());
+
+ m_wellPropWriter[srn].registerSegPhasePropf( "PhaseDensity", fluid.phaseMassDensity());
+ m_wellPropWriter[srn].registerSegPhasePropf( "PhaseViscosity", fluid.phaseViscosity());
+ m_wellPropWriter[srn].registerSegPhasePropDerf( "dPhaseDensity", cp_der, fluid.dPhaseMassDensity());
+ m_wellPropWriter[srn].registerSegPhaseProp( "PhaseVolumeFraction", subRegion.getField< fields::well::phaseVolumeFraction >());
+ m_wellPropWriter[srn].registerSegPhasePropDer( "dPhaseVolume", cp_der, subRegion.getField< fields::well::dPhaseVolumeFraction >());
+ if( isThermal() )
+ {
+ m_wellPropWriter[srn].registerSegPhasePropf( "InternalEnergy", fluid.phaseInternalEnergy());
+ m_wellPropWriter[srn].registerSegPhasePropDerf( "dPhaseEnthalpy", cp_der, fluid.dPhaseEnthalpy());
+
+ m_wellPropWriter[srn].registerSegPhasePropf( "PhaseEnthalpy", fluid.phaseEnthalpy());
+ m_wellPropWriter[srn].registerSegPhasePropDerf( "dPhaseInternalEnergy", cp_der, fluid.dPhaseInternalEnergy());
+ }
+ m_wellPropWriter[srn].registerSegPhaseComponentPropf( "PhaseCompFrac", fluid.phaseCompFraction());
+
+ // Perforation properties
+ m_wellPropWriter[srn].registerPerf2dProp( {"X", "Y", "Z"}, perforationData.getLocation());
+ m_wellPropWriter[srn].registerPerfResProp( "Pressure", compFlowAccessors.get( fields::flow::pressure {} ));
+ if( isThermal() )
+ {
+ m_wellPropWriter[srn].registerPerfResProp( "Temperature", compFlowAccessors.get( fields::flow::temperature{} ));
+ m_wellPropWriter[srn].registerPerfResPhasePropf( "PhaseEnthalpy", multiFluidAccessors.get( fields::multifluid::phaseEnthalpy{} ));
+ m_wellPropWriter[srn].registerPerfResPhasePropf( "PhaseInternalEnergy", multiFluidAccessors.get( fields::multifluid::phaseInternalEnergy{} ));
+ }
+ m_wellPropWriter[srn].registerPerfComponentProp( "CompPerfRate", perforationData.getField< fields::well::compPerforationRate >());
+ // m_wellPropWriter[srn].registerPerfResComponentProp( "ComponentDensity", compFlowAccessors.get(
+ // fields::flow::globalCompDensity{} ));
+ m_wellPropWriter[srn].registerPerfResPhaseComponentProp( "PhaseCompFrac", multiFluidAccessors.get( fields::multifluid::phaseCompFraction{} ));
+ m_wellPropWriter[srn].registerPerfResPhaseProp( "PhaseVolFrac", compFlowAccessors.get( fields::flow::phaseVolumeFraction{} ));
+ m_wellPropWriter[srn].registerPerfResPhasePropf( "Viscosity", multiFluidAccessors.get( fields::multifluid::phaseViscosity{} ));
+ m_wellPropWriter[srn].registerPerfResPhasePropf( "RelPerm", relPermAccessors.get( fields::relperm::phaseRelPerm{} ));
+
+ m_wellPropWriter[srn].write( ctime, dt, cycle, subevent, num_timesteps,
+ current_newton_iteration,
+ num_timestep_cuts );
+ }
+ }
} );
} );
+
+}
+
+void CompositionalMultiphaseWell::outputSingleWellDebug( real64 const time,
+ real64 const dt,
+ integer num_timesteps,
+ integer current_newton_iteration,
+ integer num_timestep_cuts,
+ MeshLevel & mesh,
+ WellElementSubRegion & subRegion,
+ DofManager const & dofManager,
+ CRSMatrixView< real64, globalIndex const > const & localMatrix,
+ arrayView1d< const real64 > const & localRhs )
+{
+ GEOS_UNUSED_VAR( time );
+ GEOS_UNUSED_VAR( dofManager );
+ GEOS_UNUSED_VAR( localMatrix );
+ GEOS_UNUSED_VAR( localRhs );
+
+
+ if( m_writeSegDebug > 1 )
+ {
+ CompositionalMultiphaseBase const & flowSolver = getParent().getGroup< CompositionalMultiphaseBase >( getFlowSolverName() );
+ auto solver_names = getParent().getSubGroupsNames();
+//integer n = solver_names.size();
+// Bit of a hack, cases with > 3 solvers we need to find the base solver for wells
+// Assume that solver definition order follows coupledreswell, res, and then well
+//std::string coupled_solver_name = solver_names[n-3];
+
+//GeosxState & gs = getGlobalState();
+
+//CompositionalMultiphaseReservoirAndWells< CompositionalMultiphaseBase > * solver =
+// &(gs.getProblemManager().getPhysicsSolverManager().getGroup< geos::CompositionalMultiphaseReservoirAndWells<
+// geos::CompositionalMultiphaseBase > >( coupled_solver_name ));
+
+ EventManager const & event = getGroupByPath< EventManager >( "/Problem/Events" );
+ // real64 const & ctime = event.getReference< real64 >( EventManager::viewKeyStruct::timeString() );
+//real64 const dt = event.getReference< real64 >( EventManager::viewKeyStruct::dtString() );
+ integer const & cycle = event.getReference< integer >( EventManager::viewKeyStruct::cycleString() );
+ integer const & subevent = event.getReference< integer >( EventManager::viewKeyStruct::currentSubEventString() );
+
+
+// std::cout << "tjbtime1 " << ctime << " " << m_globalNumTimeSteps << " " << dt << " " << cycle << " " << subevent
+// << " " << m_numTimeStepCuts << " " << m_currentNewtonIteration << std::endl;
+ if( true ) // need to fix for restarts cycle >= m_writeSegDebug )
+ {
+//SolverStatistics & solver_stat = solver->getSolverStatistics();
+//integer num_timesteps = solver_stat.getReference< integer >( SolverStatistics::viewKeyStruct::numTimeStepsString());
+//integer current_newton_iteration = solver_stat.getReference< integer >(
+// SolverStatistics::viewKeyStruct::numCurrentNonlinearIterationsString());
+//integer num_timestep_cuts = solver_stat.getReference< integer >( SolverStatistics::viewKeyStruct::numTimeStepCutsString());
+//std::cout << "tjbtime2 " << ctime << " " << m_globalNumTimeSteps << " " << dt << " " << cycle << " " << subevent
+//<< " " << m_numTimeStepCuts << " " << m_currentNewtonIteration << std::endl;
+ string & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString() );
+ fluidName = getConstitutiveName< MultiFluidBase >( subRegion );
+ WellControls & wellControls = getWellControls( subRegion );
+
+
+ MultiFluidBase const & fluid = subRegion.getConstitutiveModel< MultiFluidBase >( fluidName );
+ PerforationData & perforationData = *subRegion.getPerforationData();
+ using CompFlowAccessors =
+ StencilAccessors< fields::flow::pressure,
+ fields::flow::temperature,
+ fields::flow::phaseVolumeFraction,
+
+
+
+ fields::flow::dPhaseVolumeFraction,
+ fields::flow::globalCompDensity,
+ fields::flow::dGlobalCompFraction_dGlobalCompDensity >;
+ string const flowSolverName = flowSolver.getName();
+ CompFlowAccessors compFlowAccessors( mesh.getElemManager(), flowSolver.getName() );
+
+ using MultiFluidAccessors =
+ StencilMaterialAccessors< MultiFluidBase,
+ fields::multifluid::phaseEnthalpy,
+ fields::multifluid::phaseDensity,
+ fields::multifluid::phaseViscosity,
+ fields::multifluid::dPhaseDensity,
+ fields::multifluid::phaseViscosity,
+ fields::multifluid::phaseInternalEnergy,
+ fields::multifluid::dPhaseViscosity,
+ fields::multifluid::phaseCompFraction,
+ fields::multifluid::dPhaseCompFraction >;
+ MultiFluidAccessors multiFluidAccessors( mesh.getElemManager(), flowSolver.getName() );
+
+ using RelPermAccessors =
+ StencilMaterialAccessors< RelativePermeabilityBase,
+ fields::relperm::phaseRelPerm,
+ fields::relperm::dPhaseRelPerm_dPhaseVolFraction >;
+
+ RelPermAccessors relPermAccessors( mesh.getElemManager(), flowSolver.getName() );
+
+
+ string const srn = subRegion.getName();
+
+ std::vector< string > cp_der {"dP", "dT"};
+ for( integer i=0; i());
+ if( isThermal() )
+ {
+ m_wellPropWriter[srn].registerSegProp( "Temperature", subRegion.getField< fields::well::temperature >());
+ }
+ m_wellPropWriter[srn].registerSegComponentProp( "ComponentDensity", subRegion.getField< fields::well::globalCompDensity >());
+
+ m_wellPropWriter[srn].registerSegProp( "TotalRate", subRegion.getField< fields::well::mixtureConnectionRate >());
+
+ m_wellPropWriter[srn].registerSegProp( "MassDensity", subRegion.getField< fields::well::totalMassDensity >());
+
+ m_wellPropWriter[srn].registerSegComponentProp( "CompFraction", subRegion.getField< fields::well::globalCompFraction >());
+
+ m_wellPropWriter[srn].registerSegPhasePropf( "PhaseDensity", fluid.phaseMassDensity());
+ m_wellPropWriter[srn].registerSegPhasePropf( "PhaseViscosity", fluid.phaseViscosity());
+ m_wellPropWriter[srn].registerSegPhasePropDerf( "dPhaseDensity", cp_der, fluid.dPhaseMassDensity());
+ m_wellPropWriter[srn].registerSegPhaseProp( "PhaseVolumeFraction", subRegion.getField< fields::well::phaseVolumeFraction >());
+ m_wellPropWriter[srn].registerSegPhasePropDer( "dPhaseVolume", cp_der, subRegion.getField< fields::well::dPhaseVolumeFraction >());
+ if( isThermal() )
+ {
+ m_wellPropWriter[srn].registerSegPhasePropf( "InternalEnergy", fluid.phaseInternalEnergy());
+ m_wellPropWriter[srn].registerSegPhasePropDerf( "dPhaseEnthalpy", cp_der, fluid.dPhaseEnthalpy());
+
+ m_wellPropWriter[srn].registerSegPhasePropf( "PhaseEnthalpy", fluid.phaseEnthalpy());
+ m_wellPropWriter[srn].registerSegPhasePropDerf( "dPhaseInternalEnergy", cp_der, fluid.dPhaseInternalEnergy());
+ }
+ m_wellPropWriter[srn].registerSegPhaseComponentPropf( "PhaseCompFrac", fluid.phaseCompFraction());
+
+// Perforation properties
+ m_wellPropWriter[srn].registerPerf2dProp( {"X", "Y", "Z"}, perforationData.getLocation());
+ m_wellPropWriter[srn].registerPerfResProp( "Pressure", compFlowAccessors.get( fields::flow::pressure {} ));
+ if( isThermal() )
+ {
+ m_wellPropWriter[srn].registerPerfResProp( "Temperature", compFlowAccessors.get( fields::flow::temperature{} ));
+ m_wellPropWriter[srn].registerPerfResPhasePropf( "PhaseEnthalpy", multiFluidAccessors.get( fields::multifluid::phaseEnthalpy{} ));
+ m_wellPropWriter[srn].registerPerfResPhasePropf( "PhaseInternalEnergy", multiFluidAccessors.get( fields::multifluid::phaseInternalEnergy{} ));
+ }
+ m_wellPropWriter[srn].registerPerfComponentProp( "CompPerfRate", perforationData.getField< fields::well::compPerforationRate >());
+ //m_wellPropWriter[srn].registerPerfResComponentProp( "ComponentDensity", compFlowAccessors.get( fields::flow::globalCompDensity{} ));
+ m_wellPropWriter[srn].registerPerfResPhaseComponentProp( "PhaseCompFrac", multiFluidAccessors.get( fields::multifluid::phaseCompFraction{} ));
+ m_wellPropWriter[srn].registerPerfResPhaseProp( "PhaseVolFrac", compFlowAccessors.get( fields::flow::phaseVolumeFraction{} ));
+ m_wellPropWriter[srn].registerPerfResPhasePropf( "Viscosity", multiFluidAccessors.get( fields::multifluid::phaseViscosity{} ));
+ m_wellPropWriter[srn].registerPerfResPhasePropf( "RelPerm", relPermAccessors.get( fields::relperm::phaseRelPerm{} ));
+
+ m_wellPropWriter[srn].write( my_ctime, dt, cycle, subevent, num_timesteps,
+ current_newton_iteration,
+ num_timestep_cuts );
+ }
+
+ }
+
+
}
+
+void CompositionalMultiphaseWell::printSegRates( real64 const & time,
+ real64 const & dt,
+ integer num_timesteps,
+ integer num_timestep_cuts,
+ integer current_newton_iteration,
+ DomainPartition & domain )
+{
+ GEOS_UNUSED_VAR( time );
+
+
+ forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &,
+ MeshLevel & mesh,
+ string_array const & regionNames )
+ {
+ mesh.getElemManager().forElementSubRegions< WellElementSubRegion >( regionNames,
+ [&]( localIndex const,
+ WellElementSubRegion & subRegion )
+ {
+
+ if( m_writeSegDebug > 0 )
+ {
+ CompositionalMultiphaseBase const & flowSolver = getParent().getGroup< CompositionalMultiphaseBase >( getFlowSolverName() );
+ auto solver_names = getParent().getSubGroupsNames();
+ //integer n = solver_names.size();
+ // Bit of a hack, cases with > 3 solvers we need to find the base solver for wells
+ // Assume that solver definition order follows coupledreswell, res, and then well
+ //std::string coupled_solver_name = solver_names[n-3];
+
+ //GeosxState & gs = getGlobalState();
+
+ //CompositionalMultiphaseReservoirAndWells< CompositionalMultiphaseBase > * solver =
+ // &(gs.getProblemManager().getPhysicsSolverManager().getGroup< geos::CompositionalMultiphaseReservoirAndWells<
+ // geos::CompositionalMultiphaseBase > >( coupled_solver_name ));
+
+ EventManager const & event = getGroupByPath< EventManager >( "/Problem/Events" );
+ real64 const & ctime = event.getReference< real64 >( EventManager::viewKeyStruct::timeString() );
+ //real64 const & dt = event.getReference< real64 >( EventManager::viewKeyStruct::dtString() );
+ integer const & cycle = event.getReference< integer >( EventManager::viewKeyStruct::cycleString() );
+ integer const & subevent = event.getReference< integer >( EventManager::viewKeyStruct::currentSubEventString() );
+
+
+ // std::cout << "tjbtime1 " << ctime << " " << m_globalNumTimeSteps << " " << dt << " " << cycle << " " << subevent
+ // << " " << m_numTimeStepCuts << " " << m_currentNewtonIteration << std::endl;
+ if( true ) // need to fix for restarts cycle >= m_writeSegDebug )
+ {
+ //SolverStatistics & solver_stat = solver->getSolverStatistics();
+ //integer num_timesteps = solver_stat.getReference< integer >( SolverStatistics::viewKeyStruct::numTimeStepsString());
+ //integer current_newton_iteration = solver_stat.getReference< integer >(
+ // SolverStatistics::viewKeyStruct::numCurrentNonlinearIterationsString());
+ //integer num_timestep_cuts = solver_stat.getReference< integer >( SolverStatistics::viewKeyStruct::numTimeStepCutsString());
+ //std::cout << "tjbtime2 " << ctime << " " << m_globalNumTimeSteps << " " << dt << " " << cycle << " " << subevent
+ //<< " " << m_numTimeStepCuts << " " << m_currentNewtonIteration << std::endl;
+ string & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString() );
+ fluidName = getConstitutiveName< MultiFluidBase >( subRegion );
+ WellControls & wellControls = getWellControls( subRegion );
+
+
+ MultiFluidBase const & fluid = subRegion.getConstitutiveModel< MultiFluidBase >( fluidName );
+ PerforationData & perforationData = *subRegion.getPerforationData();
+ using CompFlowAccessors =
+ StencilAccessors< fields::flow::pressure,
+ fields::flow::temperature,
+ fields::flow::phaseVolumeFraction,
+ fields::flow::dPhaseVolumeFraction,
+ fields::flow::globalCompDensity,
+ fields::flow::dGlobalCompFraction_dGlobalCompDensity >;
+ string const flowSolverName = flowSolver.getName();
+ CompFlowAccessors compFlowAccessors( mesh.getElemManager(), flowSolver.getName() );
+
+ using MultiFluidAccessors =
+ StencilMaterialAccessors< MultiFluidBase,
+ fields::multifluid::phaseDensity,
+ fields::multifluid::dPhaseDensity,
+ fields::multifluid::phaseInternalEnergy,
+ fields::multifluid::phaseEnthalpy,
+ fields::multifluid::dPhaseDensity,
+ fields::multifluid::phaseViscosity,
+ fields::multifluid::dPhaseViscosity,
+ fields::multifluid::phaseCompFraction,
+ fields::multifluid::dPhaseCompFraction >;
+ MultiFluidAccessors multiFluidAccessors( mesh.getElemManager(), flowSolver.getName() );
+
+ using RelPermAccessors =
+ StencilMaterialAccessors< RelativePermeabilityBase,
+ fields::relperm::phaseRelPerm,
+ fields::relperm::dPhaseRelPerm_dPhaseVolFraction >;
+
+ RelPermAccessors relPermAccessors( mesh.getElemManager(), flowSolver.getName() );
+
+
+ string const srn = subRegion.getName();
+
+ std::vector< string > cp_der {"dP", "dT"};
+ for( integer i=0; i());
+ if( isThermal() )
+ {
+ m_wellPropWriter_eot[srn].registerSegProp( "Temperature", subRegion.getField< fields::well::temperature >());
+ }
+ m_wellPropWriter_eot[srn].registerSegComponentProp( "ComponentDensity", subRegion.getField< fields::well::globalCompDensity >());
+
+ m_wellPropWriter_eot[srn].registerSegProp( "TotalRate", subRegion.getField< fields::well::mixtureConnectionRate >());
+
+ m_wellPropWriter_eot[srn].registerSegProp( "MassDensity", subRegion.getField< fields::well::totalMassDensity >());
+
+ m_wellPropWriter_eot[srn].registerSegComponentProp( "CompFraction", subRegion.getField< fields::well::globalCompFraction >());
+
+ m_wellPropWriter_eot[srn].registerSegPhasePropf( "PhaseDensity", fluid.phaseMassDensity());
+ m_wellPropWriter_eot[srn].registerSegPhasePropf( "PhaseViscosity", fluid.phaseViscosity());
+ m_wellPropWriter_eot[srn].registerSegPhaseProp( "PhaseVolumeFraction", subRegion.getField< fields::well::phaseVolumeFraction >());
+ if( isThermal() )
+ {
+ m_wellPropWriter_eot[srn].registerSegPhasePropf( "InternalEnergy", fluid.phaseInternalEnergy());
+ m_wellPropWriter_eot[srn].registerSegPhasePropf( "PhaseEnthalpy", fluid.phaseEnthalpy());
+ }
+ m_wellPropWriter_eot[srn].registerSegPhaseComponentPropf( "PhaseCompFrac", fluid.phaseCompFraction());
+
+ // Perforation properties
+ m_wellPropWriter_eot[srn].registerPerf2dProp( {"X", "Y", "Z"}, perforationData.getLocation());
+
+
+ m_wellPropWriter_eot[srn].registerPerfResProp( "Pressure", compFlowAccessors.get( fields::flow::pressure {} ));
+ if( isThermal() )
+ {
+ m_wellPropWriter_eot[srn].registerPerfResProp( "Temperature", compFlowAccessors.get( fields::flow::temperature{} ));
+ m_wellPropWriter_eot[srn].registerPerfResPhasePropf( "PhaseEnthalpy", multiFluidAccessors.get( fields::multifluid::phaseEnthalpy{} ));
+ m_wellPropWriter_eot[srn].registerPerfResPhasePropf( "PhaseInternalEnergy", multiFluidAccessors.get( fields::multifluid::phaseInternalEnergy{} ));
+ }
+ m_wellPropWriter_eot[srn].registerPerfComponentProp( "CompPerfRate", perforationData.getField< fields::well::compPerforationRate >());
+ //m_wellPropWriter_eot[srn].registerPerfResComponentProp( "ComponentDensity", compFlowAccessors.get(
+ // fields::flow::globalCompDensity{} ));
+ m_wellPropWriter_eot[srn].registerPerfResPhaseComponentProp( "PhaseCompFrac", multiFluidAccessors.get( fields::multifluid::phaseCompFraction{} ));
+ m_wellPropWriter_eot[srn].registerPerfResPhaseProp( "PhaseVolFrac", compFlowAccessors.get( fields::flow::phaseVolumeFraction{} ));
+ m_wellPropWriter_eot[srn].registerPerfResPhasePropf( "Viscosity", multiFluidAccessors.get( fields::multifluid::phaseViscosity{} ));
+ m_wellPropWriter_eot[srn].registerPerfResPhasePropf( "RelPerm", relPermAccessors.get( fields::relperm::phaseRelPerm{} ));
+
+ m_wellPropWriter_eot[srn].write( ctime, dt, cycle, subevent, num_timesteps,
+ current_newton_iteration,
+ num_timestep_cuts );
+ }
+ }
+ } );
+ } );
+
+}
void CompositionalMultiphaseWell::initializePostInitialConditionsPreSubGroups()
{
WellSolverBase::initializePostInitialConditionsPreSubGroups();
@@ -619,63 +1017,35 @@ 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();
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 )
+ // 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 ) );
@@ -692,8 +1062,7 @@ void CompositionalMultiphaseWell::updateVolRatesForConstraint( WellElementSubReg
return;
}
- integer constexpr maxNumComp = constitutive::MultiFluidBase::MAX_NUM_COMPONENTS;
- integer const numComp = m_numComponents;
+
integer const numPhase = m_numPhases;
localIndex const iwelemRef = subRegion.getTopWellElementIndex();
@@ -701,60 +1070,21 @@ void CompositionalMultiphaseWell::updateVolRatesForConstraint( WellElementSubReg
// 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 >();
-
// 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();
+ 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";
- integer const useSurfaceConditions = wellControls.useSurfaceConditions();
- real64 flashPressure;
- real64 flashTemperature;
- if( useSurfaceConditions )
- {
- // use surface conditions
- flashPressure = wellControls.getSurfacePressure();
- flashTemperature = wellControls.getSurfaceTemperature();
- }
- else
- {
- // 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
- {
- // use reservoir region averages
- flashTemperature = wellControls.getRegionAverageTemperature();
- }
- }
arrayView1d< real64 > const & currentPhaseVolRate =
wellControls.getReference< array1d< real64 > >( CompositionalMultiphaseWell::viewKeyStruct::currentPhaseVolRateString() );
- arrayView2d< real64 > const & dCurrentPhaseVolRate =
- wellControls.getReference< array2d< real64 > >( CompositionalMultiphaseWell::viewKeyStruct::dCurrentPhaseVolRateString() );
real64 & currentTotalVolRate =
wellControls.getReference< real64 >( CompositionalMultiphaseWell::viewKeyStruct::currentTotalVolRateString() );
@@ -762,165 +1092,173 @@ void CompositionalMultiphaseWell::updateVolRatesForConstraint( WellElementSubReg
real64 & currentMassRate =
wellControls.getReference< real64 >( CompositionalMultiphaseWell::viewKeyStruct::currentMassRateString() );
- arrayView1d< real64 > const & dCurrentTotalVolRate =
- wellControls.getReference< array1d< real64 > >( CompositionalMultiphaseWell::viewKeyStruct::dCurrentTotalVolRateString() );
-
real64 & massDensity =
wellControls.getReference< real64 >( CompositionalMultiphaseWell::viewKeyStruct::massDensityString() );
- constitutive::constitutiveUpdatePassThru( fluidSeparator, [&] ( auto & castedFluidSeparator )
+
+ // 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 )
{
- // typename TYPEOFREF( castedFluid ) ::KernelWrapper fluidWrapper = castedFluid.createKernelWrapper();
- typename TYPEOFREF( castedFluidSeparator ) ::KernelWrapper fluidSeparatorWrapper = castedFluidSeparator.createKernelWrapper();
- geos::internal::kernelLaunchSelectorCompThermSwitch( numComp, isThermal, [&] ( auto NC, auto ISTHERMAL )
- {
- 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
+ // Step 1: update the total volume rate
- }
- 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 1.1: compute the inverse of the total density and derivatives
+ massDensity = totalDens[iwelemRef][0];
+ real64 const totalDensInv = 1.0 / totalDens[iwelemRef][0];
- 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 1.2: divide the total mass/molar rate by the total density to get the total volumetric rate
+ currentTotalVolRate = currentTotalRate * totalDensInv;
- 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;
- }
+ // Step 2: update the phase volume rate
+ for( integer ip = 0; ip < numPhase; ++ip )
+ {
+ // Step 2.1: compute the inverse of the (phase density * phase fraction) and derivatives
- 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 ) );
- }
+ // skip the rest of this function if phase ip is absent
+ bool const phaseExists = (phaseFrac[iwelemRef][0][ip] > 0);
+ if( !phaseExists )
+ {
+ continue;
+ }
- dCurrentTotalVolRate[COFFSET_WJ::dQ] = totalDensInv;
- for( integer ic = 0; ic < numComp; ++ic )
- {
- dCurrentTotalVolRate[COFFSET_WJ::dC+ic] = currentTotalRate * dTotalDensInv_dCompDens[ic];
- }
+ real64 const phaseDensInv = 1.0 / phaseDens[iwelemRef][0][ip];
+ real64 const phaseFracTimesPhaseDensInv = phaseFrac[iwelemRef][0][ip] * phaseDensInv;
- // Step 3: update the phase volume rate
- for( integer ip = 0; ip < numPhase; ++ip )
- {
+ // 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;
+ }
+ } );
- // Step 3.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;
- }
+void CompositionalMultiphaseWell::calculateReferenceElementRates( WellElementSubRegion & subRegion )
+{
+ GEOS_MARK_FUNCTION;
- 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;
+ // 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();
- // 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;
- }
+ WellControls & wellControls = getWellControls( subRegion );
- 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() );
+ // subRegion data
+ arrayView1d< real64 const > const & connRate = subRegion.getField< fields::well::mixtureConnectionRate >();
- 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] ) );
- }
+ // fluid data
+ constitutive::MultiFluidBase & fluidSeparator = wellControls.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 = wellControls.getName();
+ bool const logSurfaceCondition = isLogLevelActive< logInfo::BoundaryConditions >( wellControls.getLogLevel());
+ string const massUnit = m_useMass ? "kg" : "mol";
+
+ integer const useSurfaceConditions = wellControls.useSurfaceConditions();
+
+ arrayView1d< real64 > const & currentPhaseVolRate =
+ wellControls.getReference< array1d< real64 > >( CompositionalMultiphaseWell::viewKeyStruct::currentPhaseVolRateString() );
+
+ real64 & currentTotalVolRate =
+ wellControls.getReference< real64 >( CompositionalMultiphaseWell::viewKeyStruct::currentTotalVolRateString() );
+
+ real64 & currentMassRate =
+ wellControls.getReference< real64 >( CompositionalMultiphaseWell::viewKeyStruct::currentMassRateString() );
+
+ real64 & massDensity =
+ wellControls.getReference< real64 >( CompositionalMultiphaseWell::viewKeyStruct::massDensityString() );
+
+ 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,
+ &numPhase,
+ connRate,
+ totalDens,
+ phaseDens,
+ phaseFrac,
+ logSurfaceCondition,
+ &useSurfaceConditions,
+ ¤tTotalVolRate,
+ currentPhaseVolRate,
+ ¤tMassRate,
+ &iwelemRef,
+ &wellControlsName,
+ &massUnit,
+ &massDensity] ( localIndex const )
+ {
+ GEOS_UNUSED_VAR( massUnit );
+
+
+ // 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
+ massDensity = totalDens[iwelemRef][0];
+ real64 const totalDensInv = 1.0 / totalDens[iwelemRef][0];
+
+
+ // Step 2.2: divide the total mass/molar rate by the total density to get the total volumetric rate
+ currentTotalVolRate = currentTotalRate * totalDensInv;
+
+
+ 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 ) );
+ }
+
+ // 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)
+
+ // 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 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] ) );
+ }
+ }
} );
} );
}
-
void CompositionalMultiphaseWell::updateFluidModel( WellElementSubRegion & subRegion )
{
GEOS_MARK_FUNCTION;
@@ -947,6 +1285,102 @@ void CompositionalMultiphaseWell::updateFluidModel( WellElementSubRegion & subRe
}
+void CompositionalMultiphaseWell::updateSeparator( 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();
+
+ WellControls & wellControls = getWellControls( subRegion );
+
+ // 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 = wellControls.getMultiFluidSeparator();
+
+ // control data
+
+ string const wellControlsName = wellControls.getName();
+ bool const logSurfaceCondition = isLogLevelActive< logInfo::BoundaryConditions >( wellControls.getLogLevel());
+ string const massUnit = m_useMass ? "kg" : "mol";
+
+ integer const useSurfaceConditions = wellControls.useSurfaceConditions();
+ real64 flashPressure;
+ real64 flashTemperature;
+ if( useSurfaceConditions )
+ {
+ // use surface conditions
+ flashPressure = wellControls.getSurfacePressure();
+ flashTemperature = wellControls.getSurfaceTemperature();
+ }
+ else
+ {
+ // 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
+ {
+ // use reservoir region averages
+ flashTemperature = wellControls.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,
+ useSurfaceConditions,
+ 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( useSurfaceConditions )
+ {
+ // 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;
@@ -995,10 +1429,20 @@ void CompositionalMultiphaseWell::updateTotalMassDensity( WellElementSubRegion &
}
-void CompositionalMultiphaseWell::updateState( DomainPartition & domain )
+real64 CompositionalMultiphaseWell::updateWellState( WellElementSubRegion & subRegion )
{
GEOS_MARK_FUNCTION;
+ real64 maxPhaseVolFrac = updateSubRegionState( subRegion );
+
+ return maxPhaseVolFrac;
+
+}
+
+void CompositionalMultiphaseWell::updateState( DomainPartition & domain )
+{
+ GEOS_MARK_FUNCTION;
+ //tjb
real64 maxPhaseVolFrac = 0.0;
forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &,
MeshLevel & mesh,
@@ -1008,9 +1452,14 @@ void CompositionalMultiphaseWell::updateState( DomainPartition & domain )
WellElementSubRegion & subRegion )
{
WellControls & wellControls = getWellControls( subRegion );
- if( wellControls.getWellStatus() == WellControls::Status::OPEN )
+ if( wellControls.getWellState())
{
+#if 1
+ real64 const maxRegionPhaseVolFrac = updateWellState( subRegion );
+#else
real64 const maxRegionPhaseVolFrac = updateSubRegionState( subRegion );
+
+#endif
maxPhaseVolFrac = LvArray::math::max( maxRegionPhaseVolFrac, maxPhaseVolFrac );
}
} );
@@ -1025,30 +1474,259 @@ void CompositionalMultiphaseWell::updateState( DomainPartition & domain )
real64 CompositionalMultiphaseWell::updateSubRegionState( WellElementSubRegion & subRegion )
{
- // update properties
- updateGlobalComponentFraction( subRegion );
+ real64 maxPhaseVolChange=0.0;
+ WellControls & wellControls = getWellControls( subRegion );
+ if( wellControls.getWellState())
+ {
+ if( m_useNewCode )
+ {
+ // update properties
+ updateGlobalComponentFraction( subRegion );
+
+ // update densities, phase fractions, phase volume fractions
+
+ updateFluidModel( subRegion ); // Calculate fluid properties
+
+ updateSeparator( subRegion ); // Calculate fluid properties at control conditions
- // update volumetric rates for the well constraints
- // note: this must be called before updateFluidModel
- updateVolRatesForConstraint( subRegion );
+ updateVolRatesForConstraint( subRegion ); // remove tjb ??
- // update densities, phase fractions, phase volume fractions
+ maxPhaseVolChange = updatePhaseVolumeFraction( subRegion );
+ updateTotalMassDensity( subRegion );
- updateFluidModel( subRegion ); // Calculate fluid properties;
- real64 maxPhaseVolChange = updatePhaseVolumeFraction( subRegion );
- updateTotalMassDensity( subRegion );
- // update the current BHP pressure
- updateBHPForConstraint( subRegion );
+ // Calculate the reference element rates
+ calculateReferenceElementRates( subRegion );
+
+ // update the current BHP
+ updateBHPForConstraint( subRegion );
+
+ }
+ else
+ {
+ // update properties
+ updateGlobalComponentFraction( subRegion );
+
+ // update volumetric rates for the well constraints
+ // note: this must be called before updateFluidModel
+
+ updateVolRatesForConstraint( subRegion );
+
+ // update densities, phase fractions, phase volume fractions
+
+ updateFluidModel( subRegion ); // Calculate fluid properties;
+ maxPhaseVolChange = updatePhaseVolumeFraction( subRegion );
+ updateTotalMassDensity( subRegion );
+ // update the current BHP pressure
+ updateBHPForConstraint( subRegion );
+ }
+
+ }
return maxPhaseVolChange;
}
-void CompositionalMultiphaseWell::initializeWells( DomainPartition & domain, real64 const & time_n )
+void CompositionalMultiphaseWell::initializeWell( DomainPartition & domain, MeshLevel & mesh, WellElementSubRegion & subRegion, real64 const & time_n )
{
GEOS_MARK_FUNCTION;
-
+ GEOS_UNUSED_VAR( domain );
integer const numComp = m_numComponents;
integer const numPhase = m_numPhases;
+ m_nextDt = -1;
+ // TODO: change the way we access the flowSolver here
+ CompositionalMultiphaseBase const & flowSolver = getParent().getGroup< CompositionalMultiphaseBase >( getFlowSolverName() );
+
+
+ compositionalMultiphaseWellKernels::PresTempCompFracInitializationKernel::CompFlowAccessors
+ resCompFlowAccessors( mesh.getElemManager(), flowSolver.getName() );
+ compositionalMultiphaseWellKernels::PresTempCompFracInitializationKernel::MultiFluidAccessors
+ resMultiFluidAccessors( mesh.getElemManager(), flowSolver.getName() );
+
+ WellControls & wellControls = getWellControls( subRegion );
+ PerforationData const & perforationData = *subRegion.getPerforationData();
+ arrayView2d< real64 const > const compPerfRate = perforationData.getField< fields::well::compPerforationRate >();
+
+ bool const hasNonZeroRate = MpiWrapper::max< integer >( hasNonZero( compPerfRate ));
+
+ if( time_n <= 0.0 || ( wellControls.isWellOpen( ) && !hasNonZeroRate ) )
+ {
+ wellControls.setWellState( true );
+ if( wellControls.getCurrentConstraint() == nullptr )
+ {
+ // tjb needed for backward compatibility. and these 2 lists must be consistent
+ ConstraintTypeId inputControl = ConstraintTypeId( wellControls.getInputControl());
+ if( wellControls.isProducer() )
+ {
+ wellControls.forSubGroups< MinimumBHPConstraint, ProductionConstraint< VolumeRateConstraint >, ProductionConstraint< MassRateConstraint >,
+ ProductionConstraint< PhaseVolumeRateConstraint > >( [&]( auto & constraint )
+ {
+ if( constraint.getControl() == inputControl )
+ {
+ wellControls.setCurrentConstraint( &constraint );
+ wellControls.setControl( static_cast< WellControls::Control >(inputControl) ); // tjb old
+ }
+
+ } );
+ }
+ else
+ {
+
+ wellControls.forSubGroups< MaximumBHPConstraint, InjectionConstraint< VolumeRateConstraint >, InjectionConstraint< MassRateConstraint >,
+ InjectionConstraint< PhaseVolumeRateConstraint > >( [&]( auto & constraint )
+ {
+ if( constraint.getControl() == inputControl )
+ {
+ wellControls.setCurrentConstraint( &constraint );
+ wellControls.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::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 >();
+ arrayView1d< localIndex const > const & perfWellElemIndex =perforationData.getField< fields::perforation::wellElementIndex >();
+ // 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,
+ perfWellElemIndex,
+ 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( subRegion );
+
+ // 6) Estimate the well rates
+ // TODO: initialize rates using perforation rates
+ compositionalMultiphaseWellKernels::
+ RateInitializationKernel::
+ launch( subRegion.size(),
+ wellControls,
+ 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 = wellControls.getCurrentConstraint();
+ constraint->setBHP ( wellControls.getReference< real64 >( CompositionalMultiphaseWell::viewKeyStruct::currentBHPString() ));
+ constraint->setPhaseVolumeRates ( wellControls.getReference< array1d< real64 > >(
+ CompositionalMultiphaseWell::viewKeyStruct::currentPhaseVolRateString() ) );
+ constraint->setTotalVolumeRate ( wellControls.getReference< real64 >(
+ CompositionalMultiphaseWell::viewKeyStruct::currentTotalVolRateString() ));
+ constraint->setMassRate( wellControls.getReference< real64 >( CompositionalMultiphaseWell::viewKeyStruct::currentMassRateString() ));
+ // 7) Copy well / fluid dofs to "prop"_n variables
+ saveState( subRegion );
+ }
+ else if( !hasNonZeroRate )
+ {
+ wellControls.setWellState( false );
+ GEOS_LOG_RANK_0( "tjb shut wells "<< subRegion.getName());
+ }
+ else
+ {
+ wellControls.setWellState( true );
+ // setup if restart
+ if( wellControls.getCurrentConstraint() == nullptr )
+ {
+ updateSubRegionState( subRegion );
+ if( wellControls.isProducer() )
+ {
+ wellControls.forSubGroups< MinimumBHPConstraint, ProductionConstraint< VolumeRateConstraint >, ProductionConstraint< MassRateConstraint >,
+ ProductionConstraint< PhaseVolumeRateConstraint > >( [&](
+ auto
+ & constraint )
+ {
+ if( ConstraintTypeId( wellControls.getControl()) == constraint.getControl() )
+ {
+ wellControls.setCurrentConstraint( &constraint );
+ }
+ } );
+ }
+ else
+ {
+ wellControls.forSubGroups< MaximumBHPConstraint, InjectionConstraint< VolumeRateConstraint >, InjectionConstraint< MassRateConstraint >, InjectionConstraint< PhaseVolumeRateConstraint > >( [&](
+ auto
+ &
+ constraint )
+ {
+ if( ConstraintTypeId( wellControls.getControl()) == constraint.getControl() )
+ {
+ wellControls.setCurrentConstraint( &constraint );
+ }
+ } );
+ }
+ }
+
+ }
+}
+
+
+void CompositionalMultiphaseWell::initializeWells( DomainPartition & domain, real64 const & time_n )
+{
+ GEOS_MARK_FUNCTION;
+ m_nextDt = -1;
// TODO: change the way we access the flowSolver here
CompositionalMultiphaseBase const & flowSolver = getParent().getGroup< CompositionalMultiphaseBase >( getFlowSolverName() );
@@ -1068,103 +1746,68 @@ void CompositionalMultiphaseWell::initializeWells( DomainPartition & domain, rea
[&]( localIndex const,
WellElementSubRegion & subRegion )
{
- WellControls & wellControls = getWellControls( subRegion );
- PerforationData const & perforationData = *subRegion.getPerforationData();
- arrayView2d< real64 const > const compPerfRate = perforationData.getField< fields::well::compPerforationRate >();
- bool const hasNonZeroRate = MpiWrapper::max< integer >( hasNonZero( compPerfRate ));
+ initializeWell( domain, mesh, subRegion, time_n );
+ } );
- if( wellControls.isWellOpen() && !hasNonZeroRate )
- {
- // 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 )
- {
- typename TYPEOFREF( castedFluid ) ::KernelWrapper fluidWrapper = castedFluid.createKernelWrapper();
-
- thermalCompositionalMultiphaseBaseKernels::
- FluidUpdateKernel::
- launch< serialPolicy >( subRegion.size(),
- fluidWrapper,
- wellElemPressure,
- wellElemTemp,
- wellElemCompFrac );
- } );
+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 );
- compositionalMultiphaseWellKernels::
- CompDensInitializationKernel::launch( subRegion.size(),
- numComp,
- wellElemCompFrac,
- wellElemTotalDens,
- wellElemCompDens );
+ BitFlags< isothermalCompositionalMultiphaseBaseKernels::KernelFlags > kernelFlags;
+ if( m_useTotalMassEquation )
+ kernelFlags.set( isothermalCompositionalMultiphaseBaseKernels::KernelFlags::TotalMassEquation );
- // 5) Recompute the pressure-dependent properties
- updateSubRegionState( subRegion );
+ string const wellDofKey = dofManager.getKey( wellElementDofName());
+
+ WellControls const & well_controls = getWellControls( subRegion );
+ if( well_controls.isWellOpen( ) && !m_keepVariablesConstantDuringInitStep )
+ {
+ string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString());
+ MultiFluidBase const & fluid = getConstitutiveModel< MultiFluidBase >( subRegion, fluidName );
+ int numComponents = fluid.numFluidComponents();
+
+ 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 );
+ }
+ }
- // 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 );
- }
- } );
- } );
}
void CompositionalMultiphaseWell::assembleFluxTerms( real64 const & time,
@@ -1190,43 +1833,9 @@ void CompositionalMultiphaseWell::assembleFluxTerms( real64 const & time,
[&]( localIndex const,
WellElementSubRegion & subRegion )
{
- 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();
- 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 );
- }
- }
+ assembleWellFluxTerms( time, dt, subRegion, dofManager, localMatrix, localRhs );
+
} );
} );
@@ -1256,106 +1865,348 @@ void CompositionalMultiphaseWell::assembleAccumulationTerms( real64 const & time
[&]( localIndex const,
WellElementSubRegion & subRegion )
{
- 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 )
+
+ assembleWellAccumulationTerms( time, dt, subRegion, dofManager, localMatrix, 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 );
+ GEOS_UNUSED_VAR( dt );
+
+ BitFlags< isothermalCompositionalMultiphaseBaseKernels::KernelFlags > kernelFlags;
+ if( m_useTotalMassEquation )
+ kernelFlags.set( isothermalCompositionalMultiphaseBaseKernels::KernelFlags::TotalMassEquation );
+
+ string const wellDofKey = dofManager.getKey( wellElementDofName() );
+
+ 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( isThermal() )
+ {
+
+ thermalCompositionalMultiphaseWellKernels::
+ ElementBasedAssemblyKernelFactory::
+ createAndLaunch< parallelDevicePolicy<> >( numComponents,
+ numPhases,
+ wellControls,
+ wellControls.isProducer(),
+ dofManager.rankOffset(),
+ kernelFlags,
+ wellDofKey,
+ subRegion,
+ fluid,
+ localMatrix,
+ localRhs );
+ }
+ else
+ {
+ compositionalMultiphaseWellKernels::
+ ElementBasedAssemblyKernelFactory::
+ createAndLaunch< parallelDevicePolicy<> >( numComponents,
+ numPhases,
+ wellControls,
+ 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 )
{
- 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 );
+ 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;
+ }
}
- else
+ }
+ } );
+
+ }
+ else
+ {
+//wellControls.setWellOpen(false);
+// get the degrees of freedom and ghosting info
+ // 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 > mixConnRate = subRegion.getField< fields::well::mixtureConnectionRate >();
+ localIndex rank_offset = dofManager.rankOffset();
+ //bool wellClosed = wellControls.getWellStatus() != WellControls::Status::OPEN;
+ forAll< parallelDevicePolicy<> >( subRegion.size(), [=] GEOS_HOST_DEVICE ( localIndex const ei )
+ {
+ if( wellElemGhostRank[ei] < 0 )
+ {
+ 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++ )
{
- compositionalMultiphaseWellKernels::
- ElementBasedAssemblyKernelFactory::
- createAndLaunch< parallelDevicePolicy<> >( numComponents,
- numPhases,
- wellControls.isProducer(),
- dofManager.rankOffset(),
- kernelFlags,
- wellDofKey,
- subRegion,
- fluid,
- localMatrix,
- localRhs );
+ globalIndex const rindex = localRow+i;
+ globalIndex const cindex =dofIndex + i;
+ localMatrix.template addToRow< serialAtomic >( rindex,
+ &cindex,
+ &unity,
+ 1 );
+ localRhs[rindex] = 0.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();
- 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 )
- {
- 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;
- }
- }
- }
- } );
}
- else
+ } );
+ }
+
+
+
+}
+
+void
+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
+ WellControls const & wellControls = getWellControls( subRegion );
+ //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( !wellControls.isWellOpen( ) )
+ {
+ return;
+ }
+
+ PerforationData const * const perforationData = subRegion.getPerforationData();
+
+ // get the degrees of freedom
+ string const wellDofKey = dofManager.getKey( wellElementDofName() );
+#if 1
+ if( isThermal ( ) )
+ {
+ coupledReservoirAndWellKernels::
+ ThermalCompositionalMultiPhaseWellFluxKernelFactory::
+ createAndLaunch< parallelDevicePolicy<> >( numComps,
+ wellControls,
+ wellControls.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 );
+ }
+#endif
+
+}
+
+void
+CompositionalMultiphaseWell::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 ) )
+{}
+
+real64
+CompositionalMultiphaseWell::calculateWellResidualNorm( real64 const & time_n,
+ real64 const & dt,
+ 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() );
+
+
+
+ string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString() );
+ MultiFluidBase const & fluid = subRegion.getConstitutiveModel< MultiFluidBase >( fluidName );
+
+ WellControls const & wellControls = getWellControls( subRegion );
+
+ if( wellControls.isWellOpen( ) )
+ {
+ // step 1: compute the norm in the subRegion
+ if( isThermal() )
+ {
+ real64 subRegionResidualNorm[2]{};
+
+ thermalCompositionalMultiphaseWellKernels::ResidualNormKernelFactory::
+ createAndLaunch< parallelDevicePolicy<> >( m_numComponents,
+ 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 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 )
+ if( subRegionResidualNorm[i] > localResidualNorm[i] )
{
- if( wellElemGhostRank[ei] < 0 )
- {
- globalIndex const dofIndex = wellElemDofNumber[ei];
- localIndex const localRow = dofIndex - rank_offset;
+ localResidualNorm[i] = subRegionResidualNorm[i];
+ }
+ }
+
+ }
+ else
+ {
+ real64 subRegionResidualNorm[1]{};
+ compositionalMultiphaseWellKernels::ResidualNormKernelFactory::
+ createAndLaunch< parallelDevicePolicy<> >( m_numComponents,
+ numDofPerWellElement(),
+ 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];
+ }
+ }
+ }
+ else
+ {
+ for( integer i=0; i( rindex,
- &cindex,
- &unity,
- 1 );
- localRhs[rindex] = 0.0;
- }
- }
- } );
- }
- } ); // forElementSubRegions
- } ); // forDiscretizationOnMeshTargets
+ GEOS_LOG_LEVEL_RANK_0( logInfo::ResidualNorm, GEOS_FMT( " ( R{} ) = ( {:4.2e} ) ( Renergy ) = ( {:4.2e} )",
+ coupledSolverAttributePrefix(), globalResidualNorm[0], globalResidualNorm[1] ));
+
+ }
+ else
+ {
+ resNorm= MpiWrapper::max( resNorm );
+
+ GEOS_LOG_LEVEL_RANK_0( logInfo::ResidualNorm, GEOS_FMT( " ( R{} ) = ( {:4.2e} )",
+ coupledSolverAttributePrefix(), resNorm ));
+ }
+ return resNorm;
}
@@ -1403,61 +2254,69 @@ CompositionalMultiphaseWell::calculateResidualNorm( real64 const & time_n,
WellControls const & wellControls = getWellControls( subRegion );
// step 1: compute the norm in the subRegion
-
- if( isThermal() )
+ if( wellControls.isWellOpen( ) )
{
- real64 subRegionResidualNorm[2]{};
+ if( isThermal() )
+ {
+ real64 subRegionResidualNorm[2]{};
- thermalCompositionalMultiphaseWellKernels::ResidualNormKernelFactory::
- createAndLaunch< parallelDevicePolicy<> >( m_numComponents,
- m_targetPhaseIndex,
- rankOffset,
- wellDofKey,
- localRhs,
- subRegion,
- fluid,
- wellControls,
- time_n,
- dt,
- m_nonlinearSolverParameters.m_minNormalizer,
- subRegionResidualNorm );
- // step 2: reduction across meshBodies/regions/subRegions
+ thermalCompositionalMultiphaseWellKernels::ResidualNormKernelFactory::
+ createAndLaunch< parallelDevicePolicy<> >( m_numComponents,
+ 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] )
+ for( integer i=0; i localResidualNorm[i] )
+ {
+ localResidualNorm[i] = subRegionResidualNorm[i];
+ }
}
- }
- }
- else
- {
- 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 );
+ }
+ else
+ {
+ real64 subRegionResidualNorm[1]{};
+ compositionalMultiphaseWellKernels::ResidualNormKernelFactory::
+ createAndLaunch< parallelDevicePolicy<> >( m_numComponents,
+ numDofPerWellElement(),
+ rankOffset,
+ wellDofKey,
+ localRhs,
+ subRegion,
+ fluid,
+ wellControls,
+ time_n,
+ dt,
+ m_nonlinearSolverParameters.m_minNormalizer,
+ subRegionResidualNorm );
- // step 2: reduction across meshBodies/regions/subRegions
+ // step 2: reduction across meshBodies/regions/subRegions
- if( subRegionResidualNorm[0] > localResidualNorm[0] )
+ if( subRegionResidualNorm[0] > localResidualNorm[0] )
+ {
+ localResidualNorm[0] = subRegionResidualNorm[0];
+ }
+ }
+ }
+ else
+ {
+ for( integer i=0; i 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;
+
+ 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 )
+ : 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 );
+
+
+ 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::scalingForSystemSolution( DomainPartition & domain,
DofManager const & dofManager,
@@ -1608,88 +2577,83 @@ CompositionalMultiphaseWell::scalingForSystemSolution( DomainPartition & domain,
}
bool
-CompositionalMultiphaseWell::checkSystemSolution( DomainPartition & domain,
- DofManager const & dofManager,
- arrayView1d< real64 const > const & localSolution,
- real64 const scalingFactor )
+CompositionalMultiphaseWell::checkWellSystemSolution( ElementSubRegionBase & 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 );
+ 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 );
+ 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 = 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 );
@@ -1701,94 +2665,216 @@ 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 );
}
+bool
+CompositionalMultiphaseWell::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 )
+ {
+ mesh.getElemManager().forElementSubRegions( regionNames,
+ [&]( localIndex const,
+ ElementSubRegionBase & subRegion )
+ {
+ integer localCheck = checkWellSystemSolution( subRegion, dofManager, localSolution, scalingFactor );
+ globalCheck = MpiWrapper::min( localCheck );
+ } );
+ } );
+ return globalCheck;
+}
+
+void CompositionalMultiphaseWell::computeWellPerforationRates( real64 const & time_n,
+ real64 const & GEOS_UNUSED_PARAM( dt ),
+ ElementRegionManager const & elemManager,
+ WellElementSubRegion & subRegion )
+{
+ GEOS_MARK_FUNCTION;
+ GEOS_UNUSED_VAR( time_n );
+
+ CompositionalMultiphaseBase const & flowSolver = getParent().getGroup< CompositionalMultiphaseBase >( getFlowSolverName() );
+
+ PerforationData * const perforationData = subRegion.getPerforationData();
+ WellControls const & wellControls = getWellControls( subRegion );
+ if( wellControls.isWellOpen() && !m_keepVariablesConstantDuringInitStep )
+ {
+
+ string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString() );
+ MultiFluidBase const & fluid = getConstitutiveModel< MultiFluidBase >( subRegion, fluidName );
+ bool 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
+ {
+// 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 )
+ {
+ compPerfRate[iperf][ic] = 0.0;
+ }
+ }
+ }
+
+
+}
+
+
void CompositionalMultiphaseWell::computePerforationRates( real64 const & time_n,
- real64 const & GEOS_UNUSED_PARAM( dt ),
+ real64 const & dt,
DomainPartition & domain )
{
GEOS_MARK_FUNCTION;
- GEOS_UNUSED_VAR( time_n );
+ GEOS_UNUSED_VAR( dt );
+ forDiscretizationOnMeshTargets ( domain.getMeshBodies(), [&] ( string const &,
+ MeshLevel & mesh,
+ string_array const & regionNames )
+ {
+
+ // TODO: change the way we access the flowSolver here
+
+ ElementRegionManager & elemManager = mesh.getElemManager();
+
+ elemManager.forElementSubRegions< WellElementSubRegion >( regionNames, [&]( localIndex const,
+ WellElementSubRegion & subRegion )
+ {
+ computeWellPerforationRates( time_n, dt, elemManager, subRegion );
+
+ } );
+
+ } );
+
+}
+
+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 );
+ GEOS_UNUSED_VAR( dt );
+ // update all the fields using the global damping coefficients
+ dofManager.addVectorToField( localSolution,
+ wellElementDofName(),
+ fields::well::pressure::key(),
+ scalingFactor,
+ pressureMask );
+
+ dofManager.addVectorToField( localSolution,
+ wellElementDofName(),
+ fields::well::globalCompDensity::key(),
+ scalingFactor,
+ componentMask );
+
+ dofManager.addVectorToField( localSolution,
+ wellElementDofName(),
+ fields::well::mixtureConnectionRate::key(),
+ scalingFactor,
+ connRateMask );
- forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &,
- MeshLevel & mesh,
- string_array const & regionNames )
+ if( isThermal() )
{
+ DofManager::CompMask temperatureMask( m_numDofPerWellElement, numFluidComponents()+2, numFluidComponents()+3 );
- // TODO: change the way we access the flowSolver here
- CompositionalMultiphaseBase const & flowSolver = getParent().getGroup< CompositionalMultiphaseBase >( getFlowSolverName() );
- ElementRegionManager & elemManager = mesh.getElemManager();
-
- elemManager.forElementSubRegions< WellElementSubRegion >( regionNames, [&]( localIndex const,
- WellElementSubRegion & subRegion )
- {
- 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();
+ dofManager.addVectorToField( localSolution,
+ wellElementDofName(),
+ fields::well::temperature::key(),
+ scalingFactor,
+ temperatureMask );
- 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
- {
- // 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;
- }
- }
- }
- } );
+ }
- } );
+#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( subRegion );
+ }
+#endif
+ // synchronize
+ FieldIdentifiers fieldsToBeSync;
+ if( isThermal() )
+ {
+ fieldsToBeSync.addElementFields( { fields::well::pressure::key(),
+ fields::well::globalCompDensity::key(),
+ fields::well::mixtureConnectionRate::key(),
+ fields::well::temperature::key() },
+ getTargetRegionNames() );
+ }
+ else
+ {
+ fieldsToBeSync.addElementFields( { fields::well::pressure::key(),
+ fields::well::globalCompDensity::key(),
+ fields::well::mixtureConnectionRate::key() },
+ getTargetRegionNames() );
+ }
+ CommunicationTools::getInstance().synchronizeFields( fieldsToBeSync,
+ mesh,
+ domain.getNeighbors(),
+ true );
}
-
void
CompositionalMultiphaseWell::applySystemSolution( DofManager const & dofManager,
arrayView1d< real64 const > const & localSolution,
@@ -1869,6 +2955,59 @@ CompositionalMultiphaseWell::applySystemSolution( DofManager const & dofManager,
}
+void CompositionalMultiphaseWell::chopNegativeDensities( WellElementSubRegion & subRegion )
+{
+ integer const numComp = m_numComponents;
+
+
+ arrayView1d< integer const > const & wellElemGhostRank = subRegion.ghostRank();
+
+ arrayView2d< real64, compflow::USD_COMP > const & wellElemCompDens =
+ subRegion.getField< fields::well::globalCompDensity >();
+
+ //arrayView2d< real64, compflow::USD_COMP > const & wellElemCompDens_n =
+ // subRegion.getField< fields::well::globalCompDensity_n >();
+
+ 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.0;
+ }
+ }
+ }
+ } );
+
+ WellControls const & wellControls = getWellControls( subRegion );
+ if( isThermal() && !wellControls.isInjector() )
+ {
+ real64 minTemp =290.0;
+ arrayView1d< real64 > const & wellElemTemperature =
+ subRegion.getField< well::temperature >();
+
+ forAll< parallelDevicePolicy<> >( subRegion.size(), [=] GEOS_HOST_DEVICE ( localIndex const iwelem )
+ {
+ if( wellElemGhostRank[iwelem] < 0 )
+ {
+ if( wellElemTemperature[iwelem] < minTemp )
+ {
+ GEOS_LOG_LEVEL_BY_RANK( logInfo::Solution,
+ GEOS_FMT( "{}: Bad temperature update, value set to injection temp = {} K",
+ wellElemTemperature[iwelem], minTemp ) );
+ wellElemTemperature[iwelem] = minTemp;
+ }
+ }
+ } );
+ }
+
+}
+
void CompositionalMultiphaseWell::chopNegativeDensities( DomainPartition & domain )
{
integer const numComp = m_numComponents;
@@ -1904,6 +3043,28 @@ void CompositionalMultiphaseWell::chopNegativeDensities( DomainPartition & domai
}
}
} );
+ WellControls const & wellControls = getWellControls( subRegion );
+ if( isThermal() && !wellControls.isInjector() )
+ {
+
+ real64 minTemp =290.0;
+ arrayView1d< real64 > const & wellElemTemperature =
+ subRegion.getField< well::temperature >();
+
+ forAll< parallelDevicePolicy<> >( subRegion.size(), [=] GEOS_HOST_DEVICE ( localIndex const iwelem )
+ {
+ if( wellElemGhostRank[iwelem] < 0 )
+ {
+ if( wellElemTemperature[iwelem] < minTemp )
+ {
+ // GEOS_LOG_LEVEL_BY_RANK( logInfo::Solution,
+ // GEOS_FMT( "{}: Bad temperature update, value set to injection temp = {} K",
+ // wellElemTemperature[iwelem], minTemp ) );
+ wellElemTemperature[iwelem] = minTemp;
+ }
+ }
+ } );
+ }
} );
} );
@@ -1960,15 +3121,158 @@ void CompositionalMultiphaseWell::resetStateToBeginningOfStep( DomainPartition &
} );
}
+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.
+ WellControls & wellControls = getWellControls( subRegion );
+ if( !subRegion.isLocallyOwned() || !( wellControls.getWellStatus() == WellControls::Status::OPEN ))
+ {
+ return;
+ }
+
+ if( wellControls.isProducer() )
+ {
+ wellControls.forSubGroups< MinimumBHPConstraint, ProductionConstraint< PhaseVolumeRateConstraint >, ProductionConstraint< MassRateConstraint >, ProductionConstraint< VolumeRateConstraint >,
+ ProductionConstraint< LiquidRateConstraint >
+ >( [&]( auto & constraint )
+ {
+ if( constraint.getName() == wellControls.getCurrentConstraint()->getName())
+ {
+ // found limiting constraint
+
+ // fluid data
+ constitutive::MultiFluidBase & fluidSeparator = wellControls.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,
+ wellControls,
+ constraint,
+ subRegion,
+ dofManager.getKey( wellElementDofName() ),
+ dofManager.rankOffset(),
+ localMatrix,
+ localRhs );
+ } );
+ }
+ } );
+ }
+ else
+ {
+ wellControls.forSubGroups< MaximumBHPConstraint, InjectionConstraint< PhaseVolumeRateConstraint >, InjectionConstraint< MassRateConstraint >,
+ InjectionConstraint< VolumeRateConstraint >,
+ InjectionConstraint< LiquidRateConstraint >
+ >( [&]( auto & constraint )
+ {
+ if( constraint.getName() == wellControls.getCurrentConstraint()->getName())
+ {
+ // found limiting constraint
+
+ // fluid data
+ constitutive::MultiFluidBase & fluidSeparator = wellControls.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,
+ wellControls,
+ constraint,
+ subRegion,
+ dofManager.getKey( wellElementDofName() ),
+ dofManager.rankOffset(),
+ localMatrix,
+ 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;
+
+
+ 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(),
+ elemStatus,
+ wellElemDofNumber,
+ wellElemGravCoef,
+ nextWellElemIndex,
+ wellElemPres,
+ wellElemTotalMassDens,
+ dWellElemTotalMassDens,
+ controlHasSwitched,
+ localMatrix,
+ localRhs );
+
+ }
+
+}
+
void CompositionalMultiphaseWell::assemblePressureRelations( real64 const & time_n,
- real64 const & GEOS_UNUSED_PARAM( dt ),
+ real64 const & dt,
DomainPartition const & domain,
DofManager const & dofManager,
CRSMatrixView< real64, globalIndex const > const & localMatrix,
arrayView1d< real64 > const & localRhs )
{
GEOS_MARK_FUNCTION;
-
+ GEOS_UNUSED_PARAM( dt )
forDiscretizationOnMeshTargets ( domain.getMeshBodies(), [&] ( string const &,
MeshLevel const & mesh,
string_array const & regionNames )
@@ -1981,109 +3285,57 @@ void CompositionalMultiphaseWell::assemblePressureRelations( real64 const & time
WellElementSubRegion const & subRegion )
{
- WellControls & wellControls = getWellControls( subRegion );
+ assembleWellPressureRelations( time_n, dt, subRegion, dofManager, localMatrix, localRhs );
+ } );
+ } );
- 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::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 );
+
+ string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString() );
+ MultiFluidBase const & fluid = getConstitutiveModel< MultiFluidBase >( subRegion, fluidName );
+ fluid.saveConvergedState();
+
- } );
- } );
}
void CompositionalMultiphaseWell::implicitStepSetup( real64 const & time_n,
@@ -2164,6 +3416,8 @@ void CompositionalMultiphaseWell::implicitStepComplete( real64 const & time_n,
if( getLogLevel() > 0 )
{
printRates( time_n, dt, domain );
+ auto iterInfo = currentIter( time_n, dt );
+ printSegRates( time_n, dt, std::get< 0 >( iterInfo ), std::get< 1 >( iterInfo ), std::get< 2 >( iterInfo ), domain );
}
}
@@ -2313,5 +3567,274 @@ void CompositionalMultiphaseWell::printRates( real64 const & time_n,
} );
}
+
+bool CompositionalMultiphaseWell::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 )
+{
+ WellControls & wellControls = getWellControls( subRegion );
+ bool useEstimator = coupledIterationNumber < wellControls.estimateSolution();
+
+ if( useEstimator )
+ {
+ // 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 = wellControls.getCurrentConstraint();
+ if( wellControls.isProducer() )
+ {
+ constraintList = wellControls.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( wellControls.getMinBHPConstraint()->isConstraintActive() )
+ {
+ constraintList.push_back( wellControls.getMinBHPConstraint() );
+ }
+ constraintList.insert( constraintList.begin(), limitingConstraint );
+ }
+ }
+ // Solve minimum bhp constraint first
+ if( false && wellControls.getMinBHPConstraint()->isConstraintActive() )
+ {
+ // this is related to WHP option which introduces a new BHP constraint
+ limitingConstraint = wellControls.getMinBHPConstraint();
+ }
+ else if( !constraintList.empty() )
+ {
+ limitingConstraint = constraintList[0];
+ }
+ }
+ else
+ {
+ constraintList = wellControls.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( wellControls.getMaxBHPConstraint() );
+ }
+ }
+ }
+ if( wellControls.isoThermalEstimatorEnabled() )
+ {
+ wellControls.enableThermalEffects( false );
+ solveConstraint ( limitingConstraint, time_n,
+ dt,
+ cycleNumber,
+ coupledIterationNumber,
+ domain,
+ mesh,
+ elemManager,
+ subRegion,
+ dofManager );
+ wellControls.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;
+ wellControls.setControl( static_cast< WellControls::Control >(constraint->getControl()) ); // tjb old
+ wellControls.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 ( wellControls.getReference< real64 >( CompositionalMultiphaseWell::viewKeyStruct::currentBHPString() ));
+ constraint->setPhaseVolumeRates ( wellControls.getReference< array1d< real64 > >(
+ CompositionalMultiphaseWell::viewKeyStruct::currentPhaseVolRateString() ) );
+ constraint->setTotalVolumeRate ( wellControls.getReference< real64 >(
+ CompositionalMultiphaseWell::viewKeyStruct::currentTotalVolRateString() ));
+ constraint->setMassRate( wellControls.getReference< real64 >( CompositionalMultiphaseWell::viewKeyStruct::currentMassRateString() ));
+
+ }
+ }
+ solveConstraint ( limitingConstraint, time_n,
+ dt,
+ cycleNumber,
+ coupledIterationNumber,
+ domain,
+ mesh,
+ elemManager,
+ subRegion,
+ dofManager );
+ limitingConstraint->setBHP ( wellControls.getReference< real64 >( CompositionalMultiphaseWell::viewKeyStruct::currentBHPString() ));
+ limitingConstraint->setPhaseVolumeRates ( wellControls.getReference< array1d< real64 > >(
+ CompositionalMultiphaseWell::viewKeyStruct::currentPhaseVolRateString() ) );
+ limitingConstraint->setTotalVolumeRate ( wellControls.getReference< real64 >(
+ CompositionalMultiphaseWell::viewKeyStruct::currentTotalVolRateString() ));
+ limitingConstraint->setMassRate( wellControls.getReference< real64 >( CompositionalMultiphaseWell::viewKeyStruct::currentMassRateString() ));
+ GEOS_LOG_RANK_IF ( getLogLevel() > 4 && subRegion.isLocallyOwned(),
+ " Well " << subRegion.getName() << " Limiting Constraint " << limitingConstraint->getName() << " " << limitingConstraint->bottomHolePressure() << " " << limitingConstraint->phaseVolumeRates() << " " <<
+ limitingConstraint->totalVolumeRate() << " " << limitingConstraint->massRate());
+ }
+ else
+ {
+ // create list of all constraints to process
+ std::vector< WellConstraintBase * > constraintList;
+ if( wellControls.isProducer() )
+ {
+ constraintList = wellControls.getProdRateConstraints();
+ // Solve minimum bhp constraint first
+ if( wellControls.getMinBHPConstraint()->isConstraintActive() )
+ {
+ constraintList.insert( constraintList.begin(), wellControls.getMinBHPConstraint() );
+ }
+ }
+ else
+ {
+ constraintList = wellControls.getInjRateConstraints();
+ // Solve maximum bhp constraint first;
+ constraintList.insert( constraintList.begin(), wellControls.getMaxBHPConstraint() );
+ }
+ // Get current constraint
+ WellConstraintBase * limitingConstraint = nullptr;
+ for( auto & constraint : constraintList )
+ {
+ if( constraint->getName() == wellControls.getCurrentConstraint()->getName())
+ {
+ limitingConstraint = constraint;
+ // tjb. this is likely not needed. set in update state
+ constraint->setBHP ( wellControls.getReference< real64 >( CompositionalMultiphaseWell::viewKeyStruct::currentBHPString() ));
+ constraint->setPhaseVolumeRates ( wellControls.getReference< array1d< real64 > >(
+ CompositionalMultiphaseWell::viewKeyStruct::currentPhaseVolRateString() ) );
+ constraint->setTotalVolumeRate ( wellControls.getReference< real64 >(
+ CompositionalMultiphaseWell::viewKeyStruct::currentTotalVolRateString() ));
+ constraint->setMassRate( wellControls.getReference< real64 >( CompositionalMultiphaseWell::viewKeyStruct::currentMassRateString() ));
+ GEOS_LOG_RANK_IF ( getLogLevel() > 4 && subRegion.isLocallyOwned(),
+ " Well " << subRegion.getName() << " Limiting Constraint " << limitingConstraint->getName() << " " << limitingConstraint->bottomHolePressure() << " " << limitingConstraint->phaseVolumeRates() << " " <<
+ limitingConstraint->totalVolumeRate() << " " << limitingConstraint->massRate());
+ }
+ }
+
+
+ // Check current against other constraints
+ for( auto & constraint : constraintList )
+ {
+
+ if( limitingConstraint->getName() != constraint->getName())
+ {
+ if( constraint->checkViolation( *limitingConstraint, time_n ) )
+ {
+ limitingConstraint=constraint;
+ wellControls.setControl( static_cast< WellControls::Control >(constraint->getControl()) ); // tjb old
+ wellControls.setCurrentConstraint( constraint );
+ GEOS_LOG_RANK_IF ( getLogLevel() > 4 && subRegion.isLocallyOwned(),
+ " Well " << subRegion.getName() << " New Limiting Constraint " << 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;
+}
+void CompositionalMultiphaseWell::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 )
+{
+
+ WellControls & wellControls = getWellControls( subRegion );
+ bool useEstimator = coupledIterationNumber < wellControls.estimateSolution();
+ if( useEstimator )
+ {
+
+ if( getLogLevel() > 4 )
+ {
+ GEOS_LOG_RANK_0( "Well " << wellControls.getName() << " Evaluating constraint " << constraint->getName() << " value " << constraint->getConstraintValue( time_n ) << " active " <<
+ constraint->isConstraintActive() );
+ }
+ if( constraint->isConstraintActive() )
+ {
+ wellControls.setControl( static_cast< WellControls::Control >(constraint->getControl()) ); // tjb old
+ wellControls.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 = wellControls.getControl();
+ MpiWrapper::broadcast( wellControl, owner );
+ wellControls.setControl( wellControl );
+ solveNonlinearSystem( time_n,
+ dt,
+ cycleNumber,
+ domain,
+ mesh,
+ elemManager,
+ subRegion,
+ dofManager );
+
+ // Store computed well quantities for this constraint
+ constraint->setBHP ( wellControls.getReference< real64 >( CompositionalMultiphaseWell::viewKeyStruct::currentBHPString() ));
+ constraint->setPhaseVolumeRates ( wellControls.getReference< array1d< real64 > >(
+ CompositionalMultiphaseWell::viewKeyStruct::currentPhaseVolRateString() ) );
+ constraint->setTotalVolumeRate ( wellControls.getReference< real64 >(
+ CompositionalMultiphaseWell::viewKeyStruct::currentTotalVolRateString() ));
+ constraint->setMassRate( wellControls.getReference< real64 >( CompositionalMultiphaseWell::viewKeyStruct::currentMassRateString() ));
+ if( getLogLevel() > 4 )
+ {
+ GEOS_LOG_RANK_0( "Well " << wellControls.getName() << " aft solve Constraint rates " << constraint->getName() << " bhp " << constraint->bottomHolePressure() << " phaseVolRate " <<
+ constraint->phaseVolumeRates() << " totalVolRate " << constraint->totalVolumeRate() << " massRate " << constraint->massRate());
+ }
+ }
+
+
+ }
+
+}
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 fd874ec5a0e..16bc0f14555 100644
--- a/src/coreComponents/physicsSolvers/fluidFlow/wells/CompositionalMultiphaseWell.hpp
+++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/CompositionalMultiphaseWell.hpp
@@ -25,6 +25,8 @@
#include "physicsSolvers/fluidFlow/wells/WellSolverBase.hpp"
#include "physicsSolvers/fluidFlow/CompositionalMultiphaseBase.hpp"
+#include "physicsSolvers/fluidFlow/wells/WellConstraintsBase.hpp"
+
namespace geos
{
@@ -92,6 +94,13 @@ class CompositionalMultiphaseWell : public WellSolverBase
/**@{*/
+ virtual real64
+ calculateWellResidualNorm( real64 const & time_n,
+ real64 const & dt,
+ WellElementSubRegion const & subRegion,
+ DofManager const & dofManager,
+ arrayView1d< real64 const > const & localRhs ) override;
+
virtual real64
calculateResidualNorm( real64 const & time_n,
real64 const & dt,
@@ -99,17 +108,37 @@ class CompositionalMultiphaseWell : public WellSolverBase
DofManager const & dofManager,
arrayView1d< real64 const > const & localRhs ) override;
+ virtual real64
+ scalingForWellSystemSolution( ElementSubRegionBase & subRegion,
+ DofManager const & dofManager,
+ arrayView1d< real64 const > const & localSolution ) override;
+
virtual real64
scalingForSystemSolution( DomainPartition & domain,
DofManager const & dofManager,
arrayView1d< real64 const > const & localSolution ) override;
+ virtual bool
+ checkWellSystemSolution( ElementSubRegionBase & subRegion,
+ DofManager const & dofManager,
+ arrayView1d< real64 const > const & localSolution,
+ real64 const scalingFactor ) override;
+
virtual bool
checkSystemSolution( DomainPartition & domain,
DofManager const & dofManager,
arrayView1d< real64 const > const & localSolution,
real64 const scalingFactor ) override;
+ virtual void
+ applyWellSystemSolution( DofManager const & dofManager,
+ arrayView1d< real64 const > const & localSolution,
+ real64 const scalingFactor,
+ real64 const dt,
+ DomainPartition & domain,
+ MeshLevel & mesh,
+ WellElementSubRegion & subRegion ) override;
+
virtual void
applySystemSolution( DofManager const & dofManager,
arrayView1d< real64 const > const & localSolution,
@@ -159,6 +188,21 @@ class CompositionalMultiphaseWell : public WellSolverBase
*/
void updateFluidModel( WellElementSubRegion & subRegion );
+ /**
+ * @brief Update well separator using current values of pressure and composition at the reference element
+ * @param subRegion the well subregion containing all the primary and dependent fields
+ * @param targetIndex the targetIndex of the subRegion
+ */
+ void updateSeparator( 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,6 +216,16 @@ 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 computeWellPerforationRates( real64 const & time_n,
+ real64 const & GEOS_UNUSED_PARAM( dt ),
+ ElementRegionManager const & elemManager,
+ WellElementSubRegion & subRegion )override;
+
+
/**
* @brief Recompute the perforation rates for all the wells
* @param domain the domain containing the mesh and fields
@@ -183,6 +237,7 @@ class CompositionalMultiphaseWell : public WellSolverBase
* @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( WellElementSubRegion & subRegion ) override;
virtual void updateState( DomainPartition & domain ) override;
virtual real64 updateSubRegionState( WellElementSubRegion & subRegion ) override;
@@ -197,6 +252,13 @@ class CompositionalMultiphaseWell : public WellSolverBase
integer useTotalMassEquation() const { return m_useTotalMassEquation; }
+ 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;
+
/**
* @brief assembles the flux terms for all connections between well elements
* @param time_n previous time value
@@ -213,6 +275,14 @@ class CompositionalMultiphaseWell : public WellSolverBase
DofManager const & dofManager,
CRSMatrixView< real64, globalIndex const > const & localMatrix,
arrayView1d< real64 > const & localRhs )override;
+
+ 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;
+
/**
* @brief assembles the accumulation term for all the well elements
* @param domain the physical domain object
@@ -227,6 +297,29 @@ class CompositionalMultiphaseWell : public WellSolverBase
CRSMatrixView< real64, globalIndex const > const & localMatrix,
arrayView1d< real64 > const & localRhs ) override;
+ 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;
+
+ 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 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 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
@@ -236,6 +329,13 @@ class CompositionalMultiphaseWell : public WellSolverBase
* @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 ) override;
+
virtual void assemblePressureRelations( real64 const & time_n,
real64 const & dt,
DomainPartition const & domain,
@@ -243,12 +343,52 @@ class CompositionalMultiphaseWell : public WellSolverBase
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 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 outputSingleWellDebug( real64 const time,
+ real64 const dt,
+ integer num_timesteps,
+ integer current_newton_iteration,
+ integer num_timestep_cuts,
+ MeshLevel & mesh,
+ WellElementSubRegion & subRegion,
+ DofManager const & dofManager,
+ CRSMatrixView< real64, globalIndex const > const & localMatrix,
+ arrayView1d< const real64 > const & localRhs ) override;
+
+ virtual void outputWellDebug( real64 const time,
+ real64 const dt,
+ integer num_timesteps,
+ integer current_newton_iteration,
+ integer num_timestep_cuts,
+ DomainPartition & 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 );
+ void chopNegativeDensities( WellElementSubRegion & subRegion );
+
+ /**
+ * @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;
+ void initializeWell( DomainPartition & domain, MeshLevel & mesh, WellElementSubRegion & subRegion, real64 const & time_n ) override;
+
struct viewKeyStruct : WellSolverBase::viewKeyStruct
{
static constexpr char const * dofFieldString() { return "compositionalWellVars"; }
@@ -276,34 +416,16 @@ class CompositionalMultiphaseWell : public WellSolverBase
static constexpr char const * massDensityString() { return "massDensity";}
static constexpr char const * currentBHPString() { return "currentBHP"; }
- static constexpr char const * dCurrentBHPString() { return "dCurrentBHP"; }
-
- static constexpr char const * dCurrentBHP_dPresString() { return "dCurrentBHP_dPres"; }
- static constexpr char const * dCurrentBHP_dCompDensString() { return "dCurrentBHP_dCompDens"; }
static constexpr char const * currentPhaseVolRateString() { return "currentPhaseVolumetricRate"; }
- static constexpr char const * dCurrentPhaseVolRateString() { return "dCurrentPhaseVolumetricRate"; }
-
-
- static constexpr char const * dCurrentPhaseVolRate_dPresString() { return "dCurrentPhaseVolumetricRate_dPres"; }
-
- static constexpr char const * dCurrentPhaseVolRate_dCompDensString() { return "dCurrentPhaseVolumetricRate_dCompDens"; }
-
- static constexpr char const * dCurrentPhaseVolRate_dRateString() { return "dCurrentPhaseVolumetricRate_dRate"; }
static constexpr char const * currentTotalVolRateString() { return "currentTotalVolumetricRate"; }
- static constexpr char const * dCurrentTotalVolRateString() { return "dCurrentTotalVolumetricRate"; }
static constexpr char const * currentMassRateString() { return "currentMassRate"; }
- static constexpr char const * dCurrentTotalVolRate_dPresString() { return "dCurrentTotalVolumetricRate_dPres"; }
-
- static constexpr char const * dCurrentTotalVolRate_dCompDensString() { return "dCurrentTotalVolumetricRate_dCompDens"; }
-
- static constexpr char const * dCurrentTotalVolRate_dRateString() { return "dCurrentTotalVolumetricRate_dRate"; }
-
} viewKeysCompMultiphaseWell;
+ virtual void saveState( WellElementSubRegion & subRegion ) override;
protected:
virtual void postInputInitialization() override;
@@ -312,6 +434,7 @@ class CompositionalMultiphaseWell : public WellSolverBase
virtual void initializePostInitialConditionsPreSubGroups() override;
+
virtual void postRestartInitialization() override final;
/*
* @brief Utility function that checks the consistency of the constitutive models
@@ -330,11 +453,6 @@ class CompositionalMultiphaseWell : public WellSolverBase
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;
/**
* @brief Make sure that the well constraints are compatible
@@ -356,6 +474,21 @@ class CompositionalMultiphaseWell : public WellSolverBase
void printRates( real64 const & time_n,
real64 const & dt,
DomainPartition & domain ) override;
+ void printSegRates( real64 const & time_n,
+ real64 const & dt,
+ integer num_timesteps,
+ integer num_timestep_cuts,
+ integer current_newton_iteration,
+ DomainPartition & domain );
+ virtual bool evaluateConstraints( real64 const & time_n,
+ real64 const & stepDt,
+ integer const cycleNumber,
+ integer const coupledIterationNumber,
+ DomainPartition & domain,
+ MeshLevel & mesh,
+ ElementRegionManager & elemManager,
+ WellElementSubRegion & subRegion,
+ DofManager const & dofManager ) override;
private:
@@ -363,10 +496,22 @@ class CompositionalMultiphaseWell : public WellSolverBase
* @brief Initialize all the primary and secondary variables in all the wells
* @param domain the domain containing the well manager to access individual wells
*/
- void initializeWells( DomainPartition & domain, real64 const & time_n ) override;
virtual void setConstitutiveNames( ElementSubRegionBase & subRegion ) const override;
+ template< typename ... GROUPTYPES >
+ void selectLimitingConstraint( real64 const & time_n, integer const coupledIterationNumber, WellElementSubRegion & subRegion );
+
+ 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 );
/// flag indicating whether mass or molar formulation should be used
@@ -399,6 +544,7 @@ class CompositionalMultiphaseWell : public WellSolverBase
/// index of the target phase, used to impose the phase rate constraint
localIndex m_targetPhaseIndex;
+ bool m_wellDebugInit;
};
diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/SinglePhaseWell.cpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/SinglePhaseWell.cpp
index 4d49a5062e4..5299b865af4 100644
--- a/src/coreComponents/physicsSolvers/fluidFlow/wells/SinglePhaseWell.cpp
+++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/SinglePhaseWell.cpp
@@ -44,7 +44,7 @@
#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
{
@@ -143,6 +143,7 @@ void SinglePhaseWell::validateWellConstraints( real64 const & time_n,
WellElementSubRegion const & subRegion,
ElementRegionManager const & elemManager )
{
+ GEOS_UNUSED_VAR( time_n ); // tjb this will be needed with validation against tables
WellControls & wellControls = getWellControls( subRegion );
if( !wellControls.useSurfaceConditions() )
{
@@ -176,7 +177,9 @@ void SinglePhaseWell::validateWellConstraints( real64 const & time_n,
wellControls.setRegionAverageTemperature( stats.averageTemperature );
}
}
+ #if 0 // tjb vix this
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,
@@ -193,6 +196,37 @@ void SinglePhaseWell::validateWellConstraints( real64 const & time_n,
"WellControls " << wellControls.getDataContext() <<
": Target phase rate cannot be used for SinglePhaseWell",
InputError );
+#endif
+}
+
+void SinglePhaseWell::initializePostInitialConditionsPreSubGroups()
+{
+ WellSolverBase::initializePostInitialConditionsPreSubGroups();
+ createSeparator();
+}
+void SinglePhaseWell::createSeparator()
+{
+ 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() );
+ SingleFluidBase & fluid = subRegion.getConstitutiveModel< SingleFluidBase >( fluidName );
+ // setup fluid separator
+ WellControls & wellControls = getWellControls( subRegion );
+ string const fluidSeparatorName = wellControls.getName() + "Separator";
+ std::unique_ptr< constitutive::ConstitutiveBase > fluidSeparatorPtr = fluid.deliverClone( fluidSeparatorName, &wellControls );
+ fluidSeparatorPtr->allocateConstitutiveData( wellControls, 1 );
+ fluidSeparatorPtr->resize( 1 );
+ wellControls.setFluidSeparator( std::move( fluidSeparatorPtr ));
+ } );
+ } );
}
void SinglePhaseWell::updateBHPForConstraint( WellElementSubRegion & subRegion )
@@ -262,7 +296,7 @@ void SinglePhaseWell::updateBHPForConstraint( WellElementSubRegion & subRegion )
}
-void SinglePhaseWell::updateVolRateForConstraint( WellElementSubRegion & subRegion )
+void SinglePhaseWell::calculateReferenceElementRates( WellElementSubRegion & subRegion )
{
GEOS_MARK_FUNCTION;
@@ -276,9 +310,6 @@ void SinglePhaseWell::updateVolRateForConstraint( WellElementSubRegion & subRegi
// subRegion data
- arrayView1d< real64 const > const pres =
- subRegion.getField< well::pressure >();
-
arrayView1d< real64 const > const & connRate =
subRegion.getField< well::connectionRate >();
@@ -287,14 +318,71 @@ void SinglePhaseWell::updateVolRateForConstraint( WellElementSubRegion & subRegi
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();
+
// control data
+ WellControls & wellControls = getWellControls( subRegion );
+
+ real64 & currentVolRate =
+ wellControls.getReference< real64 >( SinglePhaseWell::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 );
+
+ constitutiveUpdatePassThru( fluid, [&]( auto & castedFluid )
+ {
+ typename TYPEOFREF( castedFluid ) ::KernelWrapper fluidWrapper = castedFluid.createKernelWrapper();
+ singlePhaseBaseKernels::FluidUpdateKernel::launch( fluidWrapper, pres, temp );
+ } );
+}
+void SinglePhaseWell::updateSeparator( 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();
+
+ // fluid data
+ constitutive::SingleFluidBase & fluidSeparator = wellControls.getSingleFluidSeparator();
+ arrayView2d< real64 const, constitutive::singlefluid::USD_FLUID > const & dens = fluidSeparator.density();
+
real64 flashPressure;
if( useSurfaceConditions )
{
@@ -311,31 +399,22 @@ void SinglePhaseWell::updateVolRateForConstraint( WellElementSubRegion & subRegi
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,
&flashPressure,
- ¤tVolRate,
- dCurrentVolRate,
&iwelemRef,
&wellControlsName] ( localIndex const )
{
@@ -346,7 +425,7 @@ void SinglePhaseWell::updateVolRateForConstraint( WellElementSubRegion & subRegi
if( useSurfaceConditions )
{
// we need to compute the surface density
- fluidWrapper.update( iwelemRef, 0, flashPressure );
+ fluidSeparatorWrapper.update( iwelemRef, 0, flashPressure );
if( logSurfaceCondition )
{
@@ -361,62 +440,204 @@ void SinglePhaseWell::updateVolRateForConstraint( WellElementSubRegion & subRegi
}
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( WellElementSubRegion & subRegion )
{
- GEOS_MARK_FUNCTION;
+ WellControls & wellControls = getWellControls( subRegion );
+ if( wellControls.getWellState())
+ {
+ if( m_useNewCode )
+ {
+ // update volumetric rates for the well constraints
+ // Warning! This must be called before updating the fluid model
+ //calculateReferenceElementRates( subRegion );
+
+ // update density in the well elements
+ updateFluidModel( subRegion );
+ updateSeparator( subRegion ); // Calculate fluid properties at control conditions
+
+ // Calculate the reference element rates
+ calculateReferenceElementRates( subRegion );
+ // update the current BHP
+ updateBHPForConstraint( subRegion );
+ }
+ else
+ {
+ // update volumetric rates for the well constraints
+ // Warning! This must be called before updating the fluid model
+ calculateReferenceElementRates( subRegion );
- arrayView1d< real64 const > const pres = subRegion.getField< well::pressure >();
- arrayView1d< real64 const > const temp = subRegion.getField< well::temperature >();
+ // update density in the well elements
+ updateFluidModel( subRegion );
- string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString() );
- SingleFluidBase & fluid = subRegion.getConstitutiveModel< SingleFluidBase >( fluidName );
+ // update the current BHP
+ updateBHPForConstraint( subRegion );
+ }
- constitutiveUpdatePassThru( fluid, [&]( auto & castedFluid )
- {
- typename TYPEOFREF( castedFluid ) ::KernelWrapper fluidWrapper = castedFluid.createKernelWrapper();
- singlePhaseBaseKernels::FluidUpdateKernel::launch( fluidWrapper, pres, temp );
- } );
+ }
+ return 0.0; // change in phasevolume fraction doesnt apply
}
-
-real64 SinglePhaseWell::updateSubRegionState( WellElementSubRegion & subRegion )
+void SinglePhaseWell::initializeWell( DomainPartition & domain, MeshLevel & mesh, WellElementSubRegion & subRegion, real64 const & time_n )
{
- // update volumetric rates for the well constraints
- // Warning! This must be called before updating the fluid model
- updateVolRateForConstraint( subRegion );
+ GEOS_UNUSED_VAR( domain );
+ WellControls & wellControls = getWellControls( subRegion );
+ PerforationData const & perforationData = *subRegion.getPerforationData();
- // update density in the well elements
- updateFluidModel( subRegion );
+ // get the info stored on well elements
+ arrayView1d< real64 const > const wellElemGravCoef =
+ subRegion.getField< well::gravityCoefficient >();
- // update the current BHP
- updateBHPForConstraint( subRegion );
+ // get well primary variables on well elements
+ arrayView1d< real64 > const wellElemPressure =
+ subRegion.getField< well::pressure >();
+ arrayView1d< real64 > const connRate =
+ subRegion.getField< well::connectionRate >();
+ arrayView1d< real64 > const wellElemTemperature =
+ subRegion.getField< well::temperature >();
+ // get the element region, subregion, index
+ arrayView1d< localIndex const > const resElementRegion =
+ perforationData.getField< perforation::reservoirElementRegion >();
+ arrayView1d< localIndex const > const resElementSubRegion =
+ perforationData.getField< perforation::reservoirElementSubRegion >();
+ arrayView1d< localIndex const > const resElementIndex =
+ perforationData.getField< perforation::reservoirElementIndex >();
+
+ arrayView1d< real64 const > const & perfGravCoef =
+ perforationData.getField< well::gravityCoefficient >();
+
+ bool const hasNonZeroRate = MpiWrapper::max< integer >( hasNonZero( connRate ));
+
+ if( time_n <= 0.0 || (wellControls.isWellOpen() && !hasNonZeroRate ) )
+ {
+ wellControls.setWellState( true );
+ if( wellControls.getCurrentConstraint() == nullptr )
+ {
+ if( wellControls.isProducer() )
+ {
+ wellControls.forSubGroups< MinimumBHPConstraint, ProductionConstraint< VolumeRateConstraint >, ProductionConstraint< MassRateConstraint >,
+ ProductionConstraint< PhaseVolumeRateConstraint > >( [&]( auto & constraint )
+ {
+ if( ConstraintTypeId( wellControls.getControl()) == constraint.getControl() )
+ {
+ wellControls.setCurrentConstraint( &constraint );
+ wellControls.setControl( static_cast< WellControls::Control >(constraint.getControl()) ); // tjb old
+ }
+ } );
+ }
+ else
+ {
+ wellControls.forSubGroups< MaximumBHPConstraint, InjectionConstraint< VolumeRateConstraint >, InjectionConstraint< MassRateConstraint >,
+ InjectionConstraint< PhaseVolumeRateConstraint > >( [&]( auto & constraint )
+ {
+ if( ConstraintTypeId( wellControls.getControl()) == constraint.getControl() )
+ {
+ wellControls.setCurrentConstraint( &constraint );
+ wellControls.setControl( static_cast< WellControls::Control >(constraint.getControl()) ); // tjb old
+ }
+ } );
+ }
+ }
- // note: the perforation rates are updated separately
- return 0.0; // change in phasevolume fraction doesnt apply
-}
+ // TODO: change the way we access the flowSolver here
+ SinglePhaseBase const & flowSolver = getParent().getGroup< SinglePhaseBase >( getFlowSolverName() );
+ PresTempInitializationKernel::SinglePhaseFlowAccessors resSinglePhaseFlowAccessors( mesh.getElemManager(), flowSolver.getName() );
+ PresTempInitializationKernel::SingleFluidAccessors resSingleFluidAccessors( mesh.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( 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 );
+
+ calculateReferenceElementRates( subRegion );
+ WellConstraintBase * constraint = wellControls.getCurrentConstraint();
+ constraint->setBHP ( wellControls.getReference< real64 >( SinglePhaseWell::viewKeyStruct::currentBHPString() ));
+ constraint->setTotalVolumeRate ( wellControls.getReference< real64 >(
+ SinglePhaseWell::viewKeyStruct::currentVolRateString() ));
+ //constraint->setMassRate( wellControls.getReference< real64 >( SinglePhaseWell::viewKeyStruct::currentMassRateString() ));
+ // 7) Copy well / fluid dofs to "prop"_n variables
+ saveState( subRegion );
+ }
+ else if( !hasNonZeroRate )
+ {
+ wellControls.setWellState( false );
+ GEOS_LOG_RANK_0( "tjb shut wells "<< subRegion.getName());
+ }
+ else
+ {
+ wellControls.setWellState( true );
+ // setup for restart
+ if( wellControls.getCurrentConstraint() == nullptr )
+ {
+ updateSubRegionState( subRegion );
+ if( wellControls.isProducer() )
+ {
+ wellControls.forSubGroups< MinimumBHPConstraint, ProductionConstraint< VolumeRateConstraint >, ProductionConstraint< MassRateConstraint >,
+ ProductionConstraint< PhaseVolumeRateConstraint > >( [&](
+ auto
+ & constraint )
+ {
+ if( ConstraintTypeId( wellControls.getControl()) == constraint.getControl() )
+ {
+ wellControls.setCurrentConstraint( &constraint );
+ }
+ } );
+ }
+ else
+ {
+ wellControls.forSubGroups< MaximumBHPConstraint, InjectionConstraint< VolumeRateConstraint >, InjectionConstraint< MassRateConstraint >, InjectionConstraint< PhaseVolumeRateConstraint > >( [&](
+ auto
+ &
+ constraint )
+ {
+ if( ConstraintTypeId( wellControls.getControl()) == constraint.getControl() )
+ {
+ wellControls.setCurrentConstraint( &constraint );
+ }
+ } );
+ }
+ }
+ }
+
+}
void SinglePhaseWell::initializeWells( DomainPartition & domain, real64 const & time_n )
{
GEOS_MARK_FUNCTION;
@@ -424,87 +645,16 @@ void SinglePhaseWell::initializeWells( DomainPartition & domain, real64 const &
// loop over the wells
forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &,
- MeshLevel & meshLevel,
+ MeshLevel & mesh,
string_array const & regionNames )
{
- ElementRegionManager & elemManager = meshLevel.getElemManager();
+ ElementRegionManager & elemManager = mesh.getElemManager();
elemManager.forElementSubRegions< WellElementSubRegion >( regionNames,
[&]( localIndex const,
WellElementSubRegion & subRegion )
{
- 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 )
- {
- // 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( 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 );
- }
-
+ initializeWell( domain, mesh, subRegion, time_n );
} );
} );
@@ -585,6 +735,33 @@ void SinglePhaseWell::shutDownWell( real64 const time_n,
} );
}
+real64 SinglePhaseWell::updateWellState( WellElementSubRegion & subRegion )
+{
+ GEOS_MARK_FUNCTION;
+
+ updateSubRegionState( subRegion );
+ return 0.0;
+}
+void SinglePhaseWell::updateState( DomainPartition & domain )
+{
+ GEOS_MARK_FUNCTION;
+
+ forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &,
+ MeshLevel & mesh,
+ string_array const & regionNames )
+ {
+ mesh.getElemManager().forElementSubRegions< WellElementSubRegion >( regionNames, [&]( localIndex const,
+ WellElementSubRegion & subRegion )
+ {
+ WellControls & wellControls = getWellControls( subRegion );
+ if( wellControls.getWellState())
+ {
+ updateWellState( subRegion );
+ }
+ } );
+ } );
+
+}
void SinglePhaseWell::assembleSystem( real64 const time,
real64 const dt,
DomainPartition & domain,
@@ -594,11 +771,38 @@ void SinglePhaseWell::assembleSystem( real64 const time,
{
string const wellDofKey = dofManager.getKey( wellElementDofName());
+ if( m_useNewCode )
+ {
+ // 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 );
+ {
+ 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 );
@@ -611,6 +815,48 @@ void SinglePhaseWell::assembleSystem( real64 const time,
shutDownWell( time, domain, dofManager, localMatrix, 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 );
+ WellControls const & wellControls = getWellControls( subRegion );
+ // 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,
+ wellControls,
+ subRegion,
+ fluid,
+ localMatrix,
+ localRhs );
+ }
+ else
+ {
+ singlePhaseWellKernels::
+ FaceBasedAssemblyKernelFactory::
+ createAndLaunch< parallelDevicePolicy<> >( dt,
+ dofManager.rankOffset(),
+ wellDofKey,
+ wellControls,
+ subRegion,
+ localMatrix,
+ localRhs );
+ }
+
+}
void SinglePhaseWell::assembleFluxTerms( real64 const & time_n,
real64 const & dt,
DomainPartition & domain,
@@ -619,8 +865,6 @@ void SinglePhaseWell::assembleFluxTerms( real64 const & time_n,
arrayView1d< real64 > const & localRhs )
{
GEOS_MARK_FUNCTION;
- GEOS_UNUSED_VAR( time_n );
- GEOS_UNUSED_VAR( dt );
// loop over the wells
forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &,
@@ -635,45 +879,106 @@ void SinglePhaseWell::assembleFluxTerms( real64 const & time_n,
WellElementSubRegion const & subRegion )
{
- WellControls const & wellControls = getWellControls( subRegion );
- // get a reference to the degree-of-freedom numbers
- string const wellDofKey = dofManager.getKey( wellElementDofName() );
+ assembleWellFluxTerms( time_n, dt, subRegion, dofManager, localMatrix, localRhs );
+ } );
- 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
+ } );
+}
+
+
+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;
+
+
+ // the rank that owns the reference well element is responsible for the calculations below.
+ if( !subRegion.isLocallyOwned() )
+ {
+ return;
+ }
+ WellControls & wellControls = getWellControls( subRegion );
+
+ {
+ // tjb wellControls.forSubGroups< BHPConstraint, MassConstraint, VolumeRateConstraint >( [&]( auto & constraint )
+ wellControls.forSubGroups< BHPConstraint, InjectionConstraint< VolumeRateConstraint >, ProductionConstraint< VolumeRateConstraint > >( [&]( auto & constraint )
+ {
+ if( constraint.getName() == wellControls.getCurrentConstraint()->getName())
{
- singlePhaseWellKernels::
- FaceBasedAssemblyKernelFactory::
- createAndLaunch< parallelDevicePolicy<> >( dt,
- dofManager.rankOffset(),
- wellDofKey,
- wellControls,
- subRegion,
- localMatrix,
- localRhs );
+ // found limiting constraint
+
+ // fluid data
+ constitutive::SingleFluidBase & fluidSeparator = wellControls.getSingleFluidSeparator();
+ integer isThermal = fluidSeparator.isThermal();
+
+ geos::internal::kernelLaunchSelectorThermalSwitch( isThermal, [&] ( auto ISTHERMAL )
+ {
+ integer constexpr IS_THERMAL = ISTHERMAL();
+
+ singlePhaseWellConstraintKernels::ConstraintHelper< IS_THERMAL >::assembleConstraintEquation( time_n,
+ wellControls,
+ constraint,
+ subRegion,
+ dofManager.getKey( wellElementDofName() ),
+ dofManager.rankOffset(),
+ localMatrix,
+ 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 )
+{
+
+ // 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() );
+
+ // 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();
+
+ geos::internal::kernelLaunchSelectorThermalSwitch( isThermal(), [&] ( auto ISTHERMAL )
+ {
+ PressureRelationKernel::launch< ISTHERMAL >( subRegion.size(),
+ dofManager.rankOffset(),
+ wellElemDofNumber,
+ wellElemGravCoef,
+ nextWellElemIndex,
+ wellElemPressure,
+ wellElemDensity,
+ dWellElemDensity,
+ localMatrix,
+ localRhs );
+ } );
+
+}
void SinglePhaseWell::assemblePressureRelations( real64 const & time_n,
- real64 const & GEOS_UNUSED_PARAM( dt ),
+ real64 const & dt,
DomainPartition const & domain,
DofManager const & dofManager,
CRSMatrixView< real64, globalIndex const > const & localMatrix,
@@ -694,68 +999,53 @@ void SinglePhaseWell::assemblePressureRelations( real64 const & time_n,
WellElementSubRegion const & subRegion )
{
- 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() );
+ assembleWellPressureRelations( time_n, dt, subRegion, dofManager, localMatrix, localRhs );
+ } );
+ } );
+}
+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();
+ WellControls const & wellControls = getWellControls( subRegion );
+ string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString() );
+ SingleFluidBase const & fluid = subRegion.getConstitutiveModel< SingleFluidBase >( fluidName );
- geos::internal::kernelLaunchSelectorThermalSwitch( isThermal(), [&] ( auto ISTHERMAL )
- {
- 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 )
- {
- // Note: if BHP control is not viable, we switch to TOTALVOLRATE
- // if TOTALVOLRATE is not viable, we switch to BHP
+ if( isThermal() )
+ {
+ thermalSinglePhaseWellKernels::
+ ElementBasedAssemblyKernelFactory::
+ createAndLaunch< parallelDevicePolicy<> >( wellControls.isProducer(),
+ dofManager.rankOffset(),
+ wellElemDofKey,
+ subRegion,
+ fluid,
+ localMatrix,
+ localRhs );
+ }
+ else
+ {
+ singlePhaseWellKernels::
+ ElementBasedAssemblyKernelFactory::
+ createAndLaunch< parallelDevicePolicy<> >( dofManager.rankOffset(),
+ wellElemDofKey,
+ subRegion,
+ fluid,
+ localMatrix,
+ localRhs );
+ }
+}
- 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
- {
- 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 SinglePhaseWell::assembleAccumulationTerms( real64 const & time_n,
real64 const & dt,
@@ -765,54 +1055,22 @@ void SinglePhaseWell::assembleAccumulationTerms( real64 const & time_n,
arrayView1d< real64 > const & localRhs )
{
GEOS_MARK_FUNCTION;
- GEOS_UNUSED_VAR( time_n );
- GEOS_UNUSED_VAR( dt );
+
forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &,
- MeshLevel const & mesh,
+ MeshLevel & mesh,
string_array const & regionNames )
{
- ElementRegionManager const & elemManager = mesh.getElemManager();
+ ElementRegionManager & elemManager = mesh.getElemManager();
elemManager.forElementSubRegions< WellElementSubRegion >( regionNames,
[&]( localIndex const,
- WellElementSubRegion const & subRegion )
+ WellElementSubRegion & subRegion )
{
-
- // 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
- {
- singlePhaseWellKernels::
- ElementBasedAssemblyKernelFactory::
- createAndLaunch< parallelDevicePolicy<> >( dofManager.rankOffset(),
- wellElemDofKey,
- subRegion,
- fluid,
- localMatrix,
- localRhs );
- }
+ assembleWellAccumulationTerms( time_n, dt, subRegion, dofManager, localMatrix, localRhs );
} );
} );
- // then assemble the volume balance equations
- assembleVolumeBalanceTerms( domain, dofManager, localMatrix, localRhs );
+
}
void SinglePhaseWell::assembleVolumeBalanceTerms( DomainPartition const & GEOS_UNUSED_PARAM( domain ),
@@ -823,6 +1081,50 @@ void SinglePhaseWell::assembleVolumeBalanceTerms( DomainPartition const & GEOS_U
// not implemented for single phase flow
}
+void SinglePhaseWell::outputSingleWellDebug( real64 const time,
+ real64 const dt,
+ integer num_timesteps,
+ integer current_newton_iteration,
+ integer num_timestep_cuts,
+ MeshLevel & mesh,
+ WellElementSubRegion & subRegion,
+ DofManager const & dofManager,
+ CRSMatrixView< real64, globalIndex const > const & localMatrix,
+ arrayView1d< const real64 > const & localRhs )
+{
+ GEOS_UNUSED_VAR( time );
+ GEOS_UNUSED_VAR( dofManager );
+ GEOS_UNUSED_VAR( localMatrix );
+ GEOS_UNUSED_VAR( localRhs );
+ GEOS_UNUSED_VAR( dt );
+ GEOS_UNUSED_VAR( num_timesteps );
+ GEOS_UNUSED_VAR( current_newton_iteration );
+ GEOS_UNUSED_VAR( num_timestep_cuts );
+ GEOS_UNUSED_VAR( mesh );
+ GEOS_UNUSED_VAR( subRegion );
+
+}
+
+void SinglePhaseWell::outputWellDebug( real64 const time,
+ real64 const dt,
+ integer num_timesteps,
+ integer current_newton_iteration,
+ integer num_timestep_cuts,
+ DomainPartition & domain,
+ DofManager const & dofManager,
+ CRSMatrixView< real64, globalIndex const > const & localMatrix,
+ arrayView1d< real64 > const & localRhs )
+{
+ GEOS_UNUSED_VAR( time );
+ GEOS_UNUSED_VAR( dt );
+ GEOS_UNUSED_VAR( num_timesteps );
+ GEOS_UNUSED_VAR( current_newton_iteration );
+ GEOS_UNUSED_VAR( num_timestep_cuts );
+ GEOS_UNUSED_VAR( domain );
+ GEOS_UNUSED_VAR( dofManager );
+ GEOS_UNUSED_VAR( localMatrix );
+ GEOS_UNUSED_VAR( localRhs );
+}
void SinglePhaseWell::computePerforationRates( real64 const & time_n,
real64 const & dt, DomainPartition & domain )
{
@@ -887,6 +1189,123 @@ void SinglePhaseWell::computePerforationRates( real64 const & time_n,
}
+real64
+SinglePhaseWell::calculateWellResidualNorm( real64 const & time_n,
+ real64 const & dt,
+ 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() );
+
+
+
+ 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() )
+ {
+ real64 subRegionResidualNorm[2]{};
+ thermalSinglePhaseWellKernels::ResidualNormKernelFactory::
+ createAndLaunch< parallelDevicePolicy<> >( rankOffset,
+ wellDofKey,
+ localRhs,
+ subRegion,
+ fluid,
+ wellControls,
+ time_n,
+ dt,
+ m_nonlinearSolverParameters.m_minNormalizer,
+ subRegionResidualNorm );
+ // step 2: reduction across meshBodies/regions/subRegions
+
+ for( integer i=0; i localResidualNorm[i] )
+ {
+ localResidualNorm[i] = subRegionResidualNorm[i];
+ }
+ }
+ }
+ else
+ {
+ 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];
+ }
+ }
+
+
+ real64 resNorm=localResidualNorm[0];
+ if( isThermal() )
+ {
+ real64 globalResidualNorm[2]{};
+ globalResidualNorm[0] = MpiWrapper::max( localResidualNorm[0] );
+ globalResidualNorm[1] = MpiWrapper::max( localResidualNorm[1] );
+ resNorm= std::sqrt( globalResidualNorm[0] * globalResidualNorm[0] + globalResidualNorm[1] * globalResidualNorm[1] );
+
+ GEOS_LOG_LEVEL_RANK_0_NLR( logInfo::ResidualNorm, GEOS_FMT( " ( R{} ) = ( {:4.2e} ) ( Renergy ) = ( {:4.2e} )",
+ coupledSolverAttributePrefix(), globalResidualNorm[0], globalResidualNorm[1] ));
+
+ getConvergenceStats().setResidualValue( GEOS_FMT( "R{}", coupledSolverAttributePrefix()), globalResidualNorm[0] );
+ getConvergenceStats().setResidualValue( "Renergy", globalResidualNorm[1] );
+ }
+ else
+ {
+ resNorm = MpiWrapper::max( resNorm );
+
+ GEOS_LOG_LEVEL_RANK_0_NLR( logInfo::ResidualNorm, GEOS_FMT( " ( R{} ) = ( {:4.2e} )",
+ coupledSolverAttributePrefix(), resNorm ));
+ getConvergenceStats().setResidualValue( GEOS_FMT( "R{}", coupledSolverAttributePrefix()), resNorm );
+ }
+
+ return resNorm;
+}
+real64
+SinglePhaseWell::scalingForWellSystemSolution( ElementSubRegionBase & subRegion,
+ DofManager const & dofManager,
+ arrayView1d< real64 const > const & localSolution )
+{
+ GEOS_MARK_FUNCTION;
+ GEOS_UNUSED_VAR( subRegion );
+ GEOS_UNUSED_VAR( dofManager );
+ GEOS_UNUSED_VAR( localSolution );
+
+ return 1.0;
+}
+
real64
SinglePhaseWell::calculateResidualNorm( real64 const & time_n,
real64 const & dt,
@@ -895,13 +1314,13 @@ SinglePhaseWell::calculateResidualNorm( real64 const & time_n,
arrayView1d< real64 const > const & localRhs )
{
GEOS_MARK_FUNCTION;
- integer numNorm = 1; // mass balance
+ 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 );
@@ -928,51 +1347,54 @@ SinglePhaseWell::calculateResidualNorm( real64 const & time_n,
WellControls const & wellControls = getWellControls( subRegion );
- // step 1: compute the norm in the subRegion
- if( isThermal() )
+ if( wellControls.isWellOpen() )
{
- real64 subRegionResidualNorm[2]{};
- thermalSinglePhaseWellKernels::ResidualNormKernelFactory::
- createAndLaunch< parallelDevicePolicy<> >( 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] )
+ real64 subRegionResidualNorm[2]{};
+ thermalSinglePhaseWellKernels::ResidualNormKernelFactory::
+ createAndLaunch< parallelDevicePolicy<> >( rankOffset,
+ wellDofKey,
+ localRhs,
+ subRegion,
+ fluid,
+ wellControls,
+ time_n,
+ dt,
+ m_nonlinearSolverParameters.m_minNormalizer,
+ subRegionResidualNorm );
+ // step 2: reduction across meshBodies/regions/subRegions
+
+ for( integer i=0; i localResidualNorm[i] )
+ {
+ localResidualNorm[i] = subRegionResidualNorm[i];
+ }
}
}
- }
- else
- {
- 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] )
+ else
{
- localResidualNorm[0] = subRegionResidualNorm[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];
+ }
}
}
} );
@@ -1003,6 +1425,47 @@ SinglePhaseWell::calculateResidualNorm( real64 const & time_n,
return resNorm;
}
+bool SinglePhaseWell::checkWellSystemSolution( ElementSubRegionBase & subRegion,
+ DofManager const & dofManager,
+ arrayView1d< real64 const > const & localSolution,
+ real64 const scalingFactor )
+{
+ GEOS_MARK_FUNCTION;
+
+ string const wellDofKey = dofManager.getKey( wellElementDofName() );
+ integer numNegativePressures = 0;
+ real64 minPressure = 0.0;
+
+
+ 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 );
+
+ if( numNegativePressures > 0 )
+ {
+ GEOS_LOG_LEVEL_RANK_0( logInfo::Solution,
+ GEOS_FMT( " {}: Number of negative pressure values: {}, minimum value: {} Pa",
+ getName(), numNegativePressures, fmt::format( "{:.{}f}", minPressure, 3 ) ) );
+ }
+
+ return (m_allowNegativePressure || numNegativePressures == 0) ? 1 : 0;
+}
bool SinglePhaseWell::checkSystemSolution( DomainPartition & domain,
DofManager const & dofManager,
@@ -1037,7 +1500,7 @@ bool SinglePhaseWell::checkSystemSolution( DomainPartition & domain,
arrayView1d< real64 const > const & pres =
subRegion.getField< well::pressure >();
- auto const statistics =
+ std::pair< integer, real64 > statistics =
singlePhaseBaseKernels::SolutionCheckKernel::
launch< parallelDevicePolicy<> >( localSolution, rankOffset, dofNumber, ghostRank, pres, scalingFactor );
@@ -1058,6 +1521,66 @@ bool SinglePhaseWell::checkSystemSolution( DomainPartition & domain,
return (m_allowNegativePressure || numNegativePressures == 0) ? 1 : 0;
}
+void
+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,
+ wellElementDofName(),
+ well::pressure::key(),
+ scalingFactor,
+ pressureMask );
+
+ dofManager.addVectorToField( localSolution,
+ wellElementDofName(),
+ well::connectionRate::key(),
+ scalingFactor,
+ connRateMask );
+
+ if( isThermal() )
+ {
+ DofManager::CompMask temperatureMask( m_numDofPerWellElement, 2, 3 );
+
+ dofManager.addVectorToField( localSolution,
+ wellElementDofName(),
+ fields::well::temperature::key(),
+ scalingFactor,
+ temperatureMask );
+
+ }
+
+
+ FieldIdentifiers fieldsToBeSync;
+ if( isThermal() )
+ {
+ 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::applySystemSolution( DofManager const & dofManager,
arrayView1d< real64 const > const & localSolution,
@@ -1157,6 +1680,26 @@ void SinglePhaseWell::resetStateToBeginningOfStep( DomainPartition & domain )
} );
}
+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 );
+
+ SingleFluidBase const & fluid =
+ getConstitutiveModel< SingleFluidBase >( subRegion, subRegion.getReference< string >( viewKeyStruct::fluidNamesString() ) );
+ fluid.saveConvergedState();
+}
void SinglePhaseWell::implicitStepSetup( real64 const & time,
real64 const & dt,
@@ -1300,5 +1843,73 @@ void SinglePhaseWell::printRates( real64 const & time_n,
} );
}
+bool SinglePhaseWell::evaluateConstraints( real64 const & time_n,
+ real64 const & GEOS_UNUSED_PARAM( stepDt ),
+ integer const GEOS_UNUSED_PARAM( cycleNumber ),
+ integer const GEOS_UNUSED_PARAM( coupledIterationNumber ),
+ DomainPartition & GEOS_UNUSED_PARAM( domain ),
+ MeshLevel & GEOS_UNUSED_PARAM( mesh ),
+ ElementRegionManager & GEOS_UNUSED_PARAM( elemManager ),
+ WellElementSubRegion & subRegion,
+ DofManager const & GEOS_UNUSED_PARAM( dofManager ) )
+{
+ WellControls & wellControls = getWellControls( subRegion );
+ // create list of all constraints to process
+ std::vector< WellConstraintBase * > constraintList;
+ if( wellControls.isProducer() )
+ {
+ constraintList = wellControls.getProdRateConstraints();
+ // Solve minimum bhp constraint first
+ constraintList.insert( constraintList.begin(), wellControls.getMinBHPConstraint() );
+ }
+ else
+ {
+ constraintList = wellControls.getInjRateConstraints();
+ // Solve maximum bhp constraint first;
+ constraintList.insert( constraintList.begin(), wellControls.getMaxBHPConstraint() );
+ }
+ // Get current constraint
+ WellConstraintBase * limitingConstraint = nullptr;
+ for( auto & constraint : constraintList )
+ {
+ if( constraint->getName() == wellControls.getCurrentConstraint()->getName())
+ {
+ limitingConstraint = constraint;
+ // tjb. this is likely not needed. set in update state
+ constraint->setBHP ( wellControls.getReference< real64 >( SinglePhaseWell::viewKeyStruct::currentBHPString() ));
+ constraint->setTotalVolumeRate ( wellControls.getReference< real64 >(
+ SinglePhaseWell::viewKeyStruct::currentVolRateString() ));
+
+ GEOS_LOG_RANK_IF ( getLogLevel() > 4 && subRegion.isLocallyOwned(),
+ " Well " << subRegion.getName() << " Limiting Constraint " << limitingConstraint->getName() << " " << limitingConstraint->bottomHolePressure() << " " <<
+ limitingConstraint->totalVolumeRate() );
+ }
+ }
+
+
+ // Check current against other constraints
+ for( auto & constraint : constraintList )
+ {
+
+ if( limitingConstraint->getName() != constraint->getName())
+ {
+ //std::cout << "Use estimator " << useEstimator << " Evaluating constraint " << constraint.getName() << " against constraint " <<
+ // limitingConstraint->getName() << std::endl;
+ if( constraint->checkViolation( *limitingConstraint, time_n ) )
+ {
+ wellControls.setControl( static_cast< WellControls::Control >(constraint->getControl()) ); // tjb old
+ wellControls.setCurrentConstraint( constraint );
+ GEOS_LOG_RANK_IF ( getLogLevel() > 4 && subRegion.isLocallyOwned(),
+ " Well " << subRegion.getName() << " New Limiting Constraint " << 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;
+}
+
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 7bfeb996aa4..5c7c9f0bdb2 100644
--- a/src/coreComponents/physicsSolvers/fluidFlow/wells/SinglePhaseWell.hpp
+++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/SinglePhaseWell.hpp
@@ -95,6 +95,15 @@ class SinglePhaseWell : public WellSolverBase
*/
/**@{*/
+ virtual real64 scalingForWellSystemSolution( ElementSubRegionBase & subRegion,
+ DofManager const & dofManager,
+ arrayView1d< real64 const > const & localSolution ) override;
+ virtual real64
+ calculateWellResidualNorm( real64 const & time_n,
+ real64 const & dt,
+ WellElementSubRegion const & subRegion,
+ DofManager const & dofManager,
+ arrayView1d< real64 const > const & localRhs ) override;
virtual real64
calculateResidualNorm( real64 const & time_n,
real64 const & dt,
@@ -102,6 +111,12 @@ class SinglePhaseWell : public WellSolverBase
DofManager const & dofManager,
arrayView1d< real64 const > const & localRhs ) override;
+ virtual bool
+ checkWellSystemSolution( ElementSubRegionBase & subRegion,
+ DofManager const & dofManager,
+ arrayView1d< real64 const > const & localSolution,
+ real64 const scalingFactor ) override;
+
virtual bool
checkSystemSolution( DomainPartition & domain,
DofManager const & dofManager,
@@ -109,6 +124,14 @@ class SinglePhaseWell : public WellSolverBase
real64 const scalingFactor ) override;
virtual void
+ applyWellSystemSolution( DofManager const & dofManager,
+ arrayView1d< real64 const > const & localSolution,
+ real64 const scalingFactor,
+ real64 const dt,
+ DomainPartition & domain,
+ MeshLevel & mesh,
+ WellElementSubRegion & subRegion ) override;
+ virtual void
applySystemSolution( DofManager const & dofManager,
arrayView1d< real64 const > const & localSolution,
real64 const scalingFactor,
@@ -142,7 +165,7 @@ class SinglePhaseWell : public WellSolverBase
* @brief Recompute the volumetric rate that are used in the well constraints
* @param subRegion the well subregion containing all the primary and dependent fields
*/
- virtual void updateVolRateForConstraint( WellElementSubRegion & subRegion );
+ virtual void calculateReferenceElementRates( WellElementSubRegion & subRegion );
/**
* @brief Recompute the BHP pressure that is used in the well constraints
@@ -155,6 +178,11 @@ class SinglePhaseWell : public WellSolverBase
* @param subRegion the well subRegion containing the well elements and their associated fields
*/
virtual void updateFluidModel( WellElementSubRegion & subRegion ) const;
+ /**
+ * @brief Update separator model state
+ * @param subRegion the well subRegion containing the separator
+ */
+ void updateSeparator( WellElementSubRegion & subRegion );
/**
* @brief Recompute the perforation rates for all the wells
@@ -163,6 +191,14 @@ class SinglePhaseWell : public WellSolverBase
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 domain the domain containing the mesh and fields
+ */
+ virtual real64 updateWellState( WellElementSubRegion & subRegion ) override;
+ virtual void updateState( DomainPartition & domain ) override;
+
/**
* @brief Recompute all dependent quantities from primary variables (including constitutive models) on the well
* @param subRegion the well subRegion containing the well elements and their associated fields
@@ -185,6 +221,21 @@ class SinglePhaseWell : public WellSolverBase
CRSMatrixView< real64, globalIndex const > const & localMatrix,
arrayView1d< real64 > const & localRhs ) 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 assembleWellFluxTerms( real64 const & time,
+ real64 const & dt,
+ WellElementSubRegion const & subRegion,
+ DofManager const & dofManager,
+ CRSMatrixView< real64, globalIndex const > const & localMatrix,
+ arrayView1d< real64 > const & localRhs ) override;
/**
* @brief assembles the flux terms for all connections between well elements
* @param time_n previous time value
@@ -208,6 +259,14 @@ class SinglePhaseWell : public WellSolverBase
* @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 ) override;
+
virtual void assembleAccumulationTerms( real64 const & time_n,
real64 const & dt, DomainPartition & domain,
DofManager const & dofManager,
@@ -226,6 +285,22 @@ class SinglePhaseWell : public WellSolverBase
CRSMatrixView< real64, globalIndex const > const & localMatrix,
arrayView1d< real64 > const & localRhs );
+ 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;
+
+
+ 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;
+
+
/**
* @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
@@ -242,6 +317,32 @@ class SinglePhaseWell : public WellSolverBase
CRSMatrixView< real64, globalIndex const > const & localMatrix,
arrayView1d< real64 > const & localRhs ) override;
+ virtual void outputSingleWellDebug( real64 const time,
+ real64 const dt,
+ integer num_timesteps,
+ integer current_newton_iteration,
+ integer num_timestep_cuts,
+ MeshLevel & mesh,
+ WellElementSubRegion & subRegion,
+ DofManager const & dofManager,
+ CRSMatrixView< real64, globalIndex const > const & localMatrix,
+ arrayView1d< const real64 > const & localRhs ) override;
+ virtual void outputWellDebug( real64 const time,
+ real64 const dt,
+ integer num_timesteps,
+ integer current_newton_iteration,
+ integer num_timestep_cuts,
+ DomainPartition & domain,
+ DofManager const & dofManager,
+ CRSMatrixView< real64, globalIndex const > const & localMatrix,
+ arrayView1d< real64 > const & localRhs )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;
+ void initializeWell( DomainPartition & domain, MeshLevel & mesh, WellElementSubRegion & subRegion, real64 const & time_n ) override;
/*
* @brief apply a special treatment to the wells that are shut
* @param time_n the time at the previous converged time step
@@ -255,6 +356,9 @@ class SinglePhaseWell : public WellSolverBase
DofManager const & dofManager,
CRSMatrixView< real64, globalIndex const > const & localMatrix,
arrayView1d< real64 > const & localRhs );
+
+
+ virtual void saveState( WellElementSubRegion & subRegion ) override;
struct viewKeyStruct : WellSolverBase::viewKeyStruct
{
static constexpr char const * dofFieldString() { return "singlePhaseWellVars"; }
@@ -268,6 +372,9 @@ class SinglePhaseWell : public WellSolverBase
protected:
+ virtual void initializePostInitialConditionsPreSubGroups() override;
+
+
void printRates( real64 const & time_n,
real64 const & dt,
DomainPartition & domain ) override;
@@ -279,11 +386,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
@@ -297,6 +399,22 @@ class SinglePhaseWell : public WellSolverBase
WellElementSubRegion const & subRegion,
ElementRegionManager const & elemManager ) override;
+ virtual bool evaluateConstraints( real64 const & time_n,
+ real64 const & GEOS_UNUSED_PARAM( stepDt ),
+ integer const GEOS_UNUSED_PARAM( cycleNumber ),
+ integer const GEOS_UNUSED_PARAM( coupledIterationNumber ),
+ DomainPartition & GEOS_UNUSED_PARAM( domain ),
+ MeshLevel & GEOS_UNUSED_PARAM( mesh ),
+ ElementRegionManager & GEOS_UNUSED_PARAM( elemManager ),
+ WellElementSubRegion & subRegion,
+ DofManager const & GEOS_UNUSED_PARAM( dofManager ) ) override;
+
+
+ /**
+ * @brief Create well separator
+ */
+ void createSeparator();
+
};
} // namespace geos
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..003c86d466f
--- /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->getName() << " " << 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 15914dfb8a1..538ad4d05bd 100644
--- a/src/coreComponents/physicsSolvers/fluidFlow/wells/WellControls.cpp
+++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellControls.cpp
@@ -32,26 +32,21 @@ 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_inputControl( Control::UNINITIALIZED ),
- m_currentControl( Control::UNINITIALIZED ),
- m_targetBHP( 0.0 ),
- m_targetTotalRate( 0.0 ),
- m_targetPhaseRate( 0.0 ),
- m_targetMassRate( 0.0 ),
+ m_currentControl( Control::UNINITIALIZED ), // tjb remove
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_wellOpen( false ),
+ m_estimateSolution( false ),
+ m_currentConstraint( nullptr ),
m_wellStatus( WellControls::Status::OPEN ),
- m_regionAveragePressure( -1 )
+ m_regionAveragePressure( -1 ),
+ m_enableIsoThermalEstimator( 0 )
{
setInputFlags( InputFlags::OPTIONAL_NONUNIQUE );
@@ -59,61 +54,14 @@ 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* " ) );
-
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( "Target bottom-hole pressure [Pa]" );
-
- 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( "Global component densities of the injection stream [moles/m^3 or kg/m^3]" );
-
- 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 ).
@@ -154,31 +102,18 @@ WellControls::WellControls( string const & name, Group * const parent )
" - Injector pressure at reference depth initialized as: (1+initialPressureCoefficient)*reservoirPressureAtClosestPerforation + density*g*( zRef - zPerf ) \n"
" - Producer pressure at reference depth initialized as: (1-initialPressureCoefficient)*reservoirPressureAtClosestPerforation + density*g*( zRef - zPerf ) " );
- registerWrapper( viewKeyStruct::targetBHPTableNameString(), &m_targetBHPTableName ).
- setRTTypeName( rtTypes::CustomTypes::groupNameRef ).
- setInputFlag( InputFlags::OPTIONAL ).
- setDescription( "Name of the BHP table when the rate is a time dependent function" );
-
- registerWrapper( viewKeyStruct::targetTotalRateTableNameString(), &m_targetTotalRateTableName ).
- setRTTypeName( rtTypes::CustomTypes::groupNameRef ).
+ this->registerWrapper( viewKeyStruct::estimateWellSolutionString(), &m_estimateSolution ).
+ setApplyDefaultValue( 0 ).
setInputFlag( InputFlags::OPTIONAL ).
- setDescription( "Name of the total rate table when the rate is a time dependent function" );
+ setDescription( "Flag to esitmate well solution prior to coupled reservoir and well solve." );
- registerWrapper( viewKeyStruct::targetPhaseRateTableNameString(), &m_targetPhaseRateTableName ).
- setRTTypeName( rtTypes::CustomTypes::groupNameRef ).
+ registerWrapper( viewKeyStruct::enableIsoThermalEstimatorString(), &m_enableIsoThermalEstimator ).
+ setDefaultValue( 0 ).
setInputFlag( InputFlags::OPTIONAL ).
- setDescription( "Name of the phase rate table when the rate is a time dependent function" );
+ 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::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." );
addLogLevel< logInfo::WellControl >();
}
@@ -187,28 +122,80 @@ 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;
-}
+ 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 ) );
-void WellControls::switchToTotalRateControl( real64 const & val )
-{
- m_currentControl = Control::TOTALVOLRATE;
- m_targetTotalRate = val;
-}
+ Group * constraint = nullptr;
+ if( childKey == viewKeyStruct::minimumBHPConstraintString() )
+ {
+ MinimumBHPConstraint & bhpConstraint = registerGroup< MinimumBHPConstraint >( childName );
+ m_minBHPConstraint = &bhpConstraint;
+ constraint = &bhpConstraint;
+ }
+ else if( childKey == viewKeyStruct::maximumBHPConstraintString() )
+ {
+ MaximumBHPConstraint & bhpConstraint = registerGroup< MaximumBHPConstraint >( childName );
+ m_maxBHPConstraint = &bhpConstraint;
+ constraint = &bhpConstraint;
+ }
+ else if( childKey == viewKeyStruct::productionPhaseVolumeRateConstraintString() )
+ {
+ ProductionConstraint< PhaseVolumeRateConstraint > & phaseConstraint = registerGroup< ProductionConstraint< PhaseVolumeRateConstraint > >( childName );
+ m_productionRateConstraintList.emplace_back( &phaseConstraint );
+ constraint = &phaseConstraint;
+ }
+ else if( childKey == viewKeyStruct::injectionPhaseVolumeRateConstraint() )
+ {
-void WellControls::switchToPhaseRateControl( real64 const & val )
-{
- m_currentControl = Control::PHASEVOLRATE;
- m_targetPhaseRate = val;
+ InjectionConstraint< PhaseVolumeRateConstraint > & phaseConstraint = registerGroup< InjectionConstraint< PhaseVolumeRateConstraint > >( childName );
+ m_injectionRateConstraintList.emplace_back( &phaseConstraint );
+ constraint = &phaseConstraint;
+ }
+ else if( childKey == viewKeyStruct::productionVolumeRateConstraint() )
+ {
+ ProductionConstraint< VolumeRateConstraint > & volConstraint = registerGroup< ProductionConstraint< VolumeRateConstraint > >( childName );
+ m_productionRateConstraintList.emplace_back( &volConstraint );
+ constraint = &volConstraint;
+ }
+ else if( childKey == viewKeyStruct::injectionVolumeRateConstraint() )
+ {
+ InjectionConstraint< VolumeRateConstraint > & volConstraint = registerGroup< InjectionConstraint< VolumeRateConstraint > >( childName );
+ m_injectionRateConstraintList.emplace_back( &volConstraint );
+ constraint = &volConstraint;
+ }
+ else if( childKey == viewKeyStruct::productionMassRateConstraint() )
+ {
+ ProductionConstraint< MassRateConstraint > & massConstraint = registerGroup< ProductionConstraint< MassRateConstraint > >( childName );
+ m_productionRateConstraintList.emplace_back( &massConstraint );
+ constraint = &massConstraint;
+
+ }
+ else if( childKey == viewKeyStruct::injectionMassRateConstraint() )
+ {
+ InjectionConstraint< MassRateConstraint > & massConstraint = registerGroup< InjectionConstraint< MassRateConstraint > >( childName );
+ m_injectionRateConstraintList.emplace_back( &massConstraint );
+ constraint = &massConstraint;
+ }
+ else if( childKey == viewKeyStruct::productionLiquidRateConstraint() )
+ {
+ ProductionConstraint< LiquidRateConstraint > & liquidConstraint = registerGroup< ProductionConstraint< LiquidRateConstraint > >( childName );
+ m_productionRateConstraintList.emplace_back( &liquidConstraint );
+ constraint = &liquidConstraint;
+ }
+ return constraint;
}
-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 );
+ }
}
namespace
@@ -236,6 +223,7 @@ TableFunction * createWellTable( string const & tableName,
void WellControls::postInputInitialization()
{
+
// 0) Assign the value of the current well control
// When the simulation starts from a restart file, we don't want to use the inputControl,
// because the control may have switched in the simulation that generated the restart
@@ -249,119 +237,28 @@ 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 );
-
- // 1.b) check target rates
- GEOS_THROW_IF( m_targetTotalRate < 0,
- getWrapperDataContext( viewKeyStruct::targetTotalRateString() ) << ": Target rate is negative",
- InputError );
-
- GEOS_THROW_IF( m_targetPhaseRate < 0,
- getWrapperDataContext( viewKeyStruct::targetPhaseRateString() ) << ": Target oil rate is negative",
- InputError );
-
- GEOS_THROW_IF( m_targetMassRate < 0,
- getWrapperDataContext( viewKeyStruct::targetMassRateString() ) << ": Target mass rate is negative",
- InputError );
-
- 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 );
-
- // 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" );
- 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 );
- }
// 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 );
- // 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 );
+ // 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 );
-
- 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 );
+ //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_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 );
-
- 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 );
- GEOS_THROW_IF( ((m_targetMassRate > 0.0 && m_useSurfaceConditions==0)),
- "WellControls " << getDataContext() << ": Option only valid if useSurfaceConditions set to 1",
+ // 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.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 );
- }
- else if( m_targetBHP <= 0.0 && m_targetBHPTableName.empty() )
- {
- m_targetBHP = isProducer() ? WellConstants::defaultProducerBHP : WellConstants::defaultInjectorBHP;
- GEOS_LOG_LEVEL_RANK_0( logInfo::WellControl,
- GEOS_FMT( "WellControls {}: Setting {} to default value {}", getDataContext(), viewKeyStruct::targetBHPString(), m_targetBHP ));
- }
-
// 6.2) Check incoherent information
// An injector must be controlled by TotalVolRate
@@ -376,79 +273,6 @@ void WellControls::postInputInitialization()
<< EnumStrings< Control >::toString( Control::MASSRATE ),
InputError );
- // 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 );
-
-
- // 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 );
- }
-
- // 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 );
- }
-
- // 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 );
- }
- // 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 );
- }
// 12) Create the time-dependent well status table
if( m_statusTableName.empty())
{
@@ -479,12 +303,31 @@ void WellControls::setWellStatus( real64 const & currentTime, WellControls::Stat
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;
@@ -497,13 +340,38 @@ bool WellControls::isWellOpen() const
return getWellStatus() == WellControls::Status::OPEN;
}
+void WellControls::setWellState( bool open )
+{
+ m_wellOpen = open;
+}
+
+bool WellControls::getWellState() const
+{
+ return m_wellOpen;
+}
void WellControls::setNextDtFromTables( real64 const & currentTime, real64 & nextDt )
{
- WellControls::setNextDtFromTable( m_targetBHPTable, currentTime, nextDt );
- WellControls::setNextDtFromTable( m_targetMassRateTable, currentTime, nextDt );
- WellControls::setNextDtFromTable( m_targetPhaseRateTable, currentTime, nextDt );
- WellControls::setNextDtFromTable( m_targetTotalRateTable, currentTime, nextDt );
+ if( isProducer() )
+ {
+ if( getMinBHPConstraint() != nullptr )
+ {
+ getMinBHPConstraint()->setNextDtFromTables( currentTime, nextDt );
+ }
+ for( auto const & constraint : m_productionRateConstraintList )
+ {
+ constraint->setNextDtFromTables( currentTime, nextDt );
+ }
+ }
+ else
+ {
+ getMaxBHPConstraint()->setNextDtFromTables( currentTime, nextDt );
+ for( auto const & constraint : m_injectionRateConstraintList )
+ {
+ constraint->setNextDtFromTables( currentTime, nextDt );
+ }
+ }
+
WellControls::setNextDtFromTable( m_statusTable, currentTime, nextDt );
}
@@ -521,4 +389,80 @@ 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();
+}
} //namespace geos
diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/WellControls.hpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellControls.hpp
index 301c3c42f7c..fcbf88e831e 100644
--- a/src/coreComponents/physicsSolvers/fluidFlow/wells/WellControls.hpp
+++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellControls.hpp
@@ -26,6 +26,15 @@
#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"
namespace geos
{
namespace dataRepository
@@ -124,33 +133,127 @@ class WellControls : public dataRepository::Group
///@}
/**
- * @name Getters / Setters
+ * @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;
/**
- * @brief Set the control type to BHP and set a numerical value for the control.
- * @param[in] val value for the BHP control
+ * @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.
*/
- void switchToBHPControl( real64 const & val );
+ 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;
+ }
+
/**
- * @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
+ * @copydoc forInjectionConstraints(LAMBDA &&)
*/
- void switchToTotalRateControl( real64 const & val );
+ 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 );
+ } );
+ }
+ }
/**
- * @brief Set the control type to mass rate and set a numerical value for the control.
- * @param[in] val value for the mass rate
+ * @copydoc forProductionConstraints(LAMBDA &&)
*/
- void switchToMassRateControl( real64 const & val );
+ 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 );
+ } );
+ }
+ }
/**
- * @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
+ * @name Getters / Setters
*/
- void switchToPhaseRateControl( real64 const & val );
+ ///@{
+
/**
* @brief Get the control type for the well.
@@ -164,17 +267,18 @@ class WellControls : public dataRepository::Group
*/
void setControl( Control const & newControl ) { m_currentControl = newControl; }
+ /**
+ * @brief getter for esitmator switch
+ * @return True if estimate well solution
+ */
+ integer estimateSolution() const { return m_estimateSolution; }
+
/**
* @brief Get the input control type for the well.
* @return the Control enum enforced at the well
*/
Control getInputControl() const { return m_inputControl; }
- /**
- * @brief Getter for the reference elevation where the BHP control is enforced
- * @return the reference elevation
- */
- real64 getReferenceElevation() const { return m_refElevation; }
/**
* @brief Getter for the reference gravity coefficient
@@ -189,58 +293,38 @@ class WellControls : public dataRepository::Group
/**
- * @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
- */
- const string & getTargetPhaseName() const { return m_targetPhaseName; }
/**
- * @brief Const accessor for the composition of the injection stream
- * @return a global component fraction vector
+ * @brief Const accessor for the phase constraint index
+ * @return phase index associated with phase constraint
*/
- arrayView1d< real64 const > getInjectionStream() const { return m_injectionStream; }
+ integer getConstraintPhaseIndex() const;
/**
- * @brief Const accessor for the temperature of the injection stream
- * @return the temperature of the injection stream
+ * @brief Return the reference elvation where pressure constraint is measured
+ * @return vertical location of constraint
*/
- real64 getInjectionTemperature() const { return m_injectionTemperature; }
+ real64 getReferenceElevation() const;
+
/**
* @brief Getter for the flag specifying whether we check rates at surface or reservoir conditions
@@ -284,6 +368,14 @@ class WellControls : public dataRepository::Group
*/
bool isWellOpen() const;
+ void setWellState( bool open );
+ bool getWellState() const;
+
+ void setCurrentConstraint( WellConstraintBase * currentConstraint ) { m_currentConstraint = currentConstraint;}
+ 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
@@ -303,6 +395,7 @@ class WellControls : public dataRepository::Group
*/
void setNextDtFromTables( real64 const & currentTime, real64 & nextDt );
+
/**
* @brief setter for multi fluid separator
* @param[in] fluidSeparatorPtr single or multiphase separator
@@ -314,6 +407,13 @@ 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
@@ -352,6 +452,27 @@ class WellControls : public dataRepository::Group
WellControls::Status getWellStatus () const { return m_wellStatus; }
///@}
+ /**
+ * @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; }
+
+
+ ///@}
+
/**
* @brief Struct to serve as a container for variable strings and keys.
* @struct viewKeyStruct
@@ -362,24 +483,10 @@ class WellControls : public dataRepository::Group
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 +495,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
@@ -404,13 +504,54 @@ class WellControls : public dataRepository::Group
static constexpr char const * enableCrossflowString() { return "enableCrossflow"; }
/// string key for the initial pressure coefficient
static constexpr char const * initialPressureCoefficientString() { return "initialPressureCoefficient"; }
-
+ /// 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"; }
+ /// 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"; }
}
/// ViewKey struct for the WellControls class
viewKeysWellControls;
static void setNextDtFromTable( TableFunction const * table, real64 const currentTime, real64 & nextDt );
+ /**
+ * @brief Create a constraint
+ * @tparam ConstraintType the type of constraint to create
+ * @param[in] constraintName name to assign to the constraint
+ */
+ template< typename ConstraintType > void createConstraint ( string const & constraintName );
+
+
+ /**
+ * @brief 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; };
+ // Lists of rate constraints
+ std::vector< WellConstraintBase * > getProdRateConstraints() { return m_productionRateConstraintList; };
+ std::vector< WellConstraintBase * > getProdRateConstraints() const { return m_productionRateConstraintList; };
+ std::vector< WellConstraintBase * > getInjRateConstraints() { return m_injectionRateConstraintList; }
+ std::vector< WellConstraintBase * > getInjRateConstraints() const { return m_injectionRateConstraintList; }
protected:
virtual void postInputInitialization() override;
@@ -434,27 +575,6 @@ 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;
@@ -470,17 +590,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;
@@ -497,20 +606,26 @@ class WellControls : public dataRepository::Group
/// Rate sign. +1 for injector, -1 for producer
real64 m_rateSign;
- /// Total rate table
- TableFunction const * m_targetTotalRateTable;
- /// Phase rate table
- TableFunction const * m_targetPhaseRateTable;
+ /// Status table
+ TableFunction const * m_statusTable;
+
+ bool m_wellOpen;
+
+ /// flag to use the estimator
+ integer m_estimateSolution;
- /// Mass rate table
- TableFunction const * m_targetMassRateTable;
+ // Current constraint
+ WellConstraintBase * m_currentConstraint;
+ // Minimum and maximum BHP and WHP constraints
+ MinimumBHPConstraint * m_minBHPConstraint;
+ MaximumBHPConstraint * m_maxBHPConstraint;
- /// BHP table
- TableFunction const * m_targetBHPTable;
- /// Status table
- TableFunction const * m_statusTable;
+ // Lists of rate constraints
+ std::vector< WellConstraintBase * > m_productionRateConstraintList;
+ std::vector< WellConstraintBase * > m_injectionRateConstraintList;
+
/// Well status
WellControls::Status m_wellStatus;
@@ -522,13 +637,19 @@ class WellControls : public dataRepository::Group
/// Region average temperature used in volume rate constraint calculations
real64 m_regionAverageTemperature;
+ bool m_thermalEffectsEnabled;
+ integer m_enableIsoThermalEstimator;
};
-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",
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..54424d9224e
--- /dev/null
+++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellLiquidRateConstraint.cpp
@@ -0,0 +1,85 @@
+/*
+ * ------------------------------------------------------------------------------------------------------------
+ * 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 );
+ if( this->m_rateSign < 0.0 )
+ {
+ // production: violated when current < constraint
+ return currentValue < constraintValue;
+ }
+ else
+ {
+ // injection: violated when current > constraint
+ return currentValue > 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/WellMassRateConstraint.cpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellMassRateConstraint.cpp
new file mode 100644
index 00000000000..6851a033f8b
--- /dev/null
+++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellMassRateConstraint.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 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 );
+ if( this->m_rateSign < 0.0 )
+ {
+ return currentValue < constraintValue;
+ }
+ else
+ {
+ return currentValue > 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/WellPhaseVolumeRateConstraint.cpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellPhaseVolumeRateConstraint.cpp
new file mode 100644
index 00000000000..533848b97a3
--- /dev/null
+++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellPhaseVolumeRateConstraint.cpp
@@ -0,0 +1,88 @@
+/*
+ * ------------------------------------------------------------------------------------------------------------
+ * 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 );
+ if( m_rateSign < 0.0 )
+ {
+ return currentValue < constraintValue;
+ }
+ else
+ {
+ return currentValue > constraintValue;
+ }
+}
+
+} //namespace geos
diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/WellPhaseVolumeRateConstraint.hpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellPhaseVolumeRateConstraint.hpp
new file mode 100644
index 00000000000..bb7880cfc0c
--- /dev/null
+++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellPhaseVolumeRateConstraint.hpp
@@ -0,0 +1,179 @@
+/*
+ * ------------------------------------------------------------------------------------------------------------
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ * Copyright (c) 2016-2024 Lawrence Livermore National Security LLC
+ * Copyright (c) 2018-2024 TotalEnergies
+ * Copyright (c) 2018-2024 The Board of Trustees of the Leland Stanford Junior University
+ * Copyright (c) 2023-2024 Chevron
+ * Copyright (c) 2019- GEOS/GEOSX Contributors
+ * All rights reserved
+ *
+ * See top level LICENSE, COPYRIGHT, CONTRIBUTORS, NOTICE, and ACKNOWLEDGEMENTS files for details.
+ * ------------------------------------------------------------------------------------------------------------
+ */
+
+/*
+ * @file WellPhaseVolumeRateConstraint.hpp
+ */
+
+
+#ifndef GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_WELLPHASEVOLUMERATECONSTRAINT_HPP
+#define GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_WELLPHASEVOLUMERATECONSTRAINT_HPP
+
+#include "common/format/EnumStrings.hpp"
+#include "dataRepository/Group.hpp"
+#include "functions/TableFunction.hpp"
+#include "WellConstraintsBase.hpp"
+#include "WellConstants.hpp"
+
+namespace geos
+{
+
+
+template< typename T >
+localIndex getPhaseIndexFromFluidModel( T const & fluidModel, std::string const & inputPhase )
+{
+ localIndex phaseIndex=-1;
+ // Find target phase index for phase rate constraint
+ for( integer ip = 0; ip < fluidModel.numFluidPhases(); ++ip )
+ {
+ if( fluidModel.phaseNames()[ip] == inputPhase )
+ {
+ phaseIndex = ip;
+ }
+ }
+ return phaseIndex;
+}
+
+/**
+ * @class PhaseVolumeRateConstraint
+ * @brief This class describes a phase rate constraint used to control a well of WellConstraintType type (Injection or Production).
+ */
+
+class PhaseVolumeRateConstraint : public WellConstraintBase
+{
+public:
+
+
+ /**
+ * @name Constructor / Destructor
+ */
+ ///@{
+
+ /**
+ * @brief Constructor for WellControls Objects.
+ * @param[in] name the name of this instantiation of WellControls in the repository
+ * @param[in] parent the parent group of this instantiation of WellControls
+ */
+ explicit PhaseVolumeRateConstraint( string const & name, dataRepository::Group * const parent );
+
+ /**
+ * @brief Default destructor.
+ */
+ ~PhaseVolumeRateConstraint() override;
+
+ /**
+ * @brief Deleted default constructor.
+ */
+ PhaseVolumeRateConstraint() = delete;
+
+ /**
+ * @brief Deleted copy constructor.
+ */
+ PhaseVolumeRateConstraint( PhaseVolumeRateConstraint const & ) = delete;
+
+ /**
+ * @brief Deleted move constructor.
+ */
+ PhaseVolumeRateConstraint( PhaseVolumeRateConstraint && ) = delete;
+
+ /**
+ * @brief Deleted assignment operator.
+ * @return a reference to a constraint object
+ */
+ PhaseVolumeRateConstraint & operator=( PhaseVolumeRateConstraint const & ) = delete;
+
+ /**
+ * @brief Deleted move operator.
+ * @return a reference to a constraint object
+ */
+ PhaseVolumeRateConstraint & operator=( PhaseVolumeRateConstraint && ) = delete;
+
+ ///@}
+
+ /**
+ * @brief name of the node manager in the object catalog
+ * @return string that contains the catalog name to generate a new Constraint object through the object catalog.
+ */
+ static string catalogName()
+ {
+ return "PhaseVolumeRateConstraint";
+ }
+
+ /**
+ * @name Getters / Setters
+ */
+ ///@{
+
+ // Temp interface - tjb
+ virtual ConstraintTypeId getControl() const override { return ConstraintTypeId::PHASEVOLRATE; };
+
+ /**
+ * @brief Get the target phase name
+ * @return the target phase name
+ */
+ const string & getPhaseName() const { return m_phaseName; }
+
+ /**
+ * @brief Get the target phase index
+ * @return the target phase index
+ */
+ const localIndex & getPhaseIndex() const { return m_phaseIndex; }
+
+ ///@}
+
+ struct viewKeyStruct
+ {
+ /// String key for the well target phase rate
+ static constexpr char const * phaseRateString() { return "phaseRate"; }
+ /// String key for the well target phase name
+ static constexpr char const * phaseNameString() { return "phaseName"; }
+ };
+
+ /**
+ * @brief Validate phase type is consistent with fluidmodel
+ */
+ template< typename T > void validatePhaseType( T const & fluidModel );
+ ///@}
+
+ virtual bool checkViolation( WellConstraintBase const & currentConstraint, real64 const & currentTime ) const override;
+protected:
+
+ virtual void postInputInitialization() override;
+
+private:
+
+ /// Name of the targeted phase
+ string m_phaseName;
+
+ /// Index of the target phase, used to impose the phase rate constraint
+ localIndex m_phaseIndex;
+
+};
+
+template< typename T >
+void PhaseVolumeRateConstraint::validatePhaseType( T const & fluidModel )
+{
+ // Find target phase index for phase rate constraint
+ m_phaseIndex = getPhaseIndexFromFluidModel( fluidModel, this->template getReference< string >( viewKeyStruct::phaseNameString()));
+
+ GEOS_THROW_IF( m_phaseIndex == -1,
+ "PhaseVolumeRateConstraint " << this->template getReference< string >( viewKeyStruct::phaseNameString()) <<
+ ": Invalid phase type for simulation fluid model",
+ InputError );
+}
+
+} //namespace geos
+
+#endif //GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_WELLPHASEVOLUMERATECONSTRAINT_HPP
diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/WellProductionConstraint.cpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellProductionConstraint.cpp
new file mode 100644
index 00000000000..4ec3ec3299d
--- /dev/null
+++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellProductionConstraint.cpp
@@ -0,0 +1,70 @@
+/*
+ * ------------------------------------------------------------------------------------------------------------
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ * Copyright (c) 2016-2024 Lawrence Livermore National Security LLC
+ * Copyright (c) 2018-2024 TotalEnergies
+ * Copyright (c) 2018-2024 The Board of Trustees of the Leland Stanford Junior University
+ * Copyright (c) 2023-2024 Chevron
+ * Copyright (c) 2019- GEOS/GEOSX Contributors
+ * All rights reserved
+ *
+ * See top level LICENSE, COPYRIGHT, CONTRIBUTORS, NOTICE, and ACKNOWLEDGEMENTS files for details.
+ * ------------------------------------------------------------------------------------------------------------
+ */
+
+/*
+ * @file WellProductionConstraint.cpp
+ */
+
+#include "LogLevelsInfo.hpp"
+#include "WellProductionConstraint.hpp"
+#include "WellConstants.hpp"
+#include "dataRepository/InputFlags.hpp"
+#include "functions/FunctionManager.hpp"
+
+#include "WellLiquidRateConstraint.hpp"
+#include "WellMassRateConstraint.hpp"
+#include "WellPhaseVolumeRateConstraint.hpp"
+#include "WellVolumeRateConstraint.hpp"
+
+namespace geos
+{
+
+template< typename ConstraintRateType >
+ProductionConstraint< ConstraintRateType >::ProductionConstraint( string const & name, Group * const parent )
+ : ConstraintRateType( name, parent )
+{
+ // set rate sign for producers (base class member)
+ this->m_rateSign = -1.0;
+}
+template< typename ConstraintRateType >
+ProductionConstraint< ConstraintRateType >::~ProductionConstraint()
+{}
+
+template< typename ConstraintRateType >
+void ProductionConstraint< ConstraintRateType >::postInputInitialization()
+{
+ // Validate value and table options
+ ConstraintRateType::postInputInitialization();
+
+}
+// Register concrete wrapper constraint types and instantiate templates.
+
+template class ProductionConstraint< LiquidRateConstraint >;
+using ProductionLiquidRateConstraint = ProductionConstraint< LiquidRateConstraint >;
+REGISTER_CATALOG_ENTRY( WellConstraintBase, ProductionLiquidRateConstraint, string const &, Group * const )
+
+template class ProductionConstraint< MassRateConstraint >;
+using ProductionMassRateConstraint = ProductionConstraint< MassRateConstraint >;
+REGISTER_CATALOG_ENTRY( WellConstraintBase, ProductionMassRateConstraint, string const &, Group * const )
+
+template class ProductionConstraint< PhaseVolumeRateConstraint >;
+using ProductionPhaseVolumeRateConstraint = ProductionConstraint< PhaseVolumeRateConstraint >;
+REGISTER_CATALOG_ENTRY( WellConstraintBase, ProductionPhaseVolumeRateConstraint, string const &, Group * const )
+
+template class ProductionConstraint< VolumeRateConstraint >;
+using ProductionVolumeRateConstraint = ProductionConstraint< VolumeRateConstraint >;
+REGISTER_CATALOG_ENTRY( WellConstraintBase, ProductionVolumeRateConstraint, string const &, Group * const )
+
+} //namespace geos
diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/WellProductionConstraint.hpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellProductionConstraint.hpp
new file mode 100644
index 00000000000..ce1ca16de14
--- /dev/null
+++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellProductionConstraint.hpp
@@ -0,0 +1,106 @@
+/*
+ * ------------------------------------------------------------------------------------------------------------
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ * Copyright (c) 2016-2024 Lawrence Livermore National Security LLC
+ * Copyright (c) 2018-2024 TotalEnergies
+ * Copyright (c) 2018-2024 The Board of Trustees of the Leland Stanford Junior University
+ * Copyright (c) 2023-2024 Chevron
+ * Copyright (c) 2019- GEOS/GEOSX Contributors
+ * All rights reserved
+ *
+ * See top level LICENSE, COPYRIGHT, CONTRIBUTORS, NOTICE, and ACKNOWLEDGEMENTS files for details.
+ * ------------------------------------------------------------------------------------------------------------
+ */
+
+/*
+ * @file WellProductionConstraints.hpp
+ */
+
+
+#ifndef GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_WELLPRODUCTIONCONSTRAINT_HPP
+#define GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_WELLPRODUCTIONCONSTRAINT_HPP
+
+#include "common/format/EnumStrings.hpp"
+#include "dataRepository/Group.hpp"
+#include "functions/TableFunction.hpp"
+
+namespace geos
+{
+using namespace dataRepository;
+/**
+ * @class ProductionConstraint
+ * @brief This class describes constraint used to control a production well.
+ */
+
+template< typename ConstraintType >
+class ProductionConstraint : public ConstraintType
+{
+public:
+ /**
+ * @name Constructor / Destructor
+ */
+ ///@{
+
+ /**
+ * @brief Constructor for WellControls Objects.
+ * @param[in] name the name of this instantiation of WellControls in the repository
+ * @param[in] parent the parent group of this instantiation of WellControls
+ */
+ explicit ProductionConstraint( string const & name, dataRepository::Group * const parent );
+
+ /**
+ * @brief Default destructor.
+ */
+ ~ProductionConstraint() override;
+
+ /**
+ * @brief Deleted default constructor.
+ */
+ ProductionConstraint() = delete;
+
+ /**
+ * @brief Deleted copy constructor.
+ */
+ ProductionConstraint( ProductionConstraint const & ) = delete;
+
+ /**
+ * @brief Deleted move constructor.
+ */
+ ProductionConstraint( ProductionConstraint && ) = delete;
+
+ /**
+ * @brief Deleted assignment operator.
+ * @return a reference to a constraint object
+ */
+ ProductionConstraint & operator=( ProductionConstraint const & ) = delete;
+
+ /**
+ * @brief Deleted move operator.
+ * @return a reference to a constraint object
+ */
+ ProductionConstraint & operator=( ProductionConstraint && ) = delete;
+
+ ///@}
+
+ /**
+ * @brief name of the node manager in the object catalog
+ * @return string that contains the catalog name to generate a new Constraint object through the object catalog.
+ */
+ static string catalogName()
+ {
+ return "Production"+ConstraintType::catalogName();
+ }
+ virtual string getCatalogName() const override { return catalogName(); }
+protected:
+
+ virtual void postInputInitialization() override;
+
+ static bool isViolated( const real64 & currentValue, const real64 & constraintValue )
+ { return currentValue < constraintValue; }
+};
+
+
+} //namespace geos
+
+#endif //GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_WELLPRODUCTIONCONSTRAINT_HPP
diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/WellPropWriter.hpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellPropWriter.hpp
new file mode 100644
index 00000000000..ea8a019a4dd
--- /dev/null
+++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellPropWriter.hpp
@@ -0,0 +1,629 @@
+/*
+ * ------------------------------------------------------------------------------------------------------------
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ * Copyright (c) 2018-2020 Lawrence Livermore National Security LLC
+ * Copyright (c) 2018-2020 The Board of Trustees of the Leland Stanford Junior University
+ * Copyright (c) 2018-2020 TotalEnergies
+ * Copyright (c) 2019- GEOSX Contributors
+ * All rights reserved
+ *
+ * See top level LICENSE, COPYRIGHT, CONTRIBUTORS, NOTICE, and ACKNOWLEDGEMENTS files for details.
+ * ------------------------------------------------------------------------------------------------------------
+ */
+
+/**
+ * @file WellPropWriter.hpp
+ */
+
+#ifndef GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_WELLPROPWRITER_HPP
+#define GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_WELLPROPWRITER_HPP
+
+#include