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 + +#include "codingUtilities/Utilities.hpp" +#include "common/DataTypes.hpp" +#include "constitutive/fluid/multifluid/MultiFluidBase.hpp" +#include "constitutive/fluid/multifluid/MultiFluidFields.hpp" +#include "constitutive/fluid/multifluid/Layouts.hpp" +#include "physicsSolvers/fluidFlow/CompositionalMultiphaseBaseFields.hpp" +#include "physicsSolvers/fluidFlow/FlowSolverBaseFields.hpp" +#include "physicsSolvers/fluidFlow/wells/CompositionalMultiphaseWellFields.hpp" +#include "physicsSolvers/fluidFlow/wells/WellSolverBaseFields.hpp" + +#include "mesh/PerforationFields.hpp" + +namespace geos +{ + +/******************************** WellPropWriter ********************************/ + +class WellPropWriter +{ +public: + template< bool cond, typename U > + using resolvedType = typename std::enable_if< cond, U >::type; + + struct prop_wrapper + { + virtual ~prop_wrapper() = default; + }; + + template< typename T > + struct typed_prop_wrapper : public prop_wrapper + { + typed_prop_wrapper( const T * prop ): m_prop( prop ){} + const T m_prop; + }; + + typedef std::multimap< std::type_index, prop_wrapper > type_info_to_prop_map; + + struct prop_writer + { + virtual void write_prop( const integer idx, std::ofstream & stream ) = 0; + }; + + struct perf_prop_writer + { + virtual void write_prop( const integer j, const integer er, const integer esr, const integer ei, std::ofstream & stream ) = 0; + }; + template< typename T > struct typed_1d_prop_writer : public prop_writer + { + typedef T value_type; + typedef typed_prop_wrapper< value_type > prop_wrapper_type; + + typed_1d_prop_writer( value_type & prop ): m_prop( prop ){} + value_type m_prop; + + virtual void write_prop( const integer idx, std::ofstream & stream ) + { + stream << "," << (m_prop)[idx]; + } + }; + template< typename T > struct typed_1d_perf_res_prop_writer : public perf_prop_writer + { + typedef T value_type; + typedef typed_prop_wrapper< value_type > prop_wrapper_type; + + typed_1d_perf_res_prop_writer( value_type const & prop ): m_prop( prop ){} + value_type const m_prop; + + virtual void write_prop( const integer g, const integer er, const integer esr, const integer ei, std::ofstream & stream ) + { + GEOS_UNUSED_VAR( g ); + stream << "," << m_prop[er][esr][ei]; + } + }; + template< typename T > struct typed_1d_perf_prop_writer : public perf_prop_writer + { + typedef T value_type; + typedef typed_prop_wrapper< value_type > prop_wrapper_type; + + typed_1d_perf_prop_writer( value_type const & prop ): m_prop( prop ){} + value_type const m_prop; + + virtual void write_prop( const integer j, const integer er, const integer esr, const integer ei, std::ofstream & stream ) + { + GEOS_UNUSED_VAR( er ); + GEOS_UNUSED_VAR( esr ); + GEOS_UNUSED_VAR( ei ); + stream << "," << m_prop[j]; + } + }; + template< typename T > struct typed_2d_perf_res_prop_writer1 : public perf_prop_writer + { + typedef T value_type; + typedef typed_prop_wrapper< value_type > prop_wrapper_type; + + typed_2d_perf_res_prop_writer1( value_type & prop, integer dim2 ): m_prop( prop ), m_Dim2( dim2 ){} + value_type const m_prop; + const integer m_Dim2; + + virtual void write_prop( const integer j, const integer er, const integer esr, const integer ei, std::ofstream & stream ) + { + GEOS_UNUSED_VAR( er ); + GEOS_UNUSED_VAR( esr ); + GEOS_UNUSED_VAR( ei ); + for( integer i=0; i struct typed_2d_perf_res_prop_writerf : public perf_prop_writer + { + typedef T value_type; + typedef typed_prop_wrapper< value_type > prop_wrapper_type; + + typed_2d_perf_res_prop_writerf( value_type & prop, integer dim2 ): m_prop( prop ), m_Dim2( dim2 ){} + value_type const m_prop; + const integer m_Dim2; + + virtual void write_prop( const integer g, const integer er, const integer esr, const integer ei, std::ofstream & stream ) + { + GEOS_UNUSED_VAR( g ); + for( integer i=0; i struct typed_2d_perf_res_phase_prop_writer : public perf_prop_writer + { + typedef T value_type; + typedef typed_prop_wrapper< value_type > prop_wrapper_type; + + typed_2d_perf_res_phase_prop_writer( value_type & prop, integer dim2 ): m_prop( prop ), m_Dim2( dim2 ){} + value_type const m_prop; + const integer m_Dim2; + + virtual void write_prop( const integer g, const integer er, const integer esr, const integer ei, std::ofstream & stream ) + { + GEOS_UNUSED_VAR( g ); + for( integer i=0; i struct typed_2d_perf_res_prop_writer : public perf_prop_writer + { + typedef T value_type; + typedef typed_prop_wrapper< value_type > prop_wrapper_type; + + typed_2d_perf_res_prop_writer( value_type & prop, integer dim2 ): m_prop( prop ), m_Dim2( dim2 ){} + value_type const m_prop; + const integer m_Dim2; + + virtual void write_prop( const integer g, const integer er, const integer esr, const integer ei, std::ofstream & stream ) + { + GEOS_UNUSED_VAR( g ); + GEOS_UNUSED_VAR( er ); + GEOS_UNUSED_VAR( esr ); + for( integer i=0; i struct typed_2d_prop_writer : public prop_writer + { + typedef T value_type; + typedef typed_prop_wrapper< value_type > prop_wrapper_type; + + typed_2d_prop_writer( value_type & prop, integer dim2 ): m_prop( prop ), m_Dim2( dim2 ){} + value_type m_prop; + const integer m_Dim2; + + virtual void write_prop( const integer idx, std::ofstream & stream ) + { + + for( integer i=0; i struct typed_f2d_prop_writer : public prop_writer + { + typedef T value_type; + typedef typed_prop_wrapper< value_type > prop_wrapper_type; + + typed_f2d_prop_writer( value_type & prop, integer dim2 ): m_prop( prop ), m_Dim2( dim2 ){} + value_type m_prop; + const integer m_Dim2; + + virtual void write_prop( const integer idx, std::ofstream & stream ) + { + for( integer i=0; i struct typed_f2d_perf_prop_writer : public perf_prop_writer + { + typedef T value_type; + typedef typed_prop_wrapper< value_type > prop_wrapper_type; + + typed_f2d_perf_prop_writer( value_type & prop, integer dim2 ): m_prop( prop ), m_Dim2( dim2 ){} + value_type m_prop; + const integer m_Dim2; + + virtual void write_prop( const integer g, const integer er, const integer esr, const integer ei, std::ofstream & stream ) + { + GEOS_UNUSED_VAR( g ); + for( integer i=0; i struct typed_3d_prop_writer : public prop_writer + { + typedef T value_type; + typedef typed_prop_wrapper< value_type > prop_wrapper_type; + + typed_3d_prop_writer( value_type & prop, integer dim2, integer dim3 ): m_prop( prop ), m_Dim2( dim2 ), m_Dim3( dim3 ){} + value_type m_prop; + const integer m_Dim2; + const integer m_Dim3; + + virtual void write_prop( const integer idx, std::ofstream & stream ) + { + for( integer i=0; i struct typed_f3d_prop_writer : public prop_writer + { + typedef T value_type; + typedef typed_prop_wrapper< value_type > prop_wrapper_type; + + typed_f3d_prop_writer( value_type & prop, integer dim2, integer dim3 ): m_prop( prop ), m_Dim2( dim2 ), m_Dim3( dim3 ){} + value_type m_prop; + const integer m_Dim2; + const integer m_Dim3; + + virtual void write_prop( const integer idx, std::ofstream & stream ) + { + for( integer i=0; i struct typed_f3d_perf_res_prop_writer : public perf_prop_writer + { + typedef T value_type; + typedef typed_prop_wrapper< value_type > prop_wrapper_type; + + typed_f3d_perf_res_prop_writer( value_type & prop, integer dim2, integer dim3 ): m_prop( prop ), m_Dim2( dim2 ), m_Dim3( dim3 ){} + value_type m_prop; + const integer m_Dim2; + const integer m_Dim3; + + virtual void write_prop( const integer g, const integer er, const integer esr, const integer ei, std::ofstream & stream ) + { + GEOS_UNUSED_VAR( g ); + for( integer i=0; i prop_to_writer_map; + typedef std::vector< prop_writer * > prop_to_writer_vec; + typedef std::vector< perf_prop_writer * > perfprop_to_writer_vec; + + WellPropWriter () + { + m_initialized=0; + } + WellPropWriter( WellPropWriter && ) = default; + ~WellPropWriter() + { + if( m_outputFile.is_open() ) + { + m_outputFile.close(); + } + if( m_perfOutputFile.is_open() ) + { + m_perfOutputFile.close(); + } + } + void initialize_perf( int myrank, const string & outputDir, const string & wellName, PerforationData const & perfData ) + { + m_perfOutputFile.open( outputDir + "/" + wellName + "_perf_"+ std::to_string( myrank )+".csv" ); + m_numPerforations=perfData.size(); + m_resElementRegion = perfData.getField< fields::perforation::reservoirElementRegion >(); + m_resElementSubRegion = perfData.getField< fields::perforation::reservoirElementSubRegion >(); + m_resElementIndex = perfData.getField< fields::perforation::reservoirElementIndex >(); + m_perfWellElemIndex = perfData.getField< fields::perforation::wellElementIndex >(); + m_perfResElemGlobalIndex = perfData.getField< fields::perforation::reservoirElementGlobalIndex >(); + } + void initialize_seg( int myrank, const string & outputDir, const string & wellName, + string_array phaseNames, + string_array componentNames, + WellElementSubRegion & subRegion ) + { + m_outputFile.open( outputDir + "/" + wellName + "_seg_" + std::to_string( myrank )+".csv" ); + m_numSegments = subRegion.size(); + m_phaseNames = phaseNames; + m_componentNames = componentNames; + m_numPhase = m_phaseNames.size(); + m_numComponent = m_componentNames.size(); + m_elemGhostRank = subRegion.ghostRank(); + m_globalWellElementIndex = subRegion.getGlobalWellElementIndex(); + } + + bool initialized() + { + return m_initialized == 1; + } + template< typename T > + void registerSegProp( std::string const & name, const T & prop ) + { + if( m_initialized == 0 ) + m_header.push_back( name ); + m_propWriterVec.push_back( new typed_1d_prop_writer( prop )); + } + template< typename T > + void registerPerfResProp( std::string const & name, const T & prop ) + { + if( m_initialized == 0 ) + m_perfHeader.push_back( name ); + m_perfPropWriterVec.push_back( new typed_1d_perf_res_prop_writer( prop )); + } + template< typename T > + void registerPerfComponentProp( std::string const & name, const T & prop ) + { + if( m_initialized == 0 ) + for( const auto & c : m_componentNames ) + { + m_perfHeader.push_back( name+"_"+c ); + } + m_perfPropWriterVec.push_back( new typed_2d_perf_res_prop_writer1( prop, m_numComponent )); + } + template< typename T > + void registerSeg2dProp( std::vector< std::string > const & name, const T & prop ) + { + if( m_initialized == 0 ) + for( const auto & c : name ) + { + m_header.push_back( c ); + } + m_propWriterVec.push_back( new typed_2d_prop_writer( prop, name.size() )); + } + + + template< typename T > + void registerPerf1dProp( std::vector< std::string > const & name, const T & prop ) + { + if( m_initialized == 0 ) + for( const auto & c : name ) + { + m_perfHeader.push_back( c ); + } + m_perfPropWriterVec.push_back( new typed_1d_perf_prop_writer( prop )); + } + template< typename T > + void registerPerf2dProp( std::vector< std::string > const & name, const T & prop ) + { + if( m_initialized == 0 ) + for( const auto & c : name ) + { + m_perfHeader.push_back( c ); + } + m_perfPropWriterVec.push_back( new typed_2d_perf_res_prop_writer1( prop, name.size() )); + } + template< typename T > + void registerPerfResComponentProp( std::string const & name, const T & prop ) + { + if( m_initialized == 0 ) + for( const auto & c : m_componentNames ) + { + m_perfHeader.push_back( name+"_"+c ); + } + m_perfPropWriterVec.push_back( new typed_2d_perf_res_prop_writer( prop, m_numComponent )); + } + + template< typename T > + void registerPerfResPhaseComponentProp( std::string const & name, const T & prop ) + { + if( m_initialized == 0 ) + for( const auto & p : m_phaseNames ) + { + for( const auto & c : m_componentNames ) + { + m_perfHeader.push_back( name+"_"+p+"_"+c ); + } + } + m_perfPropWriterVec.push_back( new typed_f3d_perf_res_prop_writer( prop, m_numPhase, m_numComponent )); + } + template< typename T > + void registerSegPhasePropf( std::string const & name, const T & prop ) + { + if( m_initialized == 0 ) + for( const auto & p : m_phaseNames ) + { + m_header.push_back( name+"_"+p ); + } + m_propWriterVec.push_back( new typed_f2d_prop_writer( prop, m_numPhase )); + } + template< typename T > + void registerSegPhasePropDerf( std::string const & name, const std::vector< string > & der_names, const T & prop ) + { + if( m_initialized == 0 ) + for( const auto & p : m_phaseNames ) + { + for( const auto & d:der_names ) + m_header.push_back( name+"_"+p +"_"+d ); + } + + m_propWriterVec.push_back( new typed_f3d_prop_writer( prop, m_numPhase, der_names.size())); + + } + template< typename T > + void registerSegPhasePropDer( std::string const & name, const std::vector< string > & der_names, const T & prop ) + { + if( m_initialized == 0 ) + for( const auto & p : m_phaseNames ) + { + for( const auto & d:der_names ) + m_header.push_back( name+"_"+p +"_"+d ); + } + m_propWriterVec.push_back( new typed_3d_prop_writer( prop, m_numPhase, der_names.size())); + } + template< typename T > + void registerSegPhaseProp( std::string const & name, const T & prop ) + { + if( m_initialized == 0 ) + for( const auto & p : m_phaseNames ) + { + m_header.push_back( name+"_"+p ); + } + + m_propWriterVec.push_back( new typed_2d_prop_writer( prop, m_numPhase )); + } + template< typename T > + void registerPerfResPhaseProp( std::string const & name, const T & prop ) + { + if( m_initialized == 0 ) + for( const auto & p : m_phaseNames ) + { + m_perfHeader.push_back( name+"_"+p ); + } + + m_perfPropWriterVec.push_back( new typed_2d_perf_res_phase_prop_writer( prop, m_numPhase )); + } + template< typename T > + void registerPerfResPhasePropf( std::string const & name, const T & prop ) + { + if( m_initialized == 0 ) + for( const auto & p : m_phaseNames ) + { + m_perfHeader.push_back( name+"_"+p ); + } + + m_perfPropWriterVec.push_back( new typed_2d_perf_res_prop_writerf( prop, m_numPhase )); + } + template< typename T > + void registerPerfPhasePropf( std::string const & name, const T & prop ) + { + if( m_initialized == 0 ) + for( const auto & p : m_phaseNames ) + { + m_perfHeader.push_back( name+"_"+p ); + } + + m_perfPropWriterVec.push_back( new typed_f2d_perf_prop_writer( prop, m_numPhase )); + } + template< typename T > + void registerPerfComponentPropf( std::string const & name, const T & prop ) + { + if( m_initialized == 0 ) + for( const auto & p : m_componentNames ) + { + m_perfHeader.push_back( name+"_"+p ); + } + + m_perfPropWriterVec.push_back( new typed_f2d_perf_prop_writer( prop, m_numComponent )); + } + + template< typename T > + void registerSegComponentProp( std::string const & name, const T & prop ) + { + if( m_initialized == 0 ) + for( const auto & c : m_componentNames ) + { + m_header.push_back( name+"_"+c ); + } + m_propWriterVec.push_back( new typed_2d_prop_writer( prop, m_numComponent )); + } + template< typename T > + void registerSegPhaseComponentProp( std::string const & name, const T & prop ) + { + if( m_initialized == 0 ) + for( const auto & p : m_phaseNames ) + { + for( const auto & c : m_componentNames ) + { + m_header.push_back( name+"_"+p+"_"+c ); + } + } + m_propWriterVec.push_back( new typed_3d_prop_writer( prop, m_numPhase, m_numComponent )); + } + template< typename T > + void registerSegPhaseComponentPropf( std::string const & name, const T & prop ) + { + if( m_initialized == 0 ) + for( const auto & p : m_phaseNames ) + { + for( const auto & c : m_componentNames ) + { + m_header.push_back( name+"_"+p+"_"+c ); + } + } + m_propWriterVec.push_back( new typed_f3d_prop_writer( prop, m_numPhase, m_numComponent )); + } + void write( real64 time, real64 dt, integer cycle, integer subevent, integer timeStep, integer newtonIter, integer numTimeStepCuts ) + { + if( m_initialized == 0 ) + { + m_outputFile << "Time,Dt,Cycle,SubEvent,TimeStep,NewtonIteration,TimeStepCuts,Element"; + for( auto i :m_header ) + { + m_outputFile << "," << i; + } + m_outputFile << std::endl; + // for perf data + m_perfOutputFile << "Time,Dt,Cycle,SubEvent,TimeStep,NewtonIteration,TimeStepCuts,ResElement,WellElement"; + for( auto i :m_perfHeader ) + { + m_perfOutputFile << "," << i; + } + m_perfOutputFile << std::endl; + m_initialized = 1; + } + + for( integer j =0; j < m_numSegments; j++ ) + { + if( m_elemGhostRank[j] < 0 ) + { + m_outputFile << time << "," << dt << "," << cycle << "," << subevent << "," << timeStep << "," << newtonIter << "," << numTimeStepCuts<<","<write_prop( j, m_outputFile ); + cntr=cntr+1; + } + m_outputFile << std::endl; + } + } + + for( integer j =0; j < m_numPerforations; j++ ) + { + localIndex const iwelem = m_perfWellElemIndex[j]; + if( m_elemGhostRank[iwelem] < 0 ) + { + localIndex const er = m_resElementRegion[j]; + localIndex const esr = m_resElementSubRegion[j]; + localIndex const ei = m_resElementIndex[j]; + m_perfOutputFile << time << "," << dt << "," << cycle << "," << subevent << "," << timeStep << "," << newtonIter << "," << numTimeStepCuts<<","<write_prop( j, er, esr, ei, m_perfOutputFile ); + } + m_perfOutputFile << std::endl; + } + } + m_propWriterVec.clear(); + m_perfPropWriterVec.clear(); + } + +protected: + integer m_initialized; + string_array m_phaseNames; + string_array m_componentNames; + integer m_numSegments; + integer m_numComponent; + integer m_numPhase; + + std::vector< string > m_header; + std::ofstream m_outputFile; + prop_to_writer_vec m_propWriterVec; + + // perforation data + integer m_numPerforations; + arrayView1d< localIndex const > m_resElementRegion; + arrayView1d< localIndex const > m_resElementSubRegion; + arrayView1d< localIndex const > m_resElementIndex; + arrayView1d< localIndex const > m_perfWellElemIndex; + arrayView1d< globalIndex const > m_perfResElemGlobalIndex; + + std::vector< string > m_perfHeader; + std::ofstream m_perfOutputFile; + perfprop_to_writer_vec m_perfPropWriterVec; + + /// Global index of local element + arrayView1d< globalIndex const > m_globalWellElementIndex; + arrayView1d< integer const > m_elemGhostRank; +}; + + +} // end namespace geos + +#endif //GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_PERFORATIONFLUXLKERNELS_HPP diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/WellSolverBase.cpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellSolverBase.cpp index e470984d7cd..3fe22216f07 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/wells/WellSolverBase.cpp +++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellSolverBase.cpp @@ -18,7 +18,7 @@ */ #include "WellSolverBase.hpp" - +#include "physicsSolvers/LogLevelsInfo.hpp" #include "mesh/DomainPartition.hpp" #include "mesh/PerforationFields.hpp" #include "mesh/WellElementRegion.hpp" @@ -43,7 +43,13 @@ WellSolverBase::WellSolverBase( string const & name, m_numDofPerResElement( 0 ), m_isThermal( 0 ), m_ratesOutputDir( joinPath( OutputBase::getOutputDirectory(), name + "_rates" ) ), - m_keepVariablesConstantDuringInitStep( false ) + m_keepVariablesConstantDuringInitStep( false ), + m_writeSegDebug( 0 ), + m_globalNumTimeSteps( -1 ), + m_currentDt( -1.0 ), + my_ctime( 0 ), + m_nextDt( -1 ), + m_useNewCode( true ) { registerWrapper( viewKeyStruct::isThermalString(), &m_isThermal ). setApplyDefaultValue( 0 ). @@ -63,6 +69,16 @@ WellSolverBase::WellSolverBase( string const & name, setInputFlag( dataRepository::InputFlags::OPTIONAL ). setDescription( "Choose time step to honor rates/bhp tables time intervals" ); + this->registerWrapper( viewKeyStruct::useNewCodeString(), &m_useNewCode ). + setApplyDefaultValue( 1 ). + setInputFlag( InputFlags::OPTIONAL ). + setDescription( "Use new code" ); + + this->registerWrapper( viewKeyStruct::writeSegDebugFlagString(), &m_writeSegDebug ). + setApplyDefaultValue( 0 ). + setInputFlag( dataRepository::InputFlags::OPTIONAL ). + setDescription( "Write well seg/perf debug into CSV files" ); + addLogLevel< logInfo::WellControl >(); } @@ -100,8 +116,14 @@ void WellSolverBase::postInputInitialization() // 1. Set key dimensions of the problem m_numDofPerWellElement = m_isThermal ? m_numComponents + 2 : m_numComponents + 1; // 1 pressure connectionRate + temp if thermal m_numDofPerResElement = m_isThermal ? m_numComponents + 1: m_numComponents; // 1 pressure + temp if thermal - - + m_writeSegDebug=0; + if( m_writeSegDebug > 0 ) + { + if( m_writeCSV == 0 ) + { + m_writeCSV=1; + } + } // create dir for rates output if( m_writeCSV > 0 ) { @@ -293,10 +315,168 @@ void WellSolverBase::implicitStepSetup( real64 const & time_n, setPerforationStatus( time_n, domain ); // Initialize the primary and secondary variables for the first time step + if( !m_useNewCode ) + { + initializeWells( domain, time_n ); + } - initializeWells( domain, time_n ); } +void WellSolverBase::setupWellDofs( DomainPartition & domain ) +{ + if( m_estimatorDoFManager.empty() ) + { + + map< std::pair< string, string >, string_array > meshTargets; + string_array regions; + forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const & meshBodyName, + MeshLevel & meshLevel, + string_array const & regionNames ) + { + ElementRegionManager & elementRegionManager = meshLevel.getElemManager(); + elementRegionManager.forElementRegions< WellElementRegion >( regionNames, + [&]( localIndex const, + WellElementRegion & region ) + { + meshTargets.clear(); + regions.clear(); + regions.emplace_back( region.getName() ); + auto const key = std::make_pair( meshBodyName, meshLevel.getName()); + meshTargets[key] = std::move( regions ); + + DofManager regionDoFManager( region.getName()); + regionDoFManager.setDomain( domain ); + regionDoFManager.addField( wellElementDofName(), + FieldLocation::Elem, + numDofPerWellElement(), + meshTargets ); + + regionDoFManager.addCoupling( wellElementDofName(), + wellElementDofName(), + DofManager::Connector::Node ); + + regionDoFManager.reorderByRank(); + m_estimatorDoFManager.emplace( region.getName(), std::move( regionDoFManager )); + } ); + } ); + } +} + +void WellSolverBase::selectWellConstraint( real64 const & time_n, + real64 const & dt, + const integer coupledIterationNumber, + DomainPartition & domain ) +{ + GEOS_MARK_FUNCTION; + GEOS_UNUSED_VAR( dt ); + GEOS_UNUSED_VAR( coupledIterationNumber ); + + setupWellDofs( domain ); + integer cycleNumber=0; + forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const & meshBodyName, + MeshLevel & meshLevel, + string_array const & regionNames ) + { + GEOS_UNUSED_VAR( meshBodyName ); + ElementRegionManager & elementRegionManager = meshLevel.getElemManager(); + elementRegionManager.forElementRegions< WellElementRegion >( regionNames, + [&]( localIndex const, + WellElementRegion & region ) + { + WellElementSubRegion & subRegion = region.getGroup( ElementRegionBase::viewKeyStruct::elementSubRegions() ) + .getGroup< WellElementSubRegion >( region.getSubRegionName() ); + WellControls & wellControls = getWellControls( subRegion ); + if( wellControls.isWellOpen() ) + { + if( !wellControls.getWellState() ) + { + wellControls.setWellState( 1 ); + + initializeWell( domain, meshLevel, subRegion, time_n ); + } + } + else + { + wellControls.setWellState( 0 ); + } + + + if( wellControls.getWellState()) + { + //GEOS_LOG_RANK( "**** Estimate Well Solution - Start **** " << subRegion.getName() ); + auto it = m_estimatorDoFManager.find( region.getName()); + if( it == m_estimatorDoFManager.end()) + { + throw std::runtime_error( "DofManager for region " + region.getName() + " not found." ); + } + DofManager & dofManager = it->second; + +// Only build the sparsity pattern if the mesh has changed + Timestamp const meshModificationTimestamp = getMeshModificationTimestamp( domain ); + + if( meshModificationTimestamp > getSystemSetupTimestamp() ) + { + // These are esitmator matrices + setupWellSystem( domain, dofManager, m_localMatrix, m_rhs, m_solution ); + //setSystemSetupTimestamp( meshModificationTimestamp ); + + //std::ostringstream oss; + //m_dofManager.printFieldInfo( oss ); + //GEOS_LOG_LEVEL( logInfo::Fields, oss.str()) + } + evaluateConstraints( time_n, + dt, + cycleNumber, + coupledIterationNumber, + domain, + meshLevel, + elementRegionManager, + subRegion, + dofManager ); + + // 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 ); + } + } ); + } ); + +} + +void WellSolverBase::setupWellSystem( DomainPartition & domain, + DofManager & dofManager, + CRSMatrix< real64, globalIndex > & localMatrix, + ParallelVector & rhs, + ParallelVector & solution, + bool const setSparsity ) +{ + GEOS_MARK_FUNCTION; + + setupWellDofs( domain ); + + if( setSparsity ) + { + SparsityPattern< globalIndex > pattern; + dofManager.setSparsityPattern( pattern ); + localMatrix.assimilate< parallelDevicePolicy<> >( std::move( pattern ) ); + } + localMatrix.setName( this->getName() + "/matrix" ); + + rhs.setName( this->getName() + "/rhs" ); + rhs.create( dofManager.numLocalDofs(), MPI_COMM_GEOS ); + + solution.setName( this->getName() + "/solution" ); + solution.create( dofManager.numLocalDofs(), MPI_COMM_GEOS ); +} void WellSolverBase::updateState( DomainPartition & domain ) { GEOS_MARK_FUNCTION; @@ -311,6 +491,30 @@ void WellSolverBase::updateState( DomainPartition & domain ) } ); } +void WellSolverBase::assembleWellSystem( real64 const time_n, + real64 const dt, + ElementRegionManager const & elementRegionManager, + WellElementSubRegion & subRegion, + DofManager const & dofManager, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) +{ + assembleWellAccumulationTerms( time_n, dt, subRegion, dofManager, localMatrix.toViewConstSizes(), localRhs ); + + assembleWellConstraintTerms( time_n, dt, subRegion, dofManager, localMatrix.toViewConstSizes(), localRhs ); + + assembleWellPressureRelations( time_n, dt, subRegion, dofManager, localMatrix.toViewConstSizes(), localRhs ); + computeWellPerforationRates( time_n, dt, elementRegionManager, subRegion ); + assembleWellFluxTerms( time_n, dt, subRegion, dofManager, localMatrix.toViewConstSizes(), localRhs ); + my_ctime=my_ctime+1; + + // auto iterInfo = currentIter( time_n, dt ); + // outputWellDebug( time_n, dt, std::get< 0 >( iterInfo ), std::get< 1 >( iterInfo ), std::get< 2 >( iterInfo ), + // domain, dofManager, localMatrix, localRhs ); + + +} + void WellSolverBase::assembleSystem( real64 const time, real64 const dt, DomainPartition & domain, @@ -318,13 +522,41 @@ void WellSolverBase::assembleSystem( real64 const time, CRSMatrixView< real64, globalIndex const > const & localMatrix, arrayView1d< real64 > const & localRhs ) { - 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 ); + //if( false && m_useNewCode ) + { + 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 ); @@ -332,8 +564,80 @@ void WellSolverBase::assembleSystem( real64 const time, // get a reference to the degree-of-freedom numbers // then assemble the flux terms in the mass balance equations assembleFluxTerms( time, dt, domain, dofManager, localMatrix, localRhs ); + + // sort out how this work with well estimator + auto iterInfo = currentIter( time, dt ); + outputWellDebug( time, dt, std::get< 0 >( iterInfo ), std::get< 1 >( iterInfo ), std::get< 2 >( iterInfo ), + domain, dofManager, localMatrix, localRhs ); + + + my_ctime=my_ctime+1; + } +std::tuple< integer, integer, integer > +WellSolverBase::currentIter( real64 const time, real64 const dt ) +{ + if( isEqual( m_currentDt, -1.0 ) ) + { + m_globalNumTimeSteps=0; + m_currentTime=time; + m_prevTime=time; + m_currentDt=dt; + m_prevDt=dt; + m_numTimeStepCuts=0; + m_currentNewtonIteration=0; + } + else + { + if( !isEqual( time, m_currentTime ) ) + { + m_globalNumTimeSteps++; + m_prevTime=m_currentTime; + m_prevDt=m_currentDt; + m_currentTime=time; + m_currentDt=dt; + m_currentNewtonIteration=0; + m_numTimeStepCuts=0; + } + else + { + if( dt < m_currentDt ) + { + // timestep cut + m_globalNumTimeSteps++; + m_prevTime=m_currentTime; + m_prevDt=m_currentDt; + m_currentTime=time; + m_currentDt=dt; + m_currentNewtonIteration=0; + m_numTimeStepCuts++; + m_currentNewtonIteration=0; + } + /* + else if ( isEqual(dt,m_currentDt ) ) + { + // next timestep + m_globalNumTimeSteps++; + m_prevTime=m_currentTime; + m_prevDt=m_currentDt; + m_currentTime=time; + m_currentDt=dt; + m_currentNewtonIteration=0; + m_numTimeStepCuts=0; + m_currentNewtonIteration=0; + }*/ + else + { + // continuation of current timestep + m_currentNewtonIteration++; + } + } + } + + return std::tuple< integer, integer, integer >( m_globalNumTimeSteps, m_numTimeStepCuts, m_currentNewtonIteration ); + +} void WellSolverBase::initializePostInitialConditionsPreSubGroups() { PhysicsSolverBase::initializePostInitialConditionsPreSubGroups(); @@ -386,8 +690,15 @@ void WellSolverBase::precomputeData( DomainPartition & domain ) wellElemGravCoef[iwelem] = LvArray::tensorOps::AiBi< 3 >( wellElemLocation[iwelem], gravVector ); } ); + wellControls.forSubGroups< BHPConstraint >( [&]( auto & constraint ) + { + // set the reference well element where the BHP control is applied + real64 const refElev1 = constraint.getReferenceElevation(); + constraint.setReferenceGravityCoef( refElev1 * gravVector[2] ); + } ); + // set the reference well element where the BHP control is applied - wellControls.setReferenceGravityCoef( refElev * gravVector[2] ); + wellControls.setReferenceGravityCoef( refElev * gravVector[2] ); // tjb remove } ); } ); } @@ -407,6 +718,11 @@ real64 WellSolverBase::setNextDt( real64 const & currentTime, const real64 & cur FunctionManager & functionManager = FunctionManager::getInstance(); real64 nextDt = PhysicsSolverBase::setNextDt( currentTime, currentDt, domain ); + if( m_nextDt > 0 ) + { + nextDt = m_nextDt; + m_nextDt=-1; + } if( m_timeStepFromTables ) { forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &, @@ -441,4 +757,345 @@ real64 WellSolverBase::setNextDt( real64 const & currentTime, const real64 & cur return nextDt; } + +bool WellSolverBase::solveNonlinearSystem( real64 const & time_n, + real64 const & stepDt, + integer const cycleNumber, + DomainPartition & domain, + MeshLevel & mesh, + ElementRegionManager & elemManager, + WellElementSubRegion & subRegion, + DofManager const & dofManager ) +{ + integer const maxNewtonIter = m_nonlinearSolverParameters.m_maxIterNewton; + integer dtAttempt = m_nonlinearSolverParameters.m_numTimeStepAttempts; + integer configurationLoopIter = m_nonlinearSolverParameters.m_numConfigurationAttempts; + integer const minNewtonIter = m_nonlinearSolverParameters.m_minIterNewton; + real64 const newtonTol = m_nonlinearSolverParameters.m_newtonTol; + +// keep residual from previous iteration in case we need to do a line search + real64 lastResidual = 1e99; + integer newtonIter = 0; + real64 scaleFactor = 1.0; + + bool isNewtonConverged = false; + + for( newtonIter = 0; newtonIter < maxNewtonIter; ++newtonIter ) + { + if( m_nonlinearSolverParameters.getLogLevel() > 4 ) + GEOS_LOG_LEVEL_RANK_0( logInfo::NonlinearSolver, + GEOS_FMT( " Well: {} Est Attempt: NewtonIter: {:2}", subRegion.getName(), stepDt, newtonIter )); + + { + Timer timer( m_timers.get_inserted( "assemble" ) ); + +// We sync the nonlinear convergence history. The coupled solver parameters are the one being +// used. We want to propagate the info to subsolvers. It can be important for solvers that +// have special treatment for specific iterations. + synchronizeNonlinearSolverParameters(); + +// zero out matrix/rhs before assembly + m_localMatrix.zero(); + m_rhs.zero(); + + arrayView1d< real64 > const localRhs = m_rhs.open(); + +// call assemble to fill the matrix and the rhs + assembleWellSystem( time_n, + stepDt, + elemManager, + subRegion, + dofManager, + m_localMatrix.toViewConstSizes(), + localRhs ); + +// apply boundary conditions to system + applyWellBoundaryConditions( time_n, + stepDt, + elemManager, + subRegion, + dofManager, + localRhs, + m_localMatrix.toViewConstSizes() ); + + m_rhs.close(); + + if( m_assemblyCallback ) + { +// Make a copy of LA objects and ship off to the callback + array1d< real64 > localRhsCopy( m_rhs.localSize() ); + localRhsCopy.setValues< parallelDevicePolicy<> >( m_rhs.values() ); + m_assemblyCallback( m_localMatrix, std::move( localRhsCopy ) ); + } + } + + outputSingleWellDebug( time_n, stepDt, 0, newtonIter, 0, + mesh, subRegion, dofManager, m_localMatrix.toViewConstSizes(), m_rhs.values() ); + real64 residualNorm = 0; + { + Timer timer( m_timers.get_inserted( "convergence check" ) ); + +// get residual norm + residualNorm = calculateWellResidualNorm( time_n, stepDt, subRegion, dofManager, m_rhs.values() ); + if( m_nonlinearSolverParameters.getLogLevel() > 4 ) + GEOS_LOG_LEVEL_RANK_0( logInfo::Convergence, + GEOS_FMT( " ( R ) = ( {:4.2e} )", residualNorm ) ); + } + //auto iterInfo = currentIter( time_n, dt ); + //outputSingleWellDebug( time_n, stepDt, 0, newtonIter, 0, + // mesh, subRegion, dofManager, m_localMatrix.toViewConstSizes(), m_rhs.values() ); +// if the residual norm is less than the Newton tolerance we denote that we have +// converged and break from the Newton loop immediately. + std::cout << " Well: " << subRegion.getName() << " Est Attempt: " << dtAttempt + << ", ConfigurationIter: " << configurationLoopIter + << ", NewtonIter: " << newtonIter + << ", Residual Norm: " << residualNorm << std::endl; + if( residualNorm < newtonTol && newtonIter >= minNewtonIter ) + { + isNewtonConverged = true; + break; + } + +// if the residual norm is above the max allowed residual norm, we break from +// the Newton loop to avoid crashes due to Newton divergence + if( residualNorm > m_nonlinearSolverParameters.m_maxAllowedResidualNorm ) + { + string const maxAllowedResidualNormString = NonlinearSolverParameters::viewKeysStruct::maxAllowedResidualNormString(); + if( m_nonlinearSolverParameters.getLogLevel() > 4 ) + GEOS_LOG_LEVEL_RANK_0( logInfo::Convergence, + GEOS_FMT( " The residual norm is above the {} of {}. Newton loop terminated.", + maxAllowedResidualNormString, + m_nonlinearSolverParameters.m_maxAllowedResidualNorm ) ); + isNewtonConverged = false; + break; + } + + + // do line search in case residual has increased + + if( m_nonlinearSolverParameters.m_lineSearchAction != NonlinearSolverParameters::LineSearchAction::None + && residualNorm > lastResidual * m_nonlinearSolverParameters.m_lineSearchResidualFactor + && newtonIter >= m_nonlinearSolverParameters.m_lineSearchStartingIteration ) + { + bool lineSearchSuccess = false; + if( m_nonlinearSolverParameters.m_lineSearchInterpType == NonlinearSolverParameters::LineSearchInterpolationType::Linear ) + { + residualNorm = lastResidual; + lineSearchSuccess = lineSearch1( time_n, + stepDt, + cycleNumber, + domain, + elemManager, + subRegion, + mesh, + dofManager, + m_localMatrix.toViewConstSizes(), + m_rhs, + m_solution, + scaleFactor, + residualNorm ); + } + else + { + lineSearchSuccess = lineSearchWithParabolicInterpolation( time_n, + stepDt, + cycleNumber, + newtonIter, + domain, + dofManager, + m_localMatrix.toViewConstSizes(), + m_rhs, + m_solution, + scaleFactor, + lastResidual, + residualNorm ); + } + + if( !lineSearchSuccess ) + { + if( m_nonlinearSolverParameters.m_lineSearchAction == NonlinearSolverParameters::LineSearchAction::Attempt ) + { + if( m_nonlinearSolverParameters.getLogLevel() > 4 ) + GEOS_LOG_LEVEL_RANK_0( logInfo::LineSearch, + " Line search failed to produce reduced residual. Accepting iteration." ); + } + else if( m_nonlinearSolverParameters.m_lineSearchAction == NonlinearSolverParameters::LineSearchAction::Require ) + { +// if line search failed, then break out of the main Newton loop. Timestep will be cut. + if( m_nonlinearSolverParameters.getLogLevel() > 4 ) + GEOS_LOG_LEVEL_RANK_0( logInfo::LineSearch, + " Line search failed to produce reduced residual. Exiting Newton Loop." ); + break; + } + } + } + + { + Timer timer( m_timers.get_inserted( "linear solver total" ) ); + +// if using adaptive Krylov tolerance scheme, update tolerance. + LinearSolverParameters::Krylov & krylovParams = m_linearSolverParameters.get().krylov; + if( krylovParams.useAdaptiveTol ) + { + krylovParams.relTolerance = newtonIter > 0 ? eisenstatWalker( residualNorm, lastResidual, krylovParams ) : krylovParams.weakestTol; + } + +// TODO: Trilinos currently requires this, re-evaluate after moving to Tpetra-based solvers + if( m_precond ) + { + m_precond->clear(); + } + + { + Timer timer_setup( m_timers.get_inserted( "linear solver create" ) ); + +// Compose parallel LA matrix/rhs out of local LA matrix/rhs +// + m_matrix.create( m_localMatrix.toViewConst(), dofManager.numLocalDofs(), MPI_COMM_GEOS ); + } + +// Output the linear system matrix/rhs for debugging purposes + string tag = "_"+std::to_string( my_ctime ); + debugOutputSystem( time_n, cycleNumber, newtonIter, m_matrix, m_rhs, tag ); + +// Solve the linear system + solveLinearSystem( dofManager, m_matrix, m_rhs, m_solution ); + +// Increment the solver statistics for reporting purposes + getIterationStats().updateNonlinearIteration( m_linearSolverResult.numIterations ); + +// Output the linear system solution for debugging purposes + debugOutputSolution( time_n, cycleNumber, newtonIter, m_solution, tag ); + } + + { + Timer timer( m_timers.get_inserted( "apply solution" ) ); + +// Compute the scaling factor for the Newton update + scaleFactor = scalingForWellSystemSolution( subRegion, dofManager, m_solution.values() ); + if( m_nonlinearSolverParameters.getLogLevel() > 4 ) + GEOS_LOG_LEVEL_RANK_0( logInfo::Solution, + GEOS_FMT( " {}: Global solution scaling factor = {}", getName(), scaleFactor ) ); + + if( !checkWellSystemSolution( subRegion, dofManager, m_solution.values(), scaleFactor ) ) + { +// TODO try chopping (similar to line search) + if( m_nonlinearSolverParameters.getLogLevel() > 4 ) + GEOS_LOG_RANK_0( GEOS_FMT( " {}: Solution check failed. Newton loop terminated.", getName()) ); + break; + } + +// apply the system solution to the fields/variables + applyWellSystemSolution( dofManager, m_solution.values(), scaleFactor, stepDt, domain, mesh, subRegion ); + } + + { + Timer timer( m_timers.get_inserted( "update state" ) ); + + // update derived variables (constitutive models) + updateWellState( subRegion ); + } + + lastResidual = residualNorm; + } + return isNewtonConverged; +} + +bool WellSolverBase::lineSearch1( real64 const & time_n, + real64 const & dt, + integer const GEOS_UNUSED_PARAM( cycleNumber ), + DomainPartition & domain, + ElementRegionManager & elemManager, + WellElementSubRegion & subRegion, + MeshLevel & mesh, + DofManager const & dofManager, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + ParallelVector & rhs, + ParallelVector & solution, + real64 const scaleFactor, + real64 & lastResidual ) +{ + Timer timer( m_timers["line search"] ); + + integer const maxNumberLineSearchCuts = m_nonlinearSolverParameters.m_lineSearchMaxCuts; + real64 const lineSearchCutFactor = m_nonlinearSolverParameters.m_lineSearchCutFactor; + + // flag to determine if we should solve the system and apply the solution. If the line + // search fails we just bail. + bool lineSearchSuccess = false; + + real64 residualNorm = lastResidual; + + // scale factor is value applied to the previous solution. In this case we want to + // subtract a portion of the previous solution. + real64 localScaleFactor = -scaleFactor; + real64 cumulativeScale = scaleFactor; + + // main loop for the line search. + for( integer lineSearchIteration = 0; lineSearchIteration < maxNumberLineSearchCuts; ++lineSearchIteration ) + { + // cut the scale factor by half. This means that the scale factors will + // have values of -0.5, -0.25, -0.125, ... + localScaleFactor *= lineSearchCutFactor; + cumulativeScale += localScaleFactor; + + if( !checkWellSystemSolution( subRegion, dofManager, m_solution.values(), localScaleFactor ) ) + { + GEOS_LOG_LEVEL_RANK_0( logInfo::LineSearch, + GEOS_FMT( " Line search {}, solution check failed", lineSearchIteration ) ); + continue; + } + + + applyWellSystemSolution( dofManager, solution.values(), localScaleFactor, dt, domain, mesh, subRegion ); + // update non-primary variables (constitutive models) + + updateWellState( subRegion ); + // re-assemble system + localMatrix.zero(); + rhs.zero(); + + arrayView1d< real64 > const localRhs = rhs.open(); + + // call assemble to fill the matrix and the rhs + assembleWellSystem( time_n, + dt, + elemManager, + subRegion, + dofManager, + localMatrix, + localRhs ); + +// apply boundary conditions to system + applyWellBoundaryConditions( time_n, + dt, + elemManager, + subRegion, + dofManager, + localRhs, + localMatrix ); + + rhs.close(); + + GEOS_LOG_LEVEL_RANK_0( logInfo::LineSearch, + GEOS_FMT( " Line search @ {:0.3f}: ", cumulativeScale )); + + // get residual norm + residualNorm = calculateWellResidualNorm( time_n, dt, subRegion, dofManager, rhs.values() ); + GEOS_LOG_LEVEL_RANK_0( logInfo::LineSearch, + GEOS_FMT( " ( R ) = ( {:4.2e} )", residualNorm ) ); + + // if the residual norm is less than the last residual, we can proceed to the + // solution step + if( residualNorm < lastResidual ) + { + lineSearchSuccess = true; + break; + } + } + + lastResidual = residualNorm; + return lineSearchSuccess; +} + } // namespace geos diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/WellSolverBase.hpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellSolverBase.hpp index a8159378b50..9d8ea0d26d6 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/wells/WellSolverBase.hpp +++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellSolverBase.hpp @@ -21,6 +21,7 @@ #define GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_WELLSOLVERBASE_HPP_ #include "physicsSolvers/PhysicsSolverBase.hpp" +#include "physicsSolvers/fluidFlow/wells/WellPropWriter.hpp" namespace geos { @@ -159,6 +160,21 @@ class WellSolverBase : public PhysicsSolverBase virtual void registerDataOnMesh( Group & meshBodies ) override; + void selectWellConstraint( real64 const & time_n, + real64 const & dt, + integer const coupledIterationNumber, + DomainPartition & domain ); + + + void setupWellDofs( DomainPartition & domain ); + + void setupWellSystem ( DomainPartition & domain, + DofManager & dofManager, + CRSMatrix< real64, globalIndex > & localMatrix, + ParallelVector & rhs, + ParallelVector & solution, + bool const setSparsity = true ); + virtual void setupDofs( DomainPartition const & domain, DofManager & dofManager ) const override; @@ -177,6 +193,13 @@ class WellSolverBase : public PhysicsSolverBase CRSMatrixView< real64, globalIndex const > const & GEOS_UNUSED_PARAM( localMatrix ), arrayView1d< real64 > const & GEOS_UNUSED_PARAM( localRhs ) ) override {} + virtual void applyWellBoundaryConditions( real64 const GEOS_UNUSED_PARAM( time_n ), + real64 const GEOS_UNUSED_PARAM( dt ), + ElementRegionManager & GEOS_UNUSED_PARAM( elemManager ), + WellElementSubRegion & GEOS_UNUSED_PARAM( subRegion ), + DofManager const & GEOS_UNUSED_PARAM( dofManager ), + arrayView1d< real64 > const & GEOS_UNUSED_PARAM( localRhs ), + CRSMatrixView< real64, globalIndex const > const & GEOS_UNUSED_PARAM( localMatrix ) ) {} /**@}*/ @@ -189,6 +212,15 @@ class WellSolverBase : public PhysicsSolverBase * @param matrix the system matrix * @param rhs the system right-hand side vector */ + + void assembleWellSystem( real64 const time, + real64 const dt, + ElementRegionManager const & elementRegionManager, + WellElementSubRegion & subRegion, + DofManager const & dofManager, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ); + virtual void assembleSystem( real64 const time, real64 const dt, DomainPartition & domain, @@ -196,6 +228,14 @@ class WellSolverBase : public PhysicsSolverBase CRSMatrixView< real64, globalIndex const > const & localMatrix, arrayView1d< real64 > const & localRhs ) override; + virtual void assembleWellFluxTerms( real64 const & GEOS_UNUSED_PARAM( time ), + real64 const & GEOS_UNUSED_PARAM( dt ), + WellElementSubRegion const & GEOS_UNUSED_PARAM( subRegion ), + DofManager const & GEOS_UNUSED_PARAM( dofManager ), + CRSMatrixView< real64, globalIndex const > const & GEOS_UNUSED_PARAM( localMatrix ), + arrayView1d< real64 > const & GEOS_UNUSED_PARAM( localRhs ) ) = 0; + + /** * @brief assembles the flux terms for all connections between well elements * @param time_n previous time value @@ -212,6 +252,16 @@ class WellSolverBase : public PhysicsSolverBase CRSMatrixView< real64, globalIndex const > const & localMatrix, arrayView1d< real64 > const & localRhs ) = 0; + + + virtual void assembleWellAccumulationTerms( real64 const & time, + real64 const & dt, + WellElementSubRegion & subRegion, + DofManager const & dofManager, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) = 0; + + /** * @brief assembles the accumulation term for all the well elements * @param domain the physical domain object @@ -226,8 +276,22 @@ class WellSolverBase : public PhysicsSolverBase CRSMatrixView< real64, globalIndex const > const & localMatrix, arrayView1d< real64 > const & localRhs ) = 0; + virtual void assembleWellConstraintTerms( real64 const & GEOS_UNUSED_PARAM( time ), + real64 const & GEOS_UNUSED_PARAM( dt ), + WellElementSubRegion const & GEOS_UNUSED_PARAM( subRegion ), + DofManager const & GEOS_UNUSED_PARAM( dofManager ), + CRSMatrixView< real64, globalIndex const > const & GEOS_UNUSED_PARAM( localMatrix ), + arrayView1d< real64 > const & GEOS_UNUSED_PARAM( localRhs ) ) = 0; + + virtual void assembleWellPressureRelations ( real64 const & GEOS_UNUSED_PARAM( time ), + real64 const & GEOS_UNUSED_PARAM( dt ), + WellElementSubRegion const & GEOS_UNUSED_PARAM( subRegion ), + DofManager const & GEOS_UNUSED_PARAM( dofManager ), + CRSMatrixView< real64, globalIndex const > const & GEOS_UNUSED_PARAM( localMatrix ), + arrayView1d< real64 > const & GEOS_UNUSED_PARAM( localRhs ) ) = 0; /** - * @brief assembles the pressure relations at all connections between well elements except at the well head + * @brief assembles the pressure relations at all connections between well elements except at + * the well head * @param time_n time at the beginning of the time step * @param dt the time step size * @param domain the physical domain object @@ -242,18 +306,54 @@ class WellSolverBase : public PhysicsSolverBase CRSMatrixView< real64, globalIndex const > const & localMatrix, arrayView1d< real64 > const & localRhs ) = 0; + virtual void outputSingleWellDebug( real64 const GEOS_UNUSED_PARAM( time ), + real64 const GEOS_UNUSED_PARAM( dt ), + integer GEOS_UNUSED_PARAM( num_timesteps ), + integer GEOS_UNUSED_PARAM( current_newton_iteration ), + integer GEOS_UNUSED_PARAM( num_timestep_cuts ), + MeshLevel & GEOS_UNUSED_PARAM( mesh ), + WellElementSubRegion & GEOS_UNUSED_PARAM( subRegion ), + DofManager const & GEOS_UNUSED_PARAM( dofManager ), + CRSMatrixView< real64, globalIndex const > const & GEOS_UNUSED_PARAM( localMatrix ), + arrayView1d< const real64 > const & GEOS_UNUSED_PARAM( localRhs ) ) = 0; + + 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 ) = 0; /** * @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 ) = 0; virtual void updateState( DomainPartition & domain ) override; + virtual void saveState( WellElementSubRegion & subRegion ) = 0; + /** + * @brief Initialize all the primary and secondary variables in all the wells + * @param domain the domain containing the well manager to access individual wells + */ + virtual void initializeWells( DomainPartition & domain, real64 const & time_n ) = 0; + virtual void initializeWell( DomainPartition & domain, MeshLevel & mesh, WellElementSubRegion & subRegion, real64 const & time_n ) = 0; /** - * @brief Recompute all dependent quantities from primary variables (including constitutive models) - * @param subRegion the well subRegion containing the well elements and their associated fields + * @brief Recompute all dependent quantities from primary variables (including constitutive + * models) + * @param subRegion the well subRegion containing the well elements and their associated + * fields */ virtual real64 updateSubRegionState( WellElementSubRegion & subRegion ) = 0; + + virtual void computeWellPerforationRates( real64 const & GEOS_UNUSED_PARAM( time_n ), + real64 const & GEOS_UNUSED_PARAM( dt ), + ElementRegionManager const & GEOS_UNUSED_PARAM( elemManager ), + WellElementSubRegion & GEOS_UNUSED_PARAM( subRegion ) ){} + /** * @brief Recompute the perforation rates for all the wells * @param domain the domain containing the mesh and fields @@ -262,6 +362,76 @@ class WellSolverBase : public PhysicsSolverBase real64 const & dt, DomainPartition & domain ) = 0; + virtual real64 + calculateWellResidualNorm( real64 const & GEOS_UNUSED_PARAM( time_n ), + real64 const & GEOS_UNUSED_PARAM( dt ), + WellElementSubRegion const & GEOS_UNUSED_PARAM( subRegion ), + DofManager const & GEOS_UNUSED_PARAM( dofManager ), + arrayView1d< real64 const > const & GEOS_UNUSED_PARAM( localRhs ) ) = 0; + + virtual real64 + scalingForWellSystemSolution( ElementSubRegionBase & GEOS_UNUSED_PARAM( subRegion ), + DofManager const & GEOS_UNUSED_PARAM( dofManager ), + arrayView1d< real64 const > const & GEOS_UNUSED_PARAM( localSolution ) ) = 0; + + virtual bool + checkWellSystemSolution( ElementSubRegionBase & GEOS_UNUSED_PARAM( subRegion ), + DofManager const & GEOS_UNUSED_PARAM( dofManager ), + arrayView1d< real64 const > const & GEOS_UNUSED_PARAM( localSolution ), + real64 const GEOS_UNUSED_PARAM( scalingFactor ) ) = 0; + virtual void + applyWellSystemSolution( DofManager const & GEOS_UNUSED_PARAM( dofManager ), + arrayView1d< real64 const > const & GEOS_UNUSED_PARAM( localSolution ), + real64 const GEOS_UNUSED_PARAM( scalingFactor ), + real64 const GEOS_UNUSED_PARAM( dt ), + DomainPartition & GEOS_UNUSED_PARAM( domain ), + MeshLevel & GEOS_UNUSED_PARAM( mesh ), + WellElementSubRegion & GEOS_UNUSED_PARAM( subRegion ) ) = 0; + + bool solveNonlinearSystem( real64 const & time_n, + real64 const & stepDt, + integer const cycleNumber, + DomainPartition & domain, + MeshLevel & mesh, + ElementRegionManager & elementRegionManager, + WellElementSubRegion & subregion, + DofManager const & dofManager ); + + /** + * @brief Function to perform line search + * @param time_n time at the beginning of the step + * @param dt the perscribed timestep + * @param cycleNumber the current cycle number + * @param domain the domain object + * @param dofManager degree-of-freedom manager associated with the linear system + * @param localMatrix the system matrix + * @param rhs the system right-hand side vector + * @param solution the solution vector + * @param scaleFactor the scaling factor to apply to the solution + * @param lastResidual (in) target value below which to reduce residual norm, (out) achieved + * residual norm + * @return return true if line search succeeded, false otherwise + * + * This function implements a nonlinear newton method for implicit problems. It requires that + * the + * other functions in the solver interface are implemented in the derived physics solver. The + * nonlinear loop includes a simple line search algorithm, and will cut the timestep if + * convergence is not achieved according to the parameters in linearSolverParameters member. + */ + bool + lineSearch1( real64 const & time_n, + real64 const & dt, + integer const cycleNumber, + DomainPartition & domain, + ElementRegionManager & elemManager, + WellElementSubRegion & subRegion, + MeshLevel & mesh, + DofManager const & dofManager, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + ParallelVector & rhs, + ParallelVector & solution, + real64 const scaleFactor, + real64 & lastResidual ); /** * @brief function to set the next time step size * @param[in] currentTime the current time @@ -274,9 +444,12 @@ class WellSolverBase : public PhysicsSolverBase DomainPartition & domain ) override; /** - * @brief Utility function to keep the well variables during a time step (used in poromechanics simulations) - * @param[in] keepVariablesConstantDuringInitStep flag to tell the solver to freeze its primary variables during a time step - * @detail This function is meant to be called by a specific task before/after the initialization step + * @brief Utility function to keep the well variables during a time step (used in + * poromechanics simulations) + * @param[in] keepVariablesConstantDuringInitStep flag to tell the solver to freeze its + * primary variables during a time step + * @detail This function is meant to be called by a specific task before/after the + * initialization step */ void setKeepVariablesConstantDuringInitStep( bool const keepVariablesConstantDuringInitStep ) { m_keepVariablesConstantDuringInitStep = keepVariablesConstantDuringInitStep; } @@ -286,10 +459,14 @@ class WellSolverBase : public PhysicsSolverBase static constexpr char const * isThermalString() { return "isThermal"; } static constexpr char const * writeCSVFlagString() { return "writeCSV"; } static constexpr char const * timeStepFromTablesFlagString() { return "timeStepFromTables"; } + static constexpr char const * writeSegDebugFlagString() { return "writeSegDebug"; } + static constexpr char const * useNewCodeString() { return "useNewCode"; } static constexpr char const * fluidNamesString() { return "fluidNames"; } }; + + std::tuple< integer, integer, integer > currentIter( real64 const time, real64 const dt ); private: /** @@ -306,11 +483,6 @@ class WellSolverBase : public PhysicsSolverBase virtual void initializePostSubGroups() override; - /** - * @brief Initialize all the primary and secondary variables in all the wells - * @param domain the domain containing the well manager to access individual wells - */ - virtual void initializeWells( DomainPartition & domain, real64 const & time_n ) = 0; /** * @brief Make sure that the well constraints are compatible @@ -327,6 +499,18 @@ class WellSolverBase : public PhysicsSolverBase real64 const & dt, DomainPartition & domain ) = 0; + virtual bool evaluateConstraints( real64 const & GEOS_UNUSED_PARAM( 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 & GEOS_UNUSED_PARAM( subRegion ), + DofManager const & GEOS_UNUSED_PARAM( dofManager ) ) { return false;}; + + + /// name of the flow solver string m_flowSolverName; @@ -354,9 +538,39 @@ class WellSolverBase : public PhysicsSolverBase /// flag to freeze the initial state during initialization in coupled problems bool m_keepVariablesConstantDuringInitStep; + /// flag to write detailed segment properties + integer m_writeSegDebug; + + integer m_globalNumTimeSteps; + real64 m_currentTime; + real64 m_currentDt; + real64 m_prevTime; + real64 m_prevDt; + integer m_numTimeStepCuts; + integer m_currentNewtonIteration; + + std::map< std::string, WellPropWriter > m_wellPropWriter; + std::map< std::string, WellPropWriter > m_wellPropWriter_eot; + + /// name of the fluid constitutive model used as a reference for component/phase description string m_referenceFluidModelName; + + /// flag to use the estimator + integer m_estimateSolution; + + + /// @brief DofManagers for each wells estimator + /// @details This DofManager is used to store the DOF numbers for the estimator + /// @note This DofManager is used in the assembly of the estimators linear system + std::map< std::string, DofManager > m_estimatorDoFManager; + integer my_ctime; //tjb + + real64 m_nextDt; + + integer m_useNewCode; + }; } diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/WellVolumeRateConstraint.cpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellVolumeRateConstraint.cpp new file mode 100644 index 00000000000..db11dd85ef2 --- /dev/null +++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellVolumeRateConstraint.cpp @@ -0,0 +1,80 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * SPDX-License-Identifier: LGPL-2.1-only + * + * Copyright (c) 2016-2024 Lawrence Livermore National Security LLC + * Copyright (c) 2018-2024 TotalEnergies + * Copyright (c) 2018-2024 The Board of Trustees of the Leland Stanford Junior University + * Copyright (c) 2023-2024 Chevron + * Copyright (c) 2019- GEOS/GEOSX Contributors + * All rights reserved + * + * See top level LICENSE, COPYRIGHT, CONTRIBUTORS, NOTICE, and ACKNOWLEDGEMENTS files for details. + * ------------------------------------------------------------------------------------------------------------ + */ + +/* + * @file WellVolumeRateConstraint.cpp + */ + +#include "LogLevelsInfo.hpp" +#include "WellVolumeRateConstraint.hpp" +#include "WellConstants.hpp" +#include "dataRepository/InputFlags.hpp" +#include "functions/FunctionManager.hpp" + +namespace geos +{ + +using namespace dataRepository; + +VolumeRateConstraint::VolumeRateConstraint( string const & name, Group * const parent ) + : WellConstraintBase( name, parent ) +{ + this->setInputFlags( InputFlags::OPTIONAL_NONUNIQUE ); + + this->registerWrapper( viewKeyStruct::volumeRateString(), &this->m_constraintValue ). + setDefaultValue( 0.0 ). + setInputFlag( InputFlags::OPTIONAL ). + setRestartFlags( RestartFlags::WRITE_AND_READ ). + setDescription( "Volumetric rate (if useSurfaceConditions: [surface m^3/s]; else [reservoir m^3/s])" ); + +} + +VolumeRateConstraint::~VolumeRateConstraint() +{} + + +void VolumeRateConstraint::postInputInitialization() +{ + // Validate table options + WellConstraintBase::postInputInitialization(); + + // check constraint value + GEOS_THROW_IF( m_constraintValue < 0, + getWrapperDataContext( viewKeyStruct::volumeRateString() ) << ": Target value is negative", + InputError ); + + GEOS_THROW_IF ((m_constraintValue <= 0.0 && m_constraintScheduleTableName.empty()), + getName() << " " << getDataContext() << ": You need to specify a volume rate constraint. \n" << + "The rate constraint can be specified using " << + "either " << viewKeyStruct::volumeRateString() << + " or " << WellConstraintBase::viewKeyStruct::constraintScheduleTableNameString(), + InputError ); +} + +bool VolumeRateConstraint::checkViolation( WellConstraintBase const & currentConstraint, real64 const & currentTime ) const +{ + real64 const currentValue = currentConstraint.totalVolumeRate(); + real64 const constraintValue = this->getConstraintValue( currentTime ); + if( this->m_rateSign < 0.0 ) + { + return currentValue < constraintValue; + } + else + { + return currentValue > constraintValue; + } +} + +} //namespace geos diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/WellVolumeRateConstraint.hpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellVolumeRateConstraint.hpp new file mode 100644 index 00000000000..1ed14597ca3 --- /dev/null +++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellVolumeRateConstraint.hpp @@ -0,0 +1,124 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * SPDX-License-Identifier: LGPL-2.1-only + * + * Copyright (c) 2016-2024 Lawrence Livermore National Security LLC + * Copyright (c) 2018-2024 TotalEnergies + * Copyright (c) 2018-2024 The Board of Trustees of the Leland Stanford Junior University + * Copyright (c) 2023-2024 Chevron + * Copyright (c) 2019- GEOS/GEOSX Contributors + * All rights reserved + * + * See top level LICENSE, COPYRIGHT, CONTRIBUTORS, NOTICE, and ACKNOWLEDGEMENTS files for details. + * ------------------------------------------------------------------------------------------------------------ + */ + +/* + * @file WellVolumeRateConstraints.hpp + */ + + +#ifndef GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_WELLTOTALVOLRATECONSTRAINTS_HPP +#define GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_WELLTOTALVOLRATECONSTRAINTS_HPP + +#include "common/format/EnumStrings.hpp" +#include "dataRepository/Group.hpp" +#include "functions/TableFunction.hpp" +#include "WellConstraintsBase.hpp" +namespace geos +{ + + + +/** + * @class VolumeRateConstraint + * @brief This class describes a volume rate constraint used to control a well. + */ + +class VolumeRateConstraint : public WellConstraintBase +{ +public: + + /** + * @name Constructor / Destructor + */ + ///@{ + + /** + * @brief Constructor for WellControls Objects. + * @param[in] name the name of this instantiation of WellControls in the repository + * @param[in] parent the parent group of this instantiation of WellControls + */ + explicit VolumeRateConstraint( string const & name, dataRepository::Group * const parent ); + + + /** + * @brief Default destructor. + */ + ~VolumeRateConstraint() override; + + /** + * @brief Deleted default constructor. + */ + VolumeRateConstraint() = delete; + + /** + * @brief Deleted copy constructor. + */ + VolumeRateConstraint( VolumeRateConstraint const & ) = delete; + + /** + * @brief Deleted move constructor. + */ + VolumeRateConstraint( VolumeRateConstraint && ) = delete; + + /** + * @brief Deleted assignment operator. + * @return a reference to a constraint object + */ + VolumeRateConstraint & operator=( VolumeRateConstraint const & ) = delete; + + /** + * @brief Deleted move operator. + * @return a reference to a constraint object + */ + VolumeRateConstraint & operator=( VolumeRateConstraint && ) = delete; + + /** + * @brief name of the node manager in the object catalog + * @return string that contains the catalog name to generate a new Constraint object through the object catalog. + */ + static string catalogName() + { + return "VolumeRateConstraint"; + } + ///@} + /** + * @brief Struct to serve as a container for variable strings and keys. + * @struct viewKeyStruct + */ + struct viewKeyStruct + { + /// String key for the volume rate + static constexpr char const * volumeRateString() { return "volumeRate"; } + }; + /** + * @name Getters / Setters + */ + ///@{ + + // Temp interface - tjb + virtual ConstraintTypeId getControl() const override { return ConstraintTypeId::TOTALVOLRATE; }; + ///@} + + virtual bool checkViolation( WellConstraintBase const & currentConstraint, real64 const & currentTime ) const override; +protected: + + virtual void postInputInitialization() override; + +}; + + +} //namespace geos + +#endif //GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_WELLCONSTRAINT_HPP diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/CompositionalMultiphaseWellConstraintKernels.hpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/CompositionalMultiphaseWellConstraintKernels.hpp new file mode 100644 index 00000000000..485545cd2d9 --- /dev/null +++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/CompositionalMultiphaseWellConstraintKernels.hpp @@ -0,0 +1,555 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * SPDX-License-Identifier: LGPL-2.1-only + * + * Copyright (c) 2016-2024 Lawrence Livermore National Security LLC + * Copyright (c) 2018-2024 TotalEnergies + * Copyright (c) 2018-2024 The Board of Trustees of the Leland Stanford Junior University + * Copyright (c) 2023-2024 Chevron + * Copyright (c) 2019- GEOS/GEOSX Contributors + * All rights reserved + * + * See top level LICENSE, COPYRIGHT, CONTRIBUTORS, NOTICE, and ACKNOWLEDGEMENTS files for details. + * ------------------------------------------------------------------------------------------------------------ + */ + +/** + * @file CompositionalMultiphaseWellConstraintKernels.hpp + */ + +#ifndef GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_WELLCONSTRAINTKERNELS_HPP +#define GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_WELLCONSTRAINTKERNELS_HPP + +#include "codingUtilities/Utilities.hpp" +#include "constitutive/fluid/multifluid/MultiFluidBase.hpp" +#include "constitutive/fluid/multifluid/MultiFluidFields.hpp" + + +#include "physicsSolvers/fluidFlow/wells/WellControls.hpp" +#include "physicsSolvers/fluidFlow/wells/WellBHPConstraints.hpp" +#include "physicsSolvers/fluidFlow/wells/WellVolumeRateConstraint.hpp" +#include "physicsSolvers/fluidFlow/wells/WellPhaseVolumeRateConstraint.hpp" +#include "physicsSolvers/fluidFlow/wells/WellMassRateConstraint.hpp" +#include "physicsSolvers/fluidFlow/wells/WellLiquidRateConstraint.hpp" +namespace geos +{ + +namespace wellConstraintKernels +{ + +/******************************** ControlEquationHelper ********************************/ +//template< integer NC, integer IS_THERMAL, typname S, typename T > +//struct ConstraintHelper< NC, IS_THERMAL > {}; + +template< integer NC, integer IS_THERMAL, typename CONSTRAINT = BHPConstraint > +struct ConstraintHelper +{ + static void assembleConstraintEquation( real64 const & time_n, + WellControls & wellControls, + BHPConstraint & constraint, + WellElementSubRegion const & subRegion, + string const & wellDofKey, + localIndex const & rankOffset, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) + { + // subRegion data + localIndex const iwelemRef = subRegion.getTopWellElementIndex(); + arrayView1d< globalIndex const > const & wellElemDofNumber = subRegion.getReference< array1d< globalIndex > >( wellDofKey ); + arrayView1d< real64 const > const & pres = subRegion.getField< fields::well::pressure >(); + arrayView1d< real64 const > const & totalMassDens = subRegion.getField< fields::well::totalMassDensity >(); + arrayView2d< real64 const > const & dTotalMassDens = subRegion.getField< fields::well::dTotalMassDensity >(); + arrayView1d< real64 const > const wellElemGravCoef = subRegion.getField< fields::well::gravityCoefficient >(); + + // setup row/column indices for constraint equation + using COFFSET_WJ = compositionalMultiphaseWellKernels::ColOffset_WellJac< NC, IS_THERMAL >; + using WJ_ROFFSET = compositionalMultiphaseWellKernels::RowOffset_WellJac< NC, IS_THERMAL >; + using Deriv = constitutive::multifluid::DerivativeOffset; + + localIndex const eqnRowIndex = wellElemDofNumber[iwelemRef] + WJ_ROFFSET::CONTROL - rankOffset; + globalIndex dofColIndices[COFFSET_WJ::nDer]{}; + for( integer ic = 0; ic < COFFSET_WJ::nDer; ++ic ) + { + dofColIndices[ ic ] = wellElemDofNumber[iwelemRef] + ic; + } + + // constraint data + real64 const & targetBHP = constraint.getConstraintValue( time_n ); + real64 const & refGravCoef = constraint.getReferenceGravityCoef(); + + // current constraint value + real64 const & currentBHP = + wellControls.getReference< real64 >( CompositionalMultiphaseWell::viewKeyStruct::currentBHPString() ); + + // residual + real64 controlEqn = currentBHP - targetBHP; + + // setup Jacobian terms + real64 dControlEqn[NC+2+IS_THERMAL]{}; + + // bring everything back to host, capture the scalars by reference + forAll< serialPolicy >( 1, [pres, + totalMassDens, + dTotalMassDens, + wellElemGravCoef, + &dControlEqn, + &iwelemRef, + &refGravCoef] ( localIndex const ) + { + real64 const diffGravCoef = refGravCoef - wellElemGravCoef[iwelemRef]; + dControlEqn[COFFSET_WJ::dP] = 1 + dTotalMassDens[iwelemRef][Deriv::dP] * diffGravCoef; + for( integer ic = 0; ic < NC; ++ic ) + { + dControlEqn[COFFSET_WJ::dC+ic] = dTotalMassDens[iwelemRef][Deriv::dC+ic] * diffGravCoef; + } + if constexpr ( IS_THERMAL ) + { + dControlEqn[COFFSET_WJ::dT] = dTotalMassDens[iwelemRef][Deriv::dT] * diffGravCoef; + } + } ); + + // add solver matrices + localRhs[eqnRowIndex] += controlEqn; + localMatrix.addToRowBinarySearchUnsorted< serialAtomic >( eqnRowIndex, + dofColIndices, + dControlEqn, + COFFSET_WJ::nDer ); + } + + + template< template< typename U > class T, typename U=PhaseVolumeRateConstraint > + static void assembleConstraintEquation( real64 const & time_n, + WellControls & wellControls, + T< PhaseVolumeRateConstraint > & constraint, + WellElementSubRegion const & subRegion, + string const & wellDofKey, + localIndex const & rankOffset, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) + { + // subRegion data + + localIndex const iwelemRef = subRegion.getTopWellElementIndex(); + arrayView1d< globalIndex const > const & wellElemDofNumber = subRegion.getReference< array1d< globalIndex > >( wellDofKey ); + arrayView1d< real64 const > const & connRate = subRegion.getField< fields::well::mixtureConnectionRate >(); + arrayView2d< real64 const, compflow::USD_COMP > const & compFrac = subRegion.getField< fields::well::globalCompFraction >(); + arrayView3d< real64 const, compflow::USD_COMP_DC > const & dCompFrac_dCompDens = subRegion.getField< fields::well::dGlobalCompFraction_dGlobalCompDensity >(); + + // setup row/column indices for constraint equation + using COFFSET_WJ = compositionalMultiphaseWellKernels::ColOffset_WellJac< NC, IS_THERMAL >; + using WJ_ROFFSET = compositionalMultiphaseWellKernels::RowOffset_WellJac< NC, IS_THERMAL >; + using Deriv = constitutive::multifluid::DerivativeOffset; + + localIndex const eqnRowIndex = wellElemDofNumber[iwelemRef] + WJ_ROFFSET::CONTROL - rankOffset; + globalIndex dofColIndices[COFFSET_WJ::nDer]{}; + for( integer ic = 0; ic < COFFSET_WJ::nDer; ++ic ) + { + dofColIndices[ ic ] = wellElemDofNumber[iwelemRef] + ic; + } + + // fluid data + constitutive::MultiFluidBase & fluidSeparator = wellControls.getMultiFluidSeparator(); + + arrayView3d< real64 const, constitutive::multifluid::USD_PHASE > const & phaseFrac = fluidSeparator.phaseFraction(); + arrayView4d< real64 const, constitutive::multifluid::USD_PHASE_DC > const & dPhaseFrac = fluidSeparator.dPhaseFraction(); + arrayView3d< real64 const, constitutive::multifluid::USD_PHASE > const & phaseDens = fluidSeparator.phaseDensity(); + arrayView4d< real64 const, constitutive::multifluid::USD_PHASE_DC > const & dPhaseDens = fluidSeparator.dPhaseDensity(); + + // constraint data + integer ip = getPhaseIndexFromFluidModel( fluidSeparator, constraint.getPhaseName()); + real64 const & targetPhaseRate = constraint.getConstraintValue( time_n ); + + // current constraint value + arrayView1d< real64 > const & currentPhaseVolRate = + wellControls.getReference< array1d< real64 > >( CompositionalMultiphaseWell::viewKeyStruct::currentPhaseVolRateString() ); + integer const useSurfaceConditions = wellControls.useSurfaceConditions(); + + // residual + real64 controlEqn = currentPhaseVolRate[ip] - targetPhaseRate; + + // setup Jacobian terms + real64 dControlEqn[NC+2+IS_THERMAL]{}; + + // bring everything back to host, capture the scalars by reference + forAll< serialPolicy >( 1, [&ip, + connRate, + phaseDens, + dPhaseDens, + phaseFrac, + dPhaseFrac, + compFrac, + dCompFrac_dCompDens, + &dControlEqn, + &useSurfaceConditions, + &iwelemRef] ( localIndex const ) + { + // skip the rest of this function if phase ip is absent + bool const phaseExists = (phaseFrac[iwelemRef][0][ip] > 0); + if( phaseExists ) + { + stackArray1d< real64, NC > work( NC ); + real64 const currentTotalRate = connRate[iwelemRef]; + + real64 const phaseDensInv = 1.0 / phaseDens[iwelemRef][0][ip]; + real64 const phaseFracTimesPhaseDensInv = phaseFrac[iwelemRef][0][ip] * phaseDensInv; + real64 const dPhaseFracTimesPhaseDensInv_dPres = dPhaseFrac[iwelemRef][0][ip][Deriv::dP] * phaseDensInv + - dPhaseDens[iwelemRef][0][ip][Deriv::dP] * phaseFracTimesPhaseDensInv * phaseDensInv; + + // divide the total mass/molar rate by the (phase density * phase fraction) to get the phase volumetric rate + dControlEqn[COFFSET_WJ::dP] = ( useSurfaceConditions == 0 ) * currentTotalRate * dPhaseFracTimesPhaseDensInv_dPres; + dControlEqn[COFFSET_WJ::dQ] = phaseFracTimesPhaseDensInv; + if constexpr (IS_THERMAL ) + { + real64 const dPhaseFracTimesPhaseDensInv_dTemp = dPhaseFrac[iwelemRef][0][ip][Deriv::dT] * phaseDensInv + - dPhaseDens[iwelemRef][0][ip][Deriv::dT] * phaseFracTimesPhaseDensInv * phaseDensInv; + dControlEqn[COFFSET_WJ::dT] = ( useSurfaceConditions == 0 ) * currentTotalRate * dPhaseFracTimesPhaseDensInv_dTemp; + } + + for( integer ic = 0; ic < NC; ++ic ) + { + dControlEqn[COFFSET_WJ::dC+ic] = -phaseFracTimesPhaseDensInv * dPhaseDens[iwelemRef][0][ip][Deriv::dC+ic] * phaseDensInv; + dControlEqn[COFFSET_WJ::dC+ic] += dPhaseFrac[iwelemRef][0][ip][Deriv::dC+ic] * phaseDensInv; + dControlEqn[COFFSET_WJ::dC+ic] *= currentTotalRate; + } + applyChainRuleInPlace( NC, dCompFrac_dCompDens[iwelemRef], &dControlEqn[COFFSET_WJ::dC], work.data() ); + + } + } ); + + // add solver matrices + localRhs[eqnRowIndex] += controlEqn; + localMatrix.addToRowBinarySearchUnsorted< serialAtomic >( eqnRowIndex, + dofColIndices, + dControlEqn, + COFFSET_WJ::nDer ); + } + + template< template< typename U > class T, typename U=LiquidRateConstraint > + static void assembleConstraintEquation( real64 const & time_n, + WellControls & wellControls, + T< LiquidRateConstraint > & constraint, + WellElementSubRegion const & subRegion, + string const & wellDofKey, + localIndex const & rankOffset, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) + { + // subRegion data + + localIndex const iwelemRef = subRegion.getTopWellElementIndex(); + arrayView1d< globalIndex const > const & wellElemDofNumber = subRegion.getReference< array1d< globalIndex > >( wellDofKey ); + arrayView1d< real64 const > const & connRate = subRegion.getField< fields::well::mixtureConnectionRate >(); + arrayView2d< real64 const, compflow::USD_COMP > const & compFrac = subRegion.getField< fields::well::globalCompFraction >(); + arrayView3d< real64 const, compflow::USD_COMP_DC > const & dCompFrac_dCompDens = subRegion.getField< fields::well::dGlobalCompFraction_dGlobalCompDensity >(); + + // setup row/column indices for constraint equation + using COFFSET_WJ = compositionalMultiphaseWellKernels::ColOffset_WellJac< NC, IS_THERMAL >; + using WJ_ROFFSET = compositionalMultiphaseWellKernels::RowOffset_WellJac< NC, IS_THERMAL >; + using Deriv = constitutive::multifluid::DerivativeOffset; + + localIndex const eqnRowIndex = wellElemDofNumber[iwelemRef] + WJ_ROFFSET::CONTROL - rankOffset; + globalIndex dofColIndices[COFFSET_WJ::nDer]{}; + for( integer ic = 0; ic < COFFSET_WJ::nDer; ++ic ) + { + dofColIndices[ ic ] = wellElemDofNumber[iwelemRef] + ic; + } + + // fluid data + constitutive::MultiFluidBase & fluidSeparator = wellControls.getMultiFluidSeparator(); + + arrayView3d< real64 const, constitutive::multifluid::USD_PHASE > const & phaseFrac = fluidSeparator.phaseFraction(); + arrayView4d< real64 const, constitutive::multifluid::USD_PHASE_DC > const & dPhaseFrac = fluidSeparator.dPhaseFraction(); + arrayView3d< real64 const, constitutive::multifluid::USD_PHASE > const & phaseDens = fluidSeparator.phaseDensity(); + arrayView4d< real64 const, constitutive::multifluid::USD_PHASE_DC > const & dPhaseDens = fluidSeparator.dPhaseDensity(); + + // constraint data + real64 const & targetPhaseRate = constraint.getConstraintValue( time_n ); + const array1d< int > & phaseIndices = constraint.getPhaseIndices(); + + + // current constraint value + arrayView1d< real64 > const & currentPhaseVolRate = + wellControls.getReference< array1d< real64 > >( CompositionalMultiphaseWell::viewKeyStruct::currentPhaseVolRateString() ); + integer const useSurfaceConditions = wellControls.useSurfaceConditions(); + + // residual + real64 controlEqn = -targetPhaseRate; + for( integer ip= 0; ip < phaseIndices.size(); ++ip ) + { + controlEqn += currentPhaseVolRate[phaseIndices[ip]]; + } + + // setup Jacobian terms + real64 dControlEqn[NC+2+IS_THERMAL]{}; + + // bring everything back to host, capture the scalars by reference + forAll< serialPolicy >( 1, [phaseIndices, + connRate, + phaseDens, + dPhaseDens, + phaseFrac, + dPhaseFrac, + compFrac, + dCompFrac_dCompDens, + &dControlEqn, + &useSurfaceConditions, + &iwelemRef] ( localIndex const ) + { + + stackArray1d< real64, NC > work( NC ); + for( integer i= 0; i < phaseIndices.size(); ++i ) + { + integer ip = phaseIndices[i]; + bool const phaseExists = (phaseFrac[iwelemRef][0][ip] > 0); + // skip the rest of this function if phase ip is absent + if( phaseExists ) + { + + real64 const currentTotalRate = connRate[iwelemRef]; + + real64 const phaseDensInv = 1.0 / phaseDens[iwelemRef][0][ip]; + real64 const phaseFracTimesPhaseDensInv = phaseFrac[iwelemRef][0][ip] * phaseDensInv; + real64 const dPhaseFracTimesPhaseDensInv_dPres = dPhaseFrac[iwelemRef][0][ip][Deriv::dP] * phaseDensInv + - dPhaseDens[iwelemRef][0][ip][Deriv::dP] * phaseFracTimesPhaseDensInv * phaseDensInv; + + // divide the total mass/molar rate by the (phase density * phase fraction) to get the phase volumetric rate + dControlEqn[COFFSET_WJ::dP] += ( useSurfaceConditions == 0 ) * currentTotalRate * dPhaseFracTimesPhaseDensInv_dPres; + dControlEqn[COFFSET_WJ::dQ] += phaseFracTimesPhaseDensInv; + if constexpr (IS_THERMAL ) + { + real64 const dPhaseFracTimesPhaseDensInv_dTemp = dPhaseFrac[iwelemRef][0][ip][Deriv::dT] * phaseDensInv + - dPhaseDens[iwelemRef][0][ip][Deriv::dT] * phaseFracTimesPhaseDensInv * phaseDensInv; + dControlEqn[COFFSET_WJ::dT] += ( useSurfaceConditions == 0 ) * currentTotalRate * dPhaseFracTimesPhaseDensInv_dTemp; + } + + for( integer ic = 0; ic < NC; ++ic ) + { + real64 temp = -phaseFracTimesPhaseDensInv * dPhaseDens[iwelemRef][0][ip][Deriv::dC+ic] * phaseDensInv; + temp += dPhaseFrac[iwelemRef][0][ip][Deriv::dC+ic] * phaseDensInv; + temp *= currentTotalRate; + dControlEqn[COFFSET_WJ::dC+ic] += temp; + } + } + } + applyChainRuleInPlace( NC, dCompFrac_dCompDens[iwelemRef], &dControlEqn[COFFSET_WJ::dC], work.data() ); + } ); + + // add solver matrices + localRhs[eqnRowIndex] += controlEqn; + localMatrix.addToRowBinarySearchUnsorted< serialAtomic >( eqnRowIndex, + dofColIndices, + dControlEqn, + COFFSET_WJ::nDer ); + } + template< template< typename U > class T, typename U=VolumeRateConstraint > + static void assembleConstraintEquation( real64 const & time_n, + WellControls & wellControls, + T< VolumeRateConstraint > & constraint, + WellElementSubRegion const & subRegion, + string const & wellDofKey, + localIndex const & rankOffset, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) + { + // subRegion data + + localIndex const iwelemRef = subRegion.getTopWellElementIndex(); + arrayView1d< globalIndex const > const & wellElemDofNumber = subRegion.getReference< array1d< globalIndex > >( wellDofKey ); + arrayView1d< real64 const > const & connRate = subRegion.getField< fields::well::mixtureConnectionRate >(); + arrayView2d< real64 const, compflow::USD_COMP > const & compFrac = subRegion.getField< fields::well::globalCompFraction >(); + arrayView3d< real64 const, compflow::USD_COMP_DC > const & dCompFrac_dCompDens = subRegion.getField< fields::well::dGlobalCompFraction_dGlobalCompDensity >(); + + // setup row/column indices for constraint equation + using COFFSET_WJ = compositionalMultiphaseWellKernels::ColOffset_WellJac< NC, IS_THERMAL >; + using WJ_ROFFSET = compositionalMultiphaseWellKernels::RowOffset_WellJac< NC, IS_THERMAL >; + using Deriv = constitutive::multifluid::DerivativeOffset; + + localIndex const eqnRowIndex = wellElemDofNumber[iwelemRef] + WJ_ROFFSET::CONTROL - rankOffset; + globalIndex dofColIndices[COFFSET_WJ::nDer]{}; + for( integer ic = 0; ic < COFFSET_WJ::nDer; ++ic ) + { + dofColIndices[ ic ] = wellElemDofNumber[iwelemRef] + ic; + } + + // fluid data + constitutive::MultiFluidBase & fluidSeparator = wellControls.getMultiFluidSeparator(); + + arrayView2d< real64 const, constitutive::multifluid::USD_FLUID > const & totalDens = fluidSeparator.totalDensity(); + arrayView3d< real64 const, constitutive::multifluid::USD_FLUID_DC > const & dTotalDens = fluidSeparator.dTotalDensity(); + + // constraint data + real64 const & targetTotalVolRate = constraint.getConstraintValue( time_n ); + + // current constraint value + real64 const & currentTotalVolRate = + wellControls.getReference< real64 >( CompositionalMultiphaseWell::viewKeyStruct::currentTotalVolRateString() ); + integer const useSurfaceConditions = wellControls.useSurfaceConditions(); + + // residual + real64 controlEqn = currentTotalVolRate - targetTotalVolRate; + + // setup Jacobian terms + real64 dControlEqn[NC+2+IS_THERMAL]{}; + + // bring everything back to host, capture the scalars by reference + forAll< serialPolicy >( 1, [connRate, + totalDens, + dTotalDens, + compFrac, + dCompFrac_dCompDens, + &dControlEqn, + &useSurfaceConditions, + &iwelemRef] ( localIndex const ) + { + stackArray1d< real64, NC > work( NC ); + + real64 const currentTotalRate = connRate[iwelemRef]; + + // compute the inverse of the total density and derivatives + + real64 const totalDensInv = 1.0 / totalDens[iwelemRef][0]; + + stackArray1d< real64, NC > dTotalDensInv_dCompDens( NC ); + for( integer ic = 0; ic < NC; ++ic ) + { + dTotalDensInv_dCompDens[ic] = -dTotalDens[iwelemRef][0][Deriv::dC+ic] * totalDensInv * totalDensInv; + } + applyChainRuleInPlace( NC, dCompFrac_dCompDens[iwelemRef], dTotalDensInv_dCompDens, work.data() ); + + // Step 2.2: divide the total mass/molar rate by the total density to get the total volumetric rate + + // Compute derivatives dP dT + real64 const dTotalDensInv_dPres = -dTotalDens[iwelemRef][0][Deriv::dP] * totalDensInv * totalDensInv; + dControlEqn[COFFSET_WJ::dP] = ( useSurfaceConditions == 0 ) * currentTotalRate * dTotalDensInv_dPres; + if constexpr ( IS_THERMAL ) + { + dControlEqn[COFFSET_WJ::dT] = ( useSurfaceConditions == 0 ) * currentTotalRate * -dTotalDens[iwelemRef][0][Deriv::dT] * totalDensInv * totalDensInv; + } + + dControlEqn[COFFSET_WJ::dQ] = totalDensInv; + for( integer ic = 0; ic < NC; ++ic ) + { + dControlEqn[COFFSET_WJ::dC+ic] = currentTotalRate * dTotalDensInv_dCompDens[ic]; + } + + } ); + + // add solver matrices + localRhs[eqnRowIndex] += controlEqn; + localMatrix.addToRowBinarySearchUnsorted< serialAtomic >( eqnRowIndex, + dofColIndices, + dControlEqn, + COFFSET_WJ::nDer ); + } + template< template< typename U > class T, typename U=MassRateConstraint > + static void assembleConstraintEquation( real64 const & time_n, + WellControls & wellControls, + T< MassRateConstraint > & constraint, + WellElementSubRegion const & subRegion, + string const & wellDofKey, + localIndex const & rankOffset, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) + { + // subRegion data + + localIndex const iwelemRef = subRegion.getTopWellElementIndex(); + arrayView1d< globalIndex const > const & wellElemDofNumber = subRegion.getReference< array1d< globalIndex > >( wellDofKey ); + arrayView1d< real64 const > const & connRate = subRegion.getField< fields::well::mixtureConnectionRate >(); + arrayView2d< real64 const, compflow::USD_COMP > const & compFrac = subRegion.getField< fields::well::globalCompFraction >(); + arrayView3d< real64 const, compflow::USD_COMP_DC > const & dCompFrac_dCompDens = subRegion.getField< fields::well::dGlobalCompFraction_dGlobalCompDensity >(); + + // setup row/column indices for constraint equation + using COFFSET_WJ = compositionalMultiphaseWellKernels::ColOffset_WellJac< NC, IS_THERMAL >; + using WJ_ROFFSET = compositionalMultiphaseWellKernels::RowOffset_WellJac< NC, IS_THERMAL >; + using Deriv = constitutive::multifluid::DerivativeOffset; + + localIndex const eqnRowIndex = wellElemDofNumber[iwelemRef] + WJ_ROFFSET::CONTROL - rankOffset; + globalIndex dofColIndices[COFFSET_WJ::nDer]{}; + for( integer ic = 0; ic < COFFSET_WJ::nDer; ++ic ) + { + dofColIndices[ ic ] = wellElemDofNumber[iwelemRef] + ic; + } + + // fluid data + constitutive::MultiFluidBase & fluidSeparator = wellControls.getMultiFluidSeparator(); + arrayView2d< real64 const, constitutive::multifluid::USD_FLUID > const & totalDens = fluidSeparator.totalDensity(); + arrayView3d< real64 const, constitutive::multifluid::USD_FLUID_DC > const & dTotalDens = fluidSeparator.dTotalDensity(); + + // constraint data + real64 const & targetMassRate = constraint.getConstraintValue( time_n ); + + // current constraint value + real64 const & massDensity = + wellControls.getReference< real64 >( CompositionalMultiphaseWell::viewKeyStruct::massDensityString() ); + + // fix to use stored massrate + real64 const & currentTotalVolRate = + wellControls.getReference< real64 >( CompositionalMultiphaseWell::viewKeyStruct::currentTotalVolRateString() ); + integer const useSurfaceConditions = wellControls.useSurfaceConditions(); + + // residual + real64 controlEqn = massDensity*currentTotalVolRate - targetMassRate; + + // setup Jacobian terms + real64 dControlEqn[NC+2+IS_THERMAL]{}; + + // bring everything back to host, capture the scalars by reference + forAll< serialPolicy >( 1, [connRate, + massDensity, + totalDens, + dTotalDens, + compFrac, + dCompFrac_dCompDens, + &dControlEqn, + &useSurfaceConditions, + &iwelemRef] ( localIndex const ) + { + stackArray1d< real64, NC > work( NC ); + + real64 const currentTotalRate = connRate[iwelemRef]; + + // compute the inverse of the total density and derivatives + + real64 const totalDensInv = 1.0 / totalDens[iwelemRef][0]; + + stackArray1d< real64, NC > dTotalDensInv_dCompDens( NC ); + for( integer ic = 0; ic < NC; ++ic ) + { + dTotalDensInv_dCompDens[ic] = -dTotalDens[iwelemRef][0][Deriv::dC+ic] * totalDensInv * totalDensInv; + } + applyChainRuleInPlace( NC, dCompFrac_dCompDens[iwelemRef], dTotalDensInv_dCompDens, work.data() ); + + // Step 2.2: divide the total mass/molar rate by the total density to get the total volumetric rate + + // Compute derivatives dP dT + real64 const dTotalDensInv_dPres = -dTotalDens[iwelemRef][0][Deriv::dP] * totalDensInv * totalDensInv; + dControlEqn[COFFSET_WJ::dP] = ( useSurfaceConditions == 0 )*massDensity * currentTotalRate * dTotalDensInv_dPres; + if constexpr ( IS_THERMAL ) + { + dControlEqn[COFFSET_WJ::dT] = ( useSurfaceConditions == 0 ) * massDensity* currentTotalRate * -dTotalDens[iwelemRef][0][Deriv::dT] * totalDensInv * totalDensInv; + } + + dControlEqn[COFFSET_WJ::dQ] = massDensity*totalDensInv; + for( integer ic = 0; ic < NC; ++ic ) + { + dControlEqn[COFFSET_WJ::dC+ic] = massDensity* currentTotalRate * dTotalDensInv_dCompDens[ic]; + } + + } ); + + // add solver matrices + localRhs[eqnRowIndex] += controlEqn; + localMatrix.addToRowBinarySearchUnsorted< serialAtomic >( eqnRowIndex, + dofColIndices, + dControlEqn, + COFFSET_WJ::nDer ); + } +}; + + +} // end namespace wellConstraintKernels + +} // end namespace geos + +#endif //GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_WELLCONSTRAINTKERNELS_HPP diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/CompositionalMultiphaseWellKernels.cpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/CompositionalMultiphaseWellKernels.cpp index 039ae5c1813..83d0f3e9802 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/CompositionalMultiphaseWellKernels.cpp +++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/CompositionalMultiphaseWellKernels.cpp @@ -37,19 +37,19 @@ GEOS_HOST_DEVICE inline void ControlEquationHelper:: - switchControl( bool const isProducer, - WellControls::Control const & inputControl, - WellControls::Control const & currentControl, - integer const phasePhaseIndex, - real64 const & targetBHP, - real64 const & targetPhaseRate, - real64 const & targetTotalRate, - real64 const & targetMassRate, - real64 const & currentBHP, - arrayView1d< real64 const > const & currentPhaseVolRate, - real64 const & currentTotalVolRate, - real64 const & currentMassRate, - WellControls::Control & newControl ) + selectLimitingConstraint( bool const isProducer, + WellControls::Control const & inputControl, + WellControls::Control const & currentControl, + integer const phasePhaseIndex, + real64 const & targetBHP, + real64 const & targetPhaseRate, + real64 const & targetTotalRate, + real64 const & targetMassRate, + real64 const & currentBHP, + arrayView1d< real64 const > const & currentPhaseVolRate, + real64 const & currentTotalVolRate, + real64 const & currentMassRate, + WellControls::Control & newControl ) { // if isViable is true at the end of the following checks, no need to switch bool controlIsViable = false; @@ -140,6 +140,7 @@ ControlEquationHelper:: real64 const & targetPhaseRate, real64 const & targetTotalRate, real64 const & targetMassRate, + real64 const & targetValue, real64 const & currentBHP, arrayView1d< real64 const > const & dCurrentBHP, arrayView1d< real64 const > const & currentPhaseVolRate, @@ -152,7 +153,7 @@ ControlEquationHelper:: CRSMatrixView< real64, globalIndex const > const & localMatrix, arrayView1d< real64 > const & localRhs ) { - + GEOS_UNUSED_VAR( targetValue ); // tjb keeping this around if needed to compare with old constraint eqn jacgen using COFFSET_WJ = compositionalMultiphaseWellKernels::ColOffset_WellJac< NC, IS_THERMAL >; using Deriv = multifluid::DerivativeOffset; @@ -300,11 +301,6 @@ void PressureRelationKernel:: launch( localIndex const size, globalIndex const rankOffset, - bool const isLocallyOwned, - localIndex const iwelemControl, - integer const targetPhaseIndex, - WellControls const & wellControls, - real64 const & time, arrayView1d< integer const > const elemStatus, arrayView1d< globalIndex const > const & wellElemDofNumber, arrayView1d< real64 const > const & wellElemGravCoef, @@ -317,36 +313,6 @@ PressureRelationKernel:: arrayView1d< real64 > const & localRhs ) { using COFFSET_WJ = compositionalMultiphaseWellKernels::ColOffset_WellJac< NC, IS_THERMAL >; - // static well control data - bool const isProducer = wellControls.isProducer(); - WellControls::Control const currentControl = wellControls.getControl(); - WellControls::Control const inputControl = wellControls.getInputControl(); - real64 const targetBHP = wellControls.getTargetBHP( time ); - real64 const targetTotalRate = wellControls.getTargetTotalRate( time ); - real64 const targetPhaseRate = wellControls.getTargetPhaseRate( time ); - real64 const targetMassRate = wellControls.getTargetMassRate( time ); - - // dynamic well control data - real64 const & currentBHP = - wellControls.getReference< real64 >( CompositionalMultiphaseWell::viewKeyStruct::currentBHPString() ); - arrayView1d< real64 const > const & dCurrentBHP = - wellControls.getReference< array1d< real64 > >( CompositionalMultiphaseWell::viewKeyStruct::dCurrentBHPString() ); - - arrayView1d< real64 const > const & currentPhaseVolRate = - wellControls.getReference< array1d< real64 > >( CompositionalMultiphaseWell::viewKeyStruct::currentPhaseVolRateString() ); - arrayView2d< real64 const > const & dCurrentPhaseVolRate = - wellControls.getReference< array2d< real64 > >( CompositionalMultiphaseWell::viewKeyStruct::dCurrentPhaseVolRateString() ); - - real64 const & currentTotalVolRate = - wellControls.getReference< real64 >( CompositionalMultiphaseWell::viewKeyStruct::currentTotalVolRateString() ); - arrayView1d< real64 const > const & dCurrentTotalVolRate = - wellControls.getReference< array1d< real64 > >( CompositionalMultiphaseWell::viewKeyStruct::dCurrentTotalVolRateString() ); - - real64 const & currentMassRate = - wellControls.getReference< real64 >( CompositionalMultiphaseWell::viewKeyStruct::currentMassRateString() ); - - real64 const & massDensity = - wellControls.getReference< real64 >( CompositionalMultiphaseWell::viewKeyStruct::massDensityString() ); RAJA::ReduceMax< parallelDeviceReduce, localIndex > switchControl( 0 ); @@ -360,48 +326,7 @@ PressureRelationKernel:: localIndex const iwelemNext = nextWellElemIndex[iwelem]; - if( iwelemNext < 0 && isLocallyOwned ) // if iwelemNext < 0, form control equation - { - - WellControls::Control newControl = currentControl; - ControlEquationHelper::switchControl( isProducer, - inputControl, - currentControl, - targetPhaseIndex, - targetBHP, - targetPhaseRate, - targetTotalRate, - targetMassRate, - currentBHP, - currentPhaseVolRate, - currentTotalVolRate, - currentMassRate, - newControl ); - if( currentControl != newControl ) - { - switchControl.max( 1 ); - } - ControlEquationHelper::compute< NC, IS_THERMAL >( rankOffset, - newControl, - targetPhaseIndex, - targetBHP, - targetPhaseRate, - targetTotalRate, - targetMassRate, - currentBHP, - dCurrentBHP, - currentPhaseVolRate, - dCurrentPhaseVolRate, - currentTotalVolRate, - dCurrentTotalVolRate, - massDensity, - wellElemDofNumber[iwelemControl], - localMatrix, - localRhs ); - // TODO: for consistency, we should assemble here, not in compute... - - } - else if( iwelemNext >= 0 ) // if iwelemNext >= 0, form momentum equation + if( iwelemNext >= 0 ) // if iwelemNext >= 0, form momentum equation { real64 localPresRel = 0; @@ -455,11 +380,6 @@ PressureRelationKernel:: void PressureRelationKernel:: \ launch< NC, IS_THERMAL >( localIndex const size, \ globalIndex const rankOffset, \ - bool const isLocallyOwned, \ - localIndex const iwelemControl, \ - integer const targetPhaseIndex, \ - WellControls const & wellControls, \ - real64 const & time, \ arrayView1d< integer const > const elemStatus, \ arrayView1d< globalIndex const > const & wellElemDofNumber, \ arrayView1d< real64 const > const & wellElemGravCoef, \ @@ -500,6 +420,7 @@ PresTempCompFracInitializationKernel:: arrayView1d< localIndex const > const & resElementIndex, arrayView1d< real64 const > const & perfGravCoef, arrayView1d< integer const > const & perfStatus, + arrayView1d< localIndex const > const perfWellElemIndex, arrayView1d< real64 const > const & wellElemGravCoef, arrayView1d< real64 > const & wellElemPres, arrayView1d< real64 > const & wellElemTemp, @@ -512,9 +433,10 @@ PresTempCompFracInitializationKernel:: real64 const initialPresCoef = wellControls.getInitialPressureCoefficient(); WellControls::Control const currentControl = wellControls.getControl(); bool const isProducer = wellControls.isProducer(); - - - + localIndex const nLocalSeg = wellElemPres.size(); + array1d< real64 > segTemp; + GEOS_UNUSED_VAR( perfWellElemIndex ); + segTemp.resize( nLocalSeg ); // Step 1: we loop over all the perforations on this rank to compute the following quantities: // - Sum of total mass densities over the perforated reservoir elements // - Sum of the temperatures over the perforated reservoir elements @@ -528,7 +450,7 @@ PresTempCompFracInitializationKernel:: RAJA::ReduceSum< parallelDeviceReduce, real64 > sumCompFrac[MAX_NUM_COMP]{}; RAJA::ReduceMin< parallelDeviceReduce, real64 > localMinGravCoefDiff( 1e9 ); - forAll< parallelDevicePolicy<> >( perforationSize, [=] GEOS_HOST_DEVICE ( localIndex const iperf ) + forAll< parallelDevicePolicy<> >( perforationSize, [&] GEOS_HOST_DEVICE ( localIndex const iperf ) { if( perfStatus[iperf] ) { @@ -537,7 +459,9 @@ PresTempCompFracInitializationKernel:: localIndex const er = resElementRegion[iperf]; localIndex const esr = resElementSubRegion[iperf]; localIndex const ei = resElementIndex[iperf]; - + //localIndex const wi = perfWellElemIndex[iperf]; + //std::cout << "perf " << iperf << " maps to well elem " << wi << std::endl; + //segTemp[wi] = resTemp[er][esr][ei]; // save the min gravCoef difference between the reference depth and the perforation depth (times g) localMinGravCoefDiff.min( LvArray::math::abs( refWellElemGravCoef - perfGravCoef[iperf] ) ); @@ -562,6 +486,16 @@ PresTempCompFracInitializationKernel:: } } } ); +#if 0 + for( integer i=nLocalSeg-2; i>-1; --i ) + { + std::cout << "segTemp[" << i << "] = " << segTemp[i] << std::endl; + if( segTemp[i] == 0.0 ) + { + segTemp[i] = segTemp[i+1]; + } + } +#endif real64 const minGravCoefDiff = MpiWrapper::min( localMinGravCoefDiff.get() ); integer totalOpenPerfs = MpiWrapper::sum( numOpenPerfs.get() ); @@ -580,7 +514,6 @@ PresTempCompFracInitializationKernel:: { // use average temperature from reservoir avgTemp = MpiWrapper::sum( sumTemp.get() ) / totalOpenPerfs; - // use average comp frac from reservoir for( integer ic = 0; ic < numComps; ++ic ) { @@ -632,6 +565,9 @@ PresTempCompFracInitializationKernel:: if( isZero( gravCoefDiff - minGravCoefDiff ) ) { localRefPres.min( alpha * resPres[er][esr][ei] + avgTotalMassDens * ( refWellElemGravCoef - perfGravCoef[iperf] ) ); + //localRefPres.min( resPres[er][esr][ei] ); + //std::cout << "perf " << iperf << " alpha " << alpha << " resPres " << resPres[er][esr][ei] << " estimated refPres " << + // refWellElemGravCoef << " " << perfGravCoef[iperf] << std::endl; } } } ); @@ -654,8 +590,11 @@ PresTempCompFracInitializationKernel:: forAll< parallelDevicePolicy<> >( subRegionSize, [=] GEOS_HOST_DEVICE ( localIndex const iwelem ) { wellElemPres[iwelem] = refPres + avgTotalMassDens * ( wellElemGravCoef[iwelem] - refWellElemGravCoef ); + //wellElemPres[iwelem] = refPres ; wellElemTemp[iwelem] = avgTemp; - +#if 0 + wellElemTemp[iwelem] = segTemp[iwelem]; +#endif real64 sumCompFracForCheck = 0.0; for( integer ic = 0; ic < numComps; ++ic ) { @@ -688,7 +627,9 @@ PresTempCompFracInitializationKernel:: GEOS_THROW_IF( foundInconsistentCompFrac.get() == 1, wellControls.getDataContext() << "Invalid well initialization, inconsistent component fractions were found.", InputError ); - + std::cout << wellControls.getName() << " initialized with reference pressure " << refPres + << ", average temperature " << avgTemp + << ", average total mass density " << avgTotalMassDens << std::endl; } @@ -716,62 +657,92 @@ CompDensInitializationKernel:: void RateInitializationKernel:: launch( localIndex const subRegionSize, - integer const targetPhaseIndex, WellControls const & wellControls, real64 const & time, arrayView3d< real64 const, multifluid::USD_PHASE > const & phaseDens, arrayView2d< real64 const, multifluid::USD_FLUID > const & totalDens, arrayView1d< real64 > const & connRate ) { - WellControls::Control const control = wellControls.getControl(); - bool const isProducer = wellControls.isProducer(); - real64 const targetTotalRate = wellControls.getTargetTotalRate( time ); - real64 const targetPhaseRate = wellControls.getTargetPhaseRate( time ); - real64 const targetMassRate = wellControls.getTargetMassRate( time ); - - // Estimate the connection rates - forAll< parallelDevicePolicy<> >( subRegionSize, [=] GEOS_HOST_DEVICE ( localIndex const iwelem ) + if( wellControls.isProducer() ) { - if( control == WellControls::Control::BHP ) + std::vector< WellConstraintBase * > const constraints = wellControls.getProdRateConstraints(); + // Use first rate constraint to set initial connection rates + real64 const constraintVal = constraints[0]->getConstraintValue( time ); + ConstraintTypeId const controlType = constraints[0]->getControl(); + if( controlType == ConstraintTypeId::PHASEVOLRATE ) { - // if BHP constraint set rate below the absolute max rate - // with the appropriate sign (negative for prod, positive for inj) - if( isProducer ) + integer const targetPhaseIndex = wellControls.getConstraintPhaseIndex(); + + forAll< parallelDevicePolicy<> >( subRegionSize, [=] GEOS_HOST_DEVICE ( localIndex const iwelem ) { - connRate[iwelem] = LvArray::math::max( 0.1 * targetPhaseRate * phaseDens[iwelem][0][targetPhaseIndex], -1e3 ); - } - else + connRate[iwelem] = constraintVal * phaseDens[iwelem][0][targetPhaseIndex]; + } ); + } + else if( controlType == ConstraintTypeId::TOTALVOLRATE ) + { + forAll< parallelDevicePolicy<> >( subRegionSize, [=] GEOS_HOST_DEVICE ( localIndex const iwelem ) { - if( isZero( targetMassRate ) ) - { - connRate[iwelem] = LvArray::math::min( 0.1 * targetTotalRate * totalDens[iwelem][0], 1e3 ); - } - else - { - connRate[iwelem] = targetMassRate; - } + connRate[iwelem] = LvArray::math::max( 0.1 * constraintVal * totalDens[iwelem][0], -1e3 ); + } ); + } + else if( controlType == ConstraintTypeId::MASSRATE ) + { + forAll< parallelDevicePolicy<> >( subRegionSize, [=] GEOS_HOST_DEVICE ( localIndex const iwelem ) + { + connRate[iwelem] = constraintVal; + } ); + } + else if( controlType == ConstraintTypeId::BHP ) + { + // this assumes phase control presen + integer const targetPhaseIndex = wellControls.getConstraintPhaseIndex(); - } + forAll< parallelDevicePolicy<> >( subRegionSize, [&] GEOS_HOST_DEVICE ( localIndex const iwelem ) + { + + connRate[iwelem] = LvArray::math::max( 0.1 * constraintVal * phaseDens[iwelem][0][targetPhaseIndex], -1e3 ); + } ); } - else if( control == WellControls::Control::MASSRATE ) + } + else + { + std::vector< WellConstraintBase * > const constraints = wellControls.getInjRateConstraints(); + // Use first rate constraint to set initial connection rates + real64 const constraintVal = constraints[0]->getConstraintValue( time ); + ConstraintTypeId const controlType = constraints[0]->getControl(); + if( controlType == ConstraintTypeId::PHASEVOLRATE ) { - connRate[iwelem] = targetMassRate; + integer const targetPhaseIndex = wellControls.getConstraintPhaseIndex(); + + forAll< parallelDevicePolicy<> >( subRegionSize, [=] GEOS_HOST_DEVICE ( localIndex const iwelem ) + { + connRate[iwelem] = LvArray::math::max( 0.1 * constraintVal * phaseDens[iwelem][0][targetPhaseIndex], 1e3 ); + } ); } - else + else if( controlType == ConstraintTypeId::TOTALVOLRATE ) { - if( isProducer ) + forAll< parallelDevicePolicy<> >( subRegionSize, [=] GEOS_HOST_DEVICE ( localIndex const iwelem ) { - connRate[iwelem] = targetPhaseRate * phaseDens[iwelem][0][targetPhaseIndex]; - } - else + connRate[iwelem] = constraintVal * totalDens[iwelem][0]; + } ); + } + else if( controlType == ConstraintTypeId::MASSRATE ) + { + forAll< parallelDevicePolicy<> >( subRegionSize, [=] GEOS_HOST_DEVICE ( localIndex const iwelem ) { - connRate[iwelem] = targetTotalRate * totalDens[iwelem][0]; - } + connRate[iwelem] = constraintVal; + } ); } - } ); + else if( controlType == ConstraintTypeId::BHP ) + { + forAll< parallelDevicePolicy<> >( subRegionSize, [=] GEOS_HOST_DEVICE ( localIndex const iwelem ) + { + connRate[iwelem] = LvArray::math::min( 0.1 * constraintVal * totalDens[iwelem][0], 1e3 ); + } ); + } + } } - } // end namespace compositionalMultiphaseWellKernels } // end namespace geos diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/CompositionalMultiphaseWellKernels.hpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/CompositionalMultiphaseWellKernels.hpp index a8123abbb12..5569d94a7f3 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/CompositionalMultiphaseWellKernels.hpp +++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/CompositionalMultiphaseWellKernels.hpp @@ -40,6 +40,7 @@ #include "physicsSolvers/fluidFlow/wells/CompositionalMultiphaseWellFields.hpp" #include "physicsSolvers/fluidFlow/wells/WellControls.hpp" #include "physicsSolvers/fluidFlow/wells/WellSolverBaseFields.hpp" +#include "physicsSolvers/fluidFlow/wells/WellPhaseVolumeRateConstraint.hpp" namespace geos { @@ -134,19 +135,19 @@ struct ControlEquationHelper inline static void - switchControl( bool const isProducer, - WellControls::Control const & inputControl, - WellControls::Control const & currentControl, - integer const phasePhaseIndex, - real64 const & targetBHP, - real64 const & targetPhaseRate, - real64 const & targetTotalRate, - real64 const & targetMassRate, - real64 const & currentBHP, - arrayView1d< real64 const > const & currentPhaseVolRate, - real64 const & currentTotalVolRate, - real64 const & currentMassRate, - WellControls::Control & newControl ); + selectLimitingConstraint( bool const isProducer, + WellControls::Control const & inputControl, + WellControls::Control const & currentControl, + integer const phasePhaseIndex, + real64 const & targetBHP, + real64 const & targetPhaseRate, + real64 const & targetTotalRate, + real64 const & targetMassRate, + real64 const & currentBHP, + arrayView1d< real64 const > const & currentPhaseVolRate, + real64 const & currentTotalVolRate, + real64 const & currentMassRate, + WellControls::Control & newControl ); template< integer NC, integer IS_THERMAL > GEOS_HOST_DEVICE @@ -160,6 +161,7 @@ struct ControlEquationHelper real64 const & targetTotalRate, real64 const & targetMassRate, real64 const & currentBHP, + real64 const & targetValue, arrayView1d< real64 const > const & dCurrentBHP, arrayView1d< real64 const > const & currentPhaseVolRate, arrayView2d< real64 const > const & dCurrentPhaseVolRate, @@ -200,11 +202,6 @@ struct PressureRelationKernel static void launch( localIndex const size, globalIndex const rankOffset, - bool const isLocallyOwned, - localIndex const iwelemControl, - integer const targetPhaseIndex, - WellControls const & wellControls, - real64 const & time, arrayView1d< integer const > const elemStatus, arrayView1d< globalIndex const > const & wellElemDofNumber, arrayView1d< real64 const > const & wellElemGravCoef, @@ -295,6 +292,7 @@ struct PresTempCompFracInitializationKernel arrayView1d< localIndex const > const & resElementIndex, arrayView1d< real64 const > const & perfGravCoef, arrayView1d< integer const > const & perfState, + arrayView1d< localIndex const > const perfWellElemIndex, arrayView1d< real64 const > const & wellElemGravCoef, arrayView1d< real64 > const & wellElemPres, arrayView1d< real64 > const & wellElemTemp, @@ -323,7 +321,6 @@ struct RateInitializationKernel static void launch( localIndex const subRegionSize, - integer const targetPhaseIndex, WellControls const & wellControls, real64 const & currentTime, arrayView3d< real64 const, constitutive::multifluid::USD_PHASE > const & phaseDens, @@ -505,7 +502,6 @@ class ResidualNormKernel : public physicsSolverBaseKernels::ResidualNormKernelBa arrayView1d< localIndex const > const & ghostRank, integer const numComp, integer const numDof, - integer const targetPhaseIndex, WellElementSubRegion const & subRegion, constitutive::MultiFluidBase const & fluid, WellControls const & wellControls, @@ -519,20 +515,42 @@ class ResidualNormKernel : public physicsSolverBaseKernels::ResidualNormKernelBa minNormalizer ), m_numComp( numComp ), m_numDof( numDof ), - m_targetPhaseIndex( targetPhaseIndex ), m_dt( dt ), m_isLocallyOwned( subRegion.isLocallyOwned() ), m_iwelemControl( subRegion.getTopWellElementIndex() ), m_isProducer( wellControls.isProducer() ), m_currentControl( wellControls.getControl() ), - m_targetBHP( wellControls.getTargetBHP( time ) ), - m_targetTotalRate( wellControls.getTargetTotalRate( time ) ), - m_targetPhaseRate( wellControls.getTargetPhaseRate( time ) ), - m_targetMassRate( wellControls.getTargetMassRate( time ) ), m_volume( subRegion.getElementVolume() ), m_phaseDens_n( fluid.phaseDensity_n() ), m_totalDens_n( fluid.totalDensity_n() ) - {} + { + if( m_isProducer ) + { + if( wellControls.getMinBHPConstraint()->isConstraintActive()) + m_targetBHP = wellControls.getMinBHPConstraint()->getConstraintValue( time ); + // Note this assumes that there is only one rate constraint + // This is a normalizer for the balance equations. The normalizaer should be the current rate not the constraint value!! + // This is one of the reasons for restricting constraint type for a production well + // another pr will remove fix this (so the cause for difference results is isolated to one change) + m_targetPhaseIndex = wellControls.getConstraintPhaseIndex( ); + m_constraintValue = wellControls.getProdRateConstraints()[0]->getConstraintValue( time ); + + } + else + { + m_targetBHP = wellControls.getMaxBHPConstraint()->getConstraintValue( time ); + + // tjbNote this assumes that there is only one rate constraint + // This is a normalizer for the balance equations. The normalizaer should be the current rate not the constraint value!! + // This is one of the reasons for restricting constraint type for a production well + // another pr will remove fix this (so the cause for difference results is isolated to one change) + m_targetPhaseIndex = -1; + m_constraintValue = wellControls.getInjRateConstraints()[0]->getConstraintValue( time ); + + } + + + } GEOS_HOST_DEVICE virtual void computeLinf( localIndex const iwelem, @@ -561,17 +579,17 @@ class ResidualNormKernel : public physicsSolverBaseKernels::ResidualNormKernelBa else if( m_currentControl == WellControls::Control::TOTALVOLRATE ) { // the residual entry is in volume / time units - normalizer = LvArray::math::max( LvArray::math::abs( m_targetTotalRate ), m_minNormalizer ); + normalizer = LvArray::math::max( LvArray::math::abs( m_constraintValue ), m_minNormalizer ); } else if( m_currentControl == WellControls::Control::PHASEVOLRATE ) { // the residual entry is in volume / time units - normalizer = LvArray::math::max( LvArray::math::abs( m_targetPhaseRate ), m_minNormalizer ); + normalizer = LvArray::math::max( LvArray::math::abs( m_constraintValue ), m_minNormalizer ); } else if( m_currentControl == WellControls::Control::MASSRATE ) { // the residual entry is in volume / time units - normalizer = LvArray::math::max( LvArray::math::abs( m_targetMassRate ), m_minNormalizer ); + normalizer = LvArray::math::max( LvArray::math::abs( m_constraintValue ), m_minNormalizer ); } } // for the pressure difference equation, always normalize by the BHP @@ -586,18 +604,18 @@ class ResidualNormKernel : public physicsSolverBaseKernels::ResidualNormKernelBa if( m_isProducer ) // only PHASEVOLRATE is supported for now { // the residual is in mass units - normalizer = m_dt * LvArray::math::abs( m_targetPhaseRate ) * m_phaseDens_n[iwelem][0][m_targetPhaseIndex]; + normalizer = m_dt * LvArray::math::abs( m_constraintValue ) * m_phaseDens_n[iwelem][0][m_targetPhaseIndex]; } else // Type::INJECTOR, only TOTALVOLRATE is supported for now { if( m_currentControl == WellControls::Control::MASSRATE ) { - normalizer = m_dt * LvArray::math::abs( m_targetMassRate ); + normalizer = m_dt * LvArray::math::abs( m_constraintValue ); } else { // the residual is in mass units - normalizer = m_dt * LvArray::math::abs( m_targetTotalRate ) * m_totalDens_n[iwelem][0]; + normalizer = m_dt * LvArray::math::abs( m_constraintValue ) * m_totalDens_n[iwelem][0]; } } @@ -611,17 +629,17 @@ class ResidualNormKernel : public physicsSolverBaseKernels::ResidualNormKernelBa if( m_isProducer ) // only PHASEVOLRATE is supported for now { // the residual is in volume units - normalizer = m_dt * LvArray::math::abs( m_targetPhaseRate ); + normalizer = m_dt * LvArray::math::abs( m_constraintValue ); } else // Type::INJECTOR, only TOTALVOLRATE is supported for now { if( m_currentControl == WellControls::Control::MASSRATE ) { - normalizer = m_dt * LvArray::math::abs( m_targetMassRate/ m_totalDens_n[iwelem][0] ); + normalizer = m_dt * LvArray::math::abs( m_constraintValue/ m_totalDens_n[iwelem][0] ); } else { - normalizer = m_dt * LvArray::math::abs( m_targetTotalRate ); + normalizer = m_dt * LvArray::math::abs( m_constraintValue ); } } @@ -658,7 +676,7 @@ class ResidualNormKernel : public physicsSolverBaseKernels::ResidualNormKernelBa integer const m_numDof; /// Index of the target phase - integer const m_targetPhaseIndex; + integer m_targetPhaseIndex; /// Time step size real64 const m_dt; @@ -674,10 +692,9 @@ class ResidualNormKernel : public physicsSolverBaseKernels::ResidualNormKernelBa /// Controls WellControls::Control const m_currentControl; - real64 const m_targetBHP; - real64 const m_targetTotalRate; - real64 const m_targetPhaseRate; - real64 const m_targetMassRate; + real64 m_constraintValue; + real64 m_targetBHP; + /// View on the volume arrayView1d< real64 const > const m_volume; @@ -700,7 +717,6 @@ class ResidualNormKernelFactory * @tparam POLICY the policy used in the RAJA kernel * @param[in] numComp number of fluid components * @param[in] numDof number of dofs per well element - * @param[in] targetPhaseIndex the index of the target phase (for phase volume control) * @param[in] rankOffset the offset of my MPI rank * @param[in] dofKey the string key to retrieve the degress of freedom numbers * @param[in] localResidual the residual vector on my MPI rank @@ -715,7 +731,6 @@ class ResidualNormKernelFactory static void createAndLaunch( integer const numComp, integer const numDof, - integer const targetPhaseIndex, globalIndex const rankOffset, string const & dofKey, arrayView1d< real64 const > const & localResidual, @@ -731,7 +746,7 @@ class ResidualNormKernelFactory arrayView1d< integer const > const ghostRank = subRegion.ghostRank(); ResidualNormKernel kernel( rankOffset, localResidual, dofNumber, ghostRank, - numComp, numDof, targetPhaseIndex, subRegion, fluid, wellControls, time, dt, minNormalizer ); + numComp, numDof, subRegion, fluid, wellControls, time, dt, minNormalizer ); ResidualNormKernel::launchLinf< POLICY >( subRegion.size(), kernel, residualNorm ); } @@ -830,6 +845,7 @@ class ElementBasedAssemblyKernel * @param[inout] localRhs the local right-hand side vector */ ElementBasedAssemblyKernel( localIndex const numPhases, + bool const thermalEffectsEnabled, integer const isProducer, globalIndex const rankOffset, string const dofKey, @@ -839,6 +855,7 @@ class ElementBasedAssemblyKernel arrayView1d< real64 > const & localRhs, BitFlags< isothermalCompositionalMultiphaseBaseKernels::KernelFlags > const kernelFlags ) : m_numPhases( numPhases ), + m_thermalEffectsEnabled( thermalEffectsEnabled ), m_isProducer( isProducer ), m_rankOffset( rankOffset ), m_iwelemControl( subRegion.getTopWellElementIndex() ), @@ -1120,7 +1137,20 @@ class ElementBasedAssemblyKernel if constexpr ( IS_THERMAL) { - if( ei == m_iwelemControl && !m_isProducer ) + if( !m_thermalEffectsEnabled ) + { + for( integer i=0; i < numComp+1+IS_THERMAL; i++ ) + { + stack.localJacobian[numRows-1][i] = 0.0; + } + // constant Temperature + for( integer i=0; i < numComp+1+IS_THERMAL; i++ ) + stack.localJacobian[i][numRows-1] = 0.0; + stack.localJacobian[numRows-1][numRows-1] = 1.0; + + stack.localResidual[numRows-1]=0.0; + } + else if( ei == m_iwelemControl && !m_isProducer ) { // For top segment energy balance eqn replaced with T(n+1) - T = 0 // No other energy balance derivatives @@ -1137,8 +1167,9 @@ class ElementBasedAssemblyKernel stack.localResidual[numRows-1]=0.0; } - } + + } if( m_kernelFlags.isSet( isothermalCompositionalMultiphaseBaseKernels::KernelFlags::TotalMassEquation ) ) { // apply equation/variable change transformation to the component mass balance equations @@ -1197,6 +1228,8 @@ class ElementBasedAssemblyKernel /// Number of fluid phases integer const m_numPhases; + /// Flag indicating whether thermal effects are enabled + bool const m_thermalEffectsEnabled; /// Well type integer const m_isProducer; @@ -1276,6 +1309,7 @@ class ElementBasedAssemblyKernelFactory static void createAndLaunch( localIndex const numComps, localIndex const numPhases, + WellControls const & wellControls, integer const isProducer, globalIndex const rankOffset, BitFlags< isothermalCompositionalMultiphaseBaseKernels::KernelFlags > kernelFlags, @@ -1292,7 +1326,7 @@ class ElementBasedAssemblyKernelFactory integer constexpr istherm = IS_THERMAL(); ElementBasedAssemblyKernel< NUM_COMP, istherm > - kernel( numPhases, isProducer, rankOffset, dofKey, subRegion, fluid, localMatrix, localRhs, kernelFlags ); + kernel( numPhases, wellControls.thermalEffectsEnabled(), isProducer, rankOffset, dofKey, subRegion, fluid, localMatrix, localRhs, kernelFlags ); ElementBasedAssemblyKernel< NUM_COMP, istherm >::template launch< POLICY, ElementBasedAssemblyKernel< NUM_COMP, istherm > >( subRegion.size(), kernel ); } ); @@ -1607,13 +1641,11 @@ class FaceBasedAssemblyKernel // derivative with respect to upstream pressure stack.localFluxJacobian[TAG::NEXT * NC +ic][CP_Deriv::dP] = dt * dCompFlux[ic][WJ_COFFSET::dP]; stack.localFluxJacobian[TAG::CURRENT * NC+ ic][CP_Deriv::dP] = -dt * dCompFlux[ic][WJ_COFFSET::dP]; - if constexpr ( IS_THERMAL ) { stack.localFluxJacobian[TAG::NEXT * NC +ic][CP_Deriv::dT] = dt * dCompFlux[ic][WJ_COFFSET::dT]; stack.localFluxJacobian[TAG::CURRENT * NC +ic][CP_Deriv::dT] = -dt * dCompFlux[ic][WJ_COFFSET::dT]; } - // derivatives with respect to upstream component densities for( integer jdof = 0; jdof < NC; ++jdof ) { diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/PerforationFluxKernels.hpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/PerforationFluxKernels.hpp index 18a48ee1e63..e69615be8b9 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/PerforationFluxKernels.hpp +++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/PerforationFluxKernels.hpp @@ -218,7 +218,6 @@ class PerforationFluxKernel multiplier[TAG::WELL] = -1.0; real64 const gravD = ( m_perfGravCoef[iperf] - m_wellElemGravCoef[iwelem] ); - pres[TAG::WELL] += m_wellElemTotalMassDens[iwelem] * gravD; // Note LHS uses CP_Deriv while RHS uses Deriv !!! dPres[TAG::WELL][CP_Deriv::dP] += m_dWellElemTotalMassDens[iwelem][Deriv::dP] * gravD; @@ -332,7 +331,6 @@ class PerforationFluxKernel // increment component fluxes for( integer ic = 0; ic < NC; ++ic ) { - // Note this needs to be uncommented out m_compPerfRate[iperf][ic] += flux * m_resPhaseCompFrac[er][esr][ei][0][ip][ic]; dCompFrac[CP_Deriv::dP] = m_dResPhaseCompFrac[er][esr][ei][0][ip][ic][Deriv::dP]; if constexpr (IS_THERMAL) @@ -353,10 +351,6 @@ class PerforationFluxKernel m_dCompPerfRate[iperf][TAG::WELL][ic][jc] += dFlux[TAG::WELL][jc] * m_resPhaseCompFrac[er][esr][ei][0][ip][ic]; } } - if constexpr ( IS_THERMAL ) - { - fluxKernelOp( iwelem, er, esr, ei, ip, potDiff, flux, dFlux ); - } } // end resevoir is upstream phase loop @@ -480,11 +474,12 @@ class PerforationFluxKernel m_dCompPerfRate[iperf][TAG::WELL][ic][CP_Deriv::dC+jc] += m_dWellElemCompFrac_dCompDens[iwelem][ic][jc] * flux; } } - if constexpr ( IS_THERMAL ) - { - fluxKernelOp( iwelem, er, esr, ei, -1, potDiff, flux, dFlux ); - } + } // end upstream + if constexpr ( IS_THERMAL ) + { + fluxKernelOp( iwelem, er, esr, ei, potDiff, dPotDiff, flux, dFlux ); + } } /** * @brief Performs the kernel launch @@ -569,7 +564,7 @@ class PerforationFluxKernelFactory string const flowSolverName, PerforationData * const perforationData, ElementSubRegionBase const & subRegion, - ElementRegionManager & elemManager, + ElementRegionManager const & elemManager, bool const isInjector, bool const isCrossflowEnabled ) { @@ -607,9 +602,21 @@ class PerforationFluxKernel : public isothermalPerforationFluxKernels::Perforati public: using Base = isothermalPerforationFluxKernels::PerforationFluxKernel< NC, NP, IS_THERMAL >; - using Base::m_resPhaseCompFrac; - using Base::m_dResCompFrac_dCompDens; using Base::m_dWellElemCompFrac_dCompDens; + using Base::m_resPres; + using Base::m_resPhaseVolFrac; + using Base::m_dResPhaseVolFrac; + using Base::m_dResCompFrac_dCompDens; + using Base::m_resPhaseDens; + using Base::m_dResPhaseDens; + using Base::m_resPhaseVisc; + using Base::m_dResPhaseVisc; + using Base::m_resPhaseCompFrac; + using Base::m_dResPhaseCompFrac; + using Base::m_resPhaseRelPerm; + using Base::m_dResPhaseRelPerm_dPhaseVolFrac; + using Base::m_isInjector; + using Base::m_isCrossflowEnabled; /// Compile time value for the number of components static constexpr integer numComp = NC; @@ -631,6 +638,8 @@ class PerforationFluxKernel : public isothermalPerforationFluxKernels::Perforati using ThermalMultiFluidAccessors = StencilMaterialAccessors< MultiFluidBase, + fields::multifluid::phaseFraction, + fields::multifluid::dPhaseFraction, fields::multifluid::phaseEnthalpy, fields::multifluid::dPhaseEnthalpy >; @@ -645,7 +654,7 @@ class PerforationFluxKernel : public isothermalPerforationFluxKernels::Perforati using ElementViewConst = ElementRegionManager::ElementViewConst< VIEWTYPE >; PerforationFluxKernel ( PerforationData * const perforationData, - ElementSubRegionBase const & subRegion, + ElementSubRegionBase & subRegion, MultiFluidBase const & wellFluid, CompFlowAccessors const & compFlowAccessors, MultiFluidAccessors const & multiFluidAccessors, @@ -668,6 +677,8 @@ class PerforationFluxKernel : public isothermalPerforationFluxKernels::Perforati m_energyPerfFlux( perforationData->getField< fields::well::energyPerforationFlux >() ), m_dEnergyPerfFlux( perforationData->getField< fields::well::dEnergyPerforationFlux >() ), m_temp( thermalCompFlowAccessors.get( fields::flow::temperature {} ) ), + m_resPhaseFraction( thermalMultiFluidAccessors.get( fields::multifluid::phaseFraction {} ) ), + m_dResPhaseFraction( thermalMultiFluidAccessors.get( fields::multifluid::dPhaseFraction {} )), m_resPhaseEnthalpy( thermalMultiFluidAccessors.get( fields::multifluid::phaseEnthalpy {} ) ), m_dResPhaseEnthalpy( thermalMultiFluidAccessors.get( fields::multifluid::dPhaseEnthalpy {} ) ) {} @@ -690,41 +701,125 @@ class PerforationFluxKernel : public isothermalPerforationFluxKernels::Perforati } } - Base::computeFlux ( iperf, [&]( localIndex const iwelem, localIndex const er, localIndex const esr, localIndex const ei, localIndex const ip, - real64 const potDiff, real64 const flux, real64 const (&dFlux)[2][CP_Deriv::nDer] ) + Base::computeFlux ( iperf, [&]( localIndex const iwelem, localIndex const er, localIndex const esr, localIndex const ei + , real64 const potDiff, real64 const dPotDiff[2][CP_Deriv::nDer], + real64 const flux, real64 const (&dFlux)[2][CP_Deriv::nDer] ) { + real64 dMob[CP_Deriv::nDer]{}; + GEOS_UNUSED_VAR( iwelem ); if( potDiff >= 0 ) // ** reservoir cell is upstream ** { + real64 eflux = 0.0; + // loop over phases, compute and upwind phase flux + // and sum contributions to each component's perforation rate + for( integer ip = 0; ip < NP; ++ip ) + { + // skip the rest of the calculation if the phase is absent + // or if crossflow is disabled for injectors + real64 const resPhaseVolFrac = m_resPhaseVolFrac[er][esr][ei][ip]; + bool const phaseExists = (resPhaseVolFrac > 0 ); + if( !phaseExists || (m_isInjector && !m_isCrossflowEnabled) ) + { + continue; + } + + // here, we have to recompute the reservoir phase mobility (not including density) + + // density + real64 const resDens = m_resPhaseDens[er][esr][ei][0][ip]; + real64 dDens[CP_Deriv::nDer]{}; + dDens[CP_Deriv::dP] = m_dResPhaseDens[er][esr][ei][0][ip][Deriv::dP]; + dDens[CP_Deriv::dT] = m_dResPhaseDens[er][esr][ei][0][ip][Deriv::dT]; + applyChainRule( NC, m_dResCompFrac_dCompDens[er][esr][ei], + m_dResPhaseDens[er][esr][ei][0][ip], + &dDens[CP_Deriv::dC], + Deriv::dC ); + // viscosity + real64 const resVisc = m_resPhaseVisc[er][esr][ei][0][ip]; + real64 dVisc[CP_Deriv::nDer]{}; + dVisc[CP_Deriv::dP] = m_dResPhaseVisc[er][esr][ei][0][ip][Deriv::dP]; + dVisc[CP_Deriv::dT] = m_dResPhaseVisc[er][esr][ei][0][ip][Deriv::dT]; + applyChainRule( NC, m_dResCompFrac_dCompDens[er][esr][ei], + m_dResPhaseVisc[er][esr][ei][0][ip], + &dVisc[CP_Deriv::dC], + Deriv::dC ); + // enthalpy + real64 const resEnthalpy = m_resPhaseEnthalpy[er][esr][ei][0][ip]; + real64 dResEnthalpy[CP_Deriv::nDer]{}; + dResEnthalpy[CP_Deriv::dP] = m_dResPhaseEnthalpy[er][esr][ei][0][ip][Deriv::dP]; + dResEnthalpy[CP_Deriv::dT] = m_dResPhaseEnthalpy[er][esr][ei][0][ip][Deriv::dT]; + + applyChainRule( NC, m_dResCompFrac_dCompDens[er][esr][ei], + m_dResPhaseEnthalpy[er][esr][ei][0][ip], + &dResEnthalpy[CP_Deriv::dC], + Deriv::dC ); - real64 const res_enthalpy = m_resPhaseEnthalpy[er][esr][ei][0][ip]; + // relative permeability + real64 const resRelPerm = m_resPhaseRelPerm[er][esr][ei][0][ip]; + real64 dRelPerm[CP_Deriv::nDer]{}; + for( integer jc = 0; jc < CP_Deriv::nDer; ++jc ) + { + dRelPerm[jc]=0; + } + for( integer jp = 0; jp < NP; ++jp ) + { + real64 const dResRelPerm_dS = m_dResPhaseRelPerm_dPhaseVolFrac[er][esr][ei][0][ip][jp]; + dRelPerm[CP_Deriv::dP] += dResRelPerm_dS * m_dResPhaseVolFrac[er][esr][ei][jp][Deriv::dP]; + dRelPerm[CP_Deriv::dT] += dResRelPerm_dS * m_dResPhaseVolFrac[er][esr][ei][jp][Deriv::dT]; + for( integer jc = 0; jc < NC; ++jc ) + { + dRelPerm[CP_Deriv::dC+jc] += dResRelPerm_dS * m_dResPhaseVolFrac[er][esr][ei][jp][Deriv::dC+jc]; + } + } - m_energyPerfFlux[iperf] += flux * res_enthalpy; + // compute the reservoir phase mobility, including phase density and enthalpy + real64 const resPhaseMob = resDens * resRelPerm / resVisc; + real64 const resPhaseMobE = resEnthalpy * resPhaseMob; - // energy equation derivatives WRT res P & T - m_dEnergyPerfFlux[iperf][TAG::RES][CP_Deriv::dP] += dFlux[TAG::RES][CP_Deriv::dP] * res_enthalpy + - flux * m_dResPhaseEnthalpy[er][esr][ei][0][ip][Deriv::dP]; - m_dEnergyPerfFlux[iperf][TAG::RES][CP_Deriv::dT] += dFlux[TAG::RES][CP_Deriv::dT] * res_enthalpy + - flux * m_dResPhaseEnthalpy[er][esr][ei][0][ip][Deriv::dT]; - // energy equation derivatives WRT well P - m_dEnergyPerfFlux[iperf][TAG::WELL][CP_Deriv::dP] += dFlux[TAG::WELL][CP_Deriv::dP] * res_enthalpy; - m_dEnergyPerfFlux[iperf][TAG::WELL][CP_Deriv::dT] += dFlux[TAG::WELL][CP_Deriv::dT] * res_enthalpy; + // Handles all dependencies + for( integer jc = 0; jc < CP_Deriv::nDer; ++jc ) + { + dMob[jc] = resPhaseMob*dResEnthalpy[jc] + + resPhaseMobE*(dRelPerm[jc]/(resRelPerm+0.000000001) + dDens[jc] / (resDens +0.000000001) - dVisc[jc]/(resVisc+0.000000001)); + } + // compute the phase flux and derivatives using upstream cell mobility + eflux = resPhaseMobE * potDiff; + real64 dEFlux[2][CP_Deriv::nDer]{}; + // Handles all dependencies + for( integer jc = 0; jc < CP_Deriv::nDer; ++jc ) + { + dEFlux[TAG::RES][jc] = dMob[jc] * potDiff + resPhaseMobE * dPotDiff[TAG::RES][jc]; + m_dEnergyPerfFlux[iperf][TAG::WELL][jc] = resPhaseMobE * dPotDiff[TAG::WELL][jc]; + } + m_energyPerfFlux[iperf] += resPhaseVolFrac * eflux; + // energy equation derivatives WRT res P & T + m_dEnergyPerfFlux[iperf][TAG::RES][CP_Deriv::dP] += dEFlux[TAG::RES][CP_Deriv::dP] * resPhaseVolFrac + + eflux * m_dResPhaseFraction[er][esr][ei][0][ip][Deriv::dP]; + m_dEnergyPerfFlux[iperf][TAG::RES][CP_Deriv::dT] += dEFlux[TAG::RES][CP_Deriv::dT] * resPhaseVolFrac + + eflux *m_dResPhaseFraction[er][esr][ei][0][ip][Deriv::dT]; + // energy equation derivatives WRT well P - //tjb + m_dEnergyPerfFlux[iperf][TAG::WELL][CP_Deriv::dP] += dEFlux[TAG::WELL][CP_Deriv::dP]; + m_dEnergyPerfFlux[iperf][TAG::WELL][CP_Deriv::dT] += dEFlux[TAG::WELL][CP_Deriv::dT]; + real64 dProp_dC[numComp]{}; + applyChainRule( NC, + m_dResCompFrac_dCompDens[er][esr][ei], + m_dResPhaseFraction[er][esr][ei][0][ip], + dProp_dC, + Deriv::dC ); + for( integer jc = 0; jc < NC; ++jc ) + { + real64 const resPhaseCompFrac = m_resPhaseCompFrac[er][esr][ei][0][ip][jc]; + m_dEnergyPerfFlux[iperf][TAG::RES][CP_Deriv::dC+jc] += eflux * dProp_dC[jc] + + resPhaseCompFrac * dFlux[TAG::RES][CP_Deriv::dC+jc]; + } - // energy equation derivatives WRT reservoir dens - real64 dProp_dC[numComp]{}; - applyChainRule( NC, - m_dResCompFrac_dCompDens[er][esr][ei], - m_dResPhaseEnthalpy[er][esr][ei][0][ip], - dProp_dC, - Deriv::dC ); + } // end resevoir is upstream phase loop - for( integer jc = 0; jc < NC; ++jc ) - { - m_dEnergyPerfFlux[iperf][TAG::RES][CP_Deriv::dC+jc] += flux * dProp_dC[jc]; - } } else // ** reservoir cell is downstream { + for( integer iphase = 0; iphase < NP; ++iphase ) { bool const phaseExists = m_wellElemPhaseFrac[iwelem][0][iphase] > 0.0; @@ -814,6 +909,8 @@ class PerforationFluxKernel : public isothermalPerforationFluxKernels::Perforati ElementViewConst< arrayView1d< real64 const > > const m_temp; /// Views on phase enthalpies + ElementViewConst< arrayView3d< real64 const, multifluid::USD_PHASE > > const m_resPhaseFraction; + ElementViewConst< arrayView4d< real64 const, multifluid::USD_PHASE_DC > > const m_dResPhaseFraction; ElementViewConst< arrayView3d< real64 const, multifluid::USD_PHASE > > const m_resPhaseEnthalpy; ElementViewConst< arrayView4d< real64 const, multifluid::USD_PHASE_DC > > const m_dResPhaseEnthalpy; @@ -846,9 +943,9 @@ class PerforationFluxKernelFactory integer const numPhases, string const flowSolverName, PerforationData * const perforationData, - ElementSubRegionBase const & subRegion, + ElementSubRegionBase & subRegion, MultiFluidBase const & fluid, - ElementRegionManager & elemManager, + ElementRegionManager const & elemManager, bool const isInjector, bool const isCrossflowEnabled ) { diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/SinglePhaseWellConstraintKernels.hpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/SinglePhaseWellConstraintKernels.hpp new file mode 100644 index 00000000000..a7f7af725ce --- /dev/null +++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/SinglePhaseWellConstraintKernels.hpp @@ -0,0 +1,195 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * SPDX-License-Identifier: LGPL-2.1-only + * + * Copyright (c) 2016-2024 Lawrence Livermore National Security LLC + * Copyright (c) 2018-2024 TotalEnergies + * Copyright (c) 2018-2024 The Board of Trustees of the Leland Stanford Junior University + * Copyright (c) 2023-2024 Chevron + * Copyright (c) 2019- GEOS/GEOSX Contributors + * All rights reserved + * + * See top level LICENSE, COPYRIGHT, CONTRIBUTORS, NOTICE, and ACKNOWLEDGEMENTS files for details. + * ------------------------------------------------------------------------------------------------------------ + */ + +/** + * @file SinglePhaseWellConstraintKernels.hpp + */ + +#ifndef GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_SINGLEPHASEWELLCONSTRAINTKERNELS_HPP +#define GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_SINGLEPHASEWELLCONSTRAINTKERNELS_HPP + +#include "codingUtilities/Utilities.hpp" +#include "constitutive/fluid/singlefluid/SingleFluidBase.hpp" +#include "constitutive/fluid/singlefluid/SingleFluidFields.hpp" + + +#include "physicsSolvers/fluidFlow/wells/WellControls.hpp" +#include "physicsSolvers/fluidFlow/wells/WellBHPConstraints.hpp" +#include "physicsSolvers/fluidFlow/wells/WellVolumeRateConstraint.hpp" + + +namespace geos +{ + +namespace singlePhaseWellConstraintKernels +{ + +/******************************** ControlEquationHelper ********************************/ + + +template< integer IS_THERMAL > +struct ConstraintHelper +{ + static void assembleConstraintEquation( real64 const & time_n, + WellControls & wellControls, + BHPConstraint & constraint, + WellElementSubRegion const & subRegion, + string const & wellDofKey, + localIndex const & rankOffset, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) + { + // subRegion data + localIndex const iwelemRef = subRegion.getTopWellElementIndex(); + arrayView1d< globalIndex const > const & wellElemDofNumber = subRegion.getReference< array1d< globalIndex > >( wellDofKey ); + arrayView1d< real64 const > const & pres = subRegion.getField< fields::well::pressure >(); + + constitutive::SingleFluidBase & fluidSeparator = wellControls.getSingleFluidSeparator(); + arrayView2d< real64 const, constitutive::singlefluid::USD_FLUID > const & density = fluidSeparator.density(); + arrayView3d< real64 const, constitutive::singlefluid::USD_FLUID_DER > const & dDensity = fluidSeparator.dDensity(); + + arrayView1d< real64 const > const wellElemGravCoef = subRegion.getField< fields::well::gravityCoefficient >(); + + // setup row/column indices for constraint equation + using ROFFSET_WJ = singlePhaseWellKernels::RowOffset_WellJac< IS_THERMAL >; + using COFFSET_WJ = singlePhaseWellKernels::ColOffset_WellJac< IS_THERMAL >; + using Deriv = constitutive::singlefluid::DerivativeOffsetC< IS_THERMAL >; + + localIndex const eqnRowIndex = wellElemDofNumber[iwelemRef] + ROFFSET_WJ::CONTROL - rankOffset; + globalIndex dofColIndices[COFFSET_WJ::nDer]{}; + for( integer i = 0; i < COFFSET_WJ::nDer; ++i ) + { + dofColIndices[ i ] = wellElemDofNumber[iwelemRef] + i; + } + // constraint data + real64 const & targetBHP = constraint.getConstraintValue( time_n ); + real64 const & refGravCoef = constraint.getReferenceGravityCoef(); + + // current constraint value + real64 const & currentBHP = + wellControls.getReference< real64 >( SinglePhaseWell::viewKeyStruct::currentBHPString() ); + + // residual + real64 controlEqn = currentBHP - targetBHP; + + // setup Jacobian terms + real64 dControlEqn[2+IS_THERMAL]{}; + + // bring everything back to host, capture the scalars by reference + forAll< serialPolicy >( 1, [pres, + density, + dDensity, + wellElemGravCoef, + &dControlEqn, + &iwelemRef, + &refGravCoef] ( localIndex const ) + { + real64 const diffGravCoef = refGravCoef - wellElemGravCoef[iwelemRef]; + dControlEqn[COFFSET_WJ::dP] = 1.0 + dDensity[iwelemRef][0][Deriv::dP] *diffGravCoef; + if constexpr ( IS_THERMAL ) + { + dControlEqn[COFFSET_WJ::dT] = dDensity[iwelemRef][0][Deriv::dT] * diffGravCoef; + } + } ); + + // add solver matrices + localRhs[eqnRowIndex] += controlEqn; + localMatrix.addToRowBinarySearchUnsorted< serialAtomic >( eqnRowIndex, + dofColIndices, + dControlEqn, + COFFSET_WJ::nDer ); + } + template< template< typename U > class T, typename U=VolumeRateConstraint > + static void assembleConstraintEquation( real64 const & time_n, + WellControls & wellControls, + T< VolumeRateConstraint > & constraint, + WellElementSubRegion const & subRegion, + string const & wellDofKey, + localIndex const & rankOffset, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) + { + // subRegion data + + localIndex const iwelemRef = subRegion.getTopWellElementIndex(); + arrayView1d< globalIndex const > const & wellElemDofNumber = subRegion.getReference< array1d< globalIndex > >( wellDofKey ); + + + // setup row/column indices for constraint equation + using ROFFSET_WJ = singlePhaseWellKernels::RowOffset_WellJac< IS_THERMAL >; + using COFFSET_WJ = singlePhaseWellKernels::ColOffset_WellJac< IS_THERMAL >; + using Deriv = constitutive::singlefluid::DerivativeOffsetC< IS_THERMAL >; + + localIndex const eqnRowIndex = wellElemDofNumber[iwelemRef] + ROFFSET_WJ::CONTROL - rankOffset; + globalIndex dofColIndices[COFFSET_WJ::nDer]{}; + for( integer i = 0; i < COFFSET_WJ::nDer; ++i ) + { + dofColIndices[ i ] = wellElemDofNumber[iwelemRef] + i; + } + + // fluid data + constitutive::SingleFluidBase & fluidSeparator = wellControls.getSingleFluidSeparator(); + arrayView2d< real64 const, constitutive::singlefluid::USD_FLUID > const & density = fluidSeparator.density(); + arrayView3d< real64 const, constitutive::singlefluid::USD_FLUID_DER > const & dDensity = fluidSeparator.dDensity(); + + // constraint data + real64 const & targetVolRate = constraint.getConstraintValue( time_n ); + + // current constraint value + real64 & currentVolRate = + wellControls.getReference< real64 >( SinglePhaseWell::viewKeyStruct::currentVolRateString() ); + + integer const useSurfaceConditions = wellControls.useSurfaceConditions(); + + // residual + real64 controlEqn = currentVolRate - targetVolRate; + + // setup Jacobian terms + real64 dControlEqn[2+IS_THERMAL]{}; + + // bring everything back to host, capture the scalars by reference + forAll< serialPolicy >( 1, [currentVolRate, + density, + dDensity, + &dControlEqn, + &useSurfaceConditions, + &iwelemRef] ( localIndex const ) + { + // compute the inverse of the total density and derivatives + real64 const densInv = 1.0 / density[iwelemRef][0]; + + dControlEqn[COFFSET_WJ::dP] = -( useSurfaceConditions == 0 ) * dDensity[iwelemRef][0][Deriv::dP] * currentVolRate * densInv; + dControlEqn[COFFSET_WJ::dQ] = densInv; + if constexpr ( IS_THERMAL ) + { + dControlEqn[COFFSET_WJ::dT] = -( useSurfaceConditions == 0 ) * dDensity[iwelemRef][0][Deriv::dT] * currentVolRate * densInv; + } + + } ); + + // add solver matrices + localRhs[eqnRowIndex] += controlEqn; + localMatrix.addToRowBinarySearchUnsorted< serialAtomic >( eqnRowIndex, + dofColIndices, + dControlEqn, + COFFSET_WJ::nDer ); + } +}; + +} // end namespace wellConstraintKernels + +} // end namespace geos + +#endif //GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_WELLCONSTRAINTKERNELS_HPP diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/SinglePhaseWellKernels.cpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/SinglePhaseWellKernels.cpp index b0e87ffbc63..b7b32a0ecfe 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/SinglePhaseWellKernels.cpp +++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/SinglePhaseWellKernels.cpp @@ -23,6 +23,7 @@ #include "physicsSolvers/fluidFlow/wells/SinglePhaseWell.hpp" #include "physicsSolvers/fluidFlow/wells/SinglePhaseWellFields.hpp" #include "constitutive/fluid/singlefluid/SingleFluidLayouts.hpp" + namespace geos { @@ -262,14 +263,10 @@ FluxKernel:: #define INST_PressureRelationKernel( IS_THERMAL ) \ template \ - localIndex \ + void \ PressureRelationKernel:: \ launch< IS_THERMAL >( localIndex const size, \ globalIndex const rankOffset, \ - bool const isLocallyOwned, \ - localIndex const iwelemControl, \ - WellControls const & wellControls, \ - real64 const & timeAtEndOfStep, \ arrayView1d< globalIndex const > const & wellElemDofNumber, \ arrayView1d< real64 const > const & wellElemGravCoef, \ arrayView1d< localIndex const > const & nextWellElemIndex, \ @@ -283,14 +280,10 @@ INST_PressureRelationKernel( 0 ); INST_PressureRelationKernel( 1 ); template< integer IS_THERMAL > -localIndex +void PressureRelationKernel:: launch( localIndex const size, globalIndex const rankOffset, - bool const isLocallyOwned, - localIndex const iwelemControl, - WellControls const & wellControls, - real64 const & time, arrayView1d< globalIndex const > const & wellElemDofNumber, arrayView1d< real64 const > const & wellElemGravCoef, arrayView1d< localIndex const > const & nextWellElemIndex, @@ -303,23 +296,6 @@ PressureRelationKernel:: using Deriv = constitutive::singlefluid::DerivativeOffset; using COFFSET_WJ = singlePhaseWellKernels::ColOffset_WellJac< IS_THERMAL >; // static well control data - bool const isProducer = wellControls.isProducer(); - WellControls::Control const currentControl = wellControls.getControl(); - real64 const targetBHP = wellControls.getTargetBHP( time ); - real64 const targetRate = wellControls.getTargetTotalRate( time ); - - // dynamic well control data - real64 const & currentBHP = - wellControls.getReference< real64 >( SinglePhaseWell::viewKeyStruct::currentBHPString() ); - arrayView1d< real64 const > const & dCurrentBHP = - wellControls.getReference< array1d< real64 > >( SinglePhaseWell::viewKeyStruct::dCurrentBHPString() ); - - real64 const & currentVolRate = - wellControls.getReference< real64 >( SinglePhaseWell::viewKeyStruct::currentVolRateString() ); - arrayView1d< real64 const > const & dCurrentVolRate = - wellControls.getReference< array1d< real64 > >( SinglePhaseWell::viewKeyStruct::dCurrentVolRateString() ); - - RAJA::ReduceMax< parallelDeviceReduce, localIndex > switchControl( 0 ); // loop over the well elements to compute the pressure relations between well elements forAll< parallelDevicePolicy<> >( size, [=] GEOS_HOST_DEVICE ( localIndex const iwelem ) @@ -327,34 +303,7 @@ PressureRelationKernel:: localIndex const iwelemNext = nextWellElemIndex[iwelem]; - if( iwelemNext < 0 && isLocallyOwned ) // if iwelemNext < 0, form control equation - { - WellControls::Control newControl = currentControl; - ControlEquationHelper::switchControl( isProducer, - currentControl, - targetBHP, - targetRate, - currentBHP, - currentVolRate, - newControl ); - if( currentControl != newControl ) - { - switchControl.max( 1 ); - } - - ControlEquationHelper::compute< IS_THERMAL >( rankOffset, - newControl, - targetBHP, - targetRate, - currentBHP, - dCurrentBHP, - currentVolRate, - dCurrentVolRate, - wellElemDofNumber[iwelemControl], - localMatrix, - localRhs ); - } - else if( iwelemNext >= 0 ) // if iwelemNext >= 0, form momentum equation + if( iwelemNext >= 0 ) // if iwelemNext >= 0, form momentum equation { // local working variables and arrays @@ -406,7 +355,6 @@ PressureRelationKernel:: } } } ); - return switchControl.get(); } /******************************** AccumulationKernel ********************************/ @@ -599,10 +547,22 @@ RateInitializationKernel:: arrayView2d< real64 const, constitutive::singlefluid::USD_FLUID > const & wellElemDens, arrayView1d< real64 > const & connRate ) { - real64 const targetRate = wellControls.getTargetTotalRate( currentTime ); + WellControls::Control const control = wellControls.getControl(); bool const isProducer = wellControls.isProducer(); - + real64 constraintVal; + if( isProducer ) + { + std::vector< WellConstraintBase * > const constraints = wellControls.getProdRateConstraints(); + // Use first rate constraint to set initial connection rates + constraintVal = constraints[0]->getConstraintValue( currentTime ); + } + else + { + std::vector< WellConstraintBase * > const constraints = wellControls.getInjRateConstraints(); + // Use first rate constraint to set initial connection rates + constraintVal = constraints[0]->getConstraintValue( currentTime );; + } // Estimate the connection rates forAll< parallelDevicePolicy<> >( subRegionSize, [=] GEOS_HOST_DEVICE ( localIndex const iwelem ) { @@ -612,16 +572,16 @@ RateInitializationKernel:: // with the appropriate sign (negative for prod, positive for inj) if( isProducer ) { - connRate[iwelem] = LvArray::math::max( 0.1 * targetRate * wellElemDens[iwelem][0], -1e3 ); + connRate[iwelem] = LvArray::math::max( 0.1 * constraintVal * wellElemDens[iwelem][0], -1e3 ); } else { - connRate[iwelem] = LvArray::math::min( 0.1 * targetRate * wellElemDens[iwelem][0], 1e3 ); + connRate[iwelem] = LvArray::math::min( 0.1 * constraintVal * wellElemDens[iwelem][0], 1e3 ); } } else { - connRate[iwelem] = targetRate * wellElemDens[iwelem][0]; + connRate[iwelem] = constraintVal * wellElemDens[iwelem][0]; } } ); } diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/SinglePhaseWellKernels.hpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/SinglePhaseWellKernels.hpp index 5221162c73c..dcb8b61f9be 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/SinglePhaseWellKernels.hpp +++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/SinglePhaseWellKernels.hpp @@ -20,18 +20,21 @@ #ifndef GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_SINGLEPHASEWELLKERNELS_HPP #define GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_SINGLEPHASEWELLKERNELS_HPP +#include "common/DataTypes.hpp" +#include "common/GEOS_RAJA_Interface.hpp" +#include "physicsSolvers/PhysicsSolverBaseKernels.hpp" + #include "constitutive/fluid/singlefluid/SingleFluidFields.hpp" #include "constitutive/fluid/singlefluid/SingleFluidBase.hpp" #include "constitutive/fluid/singlefluid/SingleFluidLayouts.hpp" #include "constitutive/fluid/singlefluid/SingleFluidLayouts.hpp" -#include "common/DataTypes.hpp" -#include "common/GEOS_RAJA_Interface.hpp" + #include "mesh/ElementRegionManager.hpp" #include "physicsSolvers/fluidFlow/FlowSolverBaseFields.hpp" #include "physicsSolvers/fluidFlow/StencilAccessors.hpp" #include "physicsSolvers/fluidFlow/wells/WellControls.hpp" #include "physicsSolvers/fluidFlow/wells/SinglePhaseWellFields.hpp" -#include "physicsSolvers/PhysicsSolverBaseKernels.hpp" + #include "physicsSolvers/KernelLaunchSelectors.hpp" namespace geos @@ -185,13 +188,9 @@ struct PressureRelationKernel using TAG = singlePhaseWellKernels::ElemTag; template< integer IS_THERMAL > - static localIndex + static void launch( localIndex const size, globalIndex const rankOffset, - bool const isLocallyOwned, - localIndex const iwelemControl, - WellControls const & wellControls, - real64 const & time, arrayView1d< globalIndex const > const & wellElemDofNumber, arrayView1d< real64 const > const & wellElemGravCoef, arrayView1d< localIndex const > const & nextWellElemIndex, @@ -811,12 +810,19 @@ class ResidualNormKernel : public physicsSolverBaseKernels::ResidualNormKernelBa m_isLocallyOwned( subRegion.isLocallyOwned() ), m_iwelemControl( subRegion.getTopWellElementIndex() ), m_currentControl( wellControls.getControl() ), - m_targetBHP( wellControls.getTargetBHP( time ) ), - m_targetRate( wellControls.getTargetTotalRate( time ) ), - m_targetMassRate( wellControls.getTargetMassRate( time ) ), + m_constraintValue ( wellControls.getCurrentConstraint()->getConstraintValue( time )), m_volume( subRegion.getElementVolume() ), m_density_n( fluid.density_n() ) - {} + { + if( wellControls.isProducer() ) + { + m_targetBHP = wellControls.getMinBHPConstraint()->getConstraintValue( time ); + } + else + { + m_targetBHP = wellControls.getMaxBHPConstraint()->getConstraintValue( time ); + } + } GEOS_HOST_DEVICE virtual void computeLinf( localIndex const iwelem, @@ -838,12 +844,12 @@ class ResidualNormKernel : public physicsSolverBaseKernels::ResidualNormKernelBa else if( m_currentControl == WellControls::Control::TOTALVOLRATE ) { // this residual entry is in volume / time units - normalizer = LvArray::math::max( LvArray::math::abs( m_targetRate ), m_minNormalizer ); + normalizer = LvArray::math::max( LvArray::math::abs( m_constraintValue ), m_minNormalizer ); } else if( m_currentControl == WellControls::Control::MASSRATE ) { // the residual entry is in volume / time units - normalizer = LvArray::math::max( LvArray::math::abs( m_targetMassRate ), m_minNormalizer ); + normalizer = LvArray::math::max( LvArray::math::abs( m_constraintValue ), m_minNormalizer ); } } // for the pressure difference equation, always normalize by the BHP @@ -856,7 +862,7 @@ class ResidualNormKernel : public physicsSolverBaseKernels::ResidualNormKernelBa else // SinglePhaseWell::RowOffset::MASSBAL { // this residual entry is in mass units - normalizer = m_dt * LvArray::math::abs( m_targetRate ) * m_density_n[iwelem][0]; + normalizer = m_dt * LvArray::math::abs( m_constraintValue ) * m_density_n[iwelem][0]; // to make sure that everything still works well if the rate is zero, we add this check normalizer = LvArray::math::max( normalizer, m_volume[iwelem] * m_density_n[iwelem][0] ); @@ -894,9 +900,9 @@ class ResidualNormKernel : public physicsSolverBaseKernels::ResidualNormKernelBa /// Controls WellControls::Control const m_currentControl; - real64 const m_targetBHP; - real64 const m_targetRate; - real64 const m_targetMassRate; + real64 const m_constraintValue; + real64 m_targetBHP; + /// View on the volume arrayView1d< real64 const > const m_volume; diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/ThermalCompositionalMultiphaseWellKernels.hpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/ThermalCompositionalMultiphaseWellKernels.hpp index 54b7819e8bf..fe99ea6828e 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/ThermalCompositionalMultiphaseWellKernels.hpp +++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/ThermalCompositionalMultiphaseWellKernels.hpp @@ -22,6 +22,8 @@ #include "physicsSolvers/fluidFlow/wells/kernels/CompositionalMultiphaseWellKernels.hpp" #include "physicsSolvers/PhysicsSolverBaseKernels.hpp" +#include "physicsSolvers/fluidFlow/wells/WellConstraintsBase.hpp" +#include "physicsSolvers/fluidFlow/wells/WellPhaseVolumeRateConstraint.hpp" namespace geos { @@ -80,7 +82,7 @@ class TotalMassDensityKernel : public compositionalMultiphaseWellKernels::TotalM arraySlice2d< real64 const, multifluid::USD_PHASE_DC - 2 > dPhaseMassDens = m_dPhaseMassDens[ei][0]; real64 & dTotalMassDens_dT = m_dTotalMassDens[ei][Deriv::dT]; - dTotalMassDens_dT=0.0; + // Call the base compute the compute the total mass density and derivatives return Base::compute( ei, [&]( localIndex const ip ) { @@ -161,7 +163,7 @@ class ResidualNormKernel : public physicsSolverBaseKernels::ResidualNormKernelBa arrayView1d< real64 const > const & localResidual, arrayView1d< globalIndex const > const & dofNumber, arrayView1d< localIndex const > const & ghostRank, - integer const targetPhaseIndex, + WellElementSubRegion const & subRegion, MultiFluidBase const & fluid, WellControls const & wellControls, @@ -174,22 +176,39 @@ class ResidualNormKernel : public physicsSolverBaseKernels::ResidualNormKernelBa ghostRank, minNormalizer ), m_numPhases( fluid.numFluidPhases()), - m_targetPhaseIndex( targetPhaseIndex ), m_dt( dt ), m_isLocallyOwned( subRegion.isLocallyOwned() ), m_iwelemControl( subRegion.getTopWellElementIndex() ), m_isProducer( wellControls.isProducer() ), m_currentControl( wellControls.getControl() ), - m_targetBHP( wellControls.getTargetBHP( time ) ), - m_targetTotalRate( wellControls.getTargetTotalRate( time ) ), - m_targetPhaseRate( wellControls.getTargetPhaseRate( time ) ), - m_targetMassRate( wellControls.getTargetMassRate( time ) ), m_volume( subRegion.getElementVolume() ), m_phaseDens_n( fluid.phaseDensity_n() ), m_totalDens_n( fluid.totalDensity_n() ), m_phaseVolFraction_n( subRegion.getField< fields::well::phaseVolumeFraction_n >()), m_phaseInternalEnergy_n( fluid.phaseInternalEnergy_n() ) - {} + { + if( m_isProducer ) + { + // tjb This needs to be fixed should use current constraint rate for normalization + if( wellControls.getMinBHPConstraint()->isConstraintActive() ) + { + m_targetBHP = wellControls.getMinBHPConstraint()->getConstraintValue( time ); + } + if( m_currentControl == WellControls::Control::PHASEVOLRATE ) + { + m_targetPhaseIndex = wellControls.getConstraintPhaseIndex(); + m_constraintValue = wellControls.getProdRateConstraints()[0]->getConstraintValue( time ); + } + } + else + { + m_targetBHP = wellControls.getMaxBHPConstraint()->getConstraintValue( time ); + m_targetPhaseIndex = -1; + m_constraintValue = wellControls.getInjRateConstraints()[0]->getConstraintValue( time ); + + } + + } GEOS_HOST_DEVICE @@ -232,17 +251,17 @@ class ResidualNormKernel : public physicsSolverBaseKernels::ResidualNormKernelBa else if( m_currentControl == WellControls::Control::TOTALVOLRATE ) { // the residual entry is in volume / time units - normalizer = LvArray::math::max( LvArray::math::abs( m_targetTotalRate ), m_minNormalizer ); + normalizer = LvArray::math::max( LvArray::math::abs( m_constraintValue ), m_minNormalizer ); } else if( m_currentControl == WellControls::Control::PHASEVOLRATE ) { // the residual entry is in volume / time units - normalizer = LvArray::math::max( LvArray::math::abs( m_targetPhaseRate ), m_minNormalizer ); + normalizer = LvArray::math::max( LvArray::math::abs( m_constraintValue ), m_minNormalizer ); } else if( m_currentControl == WellControls::Control::MASSRATE ) { // the residual entry is in volume / time units - normalizer = LvArray::math::max( LvArray::math::abs( m_targetMassRate ), m_minNormalizer ); + normalizer = LvArray::math::max( LvArray::math::abs( m_constraintValue ), m_minNormalizer ); } } // for the pressure difference equation, always normalize by the BHP @@ -257,18 +276,18 @@ class ResidualNormKernel : public physicsSolverBaseKernels::ResidualNormKernelBa if( m_isProducer ) // only PHASEVOLRATE is supported for now { // the residual is in mass units - normalizer = m_dt * LvArray::math::abs( m_targetPhaseRate ) * m_phaseDens_n[iwelem][0][m_targetPhaseIndex]; + normalizer = m_dt * LvArray::math::abs( m_constraintValue ) * m_phaseDens_n[iwelem][0][m_targetPhaseIndex]; } else // Type::INJECTOR, only TOTALVOLRATE is supported for now { if( m_currentControl == WellControls::Control::MASSRATE ) { - normalizer = m_dt * LvArray::math::abs( m_targetMassRate ); + normalizer = m_dt * LvArray::math::abs( m_constraintValue ); } else { // the residual is in mass units - normalizer = m_dt * LvArray::math::abs( m_targetTotalRate ) * m_totalDens_n[iwelem][0]; + normalizer = m_dt * LvArray::math::abs( m_constraintValue ) * m_totalDens_n[iwelem][0]; } } @@ -282,17 +301,17 @@ class ResidualNormKernel : public physicsSolverBaseKernels::ResidualNormKernelBa if( m_isProducer ) // only PHASEVOLRATE is supported for now { // the residual is in volume units - normalizer = m_dt * LvArray::math::abs( m_targetPhaseRate ); + normalizer = m_dt * LvArray::math::abs( m_constraintValue ); } else // Type::INJECTOR, only TOTALVOLRATE is supported for now { if( m_currentControl == WellControls::Control::MASSRATE ) { - normalizer = m_dt * LvArray::math::abs( m_targetMassRate/ m_totalDens_n[iwelem][0] ); + normalizer = m_dt * LvArray::math::abs( m_constraintValue/ m_totalDens_n[iwelem][0] ); } else { - normalizer = m_dt * LvArray::math::abs( m_targetTotalRate ); + normalizer = m_dt * LvArray::math::abs( m_constraintValue ); } } @@ -339,7 +358,7 @@ class ResidualNormKernel : public physicsSolverBaseKernels::ResidualNormKernelBa integer const m_numPhases; /// Index of the target phase - integer const m_targetPhaseIndex; + integer m_targetPhaseIndex; /// Time step size real64 const m_dt; @@ -355,10 +374,9 @@ class ResidualNormKernel : public physicsSolverBaseKernels::ResidualNormKernelBa /// Controls WellControls::Control const m_currentControl; - real64 const m_targetBHP; - real64 const m_targetTotalRate; - real64 const m_targetPhaseRate; - real64 const m_targetMassRate; + real64 m_constraintValue; + real64 m_targetBHP; + /// View on the volume arrayView1d< real64 const > const m_volume; @@ -383,7 +401,6 @@ class ResidualNormKernelFactory * @tparam POLICY the policy used in the RAJA kernel * @param[in] numComp number of fluid components * @param[in] numDof number of dofs per well element - * @param[in] targetPhaseIndex the index of the target phase (for phase volume control) * @param[in] rankOffset the offset of my MPI rank * @param[in] dofKey the string key to retrieve the degress of freedom numbers * @param[in] localResidual the residual vector on my MPI rank @@ -397,7 +414,6 @@ class ResidualNormKernelFactory template< typename POLICY > static void createAndLaunch( integer const numComp, - integer const targetPhaseIndex, globalIndex const rankOffset, string const & dofKey, arrayView1d< real64 const > const & localResidual, @@ -418,7 +434,7 @@ class ResidualNormKernelFactory arrayView1d< integer const > const ghostRank = subRegion.ghostRank(); kernelType kernel( rankOffset, localResidual, dofNumber, ghostRank, - targetPhaseIndex, subRegion, fluid, wellControls, time, dt, minNormalizer ); + subRegion, fluid, wellControls, time, dt, minNormalizer ); kernelType::template launchLinf< POLICY >( subRegion.size(), kernel, residualNorm ); } ); } @@ -476,6 +492,7 @@ class ElementBasedAssemblyKernel : public compositionalMultiphaseWellKernels::El * @param[inout] localRhs the local right-hand side vector */ ElementBasedAssemblyKernel( localIndex const numPhases, + bool const thermalEffectsEnabled, integer const isProducer, globalIndex const rankOffset, string const dofKey, @@ -484,7 +501,7 @@ class ElementBasedAssemblyKernel : public compositionalMultiphaseWellKernels::El CRSMatrixView< real64, globalIndex const > const & localMatrix, arrayView1d< real64 > const & localRhs, BitFlags< isothermalCompositionalMultiphaseBaseKernels::KernelFlags > const kernelFlags ) - : Base( numPhases, isProducer, rankOffset, dofKey, subRegion, fluid, localMatrix, localRhs, kernelFlags ), + : Base( numPhases, thermalEffectsEnabled, isProducer, rankOffset, dofKey, subRegion, fluid, localMatrix, localRhs, kernelFlags ), m_phaseInternalEnergy_n( fluid.phaseInternalEnergy_n()), m_phaseInternalEnergy( fluid.phaseInternalEnergy()), m_dPhaseInternalEnergy( fluid.dPhaseInternalEnergy()) @@ -560,6 +577,14 @@ class ElementBasedAssemblyKernel : public compositionalMultiphaseWellKernels::El + phaseAmount * dPhaseInternalEnergy[ip][Deriv::dP]; real64 const dPhaseEnergy_dT = dPhaseAmount[FLUID_PROP_COFFSET::dT] * phaseInternalEnergy[ip] + phaseAmount * dPhaseInternalEnergy[ip][Deriv::dT]; + if( false && ei== 75 ) + { + std::cout << "taccum dp" < kernelFlags, @@ -645,13 +671,12 @@ class ElementBasedAssemblyKernelFactory CRSMatrixView< real64, globalIndex const > const & localMatrix, arrayView1d< real64 > const & localRhs ) { - isothermalCompositionalMultiphaseBaseKernels:: - internal::kernelLaunchSelectorCompSwitch( numComps, [&]( auto NC ) + isothermalCompositionalMultiphaseBaseKernels::internal::kernelLaunchSelectorCompSwitch( numComps, [&]( auto NC ) { localIndex constexpr NUM_COMP = NC(); ElementBasedAssemblyKernel< NUM_COMP > - kernel( numPhases, isProducer, rankOffset, dofKey, subRegion, fluid, localMatrix, localRhs, kernelFlags ); + kernel( numPhases, wellControls.thermalEffectsEnabled(), isProducer, rankOffset, dofKey, subRegion, fluid, localMatrix, localRhs, kernelFlags ); ElementBasedAssemblyKernel< NUM_COMP >::template launch< POLICY, ElementBasedAssemblyKernel< NUM_COMP > >( subRegion.size(), kernel ); } ); @@ -731,6 +756,7 @@ class FaceBasedAssemblyKernel : public compositionalMultiphaseWellKernels::FaceB , localRhs , kernelFlags ), m_numPhases ( fluid.numFluidPhases()), + m_thermalEffectsEnabled( wellControls.thermalEffectsEnabled()), m_globalWellElementIndex( subRegion.getGlobalWellElementIndex() ), m_phaseFraction( fluid.phaseFraction()), m_dPhaseFraction( fluid.dPhaseFraction()), @@ -777,7 +803,8 @@ class FaceBasedAssemblyKernel : public compositionalMultiphaseWellKernels::FaceB void complete( localIndex const iwelem, StackVariables & stack ) const { Base::complete ( iwelem, stack ); - + // tjb iso return; + if( !m_thermalEffectsEnabled ) return; using namespace compositionalMultiphaseUtilities; if( stack.numConnectedElems ==1 ) { @@ -799,6 +826,10 @@ class FaceBasedAssemblyKernel : public compositionalMultiphaseWellKernels::FaceB stack.localEnergyFluxJacobian_dQ[0][0]=0; stack.localEnergyFlux[0]=0; } + // if( m_isProducer && m_globalWellElementIndex[iwelem] == 0 ) + // { + // stack.localEnergyFlux[0]=0; + //} // Setup Jacobian global col indicies ( Mapping from local jac order to well jac order) @@ -937,9 +968,11 @@ class FaceBasedAssemblyKernel : public compositionalMultiphaseWellKernels::FaceB } // Energy equation stack.localEnergyFlux[0] = -m_dt * eflux * currentConnRate; + // tjb std::cout << "seg flux " << iwelem << " " << iwelemUp << " " << -m_dt * eflux * currentConnRate << std::endl; + stack.localEnergyFluxJacobian_dQ[0][0] = -m_dt * eflux_dq; } - else if( ( iwelemNext < 0 && m_isProducer ) || currentConnRate < 0 ) // exit connection, producer + else if( ( iwelemNext < 0 && m_isProducer ) ) // exit connection, producer { real64 eflux=0; real64 eflux_dq=0; @@ -966,9 +999,9 @@ class FaceBasedAssemblyKernel : public compositionalMultiphaseWellKernels::FaceB for( integer dof=0; dof < CP_Deriv::nDer; dof++ ) { - stack.localEnergyFluxJacobian[0][dof] *= -m_dt*currentConnRate; + stack.localEnergyFluxJacobian[0][dof] *= -m_dt*currentConnRate; } - stack.localEnergyFlux[0] = -m_dt * eflux * currentConnRate; + stack.localEnergyFlux[0] = -m_dt * eflux * currentConnRate; stack.localEnergyFluxJacobian_dQ[0][0] = -m_dt*eflux_dq; } else @@ -985,9 +1018,14 @@ class FaceBasedAssemblyKernel : public compositionalMultiphaseWellKernels::FaceB real64 dprop_dt = m_phaseEnthalpy[iwelemUp][0][ip]*m_dPhaseFraction[iwelemUp][0][ip][CP_Deriv::dT] + m_dPhaseEnthalpy[iwelemUp][0][ip][CP_Deriv::dT]*m_phaseFraction[iwelemUp][0][ip]; + //std::cout << " tjb " << iwelem << " " << iwelemNext << " " << iwelemUp << " dprop_dp " << dprop_dp << " dprop_dt " << dprop_dt + // << std::endl; stack.localEnergyFluxJacobian[TAG::NEXT ] [CP_Deriv::dP] += dprop_dp; stack.localEnergyFluxJacobian[TAG::NEXT] [CP_Deriv::dT] += dprop_dt; - + // tjb std::cout << "seg dprop_dp " << iwelem << " " << iwelemUp << " " << ip<< " " << dprop_dp << " pf " + // < m_globalWellElementIndex; diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/ThermalSinglePhaseWellKernels.hpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/ThermalSinglePhaseWellKernels.hpp index f736826561c..b19ca90845d 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/ThermalSinglePhaseWellKernels.hpp +++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/ThermalSinglePhaseWellKernels.hpp @@ -522,14 +522,21 @@ class ResidualNormKernel : public physicsSolverBaseKernels::ResidualNormKernelBa m_iwelemControl( subRegion.getTopWellElementIndex() ), m_isProducer( wellControls.isProducer() ), m_currentControl( wellControls.getControl() ), - m_targetBHP( wellControls.getTargetBHP( time ) ), - m_targetRate( wellControls.getTargetTotalRate( time ) ), - m_targetMassRate( wellControls.getTargetMassRate( time ) ), + m_constraintValue ( wellControls.getCurrentConstraint()->getConstraintValue( time )), m_volume( subRegion.getElementVolume() ), m_density_n( fluid.density_n() ), m_internalEnergy_n( fluid.internalEnergy_n() ) - {} + { + if( wellControls.isProducer() ) + { + m_targetBHP = wellControls.getMinBHPConstraint()->getConstraintValue( time ); + } + else + { + m_targetBHP = wellControls.getMaxBHPConstraint()->getConstraintValue( time ); + } + } GEOS_HOST_DEVICE @@ -568,12 +575,12 @@ class ResidualNormKernel : public physicsSolverBaseKernels::ResidualNormKernelBa else if( m_currentControl == WellControls::Control::TOTALVOLRATE ) { // the residual entry is in volume / time units - normalizer = LvArray::math::max( LvArray::math::abs( m_targetRate ), m_minNormalizer ); + normalizer = LvArray::math::max( LvArray::math::abs( m_constraintValue ), m_minNormalizer ); } else if( m_currentControl == WellControls::Control::MASSRATE ) { // the residual entry is in volume / time units - normalizer = LvArray::math::max( LvArray::math::abs( m_targetMassRate ), m_minNormalizer ); + normalizer = LvArray::math::max( LvArray::math::abs( m_constraintValue ), m_minNormalizer ); } } // for the pressure difference equation, always normalize by the BHP @@ -587,7 +594,7 @@ class ResidualNormKernel : public physicsSolverBaseKernels::ResidualNormKernelBa { // this residual entry is in mass units - normalizer = m_dt * LvArray::math::abs( m_targetRate ) * m_density_n[iwelem][0]; + normalizer = m_dt * LvArray::math::abs( m_constraintValue ) * m_density_n[iwelem][0]; // to make sure that everything still works well if the rate is zero, we add this check normalizer = LvArray::math::max( normalizer, m_volume[iwelem] * m_density_n[iwelem][0] ); @@ -640,9 +647,8 @@ class ResidualNormKernel : public physicsSolverBaseKernels::ResidualNormKernelBa /// Controls WellControls::Control const m_currentControl; - real64 const m_targetBHP; - real64 const m_targetRate; - real64 const m_targetMassRate; + real64 const m_constraintValue; + real64 m_targetBHP; /// View on the volume arrayView1d< real64 const > const m_volume; diff --git a/src/coreComponents/physicsSolvers/multiphysics/CompositionalMultiphaseReservoirAndWells.cpp b/src/coreComponents/physicsSolvers/multiphysics/CompositionalMultiphaseReservoirAndWells.cpp index b95f3493187..a590095837f 100644 --- a/src/coreComponents/physicsSolvers/multiphysics/CompositionalMultiphaseReservoirAndWells.cpp +++ b/src/coreComponents/physicsSolvers/multiphysics/CompositionalMultiphaseReservoirAndWells.cpp @@ -344,6 +344,7 @@ assembleCouplingTerms( real64 const time_n, coupledReservoirAndWellKernels:: ThermalCompositionalMultiPhaseFluxKernelFactory:: createAndLaunch< parallelDevicePolicy<> >( numComps, + wellControls, wellControls.isProducer(), dt, rankOffset, diff --git a/src/coreComponents/physicsSolvers/multiphysics/CoupledReservoirAndWellKernels.hpp b/src/coreComponents/physicsSolvers/multiphysics/CoupledReservoirAndWellKernels.hpp index 5d106df3156..993bc89aa85 100644 --- a/src/coreComponents/physicsSolvers/multiphysics/CoupledReservoirAndWellKernels.hpp +++ b/src/coreComponents/physicsSolvers/multiphysics/CoupledReservoirAndWellKernels.hpp @@ -329,7 +329,263 @@ class IsothermalCompositionalMultiPhaseFluxKernelFactory } }; +/** + * @class FaceBasedAssemblyKernel + * @tparam NUM_COMP number of fluid components + * @brief Define the interface for the assembly kernel in charge of flux terms + */ +template< integer NC, integer IS_THERMAL > +class IsothermalCompositionalMultiPhaseWellFluxKernel +{ +public: + + /// Compile time value for the number of components + static constexpr integer numComp = NC; + static constexpr integer resNumDOF = NC+1+IS_THERMAL; + + // Well jacobian column and row indicies + using WJ_COFFSET = compositionalMultiphaseWellKernels::ColOffset_WellJac< NC, IS_THERMAL >; + using WJ_ROFFSET = compositionalMultiphaseWellKernels::RowOffset_WellJac< NC, IS_THERMAL >; + + using ROFFSET = compositionalMultiphaseWellKernels::RowOffset; + using COFFSET = compositionalMultiphaseWellKernels::ColOffset; + + using CP_Deriv = multifluid::DerivativeOffsetC< NC, IS_THERMAL >; + + using TAG = compositionalMultiphaseWellKernels::SubRegionTag; + + + + /// Compute time value for the number of degrees of freedom + static constexpr integer numDof = WJ_COFFSET::nDer; + + /// Compile time value for the number of equations except volume and momentum + static constexpr integer numEqn = WJ_ROFFSET::nEqn - 2; + + /** + * @brief Constructor for the kernel interface + * @param[in] rankOffset the offset of my MPI rank + * @param[in] stencilWrapper reference to the stencil wrapper + * @param[in] dofNumberAccessor + * @param[in] compFlowAccessors + * @param[in] multiFluidAccessors + * @param[in] capPressureAccessors + * @param[in] permeabilityAccessors + * @param[in] dt time step size + * @param[inout] localMatrix the local CRS matrix + * @param[inout] localRhs the local right-hand side vector + * @param[in] kernelFlags flags packed together + */ + IsothermalCompositionalMultiPhaseWellFluxKernel( real64 const dt, + globalIndex const rankOffset, + string const wellDofKey, + WellElementSubRegion const & subRegion, + PerforationData const * const perforationData, + MultiFluidBase const & fluid, + arrayView1d< real64 > const & localRhs, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + BitFlags< isothermalCompositionalMultiphaseBaseKernels::KernelFlags > kernelFlags ) + : + m_dt( dt ), + m_numPhases ( fluid.numFluidPhases()), + m_rankOffset( rankOffset ), + m_compPerfRate( perforationData->getField< fields::well::compPerforationRate >() ), + m_dCompPerfRate( perforationData->getField< fields::well::dCompPerforationRate >() ), + m_perfWellElemIndex( perforationData->getField< fields::perforation::wellElementIndex >() ), + m_wellElemDofNumber( subRegion.getReference< array1d< globalIndex > >( wellDofKey ) ), + m_localRhs( localRhs ), + m_localMatrix( localMatrix ), + m_useTotalMassEquation ( kernelFlags.isSet( isothermalCompositionalMultiphaseBaseKernels::KernelFlags::TotalMassEquation ) ) + { } + + + /** + * @brief Compute the local flux contributions to the residual and Jacobian + * @tparam FUNC the type of the function that can be used to customize the computation of the phase fluxes + * @param[in] ie the element index + * @param[inout] stack the stack variables + * @param[in] compFluxKernelOp the function used to customize the computation of the component fluxes + */ + + template< typename FUNC = NoOpFunc > + GEOS_HOST_DEVICE + inline + void computeFlux( localIndex const iperf, + FUNC && compFluxKernelOp = NoOpFunc{} ) const + { + + using namespace compositionalMultiphaseUtilities; + // local working variables and arrays + stackArray1d< localIndex, numComp > eqnRowIndices( numComp ); + stackArray1d< globalIndex, resNumDOF > dofColIndices( resNumDOF ); + + stackArray1d< real64, numComp > localPerf( numComp ); + stackArray2d< real64, numComp *resNumDOF > localPerfJacobian( numComp, resNumDOF ); + + // get the reservoir (sub)region and element indices + //localIndex const er = m_resElementRegion[iperf]; + //localIndex const esr = m_resElementSubRegion[iperf]; + //localIndex const ei = m_resElementIndex[iperf]; + + // get the well element index for this perforation + localIndex const iwelem = m_perfWellElemIndex[iperf]; + //globalIndex const resOffset = m_resElemDofNumber[er][esr][ei]; + globalIndex const wellElemOffset = m_wellElemDofNumber[iwelem]; + + for( integer ic = 0; ic < numComp; ++ic ) + { + eqnRowIndices[ ic] = LvArray::integerConversion< localIndex >( wellElemOffset - m_rankOffset ) + WJ_ROFFSET::MASSBAL + ic; + } + for( integer jdof = 0; jdof < NC+1; ++jdof ) + { + dofColIndices[ jdof] = wellElemOffset + WJ_COFFSET::dP + jdof; + } + // For temp its different + if constexpr ( IS_THERMAL ) + { + dofColIndices[ NC+1 ] = wellElemOffset + WJ_COFFSET::dT; + } + // populate local flux vector and derivatives + + for( integer ic = 0; ic < numComp; ++ic ) + { + localPerf[ic] = -m_dt * m_compPerfRate[iperf][ic]; + } + for( integer ic = 0; ic < numComp; ++ic ) + { + localIndex localDofIndexPres = 0; + + localPerfJacobian[ic][localDofIndexPres] = -m_dt * m_dCompPerfRate[iperf][TAG::WELL ][ic][CP_Deriv::dP]; + for( integer jc = 0; jc < numComp; ++jc ) + { + localIndex const localDofIndexComp = localDofIndexPres + jc + 1; + localPerfJacobian[ic][localDofIndexComp] = -m_dt * m_dCompPerfRate[iperf][TAG::WELL ][ic][CP_Deriv::dC+jc]; + } + if constexpr ( IS_THERMAL ) + { + localIndex localDofIndexTemp = localDofIndexPres + NC + 1; + localPerfJacobian[ic][localDofIndexTemp] = -m_dt * m_dCompPerfRate[iperf][TAG::WELL ][ic][CP_Deriv::dT]; + } + } + + if( m_useTotalMassEquation ) + { + stackArray1d< real64, resNumDOF > work( resNumDOF ); + shiftBlockRowsAheadByOneAndReplaceFirstRowWithColumnSum( numComp, numComp, resNumDOF, 1, localPerfJacobian, work ); + + // Apply equation/variable change transformation(s) + shiftBlockElementsAheadByOneAndReplaceFirstElementWithSum( numComp, numComp, 1, localPerf ); + } + + for( localIndex i = 0; i < localPerf.size(); ++i ) + { + if( eqnRowIndices[i] >= 0 && eqnRowIndices[i] < m_localMatrix.numRows() ) + { + m_localMatrix.addToRowBinarySearchUnsorted< parallelDeviceAtomic >( eqnRowIndices[i], + dofColIndices.data(), + localPerfJacobian[i].dataIfContiguous(), + resNumDOF ); + RAJA::atomicAdd( parallelDeviceAtomic{}, &m_localRhs[eqnRowIndices[i]], localPerf[i] ); + } + } + + compFluxKernelOp( wellElemOffset, iwelem, dofColIndices ); + + } + + +/** + * @brief Performs the kernel launch + * @tparam POLICY the policy used in the RAJA kernels + * @tparam KERNEL_TYPE the kernel type + * @param[in] numElements the number of elements + * @param[inout] kernelComponent the kernel component providing access to setup/compute/complete functions and stack + * variables + */ + template< typename POLICY, typename KERNEL_TYPE > + static void + launch( localIndex const numElements, + KERNEL_TYPE const & kernelComponent ) + { + GEOS_MARK_FUNCTION; + forAll< POLICY >( numElements, [=] GEOS_HOST_DEVICE ( localIndex const ie ) + { + kernelComponent.computeFlux( ie ); + + } ); + } + +protected: + +/// Time step size + real64 const m_dt; + +/// Number of phases + integer const m_numPhases; + + globalIndex const m_rankOffset; +// Perfoation variables + arrayView2d< real64 const > const m_compPerfRate; + arrayView4d< real64 const > const m_dCompPerfRate; + arrayView1d< localIndex const > const m_perfWellElemIndex; + +// Element region, subregion, index + arrayView1d< globalIndex const > const m_wellElemDofNumber; + +// RHS and Jacobian + arrayView1d< real64 > const m_localRhs; + CRSMatrixView< real64, globalIndex const > m_localMatrix; + + integer const m_useTotalMassEquation; +}; + +/** + * @class FaceBasedAssemblyKernelFactory + */ +class IsothermalCompositionalMultiPhaseWellFluxKernelFactory +{ +public: + + /** + * @brief Create a new kernel and launch + * @tparam POLICY the policy used in the RAJA kernel + * @param[in] numComps the number of fluid components + * @param[in] dt time step size + * @param[in] rankOffset the offset of my MPI rank + * @param[in] useTotalMassEquation flag specifying whether to replace one component bal eqn with total mass eqn + * @param[in] dofKey string to get the element degrees of freedom numbers + * @param[in] wellControls object holding well control/constraint information + * @param[in] subregion well subregion + * @param[inout] localMatrix the local CRS matrix + * @param[inout] localRhs the local right-hand side vector + */ + template< typename POLICY > + static void + createAndLaunch( integer const numComps, + real64 const dt, + globalIndex const rankOffset, + string const wellDofKey, + WellElementSubRegion const & subRegion, + PerforationData const * const perforationData, + MultiFluidBase const & fluid, + arrayView1d< real64 > const & localRhs, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + BitFlags< isothermalCompositionalMultiphaseBaseKernels::KernelFlags > kernelFlags ) + { + isothermalCompositionalMultiphaseBaseKernels::internal::kernelLaunchSelectorCompSwitch( numComps, [&]( auto NC ) + { + integer constexpr NUM_COMP = NC(); + + using kernelType = IsothermalCompositionalMultiPhaseWellFluxKernel< NUM_COMP, 0 >; + kernelType kernel( dt, rankOffset, wellDofKey, subRegion, perforationData, + fluid, localRhs, localMatrix, kernelFlags ); + kernelType::template launch< POLICY >( perforationData->size(), kernel ); + } ); + + } +}; +/********************************************************/ /** * @class FaceBasedAssemblyKernel * @tparam NUM_COMP number of fluid components @@ -383,6 +639,7 @@ class ThermalCompositionalMultiPhaseFluxKernel : public IsothermalCompositionalM * @param[in] kernelFlags flags packed together */ ThermalCompositionalMultiPhaseFluxKernel( real64 const dt, + bool const thermalEffectsEnabled, integer const isProducer, globalIndex const rankOffset, string const wellDofKey, @@ -407,6 +664,7 @@ class ThermalCompositionalMultiPhaseFluxKernel : public IsothermalCompositionalM detectCrossflow, numCrossFlowPerforations, kernelFlags ), + m_thermalEffectsEnabled( thermalEffectsEnabled ), m_isProducer( isProducer ), m_globalWellElementIndex( subRegion.getGlobalWellElementIndex() ), m_energyPerfFlux( perforationData->getField< fields::well::energyPerforationFlux >()), @@ -432,6 +690,8 @@ class ThermalCompositionalMultiPhaseFluxKernel : public IsothermalCompositionalM stackArray1d< globalIndex, 2*resNumDOF > & dofColIndices, localIndex const iwelem ) { + if( !m_thermalEffectsEnabled ) + return; // No energy equation if top element and Injector // Top element defined by global index == 0 // Assumption is global index == 0 is top segment with fixed temp BC @@ -441,7 +701,7 @@ class ThermalCompositionalMultiPhaseFluxKernel : public IsothermalCompositionalM return; } // local working variables and arrays - stackArray1d< localIndex, 2* numComp > eqnRowIndices( 2 ); + stackArray1d< localIndex, 2* numComp > eqnRowIndices( 2* numComp ); stackArray1d< real64, 2 * numComp > localPerf( 2 ); stackArray2d< real64, 2 * resNumDOF * 2 * numComp > localPerfJacobian( 2, 2 * resNumDOF ); @@ -510,6 +770,8 @@ class ThermalCompositionalMultiPhaseFluxKernel : public IsothermalCompositionalM } protected: + /// Flag specifying whether thermal effects are enabled + bool const m_thermalEffectsEnabled; /// Well type integer const m_isProducer; @@ -545,6 +807,7 @@ class ThermalCompositionalMultiPhaseFluxKernelFactory template< typename POLICY > static void createAndLaunch( integer const numComps, + WellControls const & wellControls, integer const isProducer, real64 const dt, globalIndex const rankOffset, @@ -565,7 +828,7 @@ class ThermalCompositionalMultiPhaseFluxKernelFactory integer constexpr NUM_COMP = NC(); using kernelType = ThermalCompositionalMultiPhaseFluxKernel< NUM_COMP, 1 >; - kernelType kernel( dt, isProducer, rankOffset, wellDofKey, subRegion, resDofNumber, perforationData, + kernelType kernel( dt, wellControls.thermalEffectsEnabled(), isProducer, rankOffset, wellDofKey, subRegion, resDofNumber, perforationData, fluid, localRhs, localMatrix, detectCrossflow, numCrossFlowPerforations, kernelFlags ); kernelType::template launch< POLICY >( perforationData->size(), kernel ); } ); @@ -573,6 +836,234 @@ class ThermalCompositionalMultiPhaseFluxKernelFactory } }; +/********************************************************/ +/** + * @class FaceBasedAssemblyKernel + * @tparam NUM_COMP number of fluid components + * @brief Define the interface for the assembly kernel in charge of flux terms + */ +template< integer NC, integer IS_THERMAL > +class ThermalCompositionalMultiPhaseWellFluxKernel : public IsothermalCompositionalMultiPhaseWellFluxKernel< NC, IS_THERMAL > +{ +public: + using Base = IsothermalCompositionalMultiPhaseWellFluxKernel< NC, IS_THERMAL >; + /// Compile time value for the number of components + static constexpr integer numComp = NC; + static constexpr integer resNumDOF = NC+1+IS_THERMAL; + + // Well jacobian column and row indicies + using WJ_COFFSET = compositionalMultiphaseWellKernels::ColOffset_WellJac< NC, IS_THERMAL >; + using WJ_ROFFSET = compositionalMultiphaseWellKernels::RowOffset_WellJac< NC, IS_THERMAL >; + + using ROFFSET = compositionalMultiphaseWellKernels::RowOffset; + using COFFSET = compositionalMultiphaseWellKernels::ColOffset; + + using CP_Deriv = multifluid::DerivativeOffsetC< NC, IS_THERMAL >; + + using TAG = compositionalMultiphaseWellKernels::SubRegionTag; + + using Base::m_dt; + using Base::m_localRhs; + using Base::m_localMatrix; + using Base::m_rankOffset; + + + + /// Compute time value for the number of degrees of freedom + static constexpr integer numDof = WJ_COFFSET::nDer; + + /// Compile time value for the number of equations except volume and momentum + static constexpr integer numEqn = WJ_ROFFSET::nEqn - 2; + + /** + * @brief Constructor for the kernel interface + * @param[in] rankOffset the offset of my MPI rank + * @param[in] stencilWrapper reference to the stencil wrapper + * @param[in] dofNumberAccessor + * @param[in] compFlowAccessors + * @param[in] multiFluidAccessors + * @param[in] capPressureAccessors + * @param[in] permeabilityAccessors + * @param[in] dt time step size + * @param[inout] localMatrix the local CRS matrix + * @param[inout] localRhs the local right-hand side vector + * @param[in] kernelFlags flags packed together + */ + ThermalCompositionalMultiPhaseWellFluxKernel( real64 const dt, + bool const thermalEffectsEnabled, + integer const isProducer, + globalIndex const rankOffset, + string const wellDofKey, + WellElementSubRegion const & subRegion, + PerforationData const * const perforationData, + MultiFluidBase const & fluid, + arrayView1d< real64 > const & localRhs, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + BitFlags< isothermalCompositionalMultiphaseBaseKernels::KernelFlags > kernelFlags ) + : Base( dt, + rankOffset, + wellDofKey, + subRegion, + perforationData, + fluid, + localRhs, + localMatrix, + kernelFlags ), + m_thermalEffectsEnabled( thermalEffectsEnabled ), + m_isProducer( isProducer ), + m_globalWellElementIndex( subRegion.getGlobalWellElementIndex() ), + m_energyPerfFlux( perforationData->getField< fields::well::energyPerforationFlux >()), + m_dEnergyPerfFlux( perforationData->getField< fields::well::dEnergyPerforationFlux >()) + + { } + + + /** + * @brief Compute the local flux contributions to the residual and Jacobian + * @tparam FUNC the type of the function that can be used to customize the computation of the phase fluxes + * @param[in] ie the element index + * @param[inout] stack the stack variables + * @param[in] compFluxKernelOp the function used to customize the computation of the component fluxes + */ + + GEOS_HOST_DEVICE + inline + void computeFlux( localIndex const iperf ) const + { + Base::computeFlux( iperf, [&] ( globalIndex const & wellElemOffset, + localIndex const iwelem, + stackArray1d< globalIndex, resNumDOF > & dofColIndices ) + { + GEOS_UNUSED_VAR( dofColIndices ); + if( !m_thermalEffectsEnabled ) // tjb iso + return; + // No energy equation if top element and Injector + // Top element defined by global index == 0 + // Assumption is global index == 0 is top segment with fixed temp BC + if( !m_isProducer ) + { + if( m_globalWellElementIndex[iwelem] == 0 ) + return; + } + // local working variables and arrays + localIndex eqnRowIndices = LvArray::integerConversion< localIndex >( wellElemOffset - m_rankOffset ) + WJ_ROFFSET::ENERGYBAL; + + stackArray2d< real64, resNumDOF > localPerfJacobian( 1, resNumDOF ); + // populate local flux vector and derivatives + + real64 localPerf = -m_dt * m_energyPerfFlux[iperf]; + + // std::cout << "Local perf: " << iperf << " " << localPerf << std::endl; + localIndex localDofIndexPres = 0; + localPerfJacobian [0][localDofIndexPres] = -m_dt * m_dEnergyPerfFlux[iperf][TAG::WELL][CP_Deriv::dP]; + + // populate local flux vector and derivatives + for( integer ic = 0; ic < numComp; ++ic ) + { + localIndex const localDofIndexComp = localDofIndexPres + ic + 1; + localPerfJacobian [0][localDofIndexComp] = -m_dt * m_dEnergyPerfFlux[iperf][TAG::WELL ][CP_Deriv::dC+ic]; + } + localPerfJacobian [0][localDofIndexPres+NC+1] = -m_dt * m_dEnergyPerfFlux[iperf][TAG::WELL ][CP_Deriv::dT]; + + if( eqnRowIndices >= 0 && eqnRowIndices < m_localMatrix.numRows() ) + { + // tjb iso + m_localMatrix.template addToRowBinarySearchUnsorted< parallelDeviceAtomic >( eqnRowIndices, + dofColIndices.data(), + localPerfJacobian[0].dataIfContiguous(), + resNumDOF ); + RAJA::atomicAdd( parallelDeviceAtomic{}, &m_localRhs[eqnRowIndices], localPerf ); + } + } ); + + + } + + + /** + * @brief Performs the kernel launch + * @tparam POLICY the policy used in the RAJA kernels + * @tparam KERNEL_TYPE the kernel type + * @param[in] numElements the number of elements + * @param[inout] kernelComponent the kernel component providing access to setup/compute/complete functions and stack + * variables + */ + template< typename POLICY, typename KERNEL_TYPE > + static void + launch( localIndex const numElements, + KERNEL_TYPE const & kernelComponent ) + { + GEOS_MARK_FUNCTION; + forAll< POLICY >( numElements, [=] GEOS_HOST_DEVICE ( localIndex const ie ) + { + kernelComponent.computeFlux( ie ); + + } ); + } + +protected: + /// Thermal effects enabled + bool const m_thermalEffectsEnabled; + /// Well type + integer const m_isProducer; + + /// Global index of local element + arrayView1d< globalIndex const > m_globalWellElementIndex; + + /// Views on energy flux + arrayView1d< real64 const > const m_energyPerfFlux; + arrayView3d< real64 const > const m_dEnergyPerfFlux; +}; + +/** + * @class ThermalCompositionalMultiPhaseFluxWellKernelFactory + */ +class ThermalCompositionalMultiPhaseWellFluxKernelFactory +{ +public: + + /** + * @brief Create a new kernel and launch + * @tparam POLICY the policy used in the RAJA kernel + * @param[in] numComps the number of fluid components + * @param[in] dt time step size + * @param[in] rankOffset the offset of my MPI rank + * @param[in] useTotalMassEquation flag specifying whether to replace one component bal eqn with total mass eqn + * @param[in] dofKey string to get the element degrees of freedom numbers + * @param[in] wellControls object holding well control/constraint information + * @param[in] subregion well subregion + * @param[inout] localMatrix the local CRS matrix + * @param[inout] localRhs the local right-hand side vector + */ + template< typename POLICY > + static void + createAndLaunch( integer const numComps, + WellControls const & wellControls, + integer const isProducer, + real64 const dt, + globalIndex const rankOffset, + string const wellDofKey, + WellElementSubRegion const & subRegion, + PerforationData const * const perforationData, + MultiFluidBase const & fluid, + BitFlags< isothermalCompositionalMultiphaseBaseKernels::KernelFlags > kernelFlags, + arrayView1d< real64 > const & localRhs, + CRSMatrixView< real64, globalIndex const > const & localMatrix + ) + { + isothermalCompositionalMultiphaseBaseKernels::internal::kernelLaunchSelectorCompSwitch( numComps, [&]( auto NC ) + { + integer constexpr NUM_COMP = NC(); + + using kernelType = ThermalCompositionalMultiPhaseWellFluxKernel< NUM_COMP, 1 >; + kernelType kernel( dt, wellControls.thermalEffectsEnabled(), isProducer, rankOffset, wellDofKey, subRegion, perforationData, + fluid, localRhs, localMatrix, kernelFlags ); + kernelType::template launch< POLICY >( perforationData->size(), kernel ); + } ); + + } +}; + } // end namespace coupledReservoirAndWellKernels } // end namespace geos diff --git a/src/coreComponents/schema/schema.xsd b/src/coreComponents/schema/schema.xsd index e29fdc5fd0b..b24f88d8935 100644 --- a/src/coreComponents/schema/schema.xsd +++ b/src/coreComponents/schema/schema.xsd @@ -222,18 +222,10 @@ - - - - - - - - @@ -1536,26 +1528,10 @@ stress - traction is applied to the faces as specified by the inner product of i - - - - - - - - - - - - - - - - @@ -1564,18 +1540,6 @@ stress - traction is applied to the faces as specified by the inner product of i - - - - - - - - - - - - @@ -3532,7 +3496,44 @@ When set to `all` output both convergence & iteration information to a csv.--> - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -3573,6 +3574,8 @@ Information output from lower logLevels is added with the desired log level + + @@ -3581,6 +3584,8 @@ Information output from lower logLevels is added with the desired log level + + @@ -3589,6 +3594,17 @@ When set to `all` output both convergence & iteration information to a csv.--> + + + + + + + + + + + - If the flag is set to 1, both reservoir-to-well flow and well-to-reservoir flow are allowed at the perforations. - If the flag is set to 0, we only allow well-to-reservoir flow at the perforations.--> + + - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -3655,6 +3646,109 @@ See note on referenceReservoirRegion for reservoir condition options--> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -4060,7 +4154,7 @@ Information output from lower logLevels is added with the desired log level - + @@ -4089,7 +4183,7 @@ When set to `all` output both convergence & iteration information to a csv.--> - + @@ -5358,7 +5452,44 @@ When set to `all` output both convergence & iteration information to a csv.--> - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -5387,12 +5518,16 @@ Information output from lower logLevels is added with the desired log level + + + + diff --git a/src/coreComponents/schema/schema.xsd.other b/src/coreComponents/schema/schema.xsd.other index 380d963e2ef..c7cddccee7c 100644 --- a/src/coreComponents/schema/schema.xsd.other +++ b/src/coreComponents/schema/schema.xsd.other @@ -341,15 +341,11 @@ - - - - @@ -516,7 +512,7 @@ - + @@ -854,9 +850,29 @@ + + + + + + + + + + + + + + + + + + + + @@ -1516,7 +1532,7 @@ - + @@ -3423,7 +3439,7 @@ - + @@ -3451,7 +3467,7 @@ - + @@ -3470,11 +3486,11 @@ - + - + @@ -3484,7 +3500,7 @@ - + @@ -3494,11 +3510,11 @@ - + - + @@ -3508,7 +3524,7 @@ - + @@ -3518,7 +3534,7 @@ - + @@ -3528,7 +3544,7 @@ - + @@ -3552,7 +3568,7 @@ - + @@ -3570,7 +3586,7 @@ - + @@ -3582,7 +3598,7 @@ - + @@ -3594,7 +3610,7 @@ - + @@ -3602,11 +3618,11 @@ - + - + @@ -3629,7 +3645,7 @@ - + @@ -3655,7 +3671,7 @@ - + @@ -3676,7 +3692,7 @@ - + @@ -3706,7 +3722,7 @@ - + @@ -3720,7 +3736,7 @@ - + @@ -3751,7 +3767,7 @@ - + @@ -3796,7 +3812,7 @@ - + diff --git a/src/main/main.cpp b/src/main/main.cpp index 4673f2e228a..464e15a0497 100644 --- a/src/main/main.cpp +++ b/src/main/main.cpp @@ -23,12 +23,48 @@ #include "mainInterface/GeosxState.hpp" #include "mainInterface/version.hpp" +#include +#include // for signal() +#include using namespace geos; +static void +fpe_signal_handler( int sig, siginfo_t *sip, void *scp ) +{ + GEOS_UNUSED_VAR( sig ); + GEOS_UNUSED_VAR( scp ); + int fe_code = sip->si_code; + + printf( "In signal handler : " ); + + if( fe_code == ILL_ILLTRP ) + printf( "Illegal trap detected\n" ); + else + printf( "Code detected : %d\n", fe_code ); + + abort(); +} + +void enable_floating_point_exceptions() +{ + fenv_t env; + fegetenv( &env ); + + env.__fpcr = env.__fpcr | __fpcr_trap_invalid; + fesetenv( &env ); + + struct sigaction act; + act.sa_sigaction = fpe_signal_handler; + sigemptyset ( &act.sa_mask ); + act.sa_flags = SA_SIGINFO; + sigaction( SIGILL, &act, NULL ); +} +using namespace geos; int main( int argc, char *argv[] ) { + enable_floating_point_exceptions(); try { std::chrono::system_clock::time_point startTime = std::chrono::system_clock::now();