diff --git a/inputFiles/compositionalMultiphaseFlow/soreideWhitson/1D_100cells/1D_benchmark.xml b/inputFiles/compositionalMultiphaseFlow/soreideWhitson/1D_100cells/1D_benchmark.xml index 89b9cc7dc86..cc7e0796fa3 100644 --- a/inputFiles/compositionalMultiphaseFlow/soreideWhitson/1D_100cells/1D_benchmark.xml +++ b/inputFiles/compositionalMultiphaseFlow/soreideWhitson/1D_100cells/1D_benchmark.xml @@ -1,294 +1,397 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inputFiles/compositionalMultiphaseFlow/soreideWhitson/1D_100cells/1D_smoke.xml b/inputFiles/compositionalMultiphaseFlow/soreideWhitson/1D_100cells/1D_smoke.xml index 19a577bde98..322444e025d 100644 --- a/inputFiles/compositionalMultiphaseFlow/soreideWhitson/1D_100cells/1D_smoke.xml +++ b/inputFiles/compositionalMultiphaseFlow/soreideWhitson/1D_100cells/1D_smoke.xml @@ -1,294 +1,391 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inputFiles/compositionalMultiphaseWell/benchmarks/Class09Pb3/class09_pb3_drainageOnly_direct_base.xml b/inputFiles/compositionalMultiphaseWell/benchmarks/Class09Pb3/class09_pb3_drainageOnly_direct_base.xml index f2c31503e8a..01804189fd8 100644 --- a/inputFiles/compositionalMultiphaseWell/benchmarks/Class09Pb3/class09_pb3_drainageOnly_direct_base.xml +++ b/inputFiles/compositionalMultiphaseWell/benchmarks/Class09Pb3/class09_pb3_drainageOnly_direct_base.xml @@ -1,8 +1,6 @@ - - - - - + useSurfaceConditions="1" + surfacePressure="101325" + surfaceTemperature="288.71" + control="totalVolRate" + useMass="1"> + + + + - + - + + name="fluidTPFA"/> @@ -80,13 +84,16 @@ solidModelName="nullSolid" porosityModelName="rockPorosity" permeabilityModelName="rockPerm"/> + + + @@ -94,9 +101,8 @@ - + wettingNonWettingRelPermTableNames="{ waterRelativePermeabilityTable, gasRelativePermeabilityTable }"/> + - - - + + + + - @@ -152,11 +158,13 @@ name="initCO2CompFracTable" coordinates="{ -3238.2, -2506.13 }" values="{ 0.000001, 0.000001 }"/> + - @@ -165,37 +173,36 @@ name="waterRelativePermeabilityTable" coordinateFiles="{ tables/phaseVolumeFraction_water.txt }" voxelFile="tables/relPerm_water.txt"/> + + voxelFile="tables/relPerm_gas.txt"/> - + voxelFile="tables/capPres_water.txt"/> - + interpolation="linear"/> - + interpolation="linear"/> - - diff --git a/inputFiles/compositionalMultiphaseWell/benchmarks/Class09Pb3/class09_pb3_drainageOnly_iterative_base.xml b/inputFiles/compositionalMultiphaseWell/benchmarks/Class09Pb3/class09_pb3_drainageOnly_iterative_base.xml index 8e76b08905b..fe5f4272ad2 100644 --- a/inputFiles/compositionalMultiphaseWell/benchmarks/Class09Pb3/class09_pb3_drainageOnly_iterative_base.xml +++ b/inputFiles/compositionalMultiphaseWell/benchmarks/Class09Pb3/class09_pb3_drainageOnly_iterative_base.xml @@ -1,10 +1,7 @@ - - - - - - - - - - + + + + - + + + + - - + control="massRate" + useMass="1"> + + + + - - - + + name="fluidTPFA"/> - - - + + + - - - + wettingNonWettingRelPermTableNames="{ waterRelativePermeabilityTable, gasRelativePermeabilityTable }"/> - - - - - - - - + + + - @@ -204,11 +202,13 @@ name="initCO2CompFracTable" coordinates="{ -3238.2, -2506.13 }" values="{ 0.000001, 0.000001 }"/> + - @@ -217,43 +217,43 @@ name="waterRelativePermeabilityTable" coordinateFiles="{ tables/phaseVolumeFraction_water.txt }" voxelFile="tables/relPerm_water.txt"/> + + voxelFile="tables/relPerm_gas.txt"/> - + voxelFile="tables/capPres_water.txt"/> - + interpolation="linear"/> - + interpolation="linear"/> - diff --git a/inputFiles/compositionalMultiphaseWell/benchmarks/Class09Pb3/class09_pb3_hystRelperm_direct_base.xml b/inputFiles/compositionalMultiphaseWell/benchmarks/Class09Pb3/class09_pb3_hystRelperm_direct_base.xml index c731abcf696..01e328f6cfa 100644 --- a/inputFiles/compositionalMultiphaseWell/benchmarks/Class09Pb3/class09_pb3_hystRelperm_direct_base.xml +++ b/inputFiles/compositionalMultiphaseWell/benchmarks/Class09Pb3/class09_pb3_hystRelperm_direct_base.xml @@ -1,8 +1,6 @@ - - - - - + useSurfaceConditions="1" + surfacePressure="101325" + surfaceTemperature="288.71" + control="totalVolRate" + useMass="1"> + + + + - + - + + name="fluidTPFA"/> @@ -80,26 +84,27 @@ solidModelName="nullSolid" porosityModelName="rockPorosity" permeabilityModelName="rockPerm"/> + + + - - + - - - - - + + + + - @@ -157,59 +160,61 @@ name="initCO2CompFracTable" coordinates="{ -3238.2, -2506.13 }" values="{ 0.000001, 0.000001 }"/> + + - + voxelFile="tables/capPres_water.txt"/> - + interpolation="linear"/> - + interpolation="linear"/> - - + + + - - diff --git a/inputFiles/compositionalMultiphaseWell/benchmarks/Class09Pb3/class09_pb3_hystRelperm_iterative_base.xml b/inputFiles/compositionalMultiphaseWell/benchmarks/Class09Pb3/class09_pb3_hystRelperm_iterative_base.xml index ee1af0ef88d..b71cf698726 100644 --- a/inputFiles/compositionalMultiphaseWell/benchmarks/Class09Pb3/class09_pb3_hystRelperm_iterative_base.xml +++ b/inputFiles/compositionalMultiphaseWell/benchmarks/Class09Pb3/class09_pb3_hystRelperm_iterative_base.xml @@ -1,8 +1,6 @@ - - - - - + control="totalVolRate" + useMass="1"> + + + + - + - + + name="fluidTPFA"/> @@ -84,28 +88,27 @@ solidModelName="nullSolid" porosityModelName="rockPorosity" permeabilityModelName="rockPerm"/> + + + - - - - + - - + + + + - @@ -161,10 +164,12 @@ name="initCO2CompFracTable" coordinates="{ -3238.2, -2506.13 }" values="{ 0.000001, 0.000001 }"/> + + + + voxelFile="tables/relPerm_gas.txt"/> + voxelFile="tables/capPres_water.txt"/> - + interpolation="linear"/> - + interpolation="linear"/> - + + + - - diff --git a/inputFiles/compositionalMultiphaseWell/benchmarks/Egg/deadOilEgg_base_direct.xml b/inputFiles/compositionalMultiphaseWell/benchmarks/Egg/deadOilEgg_base_direct.xml index 048e88999d7..40d16d1a47a 100644 --- a/inputFiles/compositionalMultiphaseWell/benchmarks/Egg/deadOilEgg_base_direct.xml +++ b/inputFiles/compositionalMultiphaseWell/benchmarks/Egg/deadOilEgg_base_direct.xml @@ -1,7 +1,6 @@ - - - - + + + + - - - - + + + + - + + + + - + + + + - + + + + - + + + + - + + + + - + + + + - + + + + - + + + + - + + + + - + maxCompFractionChange="0.5" + useMass="1"> + + + + - - - - - - - - @@ -279,7 +350,6 @@ materialList="{ fluid }"/> - - - - - @@ -373,32 +439,27 @@ - - - + fieldName="wellElementConnectionRate"/> + fieldName="wellElementConnectionRate"/> + fieldName="wellElementConnectionRate"/> + fieldName="wellElementConnectionRate"/> - - diff --git a/inputFiles/compositionalMultiphaseWell/benchmarks/Egg/deadOilEgg_base_iterative.xml b/inputFiles/compositionalMultiphaseWell/benchmarks/Egg/deadOilEgg_base_iterative.xml index 615688bef78..3922880cf1a 100644 --- a/inputFiles/compositionalMultiphaseWell/benchmarks/Egg/deadOilEgg_base_iterative.xml +++ b/inputFiles/compositionalMultiphaseWell/benchmarks/Egg/deadOilEgg_base_iterative.xml @@ -1,7 +1,6 @@ - - - - + + + + - - - - + + + + - + + + + - + + + + - + + + + - + + + + - + + + + - + + + + - + + + + - + + + + - + + + + - + maxCompFractionChange="0.5" + useMass="1"> + + + + - - - - - - - - @@ -281,7 +352,6 @@ materialList="{ fluid }"/> - - - - - @@ -375,32 +441,27 @@ - - - + fieldName="wellElementConnectionRate"/> + fieldName="wellElementonnectionRate"/> + fieldName="wellElementConnectionRate"/> + fieldName="wellElementConnectionRate"/> - - diff --git a/inputFiles/compositionalMultiphaseWell/black_oil_wells_saturated_3d.xml b/inputFiles/compositionalMultiphaseWell/black_oil_wells_saturated_3d.xml index 7ebac080cbc..dba3b84b0c5 100644 --- a/inputFiles/compositionalMultiphaseWell/black_oil_wells_saturated_3d.xml +++ b/inputFiles/compositionalMultiphaseWell/black_oil_wells_saturated_3d.xml @@ -2,7 +2,6 @@ - - + - - - + + + + - + maxRelativePressureChange="0.1" + maxCompFractionChange="0.1" + useMass="1"> + + + + - - - - + - + - + - @@ -148,12 +157,14 @@ name="region" cellBlocks="{ * }" materialList="{ fluid, rock, relperm, cappres }"/> + + + materialList="{ fluid }"/> @@ -163,7 +174,7 @@ surfaceDensities="{ 800.907131537, 0.856234902739, 1020.3440 }" componentMolarWeight="{ 120e-3, 25e-3, 18e-3 }" tableFiles="{ pvto_bo.txt, pvtg_norv_bo.txt, pvtw_bo.txt }"/> - + - + - - + + + scale="0.2"/> + - + coordinates="{ 0.0, 0.05, 0.10, 0.15, 0.20, 0.25, 0.30, 0.35, 0.40, 0.45, 0.5, 0.55, 0.60, 0.65, 0.70, 0.75, 0.80, 0.85, 0.90, 0.95, 1.00 }" + values="{ 0, 0.0025, 0.0100, 0.0225, 0.0400, 0.0625, 0.0900, 0.1225, 0.1600, 0.2025, 0.2500, 0.3025, 0.3600, 0.4225, 0.4900, 0.5625, 0.6400, 0.7225, 0.8100, 0.9025, 1.0000 }"/> + + coordinates="{ 0.0, 0.05, 0.10, 0.15, 0.20, 0.25, 0.30, 0.35, 0.40, 0.45, 0.5, 0.55, 0.60, 0.65, 0.70, 0.75, 0.80, 0.85, 0.90, 0.95, 1.00 }" + values="{ 0, 0.0025, 0.0100, 0.0225, 0.0400, 0.0625, 0.0900, 0.1225, 0.1600, 0.2025, 0.2500, 0.3025, 0.3600, 0.4225, 0.4900, 0.5625, 0.6400, 0.7225, 0.8100, 0.9025, 1.0000 }"/> + + coordinates="{ 0.0, 0.05, 0.10, 0.15, 0.20, 0.25, 0.30, 0.35, 0.40, 0.45, 0.5, 0.55, 0.60, 0.65, 0.70, 0.75, 0.80, 0.85, 0.90, 0.95, 1.00 }" + values="{ 0, 0.0025, 0.0100, 0.0225, 0.0400, 0.0625, 0.0900, 0.1225, 0.1600, 0.2025, 0.2500, 0.3025, 0.3600, 0.4225, 0.4900, 0.5625, 0.6400, 0.7225, 0.8100, 0.9025, 1.0000 }"/> + + coordinates="{ 0.0, 0.05, 0.10, 0.15, 0.20, 0.25, 0.30, 0.35, 0.40, 0.45, 0.5, 0.55, 0.60, 0.65, 0.70, 0.75, 0.80, 0.85, 0.90, 0.95, 1.00 }" + values="{ 0, 0.0025, 0.0100, 0.0225, 0.0400, 0.0625, 0.0900, 0.1225, 0.1600, 0.2025, 0.2500, 0.3025, 0.3600, 0.4225, 0.4900, 0.5625, 0.6400, 0.7225, 0.8100, 0.9025, 1.0000 }"/> + coordinates="{ 0.0, 0.05, 0.10, 0.15, 0.20, 0.25, 0.30, 0.35, 0.40, 0.45, 0.5, 0.55, 0.60, 0.65, 0.70, 0.75, 0.80, 0.85, 0.90, 0.95, 1.00 }" + values="{ 10000, 9025, 8100, 7225, 6400, 5625, 4900, 4225, 3600, 3025, 2500, 2025, 1600, 1225, 900, 625, 400, 225, 100, 25, 0 }"/> + - + coordinates="{ 0.0, 0.05, 0.10, 0.15, 0.20, 0.25, 0.30, 0.35, 0.40, 0.45, 0.5, 0.55, 0.60, 0.65, 0.70, 0.75, 0.80, 0.85, 0.90, 0.95, 1.00 }" + values="{ 0, 50, 200, 450, 800, 1250, 1800, 2450, 3200, 4050, 5000, 6050, 7200, 8450, 9800, 11250, 12800, 14450, 16200, 18050, 20000 }"/> - + - + + fieldName="wellElementConnectionRate"/> - + - + - diff --git a/inputFiles/compositionalMultiphaseWell/black_oil_wells_saturated_3d_stone2.xml b/inputFiles/compositionalMultiphaseWell/black_oil_wells_saturated_3d_stone2.xml index ce669961545..a285a0d9773 100644 --- a/inputFiles/compositionalMultiphaseWell/black_oil_wells_saturated_3d_stone2.xml +++ b/inputFiles/compositionalMultiphaseWell/black_oil_wells_saturated_3d_stone2.xml @@ -2,7 +2,6 @@ - - + - - - + + + + - + maxRelativePressureChange="0.1" + maxCompFractionChange="0.1" + useMass="1"> + + + + - - - - - - - + + + + + + - - + - + - + - @@ -147,12 +157,14 @@ name="region" cellBlocks="{ * }" materialList="{ fluid, rock, relperm, cappres }"/> + + + materialList="{ fluid }"/> @@ -162,7 +174,7 @@ surfaceDensities="{ 800.907131537, 0.856234902739, 1020.3440 }" componentMolarWeight="{ 120e-3, 25e-3, 18e-3 }" tableFiles="{ pvto_bo.txt, pvtg_norv_bo.txt, pvtw_bo.txt }"/> - + - + - - + + + scale="0.2"/> + - + coordinates="{ 0.0, 0.05, 0.10, 0.15, 0.20, 0.25, 0.30, 0.35, 0.40, 0.45, 0.5, 0.55, 0.60, 0.65, 0.70, 0.75, 0.80, 0.85, 0.90, 0.95, 1.00 }" + values="{ 0, 0.0025, 0.0100, 0.0225, 0.0400, 0.0625, 0.0900, 0.1225, 0.1600, 0.2025, 0.2500, 0.3025, 0.3600, 0.4225, 0.4900, 0.5625, 0.6400, 0.7225, 0.8100, 0.9025, 1.0000 }"/> + + coordinates="{ 0.0, 0.05, 0.10, 0.15, 0.20, 0.25, 0.30, 0.35, 0.40, 0.45, 0.5, 0.55, 0.60, 0.65, 0.70, 0.75, 0.80, 0.85, 0.90, 0.95, 1.00 }" + values="{ 0, 0.0025, 0.0100, 0.0225, 0.0400, 0.0625, 0.0900, 0.1225, 0.1600, 0.2025, 0.2500, 0.3025, 0.3600, 0.4225, 0.4900, 0.5625, 0.6400, 0.7225, 0.8100, 0.9025, 1.0000 }"/> + + coordinates="{ 0.0, 0.05, 0.10, 0.15, 0.20, 0.25, 0.30, 0.35, 0.40, 0.45, 0.5, 0.55, 0.60, 0.65, 0.70, 0.75, 0.80, 0.85, 0.90, 0.95, 1.00 }" + values="{ 0, 0.0025, 0.0100, 0.0225, 0.0400, 0.0625, 0.0900, 0.1225, 0.1600, 0.2025, 0.2500, 0.3025, 0.3600, 0.4225, 0.4900, 0.5625, 0.6400, 0.7225, 0.8100, 0.9025, 1.0000 }"/> + + coordinates="{ 0.0, 0.05, 0.10, 0.15, 0.20, 0.25, 0.30, 0.35, 0.40, 0.45, 0.5, 0.55, 0.60, 0.65, 0.70, 0.75, 0.80, 0.85, 0.90, 0.95, 1.00 }" + values="{ 0, 0.0025, 0.0100, 0.0225, 0.0400, 0.0625, 0.0900, 0.1225, 0.1600, 0.2025, 0.2500, 0.3025, 0.3600, 0.4225, 0.4900, 0.5625, 0.6400, 0.7225, 0.8100, 0.9025, 1.0000 }"/> + coordinates="{ 0.0, 0.05, 0.10, 0.15, 0.20, 0.25, 0.30, 0.35, 0.40, 0.45, 0.5, 0.55, 0.60, 0.65, 0.70, 0.75, 0.80, 0.85, 0.90, 0.95, 1.00 }" + values="{ 10000, 9025, 8100, 7225, 6400, 5625, 4900, 4225, 3600, 3025, 2500, 2025, 1600, 1225, 900, 625, 400, 225, 100, 25, 0 }"/> + - + coordinates="{ 0.0, 0.05, 0.10, 0.15, 0.20, 0.25, 0.30, 0.35, 0.40, 0.45, 0.5, 0.55, 0.60, 0.65, 0.70, 0.75, 0.80, 0.85, 0.90, 0.95, 1.00 }" + values="{ 0, 50, 200, 450, 800, 1250, 1800, 2450, 3200, 4050, 5000, 6050, 7200, 8450, 9800, 11250, 12800, 14450, 16200, 18050, 20000 }"/> - + - + + fieldName="wellElementConnectionRate"/> - + - + - diff --git a/inputFiles/compositionalMultiphaseWell/black_oil_wells_unsaturated_3d.xml b/inputFiles/compositionalMultiphaseWell/black_oil_wells_unsaturated_3d.xml index 323b9a144c6..d2b7a5cd375 100644 --- a/inputFiles/compositionalMultiphaseWell/black_oil_wells_unsaturated_3d.xml +++ b/inputFiles/compositionalMultiphaseWell/black_oil_wells_unsaturated_3d.xml @@ -2,7 +2,6 @@ - - + - - - + + + + - + maxRelativePressureChange="0.1" + maxCompFractionChange="0.1" + useMass="1"> + + + + - - - - - + - - + - + - @@ -148,12 +158,14 @@ name="region" cellBlocks="{ * }" materialList="{ fluid, rock, relperm }"/> + + + materialList="{ fluid }"/> @@ -163,7 +175,7 @@ surfaceDensities="{ 800.907131537, 0.856234902739, 1020.3440 }" componentMolarWeight="{ 120e-3, 25e-3, 18e-3 }" tableFiles="{ pvto_bo.txt, pvtg_norv_bo.txt, pvtw_bo.txt }"/> - + - - + + + scale="0.1"/> + - + - + + fieldName="wellElementConnectionRate"/> - + name="vtkOutput"/> + - - diff --git a/inputFiles/compositionalMultiphaseWell/black_oil_wells_unsaturated_3d_stone2.xml b/inputFiles/compositionalMultiphaseWell/black_oil_wells_unsaturated_3d_stone2.xml index ea41c8adf9c..297511e60ae 100644 --- a/inputFiles/compositionalMultiphaseWell/black_oil_wells_unsaturated_3d_stone2.xml +++ b/inputFiles/compositionalMultiphaseWell/black_oil_wells_unsaturated_3d_stone2.xml @@ -1,260 +1,271 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inputFiles/compositionalMultiphaseWell/bos.xml b/inputFiles/compositionalMultiphaseWell/bos.xml new file mode 100644 index 00000000000..75b5a9826ad --- /dev/null +++ b/inputFiles/compositionalMultiphaseWell/bos.xml @@ -0,0 +1,307 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inputFiles/compositionalMultiphaseWell/compositional_multiphase_wells_1d.xml b/inputFiles/compositionalMultiphaseWell/compositional_multiphase_wells_1d.xml index bea2c48815d..0869a727917 100644 --- a/inputFiles/compositionalMultiphaseWell/compositional_multiphase_wells_1d.xml +++ b/inputFiles/compositionalMultiphaseWell/compositional_multiphase_wells_1d.xml @@ -23,28 +23,38 @@ targetRegions="{ Region1 }" temperature="297.15"/> - - - + + + + - + control="totalVolRate"> + + + + @@ -58,7 +68,6 @@ ny="{ 1 }" nz="{ 1 }" cellBlockNames="{ cb1 }"> - - + name="fluidTPFA"/> @@ -175,7 +182,6 @@ - - - - - - + + + + - + + + + - - - + control="totalVolRate"> + + + + @@ -69,7 +81,6 @@ ny="{ 20 }" nz="{ 1 }" cellBlockNames="{ cb1 }"> - - - + name="fluidTPFA"/> @@ -223,7 +231,6 @@ - - - - + - + + + + - + + + + - + control="totalVolRate" + maxRelativePressureChange="0.1" + maxCompFractionChange="0.1"> + + + + @@ -78,7 +97,6 @@ ny="{ 20 }" nz="{ 1 }" cellBlockNames="{ cb1 }"> - - - + name="fluidTPFA"/> @@ -223,7 +238,6 @@ - - - - + - - + - + + + + - + + + + - + control="totalVolRate" + maxRelativePressureChange="0.1" + maxCompFractionChange="0.1"> + + + + @@ -77,7 +96,6 @@ ny="{ 20 }" nz="{ 1 }" cellBlockNames="{ cb1 }"> - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inputFiles/compositionalMultiphaseWell/dome_soreide_whitson_base.xml b/inputFiles/compositionalMultiphaseWell/dome_soreide_whitson_base.xml index 2e00878f00e..d69956ea1de 100644 --- a/inputFiles/compositionalMultiphaseWell/dome_soreide_whitson_base.xml +++ b/inputFiles/compositionalMultiphaseWell/dome_soreide_whitson_base.xml @@ -1,287 +1,348 @@ + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inputFiles/compositionalMultiphaseWell/isothm_mass_inj_table.xml b/inputFiles/compositionalMultiphaseWell/isothm_mass_inj_table.xml index 14076fab917..9ecff209034 100644 --- a/inputFiles/compositionalMultiphaseWell/isothm_mass_inj_table.xml +++ b/inputFiles/compositionalMultiphaseWell/isothm_mass_inj_table.xml @@ -10,7 +10,7 @@ initialDt="1e2" targetRegions="{ region, injwell }"> - + targetRegions="{ region }"/> - - - + control="massRate" + useMass="1"> + + + + @@ -83,10 +86,8 @@ - - @@ -94,8 +95,6 @@ name="sink" xMin="{ 89.99, 89.99, -0.01 }" xMax="{ 101.01, 101.01, 1.01 }"/> - - - - @@ -143,25 +143,28 @@ name="region" cellBlocks="{ cb }" materialList="{ fluid, rock, relperm }"/> + - + + + @@ -181,11 +184,9 @@ phaseMinVolumeFraction="{ 0.0, 0.0 }" phaseRelPermExponent="{ 1.5, 1.5 }" phaseRelPermMaxValue="{ 0.9, 0.9 }"/> - - + + + - diff --git a/inputFiles/compositionalMultiphaseWell/isothm_vol_inj_table.xml b/inputFiles/compositionalMultiphaseWell/isothm_vol_inj_table.xml index d39421e36fc..1e191175e19 100644 --- a/inputFiles/compositionalMultiphaseWell/isothm_vol_inj_table.xml +++ b/inputFiles/compositionalMultiphaseWell/isothm_vol_inj_table.xml @@ -10,7 +10,7 @@ initialDt="1e2" targetRegions="{ region, injwell }"> - + targetRegions="{ region }"/> - - - + control="totalVolRate" + useMass="1"> + + + + @@ -82,10 +86,8 @@ - - @@ -93,8 +95,6 @@ name="sink" xMin="{ 89.99, 89.99, -0.01 }" xMax="{ 101.01, 101.01, 1.01 }"/> - - - - - @@ -148,25 +150,28 @@ name="region" cellBlocks="{ cb }" materialList="{ fluid, rock, relperm }"/> + - + + + @@ -186,11 +191,9 @@ phaseMinVolumeFraction="{ 0.0, 0.0 }" phaseRelPermExponent="{ 1.5, 1.5 }" phaseRelPermMaxValue="{ 0.9, 0.9 }"/> - - + + + - diff --git a/inputFiles/compositionalMultiphaseWell/resvol_constraint.xml b/inputFiles/compositionalMultiphaseWell/resvol_constraint.xml index 7fe917a2076..134b8a44552 100644 --- a/inputFiles/compositionalMultiphaseWell/resvol_constraint.xml +++ b/inputFiles/compositionalMultiphaseWell/resvol_constraint.xml @@ -23,31 +23,42 @@ targetRegions="{ Region1 }" temperature="297.15"/> - - - + + + + - + writeCSV="1"> + + + + @@ -61,7 +72,6 @@ ny="{ 1 }" nz="{ 1 }" cellBlockNames="{ cb1 }"> - - + - - + + name="fluidTPFA"/> @@ -192,7 +201,6 @@ - - - - - - - + useSurfaceConditions="1" + surfacePressure="101325" + surfaceTemperature="288.71" + control="totalVolRate" + maxCompFractionChange="0.2" + useMass="1"> + + + + - - - + - - - + - - - - - - - - @@ -185,42 +178,37 @@ - + sources="{ /Tasks/wellPressureCollection }" + filename="wellPressureHistory"/> - - - + fieldName="pressure"/> - + - - - - - + + + + + - diff --git a/inputFiles/compositionalMultiphaseWell/staged_perf_base.xml b/inputFiles/compositionalMultiphaseWell/staged_perf_base.xml index 302d728e9b4..d6359b2765e 100644 --- a/inputFiles/compositionalMultiphaseWell/staged_perf_base.xml +++ b/inputFiles/compositionalMultiphaseWell/staged_perf_base.xml @@ -10,11 +10,11 @@ targetRegions="{ reservoir, wellRegion1 }"> + directParallel="0"/> - - - + control="BHP"> + + + + - - + + name="fluidTPFA"/> @@ -91,7 +93,6 @@ - @@ -158,34 +159,31 @@ scale="1.0"/> - + name="equil" + objectPath="ElementRegions" + datumElevation="0" + datumPressure="2.214e7" + initialPhaseName="water" + componentNames="{ co2, water }" + componentFractionVsElevationTableNames="{ initCO2CompFracTable, initWaterCompFracTable }" + temperatureVsElevationTableName="initTempTable"/> + name="initCO2CompFracTable" + coordinates="{ -3000.0, 0.0 }" + values="{ 0.0, 0.0 }"/> + name="initWaterCompFracTable" + coordinates="{ -3000.0, 0.0 }" + values="{ 1.0, 1.0 }"/> - + name="initTempTable" + coordinates="{ -3000.0, 0.0 }" + values="{ 368, 288 }"/> diff --git a/inputFiles/compositionalMultiphaseWell/staircase_co2_wells_3d.xml b/inputFiles/compositionalMultiphaseWell/staircase_co2_wells_3d.xml index 388b15ca605..db9b88c034e 100644 --- a/inputFiles/compositionalMultiphaseWell/staircase_co2_wells_3d.xml +++ b/inputFiles/compositionalMultiphaseWell/staircase_co2_wells_3d.xml @@ -25,36 +25,47 @@ maxCompFractionChange="0.2" useMass="1"/> - - - + + + + - + maxCompFractionChange="0.2" + useMass="1"> + + + + - - - + cellBlockNames="{ cb-0_0_0, cb-1_0_0, cb-0_1_0, cb-1_1_0, cb-0_0_1, cb-1_0_1, cb-0_1_1, cb-1_1_1, cb-0_0_2, cb-1_0_2, cb-0_1_2, cb-1_1_2, cb-0_0_3, cb-1_0_3, cb-0_1_3, cb-1_1_3 }"> - + wettingNonWettingRelPermTableNames="{ waterRelativePermeabilityTable, gasRelativePermeabilityTable }"/> - @@ -222,35 +226,37 @@ scale="0.96"/> - + + voxelFile="relPerm_gas.txt"/> + + voxelFile="capPres_water.txt"/> + + - diff --git a/inputFiles/compositionalMultiphaseWell/staircase_co2_wells_hybrid_3d.xml b/inputFiles/compositionalMultiphaseWell/staircase_co2_wells_hybrid_3d.xml index b7e30e91dcb..3f398adb118 100644 --- a/inputFiles/compositionalMultiphaseWell/staircase_co2_wells_hybrid_3d.xml +++ b/inputFiles/compositionalMultiphaseWell/staircase_co2_wells_hybrid_3d.xml @@ -28,35 +28,48 @@ maxRelativePressureChange="0.2" useMass="1"/> - - - + + + + - + maxRelativePressureChange="0.2" + maxCompFractionChange="0.2" + useMass="1"> + + + + - - - - + diff --git a/inputFiles/poromechanics/PoroElastic_staircase_co2_3d_fim.xml b/inputFiles/poromechanics/PoroElastic_staircase_co2_3d_fim.xml index 0ca07929f8e..77dfa798145 100644 --- a/inputFiles/poromechanics/PoroElastic_staircase_co2_3d_fim.xml +++ b/inputFiles/poromechanics/PoroElastic_staircase_co2_3d_fim.xml @@ -1,13 +1,12 @@ - - - + + - - + + targetRegions="{ channel, barrier }"/> - - - + + + + - - + control="totalVolRate" + maxCompFractionChange="0.2" + useMass="1"> + + + + - + + - + + - + - - - diff --git a/inputFiles/poromechanics/PoroElastic_staircase_co2_3d_sequential.xml b/inputFiles/poromechanics/PoroElastic_staircase_co2_3d_sequential.xml index e978595a49f..4cebe6d253e 100755 --- a/inputFiles/poromechanics/PoroElastic_staircase_co2_3d_sequential.xml +++ b/inputFiles/poromechanics/PoroElastic_staircase_co2_3d_sequential.xml @@ -1,13 +1,12 @@ - - - + + - - + + targetRegions="{ channel, barrier }"/> - - - + + + + - - + control="totalVolRate" + maxCompFractionChange="0.2" + useMass="1"> + + + + - + + - + + - + - - diff --git a/inputFiles/poromechanics/PoroElastic_staircase_singlephase_3d_fim.xml b/inputFiles/poromechanics/PoroElastic_staircase_singlephase_3d_fim.xml index 95f2090ada1..ad32b0c1f53 100644 --- a/inputFiles/poromechanics/PoroElastic_staircase_singlephase_3d_fim.xml +++ b/inputFiles/poromechanics/PoroElastic_staircase_singlephase_3d_fim.xml @@ -1,13 +1,12 @@ - - - + + - - + + targetRegions="{ channel, barrier }"/> - - - + + + + - - + surfacePressure="101325" + control="totalVolRate"> + + + + - + minTime="-1e11" + maxTime="1e7"> - + + - + + - - + + - diff --git a/inputFiles/poromechanics/PoroElastic_staircase_singlephase_3d_sequential.xml b/inputFiles/poromechanics/PoroElastic_staircase_singlephase_3d_sequential.xml index 0c952c6934f..838f662c5a9 100755 --- a/inputFiles/poromechanics/PoroElastic_staircase_singlephase_3d_sequential.xml +++ b/inputFiles/poromechanics/PoroElastic_staircase_singlephase_3d_sequential.xml @@ -1,13 +1,12 @@ - - - + + - - + + targetRegions="{ channel, barrier }"/> - - - + + + + - - + surfacePressure="101325" + control="totalVolRate"> + + + + - + minTime="-1e11" + maxTime="1e7"> - + + - + - @@ -118,24 +125,25 @@ name="linearElasticityStatistics" solidSolverName="linearElasticity" logLevel="1"/> + - - + + - diff --git a/inputFiles/poromechanicsFractures/multiphasePoromechanics_FaultModel_well_fim_smoke.xml b/inputFiles/poromechanicsFractures/multiphasePoromechanics_FaultModel_well_fim_smoke.xml index 0ce10628d75..56de13ffa2b 100644 --- a/inputFiles/poromechanicsFractures/multiphasePoromechanics_FaultModel_well_fim_smoke.xml +++ b/inputFiles/poromechanicsFractures/multiphasePoromechanics_FaultModel_well_fim_smoke.xml @@ -1,14 +1,16 @@ + + - + - + maxAllowedResidualNorm="1e+15"/> + directParallel="0"/> + targetRegions="{ Region, Fault }" + discretization="FE1"/> + logLevel="3"/> - + targetRegions="{ Region, Fault }" + temperature="368.15"/> - - - + logLevel="2" + useMass="1" + writeCSV="1"> + + + + @@ -88,47 +96,44 @@ numElementsPerSegment="1"> + distanceFromHead="250"/> - + + - - - + target="/Outputs/vtkOutput"/> + target="/Solvers/reservoirSolver"/> + target="/Outputs/vtkOutput"/> + target="/Outputs/restartOutput"/> diff --git a/inputFiles/poromechanicsFractures/multiphasePoromechanics_FaultModel_well_seq_smoke.xml b/inputFiles/poromechanicsFractures/multiphasePoromechanics_FaultModel_well_seq_smoke.xml index d0959438756..4a30e02941e 100644 --- a/inputFiles/poromechanicsFractures/multiphasePoromechanics_FaultModel_well_seq_smoke.xml +++ b/inputFiles/poromechanicsFractures/multiphasePoromechanics_FaultModel_well_seq_smoke.xml @@ -1,14 +1,16 @@ + + - + - + maxAllowedResidualNorm="1e+15"/> + directParallel="0"/> + targetRegions="{ Region, Fault }" + discretization="FE1"/> + newtonMaxIter="20"/> + preconditionerType="mgr"/> - + targetRegions="{ Region, Fault }" + temperature="368.15"/> - - - + logLevel="2" + useMass="1" + writeCSV="1"> + + + + @@ -97,47 +105,44 @@ numElementsPerSegment="1"> + distanceFromHead="250"/> - + + - - - + target="/Outputs/vtkOutput"/> + target="/Solvers/reservoirSolver"/> + target="/Outputs/vtkOutput"/> + target="/Outputs/restartOutput"/> diff --git a/inputFiles/poromechanicsFractures/singlePhasePoromechanics_FaultModel_well_fim_new_smoke.xml b/inputFiles/poromechanicsFractures/singlePhasePoromechanics_FaultModel_well_fim_new_smoke.xml index 07a6e1521ff..72aad6f89b6 100644 --- a/inputFiles/poromechanicsFractures/singlePhasePoromechanics_FaultModel_well_fim_new_smoke.xml +++ b/inputFiles/poromechanicsFractures/singlePhasePoromechanics_FaultModel_well_fim_new_smoke.xml @@ -1,14 +1,16 @@ + + - + - + solverType="direct"/> - + logLevel="1"/> + targetRegions="{ Region, Fault }" + discretization="FE1"/> - + targetRegions="{ Region, Fault }"/> - - - + + + + - + control="BHP"> + + + + - + + - - - + - + - + solverType="direct"/> + targetRegions="{ Region, Fault }" + discretization="FE1"/> - + targetRegions="{ Region, Fault }"/> - - - + + + + - + control="BHP"> + + + + - + + - - - + - + - + directParallel="1"/> - + logLevel="1"/> + targetRegions="{ Region, Fault }" + discretization="FE1"/> - + targetRegions="{ Region, Fault }"/> - - - + + + + - + logLevel="2" + writeCSV="1" + type="injector"> + + + + - + + - - - + - + - + directParallel="1"/> + targetRegions="{ Region, Fault }" + discretization="FE1"/> - + targetRegions="{ Region, Fault }"/> - - - + + + + - + logLevel="2" + writeCSV="1" + type="injector"> + + + + - + + - - - + targetRegions="{ region , wellRegion2 }"> - + targetRegions="{ region }"/> - - - + control="BHP"> + + + + @@ -78,13 +82,13 @@ name="region" cellBlocks="{ * }" materialList="{ fluid, rock, thermalCond }"/> + - - - - diff --git a/inputFiles/singlePhaseWell/compressible_single_phase_wells_1d.xml b/inputFiles/singlePhaseWell/compressible_single_phase_wells_1d.xml index 4b5f64d0287..0fe903b3199 100644 --- a/inputFiles/singlePhaseWell/compressible_single_phase_wells_1d.xml +++ b/inputFiles/singlePhaseWell/compressible_single_phase_wells_1d.xml @@ -20,25 +20,35 @@ discretization="singlePhaseTPFA" targetRegions="{ Region1 }"/> - - - + + + + - + control="totalVolRate"> + + + + @@ -52,7 +62,6 @@ ny="{ 1 }" nz="{ 1 }" cellBlockNames="{ cb1 }"> - - - - - + + + + - + control="totalVolRate"> + + + + @@ -52,7 +62,6 @@ ny="{ 1 }" nz="{ 1 }" cellBlockNames="{ cb1 }"> - + distanceFromHead="1.45" + skinFactor="1"/> - - - + - + + + + - + control="totalVolRate"> + + + + @@ -53,7 +63,6 @@ ny="{ 1 }" nz="{ 1 }" cellBlockNames="{ cb1 }"> - - - - + - + + + + - + + + + - + initialPressureCoefficient="0.01" + control="totalVolRate"> + + + + @@ -60,7 +75,6 @@ ny="{ 20 }" nz="{ 1 }" cellBlockNames="{ cb1 }"> - - - + name="singlePhaseTPFA"/> diff --git a/inputFiles/singlePhaseWell/incompressible_single_phase_wells_hybrid_2d.xml b/inputFiles/singlePhaseWell/incompressible_single_phase_wells_hybrid_2d.xml index 743a1f7be70..28a95e82f30 100644 --- a/inputFiles/singlePhaseWell/incompressible_single_phase_wells_hybrid_2d.xml +++ b/inputFiles/singlePhaseWell/incompressible_single_phase_wells_hybrid_2d.xml @@ -21,32 +21,47 @@ discretization="singlePhaseHybridMimetic" targetRegions="{ Region1 }"/> - - - + + + + - + + + + - + control="totalVolRate"> + + + + @@ -60,7 +75,6 @@ ny="{ 20 }" nz="{ 1 }" cellBlockNames="{ cb1 }"> - - - - - + - + + + + - + + + + - + initialPressureCoefficient="0.01" + control="totalVolRate"> + + + + @@ -62,7 +76,6 @@ ny="{ 20 }" nz="{ 1 }" cellBlockNames="{ cb1 }"> - - - + name="singlePhaseTPFA"/> diff --git a/inputFiles/singlePhaseWell/staircase_single_phase_wells_3d.xml b/inputFiles/singlePhaseWell/staircase_single_phase_wells_3d.xml index 6789ab90542..55da1048afe 100644 --- a/inputFiles/singlePhaseWell/staircase_single_phase_wells_3d.xml +++ b/inputFiles/singlePhaseWell/staircase_single_phase_wells_3d.xml @@ -16,29 +16,39 @@ - - - + + + + - + control="totalVolRate"> + + + + @@ -51,11 +61,7 @@ nx="{ 5, 5 }" ny="{ 5, 5 }" nz="{ 3, 3, 3, 3 }" - cellBlockNames="{ cb-0_0_0, cb-1_0_0, cb-0_1_0, cb-1_1_0, - cb-0_0_1, cb-1_0_1, cb-0_1_1, cb-1_1_1, - cb-0_0_2, cb-1_0_2, cb-0_1_2, cb-1_1_2, - cb-0_0_3, cb-1_0_3, cb-0_1_3, cb-1_1_3 }"> - + cellBlockNames="{ cb-0_0_0, cb-1_0_0, cb-0_1_0, cb-1_1_0, cb-0_0_1, cb-1_0_1, cb-0_1_1, cb-1_1_1, cb-0_0_2, cb-1_0_2, cb-0_1_2, cb-1_1_2, cb-0_0_3, cb-1_0_3, cb-0_1_3, cb-1_1_3 }"> - @@ -172,9 +177,9 @@ diff --git a/inputFiles/singlePhaseWell/staircase_single_phase_wells_hybrid_3d.xml b/inputFiles/singlePhaseWell/staircase_single_phase_wells_hybrid_3d.xml index 2298a32e483..256250d8cc7 100644 --- a/inputFiles/singlePhaseWell/staircase_single_phase_wells_hybrid_3d.xml +++ b/inputFiles/singlePhaseWell/staircase_single_phase_wells_hybrid_3d.xml @@ -21,27 +21,37 @@ discretization="singlePhaseHybridMimetic" targetRegions="{ Channel }"/> - - - + + + + - + control="totalVolRate"> + + + + @@ -54,11 +64,7 @@ nx="{ 5, 5 }" ny="{ 5, 5 }" nz="{ 3, 3, 3, 3 }" - cellBlockNames="{ cb-0_0_0, cb-1_0_0, cb-0_1_0, cb-1_1_0, - cb-0_0_1, cb-1_0_1, cb-0_1_1, cb-1_1_1, - cb-0_0_2, cb-1_0_2, cb-0_1_2, cb-1_1_2, - cb-0_0_3, cb-1_0_3, cb-0_1_3, cb-1_1_3 }"> - + cellBlockNames="{ cb-0_0_0, cb-1_0_0, cb-0_1_0, cb-1_1_0, cb-0_0_1, cb-1_0_1, cb-0_1_1, cb-1_1_1, cb-0_0_2, cb-1_0_2, cb-0_1_2, cb-1_1_2, cb-0_0_3, cb-1_0_3, cb-0_1_3, cb-1_1_3 }"> - - + + referenceElevation="-0.01"/> + + diff --git a/src/coreComponents/integrationTests/wellsTests/CMakeLists.txt b/src/coreComponents/integrationTests/wellsTests/CMakeLists.txt index 5a30f2fe376..4aad90f9655 100644 --- a/src/coreComponents/integrationTests/wellsTests/CMakeLists.txt +++ b/src/coreComponents/integrationTests/wellsTests/CMakeLists.txt @@ -9,6 +9,8 @@ set( dependencyList mainInterface ) if( ENABLE_PVTPackage ) list( APPEND gtest_geosx_tests testOpenClosePerf.cpp + testThermalEstimatorProdWell.cpp + testThermalEstimatorInjWell.cpp testReservoirCompositionalMultiphaseMSWells.cpp testReservoirThermalSinglePhaseMSWells.cpp testReservoirThermalSinglePhaseMSWells_RateInj.cpp diff --git a/src/coreComponents/integrationTests/wellsTests/testIsothermalReservoirCompositionalMultiphaseMSWells.cpp b/src/coreComponents/integrationTests/wellsTests/testIsothermalReservoirCompositionalMultiphaseMSWells.cpp index da7d3606fb2..0de45dc3362 100644 --- a/src/coreComponents/integrationTests/wellsTests/testIsothermalReservoirCompositionalMultiphaseMSWells.cpp +++ b/src/coreComponents/integrationTests/wellsTests/testIsothermalReservoirCompositionalMultiphaseMSWells.cpp @@ -104,15 +104,20 @@ char const * xmlInput = logLevel="2" type="injector" control="totalVolRate" - referenceElevation="-0.01" - targetBHP="1.45e7" enableCrossflow="0" useSurfaceConditions="1" surfacePressure="1.45e7" - surfaceTemperature="300.15" - targetTotalRate="0.001" - injectionTemperature="300.15" - injectionStream="{ 1.0, 0.0 }"/> + surfaceTemperature="300.15"> + + + @@ -463,7 +468,7 @@ void testNumericalJacobian( CompositionalMultiphaseReservoirAndWells< Compositio wellElemCompDens.move( hostMemorySpace, false ); arrayView1d< real64 > const & connRate = - subRegion.getField< fields::well::mixtureConnectionRate >(); + subRegion.getField< fields::well::connectionRate >(); connRate.move( hostMemorySpace, false ); // a) compute all the derivatives wrt to the pressure in WELL elem iwelem @@ -580,6 +585,7 @@ class CompositionalMultiphaseReservoirSolverTest : public ::testing::Test solver->getSystemSolution() ); solver->implicitStepSetup( time, dt, domain ); + solver->wellSolver()->initializeWells( domain, time ); } static real64 constexpr time = 0.0; diff --git a/src/coreComponents/integrationTests/wellsTests/testIsothermalReservoirCompositionalMultiphaseSSWells.cpp b/src/coreComponents/integrationTests/wellsTests/testIsothermalReservoirCompositionalMultiphaseSSWells.cpp index 277c7a2a38c..58fe3e50a05 100644 --- a/src/coreComponents/integrationTests/wellsTests/testIsothermalReservoirCompositionalMultiphaseSSWells.cpp +++ b/src/coreComponents/integrationTests/wellsTests/testIsothermalReservoirCompositionalMultiphaseSSWells.cpp @@ -104,15 +104,20 @@ char const * xmlInput = logLevel="2" type="injector" control="totalVolRate" - referenceElevation="-0.01" - targetBHP="1.45e7" enableCrossflow="0" useSurfaceConditions="1" surfacePressure="1.45e7" - surfaceTemperature="300.15" - targetTotalRate="0.001" - injectionTemperature="300.15" - injectionStream="{ 1.0, 0.0 }"/> + surfaceTemperature="300.15"> + + + @@ -510,7 +515,7 @@ void testNumericalJacobian( CompositionalMultiphaseReservoirAndWells< Compositio wellElemCompDens.move( hostMemorySpace, false ); arrayView1d< real64 > const & connRate = - subRegion.getField< fields::well::mixtureConnectionRate >(); + subRegion.getField< fields::well::connectionRate >(); connRate.move( hostMemorySpace, false ); // a) compute all the derivatives wrt to the pressure in WELL elem iwelem @@ -531,7 +536,6 @@ void testNumericalJacobian( CompositionalMultiphaseReservoirAndWells< Compositio real64 const dP = perturbParameter * ( wellElemPressure[iwelem] + perturbParameter ); wellElemPressure.move( hostMemorySpace, true ); wellElemPressure[iwelem] += dP; - // after perturbing, update the pressure-dependent quantities in the well wellSolver.updateState( domain ); @@ -652,6 +656,7 @@ class CompositionalMultiphaseReservoirSolverTest : public ::testing::Test solver->getSystemSolution() ); solver->implicitStepSetup( time, dt, domain ); + solver->wellSolver()->initializeWells( domain, time ); } static real64 constexpr time = 0.0; diff --git a/src/coreComponents/integrationTests/wellsTests/testOpenClosePerf.cpp b/src/coreComponents/integrationTests/wellsTests/testOpenClosePerf.cpp index c45c9a21e68..5502909a7c5 100644 --- a/src/coreComponents/integrationTests/wellsTests/testOpenClosePerf.cpp +++ b/src/coreComponents/integrationTests/wellsTests/testOpenClosePerf.cpp @@ -61,13 +61,19 @@ char const * PreXmlInput = logLevel="1" targetRegions="{wellRegion1}" useMass="0"> - + + + + diff --git a/src/coreComponents/integrationTests/wellsTests/testReservoirCompositionalMultiphaseMSWells.cpp b/src/coreComponents/integrationTests/wellsTests/testReservoirCompositionalMultiphaseMSWells.cpp index f1ca32e9e81..8f33df720e6 100644 --- a/src/coreComponents/integrationTests/wellsTests/testReservoirCompositionalMultiphaseMSWells.cpp +++ b/src/coreComponents/integrationTests/wellsTests/testReservoirCompositionalMultiphaseMSWells.cpp @@ -61,21 +61,33 @@ char const * xmlInput = logLevel="1" targetRegions="{wellRegion1,wellRegion2}" useMass="0"> - - + + + + + + + + @@ -366,7 +378,7 @@ void testNumericalJacobian( CompositionalMultiphaseReservoirAndWells<> & solver, wellElemCompDens.move( hostMemorySpace, false ); arrayView1d< real64 > const & connRate = - subRegion.getField< fields::well::mixtureConnectionRate >(); + subRegion.getField< fields::well::connectionRate >(); connRate.move( hostMemorySpace, false ); // a) compute all the derivatives wrt to the pressure in WELL elem iwelem @@ -483,6 +495,7 @@ class CompositionalMultiphaseReservoirSolverTest : public ::testing::Test solver->getSystemSolution() ); solver->implicitStepSetup( time, dt, domain ); + solver->wellSolver()->initializeWells( domain, time ); } static real64 constexpr time = 0.0; diff --git a/src/coreComponents/integrationTests/wellsTests/testReservoirSinglePhaseMSWells.cpp b/src/coreComponents/integrationTests/wellsTests/testReservoirSinglePhaseMSWells.cpp index d75ad50a957..8fc88fd94ac 100644 --- a/src/coreComponents/integrationTests/wellsTests/testReservoirSinglePhaseMSWells.cpp +++ b/src/coreComponents/integrationTests/wellsTests/testReservoirSinglePhaseMSWells.cpp @@ -61,18 +61,31 @@ char const * PreXmlInput = - - + + + + + + + + + @@ -357,6 +370,8 @@ class SinglePhaseReservoirSolverTest : public ::testing::Test solver->getSystemSolution() ); solver->implicitStepSetup( TIME, DT, domain ); + + solver->wellSolver()->initializeWells( domain, TIME ); } void TestAssembleCouplingTerms() diff --git a/src/coreComponents/integrationTests/wellsTests/testReservoirThermalSinglePhaseMSWells.cpp b/src/coreComponents/integrationTests/wellsTests/testReservoirThermalSinglePhaseMSWells.cpp index d10f1da56f6..5baf73b0430 100644 --- a/src/coreComponents/integrationTests/wellsTests/testReservoirThermalSinglePhaseMSWells.cpp +++ b/src/coreComponents/integrationTests/wellsTests/testReservoirThermalSinglePhaseMSWells.cpp @@ -64,22 +64,35 @@ char const * PreXmlInput = logLevel="1" isThermal="1" targetRegions="{wellRegion1,wellRegion2}"> - - + + + + + + + + + @@ -515,6 +528,7 @@ class SinglePhaseReservoirSolverTest : public ::testing::Test solver->getSystemSolution() ); solver->implicitStepSetup( TIME, DT, domain ); + solver->wellSolver()->initializeWells( domain, TIME ); } void TestAssembleCouplingTerms() diff --git a/src/coreComponents/integrationTests/wellsTests/testReservoirThermalSinglePhaseMSWells_RateInj.cpp b/src/coreComponents/integrationTests/wellsTests/testReservoirThermalSinglePhaseMSWells_RateInj.cpp index 255b0201692..07ccbe5626a 100644 --- a/src/coreComponents/integrationTests/wellsTests/testReservoirThermalSinglePhaseMSWells_RateInj.cpp +++ b/src/coreComponents/integrationTests/wellsTests/testReservoirThermalSinglePhaseMSWells_RateInj.cpp @@ -79,19 +79,24 @@ char const * XmlInput = isThermal="1" writeCSV="1" targetRegions="{ wellRegion2 }"> - + + referenceElevation="-0.01"/> + + @@ -597,6 +602,8 @@ class SinglePhaseReservoirSolverTest : public ::testing::Test solver->getSystemSolution() ); solver->implicitStepSetup( TIME, DT, domain ); + + solver->wellSolver()->initializeWells( domain, TIME ); } void TestAssembleCouplingTerms() diff --git a/src/coreComponents/integrationTests/wellsTests/testThermalEstimatorInjWell.cpp b/src/coreComponents/integrationTests/wellsTests/testThermalEstimatorInjWell.cpp new file mode 100644 index 00000000000..12903f077f6 --- /dev/null +++ b/src/coreComponents/integrationTests/wellsTests/testThermalEstimatorInjWell.cpp @@ -0,0 +1,1003 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * SPDX-License-Identifier: LGPL-2.1-only + * + * Copyright (c) 2016-2024 Lawrence Livermore National Security LLC + * Copyright (c) 2018-2024 TotalEnergies + * Copyright (c) 2018-2024 The Board of Trustees of the Leland Stanford Junior University + * Copyright (c) 2023-2024 Chevron + * Copyright (c) 2019- GEOS/GEOSX Contributors + * All rights reserved + * + * See top level LICENSE, COPYRIGHT, CONTRIBUTORS, NOTICE, and ACKNOWLEDGEMENTS files for details. + * ------------------------------------------------------------------------------------------------------------ + */ + +#include "integrationTests/fluidFlowTests/testCompFlowUtils.hpp" + +#include "common/DataTypes.hpp" +#include "mainInterface/initialization.hpp" +#include "constitutive/fluid/multifluid/MultiFluidBase.hpp" +#include "mainInterface/ProblemManager.hpp" +#include "mesh/DomainPartition.hpp" +#include "mainInterface/GeosxState.hpp" +#include "mesh/WellElementSubRegion.hpp" +#include "physicsSolvers/PhysicsSolverManager.hpp" +#include "physicsSolvers/multiphysics/CompositionalMultiphaseReservoirAndWells.hpp" +#include "physicsSolvers/fluidFlow/CompositionalMultiphaseFVM.hpp" +#include "physicsSolvers/fluidFlow/wells/CompositionalMultiphaseWell.hpp" +#include "physicsSolvers/fluidFlow/wells/CompositionalMultiphaseWellFields.hpp" +#include "physicsSolvers/fluidFlow/wells/WellSolverBaseFields.hpp" +#include "physicsSolvers/fluidFlow/wells/kernels/CompositionalMultiphaseWellKernels.hpp" + +using namespace geos; +using namespace geos::dataRepository; +using namespace geos::constitutive; +using namespace geos::testing; + +CommandLineOptions g_commandLineOptions; + +void writeTableToFile( string const & filename, char const * str ) +{ + std::ofstream os( filename ); + ASSERT_TRUE( os.is_open() ); + os << str; + os.close(); +} + +void removeFile( string const & filename ) +{ + int const ret = std::remove( filename.c_str() ); + ASSERT_TRUE( ret == 0 ); +} +char const * co2flash = "FlashModel CO2Solubility 1e5 7.5e7 5e5 283.15 414.15 10 0\n"; +char const * pvtLiquid = "DensityFun PhillipsBrineDensity 1e5 7.5e7 5e5 283.15 414.15 10 0\n" + "ViscosityFun PhillipsBrineViscosity 0\n" + "EnthalpyFun BrineEnthalpy 1e5 7.5e7 5e5 283.15 414.15 10 0\n"; + +char const * pvtGas = "DensityFun SpanWagnerCO2Density 1e5 7.5e7 5e5 283.15 414.15 10\n" + "ViscosityFun FenghourCO2Viscosity 1e5 7.5e7 5e5 283.15 414.15 10\n" + "EnthalpyFun CO2Enthalpy 1e5 7.5e7 5e5 283.15 414.15 10\n"; +char const * xmlInput = + R"xml( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +)xml"; + +template< typename T, typename COL_INDEX > +void printCompareLocalMatrices( CRSMatrixView< T const, COL_INDEX const > const & matrix1, + CRSMatrixView< T const, COL_INDEX const > const & matrix2, std::string const & testName ) +{ + std::ofstream omat1( testName+".csv" ); + + + std::vector< std::vector< double > > fmat1( matrix1.numRows(), std::vector< double >( matrix1.numRows(), 0.0 )); + std::vector< std::vector< double > > fmat2( matrix2.numRows(), std::vector< double >( matrix2.numRows(), 0.0 )); + + for( localIndex i = 0; i < matrix1.numRows(); ++i ) + { + arraySlice1d< globalIndex const > indices1 = matrix1.getColumns( i ); + arraySlice1d< globalIndex const > indices2 = matrix2.getColumns( i ); + arraySlice1d< double const > values1 = matrix1.getEntries( i ); + arraySlice1d< double const > values2 = matrix2.getEntries( i ); + for( integer j=0; j const & rsd1, + array1d< real64 > const & rsd2, std::string const & testName ) +{ + std::ofstream omat1( testName+".csv" ); + + for( integer i=0; i +void testWellEstimatorNumericalJacobian( CompositionalMultiphaseReservoirAndWells< CompositionalMultiphaseBase > & solver, + DomainPartition & domain, + real64 const perturbParameter, + real64 const time_n, + real64 const relTol, std::string const & testName, + LAMBDA && assembleFunction ) +{ + CompositionalMultiphaseWell & wellSolver = *solver.wellSolver(); + CompositionalMultiphaseFVM & flowSolver = dynamicCast< CompositionalMultiphaseFVM & >( *solver.reservoirSolver() ); + + localIndex const NC = flowSolver.numFluidComponents(); + + CRSMatrix< real64, globalIndex > const & jacobian = wellSolver.getLocalMatrix(); + array1d< real64 > residual( jacobian.numRows() ); + DofManager const & dofManager = wellSolver.getDofManager(); + + // assemble the analytical residual + solver.resetStateToBeginningOfStep( domain ); + + residual.zero(); + jacobian.zero(); + + wellSolver.forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &, + MeshLevel & mesh, + string_array const & regionNames ) + { + mesh.getElemManager().forElementSubRegions< WellElementSubRegion >( regionNames, + [&]( localIndex const, + WellElementSubRegion & subRegion ) + { + WellControls & wellControls = wellSolver.getWellControls( subRegion ); + wellControls.setWellState( 1 ); + wellSolver.initializeWell( domain, mesh, subRegion, time_n ); + } ); + } ); + + assembleFunction( jacobian.toViewConstSizes(), residual.toView() ); + residual.move( hostMemorySpace, false ); + + // copy the analytical residual + array1d< real64 > residualOrig( residual ); + + // create the numerical jacobian + jacobian.move( hostMemorySpace ); + CRSMatrix< real64, globalIndex > jacobianFD( jacobian ); + jacobianFD.zero(); + + + string const wellDofKey = dofManager.getKey( wellSolver.wellElementDofName() ); + + // at this point we start assembling the finite-difference block by block + + + ///////////////////////////////////////////////// + // Step 2) Compute the terms in J_RW and J_WW // + ///////////////////////////////////////////////// + + // loop over the wells + if( 1 ) + wellSolver.forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &, + MeshLevel & mesh, + string_array const & regionNames ) + { + mesh.getElemManager().forElementSubRegions< WellElementSubRegion >( regionNames, + [&]( localIndex const, + WellElementSubRegion & subRegion ) + { + // get the degrees of freedom, ghosting info and next well elem index + arrayView1d< globalIndex const > const & wellElemDofNumber = + subRegion.getReference< array1d< globalIndex > >( wellDofKey ); + + // get the primary variables on the well elements + arrayView1d< real64 > const & wellElemPressure = + subRegion.getField< fields::well::pressure >(); + wellElemPressure.move( hostMemorySpace, false ); + + arrayView1d< real64 > const & wellElemTemperature = + subRegion.getField< fields::well::temperature >(); + wellElemTemperature.move( hostMemorySpace, false ); + + arrayView2d< real64, compflow::USD_COMP > const & wellElemCompDens = + subRegion.getField< fields::well::globalCompDensity >(); + wellElemCompDens.move( hostMemorySpace, false ); + + arrayView1d< real64 > const & connRate = + subRegion.getField< fields::well::mixtureConnectionRate >(); + connRate.move( hostMemorySpace, false ); + + // a) compute all the derivatives wrt to the pressure in WELL elem iwelem + for( localIndex iwelem = 0; iwelem < subRegion.size(); ++iwelem ) + { + + real64 wellElemTotalDensity = 0.0; + for( localIndex ic = 0; ic < NC; ++ic ) + { + wellElemTotalDensity += wellElemCompDens[iwelem][ic]; + } + + { + solver.resetStateToBeginningOfStep( domain ); + + // here is the perturbation in the pressure of the well element + real64 const dP = perturbParameter * ( wellElemPressure[iwelem] + perturbParameter ); + wellElemPressure.move( hostMemorySpace, true ); + wellElemPressure[iwelem] += dP; + + // after perturbing, update the pressure-dependent quantities in the well + wellSolver.updateState( domain ); + + residual.zero(); + jacobian.zero(); + assembleFunction( jacobian.toViewConstSizes(), residual.toView() ); + + fillNumericalJacobian( residual.toViewConst(), + residualOrig.toViewConst(), + wellElemDofNumber[iwelem] + compositionalMultiphaseWellKernels::ColOffset::DPRES, + dP, + jacobianFD.toViewConstSizes() ); + } + + for( localIndex jc = 0; jc < NC; ++jc ) + { + solver.resetStateToBeginningOfStep( domain ); + + real64 const dRho = perturbParameter * wellElemTotalDensity; + wellElemCompDens.move( hostMemorySpace, true ); + wellElemCompDens[iwelem][jc] += dRho; + + wellSolver.updateState( domain ); + + residual.zero(); + jacobian.zero(); + assembleFunction( jacobian.toViewConstSizes(), residual.toView() ); + + fillNumericalJacobian( residual.toViewConst(), + residualOrig.toViewConst(), + wellElemDofNumber[iwelem] + compositionalMultiphaseWellKernels::ColOffset::DCOMP + jc, + dRho, + jacobianFD.toViewConstSizes() ); + } + { + solver.resetStateToBeginningOfStep( domain ); + residual.zero(); + jacobian.zero(); + // here is the perturbation in the temperature of the well element + real64 const dT = perturbParameter * ( wellElemTemperature[iwelem] + perturbParameter ); + wellElemTemperature.move( hostMemorySpace, true ); + wellElemTemperature[iwelem] += dT; + + // after perturbing, update the pressure-dependent quantities in the well + wellSolver.updateState( domain ); + + + assembleFunction( jacobian.toViewConstSizes(), residual.toView() ); + fillNumericalJacobian( residual.toViewConst(), + residualOrig.toViewConst(), + wellElemDofNumber[iwelem] + compositionalMultiphaseWellKernels::ColOffset::DCOMP + NC+1, + dT, + jacobianFD.toViewConstSizes() ); + + } + } + + + // b) compute all the derivatives wrt to the connection in WELL elem + // iwelem + for( localIndex iwelem = 0; iwelem < subRegion.size(); ++iwelem ) + { + { + solver.resetStateToBeginningOfStep( domain ); + + // here is the perturbation in the rate of the well element + real64 const dRate = perturbParameter * ( connRate[iwelem] + perturbParameter ); + connRate.move( hostMemorySpace, true ); + connRate[iwelem] += dRate; + + wellSolver.updateState( domain ); + + residual.zero(); + jacobian.zero(); + assembleFunction( jacobian.toViewConstSizes(), residual.toView() ); + + fillNumericalJacobian( residual.toViewConst(), + residualOrig.toViewConst(), + wellElemDofNumber[iwelem] + compositionalMultiphaseWellKernels::ColOffset::DCOMP + NC, + dRate, + jacobianFD.toViewConstSizes() ); + } + } + } ); + } ); + + // assemble the analytical jacobian + solver.resetStateToBeginningOfStep( domain ); + + residual.zero(); + jacobian.zero(); + assembleFunction( jacobian.toViewConstSizes(), residual.toView() ); + printCompareLocalMatrices( jacobian.toViewConst(), jacobianFD.toViewConst(), testName ); + compareLocalMatrices( jacobian.toViewConst(), jacobianFD.toViewConst(), relTol ); +} +template< typename LAMBDA > +void testNumericalJacobian( CompositionalMultiphaseReservoirAndWells< CompositionalMultiphaseBase > & solver, + DomainPartition & domain, + real64 const perturbParameter, + real64 const relTol, std::string const & testName, + LAMBDA && assembleFunction ) +{ + CompositionalMultiphaseWell & wellSolver = *solver.wellSolver(); + CompositionalMultiphaseFVM & flowSolver = dynamicCast< CompositionalMultiphaseFVM & >( *solver.reservoirSolver() ); + + localIndex const NC = flowSolver.numFluidComponents(); + + CRSMatrix< real64, globalIndex > const & jacobian = solver.getLocalMatrix(); + array1d< real64 > residual( jacobian.numRows() ); + DofManager const & dofManager = solver.getDofManager(); + + // assemble the analytical residual + solver.resetStateToBeginningOfStep( domain ); + + residual.zero(); + jacobian.zero(); + + assembleFunction( jacobian.toViewConstSizes(), residual.toView() ); + residual.move( hostMemorySpace, false ); + + // copy the analytical residual + array1d< real64 > residualOrig( residual ); + + // create the numerical jacobian + jacobian.move( hostMemorySpace ); + CRSMatrix< real64, globalIndex > jacobianFD( jacobian ); + jacobianFD.zero(); + + string const resDofKey = dofManager.getKey( wellSolver.resElementDofName() ); + string const wellDofKey = dofManager.getKey( wellSolver.wellElementDofName() ); + + // at this point we start assembling the finite-difference block by block + + //////////////////////////////////////////////// + // Step 1) Compute the terms in J_RR and J_WR // + //////////////////////////////////////////////// + if( 1 ) + domain.forMeshBodies( [&] ( MeshBody & meshBody ) + { + meshBody.forMeshLevels( [&] ( MeshLevel & mesh ) + { + ElementRegionManager & elemManager = mesh.getElemManager(); + for( localIndex er = 0; er < elemManager.numRegions(); ++er ) + { + ElementRegionBase & elemRegion = elemManager.getRegion( er ); + elemRegion.forElementSubRegionsIndex< CellElementSubRegion >( [&]( localIndex const, CellElementSubRegion & subRegion ) + { + // get the degrees of freedom and ghosting information + arrayView1d< globalIndex const > const & dofNumber = + subRegion.getReference< array1d< globalIndex > >( resDofKey ); + + // get the primary variables on the reservoir elements + arrayView1d< real64 > const & pres = + subRegion.getField< fields::flow::pressure >(); + pres.move( hostMemorySpace, false ); + + arrayView2d< real64, compflow::USD_COMP > const & compDens = + subRegion.getField< fields::flow::globalCompDensity >(); + compDens.move( hostMemorySpace, false ); + + arrayView1d< real64 > const & temp = + subRegion.getField< fields::flow::temperature >(); + temp.move( hostMemorySpace, false ); + + // a) compute all the derivatives wrt to the pressure in RESERVOIR elem ei + for( localIndex ei = 0; ei < subRegion.size(); ++ei ) + { + real64 totalDensity = 0.0; + for( localIndex ic = 0; ic < NC; ++ic ) + { + totalDensity += compDens[ei][ic]; + } + + { + solver.resetStateToBeginningOfStep( domain ); + + // here is the perturbation in the pressure of the element + real64 const dP = perturbParameter * (pres[ei] + perturbParameter); + pres.move( hostMemorySpace, true ); + pres[ei] += dP; + + // after perturbing, update the pressure-dependent quantities in the reservoir + flowSolver.forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &, + MeshLevel & mesh2, + string_array const & regionNames2 ) + { + mesh2.getElemManager().forElementSubRegions( regionNames2, + [&]( localIndex const, + ElementSubRegionBase & subRegion2 ) + { + flowSolver.updateFluidState( subRegion2 ); + } ); + } ); + + wellSolver.updateState( domain ); + + residual.zero(); + jacobian.zero(); + assembleFunction( jacobian.toViewConstSizes(), residual.toView() ); + + fillNumericalJacobian( residual.toViewConst(), + residualOrig.toViewConst(), + dofNumber[ei], + dP, + jacobianFD.toViewConstSizes() ); + } + + for( localIndex jc = 0; jc < NC; ++jc ) + { + solver.resetStateToBeginningOfStep( domain ); + + real64 const dRho = perturbParameter * totalDensity; + compDens.move( hostMemorySpace, true ); + compDens[ei][jc] += dRho; + + flowSolver.forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &, + MeshLevel & mesh2, + string_array const & regionNames2 ) + { + mesh2.getElemManager().forElementSubRegions( regionNames2, + [&]( localIndex const, + ElementSubRegionBase & subRegion2 ) + { + flowSolver.updateFluidState( subRegion2 ); + } ); + } ); + wellSolver.updateState( domain ); + residual.zero(); + jacobian.zero(); + assembleFunction( jacobian.toViewConstSizes(), residual.toView() ); + + fillNumericalJacobian( residual.toViewConst(), + residualOrig.toViewConst(), + dofNumber[ei] + jc + 1, + dRho, + jacobianFD.toViewConstSizes() ); + } + { + solver.resetStateToBeginningOfStep( domain ); + + // here is the perturbation in the pressure of the element + real64 const dTemp = perturbParameter * (temp[ei] + perturbParameter); + temp.move( hostMemorySpace, true ); + temp[ei] += dTemp; + + // after perturbing, update the pressure-dependent quantities in the reservoir + flowSolver.forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &, + MeshLevel & mesh2, + string_array const & regionNames2 ) + { + mesh2.getElemManager().forElementSubRegions( regionNames2, + [&]( localIndex const, + ElementSubRegionBase & subRegion2 ) + { + flowSolver.updateFluidState( subRegion2 ); + } ); + } ); + + wellSolver.updateState( domain ); + + residual.zero(); + jacobian.zero(); + assembleFunction( jacobian.toViewConstSizes(), residual.toView() ); + + fillNumericalJacobian( residual.toViewConst(), + residualOrig.toViewConst(), + dofNumber[ei]+NC+1, + dTemp, + jacobianFD.toViewConstSizes() ); + } + } + } ); + } + } ); + + } ); + + ///////////////////////////////////////////////// + // Step 2) Compute the terms in J_RW and J_WW // + ///////////////////////////////////////////////// + + // loop over the wells + if( 1 ) + wellSolver.forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &, + MeshLevel & mesh, + string_array const & regionNames ) + { + mesh.getElemManager().forElementSubRegions< WellElementSubRegion >( regionNames, + [&]( localIndex const, + WellElementSubRegion & subRegion ) + { + // get the degrees of freedom, ghosting info and next well elem index + arrayView1d< globalIndex const > const & wellElemDofNumber = + subRegion.getReference< array1d< globalIndex > >( wellDofKey ); + + // get the primary variables on the well elements + arrayView1d< real64 > const & wellElemPressure = + subRegion.getField< fields::well::pressure >(); + wellElemPressure.move( hostMemorySpace, false ); + + arrayView1d< real64 > const & wellElemTemperature = + subRegion.getField< fields::well::temperature >(); + wellElemTemperature.move( hostMemorySpace, false ); + + arrayView2d< real64, compflow::USD_COMP > const & wellElemCompDens = + subRegion.getField< fields::well::globalCompDensity >(); + wellElemCompDens.move( hostMemorySpace, false ); + + arrayView1d< real64 > const & connRate = + subRegion.getField< fields::well::mixtureConnectionRate >(); + connRate.move( hostMemorySpace, false ); + + // a) compute all the derivatives wrt to the pressure in WELL elem iwelem + for( localIndex iwelem = 0; iwelem < subRegion.size(); ++iwelem ) + { + + real64 wellElemTotalDensity = 0.0; + for( localIndex ic = 0; ic < NC; ++ic ) + { + wellElemTotalDensity += wellElemCompDens[iwelem][ic]; + } + + { + solver.resetStateToBeginningOfStep( domain ); + + // here is the perturbation in the pressure of the well element + real64 const dP = perturbParameter * ( wellElemPressure[iwelem] + perturbParameter ); + wellElemPressure.move( hostMemorySpace, true ); + wellElemPressure[iwelem] += dP; + + // after perturbing, update the pressure-dependent quantities in the well + wellSolver.updateState( domain ); + + residual.zero(); + jacobian.zero(); + assembleFunction( jacobian.toViewConstSizes(), residual.toView() ); + + fillNumericalJacobian( residual.toViewConst(), + residualOrig.toViewConst(), + wellElemDofNumber[iwelem] + compositionalMultiphaseWellKernels::ColOffset::DPRES, + dP, + jacobianFD.toViewConstSizes() ); + } + + for( localIndex jc = 0; jc < NC; ++jc ) + { + solver.resetStateToBeginningOfStep( domain ); + + real64 const dRho = perturbParameter * wellElemTotalDensity; + wellElemCompDens.move( hostMemorySpace, true ); + wellElemCompDens[iwelem][jc] += dRho; + + wellSolver.updateState( domain ); + + residual.zero(); + jacobian.zero(); + assembleFunction( jacobian.toViewConstSizes(), residual.toView() ); + + fillNumericalJacobian( residual.toViewConst(), + residualOrig.toViewConst(), + wellElemDofNumber[iwelem] + compositionalMultiphaseWellKernels::ColOffset::DCOMP + jc, + dRho, + jacobianFD.toViewConstSizes() ); + } + { + solver.resetStateToBeginningOfStep( domain ); + residual.zero(); + jacobian.zero(); + // here is the perturbation in the temperature of the well element + real64 const dT = perturbParameter * ( wellElemTemperature[iwelem] + perturbParameter ); + wellElemTemperature.move( hostMemorySpace, true ); + wellElemTemperature[iwelem] += dT; + + // after perturbing, update the pressure-dependent quantities in the well + wellSolver.updateState( domain ); + + + assembleFunction( jacobian.toViewConstSizes(), residual.toView() ); + fillNumericalJacobian( residual.toViewConst(), + residualOrig.toViewConst(), + wellElemDofNumber[iwelem] + compositionalMultiphaseWellKernels::ColOffset::DCOMP + NC+1, + dT, + jacobianFD.toViewConstSizes() ); + + } + } + + + // b) compute all the derivatives wrt to the connection in WELL elem + // iwelem + for( localIndex iwelem = 0; iwelem < subRegion.size(); ++iwelem ) + { + { + solver.resetStateToBeginningOfStep( domain ); + + // here is the perturbation in the rate of the well element + real64 const dRate = perturbParameter * ( connRate[iwelem] + perturbParameter ); + connRate.move( hostMemorySpace, true ); + connRate[iwelem] += dRate; + + wellSolver.updateState( domain ); + + residual.zero(); + jacobian.zero(); + assembleFunction( jacobian.toViewConstSizes(), residual.toView() ); + + fillNumericalJacobian( residual.toViewConst(), + residualOrig.toViewConst(), + wellElemDofNumber[iwelem] + compositionalMultiphaseWellKernels::ColOffset::DCOMP + NC, + dRate, + jacobianFD.toViewConstSizes() ); + } + } + } ); + } ); + + // assemble the analytical jacobian + solver.resetStateToBeginningOfStep( domain ); + + residual.zero(); + jacobian.zero(); + assembleFunction( jacobian.toViewConstSizes(), residual.toView() ); + printCompareLocalMatrices( jacobian.toViewConst(), jacobianFD.toViewConst(), testName ); + compareLocalMatrices( jacobian.toViewConst(), jacobianFD.toViewConst(), relTol ); +} + +class CompositionalMultiphaseReservoirSolverTest : public ::testing::Test +{ +public: + + CompositionalMultiphaseReservoirSolverTest(): + state( std::make_unique< CommandLineOptions >( g_commandLineOptions ) ) + {} + +protected: + + void SetUp() override + { + setupProblemFromXML( state.getProblemManager(), xmlInput ); + solver = &state.getProblemManager().getPhysicsSolverManager().getGroup< CompositionalMultiphaseReservoirAndWells< CompositionalMultiphaseBase > >( "reservoirSystem" ); + + DomainPartition & domain = state.getProblemManager().getDomainPartition(); + + solver->setupSystem( domain, + solver->getDofManager(), + solver->getLocalMatrix(), + solver->getSystemRhs(), + solver->getSystemSolution() ); + + solver->wellSolver()->setupSystem( domain, + solver->wellSolver()->getDofManager(), + solver->wellSolver()->getLocalMatrix(), + solver->wellSolver()->getSystemRhs(), + solver->wellSolver()->getSystemSolution() ); + + solver->implicitStepSetup( time, dt, domain ); + + } + + static real64 constexpr time = 0.0; + static real64 constexpr dt = 1e4; + static real64 constexpr eps = std::numeric_limits< real64 >::epsilon(); + + GeosxState state; + CompositionalMultiphaseReservoirAndWells< CompositionalMultiphaseBase > * solver; +}; + +real64 constexpr CompositionalMultiphaseReservoirSolverTest::time; +real64 constexpr CompositionalMultiphaseReservoirSolverTest::dt; +real64 constexpr CompositionalMultiphaseReservoirSolverTest::eps; + + +TEST_F( CompositionalMultiphaseReservoirSolverTest, jacobianNumericalCheck_Perforation ) +{ + real64 const perturb = std::sqrt( eps ); + real64 const tol = 1e-1; // 10% error margin + + DomainPartition & domain = state.getProblemManager().getDomainPartition(); + + testWellEstimatorNumericalJacobian( *solver, domain, perturb, time, tol, "WellEstimator", + [&] ( CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) + { + + DofManager const & dofManager = solver->wellSolver()->getDofManager(); + solver->wellSolver()->forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &, + MeshLevel & mesh, + string_array const & regionNames ) + { + ElementRegionManager & elemManager = mesh.getElemManager(); + elemManager.forElementSubRegions< WellElementSubRegion >( regionNames, + [&]( localIndex const, + WellElementSubRegion & subRegion ) + { + // call assemble to fill the matrix and the rhs + solver->wellSolver()->assembleWellSystem( time, + dt, + elemManager, + subRegion, + dofManager, + localMatrix, + localRhs ); + +// apply boundary conditions to system + + solver->wellSolver()->applyWellBoundaryConditions( time, + dt, + elemManager, + subRegion, + dofManager, + localRhs, + localMatrix ); + + solver->wellSolver()->outputSingleWellDebug( time, dt, 0, 0, 0, mesh, subRegion, dofManager, localMatrix, localRhs ); + } ); + } ); + + + + } ); +} + + +int main( int argc, char * * argv ) +{ + writeTableToFile( "co2flash.txt", co2flash ); + writeTableToFile( "pvtliquid.txt", pvtLiquid ); + writeTableToFile( "pvtgas.txt", pvtGas ); + ::testing::InitGoogleTest( &argc, argv ); + g_commandLineOptions = *geos::basicSetup( argc, argv ); + int const result = RUN_ALL_TESTS(); + geos::basicCleanup(); + removeFile( "co2flash.txt" ); + removeFile( "pvtliquid.txt" ); + removeFile( "pvtgas.txt" ); + + return result; +} diff --git a/src/coreComponents/integrationTests/wellsTests/testThermalEstimatorProdWell.cpp b/src/coreComponents/integrationTests/wellsTests/testThermalEstimatorProdWell.cpp new file mode 100644 index 00000000000..8ef86902c2d --- /dev/null +++ b/src/coreComponents/integrationTests/wellsTests/testThermalEstimatorProdWell.cpp @@ -0,0 +1,1002 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * SPDX-License-Identifier: LGPL-2.1-only + * + * Copyright (c) 2016-2024 Lawrence Livermore National Security LLC + * Copyright (c) 2018-2024 TotalEnergies + * Copyright (c) 2018-2024 The Board of Trustees of the Leland Stanford Junior University + * Copyright (c) 2023-2024 Chevron + * Copyright (c) 2019- GEOS/GEOSX Contributors + * All rights reserved + * + * See top level LICENSE, COPYRIGHT, CONTRIBUTORS, NOTICE, and ACKNOWLEDGEMENTS files for details. + * ------------------------------------------------------------------------------------------------------------ + */ + +#include "integrationTests/fluidFlowTests/testCompFlowUtils.hpp" + +#include "common/DataTypes.hpp" +#include "mainInterface/initialization.hpp" +#include "constitutive/fluid/multifluid/MultiFluidBase.hpp" +#include "mainInterface/ProblemManager.hpp" +#include "mesh/DomainPartition.hpp" +#include "mainInterface/GeosxState.hpp" +#include "mesh/WellElementSubRegion.hpp" +#include "physicsSolvers/PhysicsSolverManager.hpp" +#include "physicsSolvers/multiphysics/CompositionalMultiphaseReservoirAndWells.hpp" +#include "physicsSolvers/fluidFlow/CompositionalMultiphaseFVM.hpp" +#include "physicsSolvers/fluidFlow/wells/CompositionalMultiphaseWell.hpp" +#include "physicsSolvers/fluidFlow/wells/CompositionalMultiphaseWellFields.hpp" +#include "physicsSolvers/fluidFlow/wells/WellSolverBaseFields.hpp" +#include "physicsSolvers/fluidFlow/wells/kernels/CompositionalMultiphaseWellKernels.hpp" + +using namespace geos; +using namespace geos::dataRepository; +using namespace geos::constitutive; +using namespace geos::testing; + +CommandLineOptions g_commandLineOptions; + +void writeTableToFile( string const & filename, char const * str ) +{ + std::ofstream os( filename ); + ASSERT_TRUE( os.is_open() ); + os << str; + os.close(); +} + +void removeFile( string const & filename ) +{ + int const ret = std::remove( filename.c_str() ); + ASSERT_TRUE( ret == 0 ); +} +char const * co2flash = "FlashModel CO2Solubility 1e5 7.5e7 5e5 283.15 414.15 10 0\n"; +char const * pvtLiquid = "DensityFun PhillipsBrineDensity 1e5 7.5e7 5e5 283.15 414.15 10 0\n" + "ViscosityFun PhillipsBrineViscosity 0\n" + "EnthalpyFun BrineEnthalpy 1e5 7.5e7 5e5 283.15 414.15 10 0\n"; + +char const * pvtGas = "DensityFun SpanWagnerCO2Density 1e5 7.5e7 5e5 283.15 414.15 10\n" + "ViscosityFun FenghourCO2Viscosity 1e5 7.5e7 5e5 283.15 414.15 10\n" + "EnthalpyFun CO2Enthalpy 1e5 7.5e7 5e5 283.15 414.15 10\n"; +char const * xmlInput = + R"xml( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +)xml"; + +template< typename T, typename COL_INDEX > +void printCompareLocalMatrices( CRSMatrixView< T const, COL_INDEX const > const & matrix1, + CRSMatrixView< T const, COL_INDEX const > const & matrix2, std::string const & testName ) +{ + std::ofstream omat1( testName+".csv" ); + + + std::vector< std::vector< double > > fmat1( matrix1.numRows(), std::vector< double >( matrix1.numRows(), 0.0 )); + std::vector< std::vector< double > > fmat2( matrix2.numRows(), std::vector< double >( matrix2.numRows(), 0.0 )); + + for( localIndex i = 0; i < matrix1.numRows(); ++i ) + { + arraySlice1d< globalIndex const > indices1 = matrix1.getColumns( i ); + arraySlice1d< globalIndex const > indices2 = matrix2.getColumns( i ); + arraySlice1d< double const > values1 = matrix1.getEntries( i ); + arraySlice1d< double const > values2 = matrix2.getEntries( i ); + for( integer j=0; j const & rsd1, + array1d< real64 > const & rsd2, std::string const & testName ) +{ + std::ofstream omat1( testName+".csv" ); + + for( integer i=0; i +void testWellEstimatorNumericalJacobian( CompositionalMultiphaseReservoirAndWells< CompositionalMultiphaseBase > & solver, + DomainPartition & domain, + real64 const perturbParameter, + real64 const time_n, + real64 const relTol, std::string const & testName, + LAMBDA && assembleFunction ) +{ + CompositionalMultiphaseWell & wellSolver = *solver.wellSolver(); + CompositionalMultiphaseFVM & flowSolver = dynamicCast< CompositionalMultiphaseFVM & >( *solver.reservoirSolver() ); + + localIndex const NC = flowSolver.numFluidComponents(); + + CRSMatrix< real64, globalIndex > const & jacobian = wellSolver.getLocalMatrix(); + array1d< real64 > residual( jacobian.numRows() ); + DofManager const & dofManager = wellSolver.getDofManager(); + + // assemble the analytical residual + solver.resetStateToBeginningOfStep( domain ); + + residual.zero(); + jacobian.zero(); + + wellSolver.forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &, + MeshLevel & mesh, + string_array const & regionNames ) + { + mesh.getElemManager().forElementSubRegions< WellElementSubRegion >( regionNames, + [&]( localIndex const, + WellElementSubRegion & subRegion ) + { + WellControls & wellControls = wellSolver.getWellControls( subRegion ); + wellControls.setWellState( 1 ); + wellSolver.initializeWell( domain, mesh, subRegion, time_n ); + } ); + } ); + + assembleFunction( jacobian.toViewConstSizes(), residual.toView() ); + residual.move( hostMemorySpace, false ); + + // copy the analytical residual + array1d< real64 > residualOrig( residual ); + + // create the numerical jacobian + jacobian.move( hostMemorySpace ); + CRSMatrix< real64, globalIndex > jacobianFD( jacobian ); + jacobianFD.zero(); + + + string const wellDofKey = dofManager.getKey( wellSolver.wellElementDofName() ); + + // at this point we start assembling the finite-difference block by block + + + ///////////////////////////////////////////////// + // Step 2) Compute the terms in J_RW and J_WW // + ///////////////////////////////////////////////// + + // loop over the wells + if( 1 ) + wellSolver.forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &, + MeshLevel & mesh, + string_array const & regionNames ) + { + mesh.getElemManager().forElementSubRegions< WellElementSubRegion >( regionNames, + [&]( localIndex const, + WellElementSubRegion & subRegion ) + { + // get the degrees of freedom, ghosting info and next well elem index + arrayView1d< globalIndex const > const & wellElemDofNumber = + subRegion.getReference< array1d< globalIndex > >( wellDofKey ); + + // get the primary variables on the well elements + arrayView1d< real64 > const & wellElemPressure = + subRegion.getField< fields::well::pressure >(); + wellElemPressure.move( hostMemorySpace, false ); + + arrayView1d< real64 > const & wellElemTemperature = + subRegion.getField< fields::well::temperature >(); + wellElemTemperature.move( hostMemorySpace, false ); + + arrayView2d< real64, compflow::USD_COMP > const & wellElemCompDens = + subRegion.getField< fields::well::globalCompDensity >(); + wellElemCompDens.move( hostMemorySpace, false ); + + arrayView1d< real64 > const & connRate = + subRegion.getField< fields::well::mixtureConnectionRate >(); + connRate.move( hostMemorySpace, false ); + + // a) compute all the derivatives wrt to the pressure in WELL elem iwelem + for( localIndex iwelem = 0; iwelem < subRegion.size(); ++iwelem ) + { + + real64 wellElemTotalDensity = 0.0; + for( localIndex ic = 0; ic < NC; ++ic ) + { + wellElemTotalDensity += wellElemCompDens[iwelem][ic]; + } + + { + solver.resetStateToBeginningOfStep( domain ); + + // here is the perturbation in the pressure of the well element + real64 const dP = perturbParameter * ( wellElemPressure[iwelem] + perturbParameter ); + wellElemPressure.move( hostMemorySpace, true ); + wellElemPressure[iwelem] += dP; + + // after perturbing, update the pressure-dependent quantities in the well + wellSolver.updateState( domain ); + + residual.zero(); + jacobian.zero(); + assembleFunction( jacobian.toViewConstSizes(), residual.toView() ); + + fillNumericalJacobian( residual.toViewConst(), + residualOrig.toViewConst(), + wellElemDofNumber[iwelem] + compositionalMultiphaseWellKernels::ColOffset::DPRES, + dP, + jacobianFD.toViewConstSizes() ); + } + + for( localIndex jc = 0; jc < NC; ++jc ) + { + solver.resetStateToBeginningOfStep( domain ); + + real64 const dRho = perturbParameter * wellElemTotalDensity; + wellElemCompDens.move( hostMemorySpace, true ); + wellElemCompDens[iwelem][jc] += dRho; + + wellSolver.updateState( domain ); + + residual.zero(); + jacobian.zero(); + assembleFunction( jacobian.toViewConstSizes(), residual.toView() ); + + fillNumericalJacobian( residual.toViewConst(), + residualOrig.toViewConst(), + wellElemDofNumber[iwelem] + compositionalMultiphaseWellKernels::ColOffset::DCOMP + jc, + dRho, + jacobianFD.toViewConstSizes() ); + } + { + solver.resetStateToBeginningOfStep( domain ); + residual.zero(); + jacobian.zero(); + // here is the perturbation in the temperature of the well element + real64 const dT = perturbParameter * ( wellElemTemperature[iwelem] + perturbParameter ); + wellElemTemperature.move( hostMemorySpace, true ); + wellElemTemperature[iwelem] += dT; + + // after perturbing, update the pressure-dependent quantities in the well + wellSolver.updateState( domain ); + + + assembleFunction( jacobian.toViewConstSizes(), residual.toView() ); + fillNumericalJacobian( residual.toViewConst(), + residualOrig.toViewConst(), + wellElemDofNumber[iwelem] + compositionalMultiphaseWellKernels::ColOffset::DCOMP + NC+1, + dT, + jacobianFD.toViewConstSizes() ); + + } + } + + + // b) compute all the derivatives wrt to the connection in WELL elem + // iwelem + for( localIndex iwelem = 0; iwelem < subRegion.size(); ++iwelem ) + { + { + solver.resetStateToBeginningOfStep( domain ); + + // here is the perturbation in the rate of the well element + real64 const dRate = perturbParameter * ( connRate[iwelem] + perturbParameter ); + connRate.move( hostMemorySpace, true ); + connRate[iwelem] += dRate; + + wellSolver.updateState( domain ); + + residual.zero(); + jacobian.zero(); + assembleFunction( jacobian.toViewConstSizes(), residual.toView() ); + + fillNumericalJacobian( residual.toViewConst(), + residualOrig.toViewConst(), + wellElemDofNumber[iwelem] + compositionalMultiphaseWellKernels::ColOffset::DCOMP + NC, + dRate, + jacobianFD.toViewConstSizes() ); + } + } + } ); + } ); + + // assemble the analytical jacobian + solver.resetStateToBeginningOfStep( domain ); + + residual.zero(); + jacobian.zero(); + assembleFunction( jacobian.toViewConstSizes(), residual.toView() ); + printCompareLocalMatrices( jacobian.toViewConst(), jacobianFD.toViewConst(), testName ); + compareLocalMatrices( jacobian.toViewConst(), jacobianFD.toViewConst(), relTol ); +} +template< typename LAMBDA > +void testNumericalJacobian( CompositionalMultiphaseReservoirAndWells< CompositionalMultiphaseBase > & solver, + DomainPartition & domain, + real64 const perturbParameter, + real64 const relTol, std::string const & testName, + LAMBDA && assembleFunction ) +{ + CompositionalMultiphaseWell & wellSolver = *solver.wellSolver(); + CompositionalMultiphaseFVM & flowSolver = dynamicCast< CompositionalMultiphaseFVM & >( *solver.reservoirSolver() ); + + localIndex const NC = flowSolver.numFluidComponents(); + + CRSMatrix< real64, globalIndex > const & jacobian = solver.getLocalMatrix(); + array1d< real64 > residual( jacobian.numRows() ); + DofManager const & dofManager = solver.getDofManager(); + + // assemble the analytical residual + solver.resetStateToBeginningOfStep( domain ); + + residual.zero(); + jacobian.zero(); + + assembleFunction( jacobian.toViewConstSizes(), residual.toView() ); + residual.move( hostMemorySpace, false ); + + // copy the analytical residual + array1d< real64 > residualOrig( residual ); + + // create the numerical jacobian + jacobian.move( hostMemorySpace ); + CRSMatrix< real64, globalIndex > jacobianFD( jacobian ); + jacobianFD.zero(); + + string const resDofKey = dofManager.getKey( wellSolver.resElementDofName() ); + string const wellDofKey = dofManager.getKey( wellSolver.wellElementDofName() ); + + // at this point we start assembling the finite-difference block by block + + //////////////////////////////////////////////// + // Step 1) Compute the terms in J_RR and J_WR // + //////////////////////////////////////////////// + if( 1 ) + domain.forMeshBodies( [&] ( MeshBody & meshBody ) + { + meshBody.forMeshLevels( [&] ( MeshLevel & mesh ) + { + ElementRegionManager & elemManager = mesh.getElemManager(); + for( localIndex er = 0; er < elemManager.numRegions(); ++er ) + { + ElementRegionBase & elemRegion = elemManager.getRegion( er ); + elemRegion.forElementSubRegionsIndex< CellElementSubRegion >( [&]( localIndex const, CellElementSubRegion & subRegion ) + { + // get the degrees of freedom and ghosting information + arrayView1d< globalIndex const > const & dofNumber = + subRegion.getReference< array1d< globalIndex > >( resDofKey ); + + // get the primary variables on the reservoir elements + arrayView1d< real64 > const & pres = + subRegion.getField< fields::flow::pressure >(); + pres.move( hostMemorySpace, false ); + + arrayView2d< real64, compflow::USD_COMP > const & compDens = + subRegion.getField< fields::flow::globalCompDensity >(); + compDens.move( hostMemorySpace, false ); + + arrayView1d< real64 > const & temp = + subRegion.getField< fields::flow::temperature >(); + temp.move( hostMemorySpace, false ); + + // a) compute all the derivatives wrt to the pressure in RESERVOIR elem ei + for( localIndex ei = 0; ei < subRegion.size(); ++ei ) + { + real64 totalDensity = 0.0; + for( localIndex ic = 0; ic < NC; ++ic ) + { + totalDensity += compDens[ei][ic]; + } + + { + solver.resetStateToBeginningOfStep( domain ); + + // here is the perturbation in the pressure of the element + real64 const dP = perturbParameter * (pres[ei] + perturbParameter); + pres.move( hostMemorySpace, true ); + pres[ei] += dP; + + // after perturbing, update the pressure-dependent quantities in the reservoir + flowSolver.forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &, + MeshLevel & mesh2, + string_array const & regionNames2 ) + { + mesh2.getElemManager().forElementSubRegions( regionNames2, + [&]( localIndex const, + ElementSubRegionBase & subRegion2 ) + { + flowSolver.updateFluidState( subRegion2 ); + } ); + } ); + + wellSolver.updateState( domain ); + + residual.zero(); + jacobian.zero(); + assembleFunction( jacobian.toViewConstSizes(), residual.toView() ); + + fillNumericalJacobian( residual.toViewConst(), + residualOrig.toViewConst(), + dofNumber[ei], + dP, + jacobianFD.toViewConstSizes() ); + } + + for( localIndex jc = 0; jc < NC; ++jc ) + { + solver.resetStateToBeginningOfStep( domain ); + + real64 const dRho = perturbParameter * totalDensity; + compDens.move( hostMemorySpace, true ); + compDens[ei][jc] += dRho; + + flowSolver.forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &, + MeshLevel & mesh2, + string_array const & regionNames2 ) + { + mesh2.getElemManager().forElementSubRegions( regionNames2, + [&]( localIndex const, + ElementSubRegionBase & subRegion2 ) + { + flowSolver.updateFluidState( subRegion2 ); + } ); + } ); + wellSolver.updateState( domain ); + residual.zero(); + jacobian.zero(); + assembleFunction( jacobian.toViewConstSizes(), residual.toView() ); + + fillNumericalJacobian( residual.toViewConst(), + residualOrig.toViewConst(), + dofNumber[ei] + jc + 1, + dRho, + jacobianFD.toViewConstSizes() ); + } + { + solver.resetStateToBeginningOfStep( domain ); + + // here is the perturbation in the pressure of the element + real64 const dTemp = perturbParameter * (temp[ei] + perturbParameter); + temp.move( hostMemorySpace, true ); + temp[ei] += dTemp; + + // after perturbing, update the pressure-dependent quantities in the reservoir + flowSolver.forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &, + MeshLevel & mesh2, + string_array const & regionNames2 ) + { + mesh2.getElemManager().forElementSubRegions( regionNames2, + [&]( localIndex const, + ElementSubRegionBase & subRegion2 ) + { + flowSolver.updateFluidState( subRegion2 ); + } ); + } ); + + wellSolver.updateState( domain ); + + residual.zero(); + jacobian.zero(); + assembleFunction( jacobian.toViewConstSizes(), residual.toView() ); + + fillNumericalJacobian( residual.toViewConst(), + residualOrig.toViewConst(), + dofNumber[ei]+NC+1, + dTemp, + jacobianFD.toViewConstSizes() ); + } + } + } ); + } + } ); + + } ); + + ///////////////////////////////////////////////// + // Step 2) Compute the terms in J_RW and J_WW // + ///////////////////////////////////////////////// + + // loop over the wells + if( 1 ) + wellSolver.forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &, + MeshLevel & mesh, + string_array const & regionNames ) + { + mesh.getElemManager().forElementSubRegions< WellElementSubRegion >( regionNames, + [&]( localIndex const, + WellElementSubRegion & subRegion ) + { + // get the degrees of freedom, ghosting info and next well elem index + arrayView1d< globalIndex const > const & wellElemDofNumber = + subRegion.getReference< array1d< globalIndex > >( wellDofKey ); + + // get the primary variables on the well elements + arrayView1d< real64 > const & wellElemPressure = + subRegion.getField< fields::well::pressure >(); + wellElemPressure.move( hostMemorySpace, false ); + + arrayView1d< real64 > const & wellElemTemperature = + subRegion.getField< fields::well::temperature >(); + wellElemTemperature.move( hostMemorySpace, false ); + + arrayView2d< real64, compflow::USD_COMP > const & wellElemCompDens = + subRegion.getField< fields::well::globalCompDensity >(); + wellElemCompDens.move( hostMemorySpace, false ); + + arrayView1d< real64 > const & connRate = + subRegion.getField< fields::well::mixtureConnectionRate >(); + connRate.move( hostMemorySpace, false ); + + // a) compute all the derivatives wrt to the pressure in WELL elem iwelem + for( localIndex iwelem = 0; iwelem < subRegion.size(); ++iwelem ) + { + + real64 wellElemTotalDensity = 0.0; + for( localIndex ic = 0; ic < NC; ++ic ) + { + wellElemTotalDensity += wellElemCompDens[iwelem][ic]; + } + + { + solver.resetStateToBeginningOfStep( domain ); + + // here is the perturbation in the pressure of the well element + real64 const dP = perturbParameter * ( wellElemPressure[iwelem] + perturbParameter ); + wellElemPressure.move( hostMemorySpace, true ); + wellElemPressure[iwelem] += dP; + + // after perturbing, update the pressure-dependent quantities in the well + wellSolver.updateState( domain ); + + residual.zero(); + jacobian.zero(); + assembleFunction( jacobian.toViewConstSizes(), residual.toView() ); + + fillNumericalJacobian( residual.toViewConst(), + residualOrig.toViewConst(), + wellElemDofNumber[iwelem] + compositionalMultiphaseWellKernels::ColOffset::DPRES, + dP, + jacobianFD.toViewConstSizes() ); + } + + for( localIndex jc = 0; jc < NC; ++jc ) + { + solver.resetStateToBeginningOfStep( domain ); + + real64 const dRho = perturbParameter * wellElemTotalDensity; + wellElemCompDens.move( hostMemorySpace, true ); + wellElemCompDens[iwelem][jc] += dRho; + + wellSolver.updateState( domain ); + + residual.zero(); + jacobian.zero(); + assembleFunction( jacobian.toViewConstSizes(), residual.toView() ); + + fillNumericalJacobian( residual.toViewConst(), + residualOrig.toViewConst(), + wellElemDofNumber[iwelem] + compositionalMultiphaseWellKernels::ColOffset::DCOMP + jc, + dRho, + jacobianFD.toViewConstSizes() ); + } + { + solver.resetStateToBeginningOfStep( domain ); + residual.zero(); + jacobian.zero(); + // here is the perturbation in the temperature of the well element + real64 const dT = perturbParameter * ( wellElemTemperature[iwelem] + perturbParameter ); + wellElemTemperature.move( hostMemorySpace, true ); + wellElemTemperature[iwelem] += dT; + + // after perturbing, update the pressure-dependent quantities in the well + wellSolver.updateState( domain ); + + + assembleFunction( jacobian.toViewConstSizes(), residual.toView() ); + fillNumericalJacobian( residual.toViewConst(), + residualOrig.toViewConst(), + wellElemDofNumber[iwelem] + compositionalMultiphaseWellKernels::ColOffset::DCOMP + NC+1, + dT, + jacobianFD.toViewConstSizes() ); + + } + } + + + // b) compute all the derivatives wrt to the connection in WELL elem + // iwelem + for( localIndex iwelem = 0; iwelem < subRegion.size(); ++iwelem ) + { + { + solver.resetStateToBeginningOfStep( domain ); + + // here is the perturbation in the rate of the well element + real64 const dRate = perturbParameter * ( connRate[iwelem] + perturbParameter ); + connRate.move( hostMemorySpace, true ); + connRate[iwelem] += dRate; + + wellSolver.updateState( domain ); + + residual.zero(); + jacobian.zero(); + assembleFunction( jacobian.toViewConstSizes(), residual.toView() ); + + fillNumericalJacobian( residual.toViewConst(), + residualOrig.toViewConst(), + wellElemDofNumber[iwelem] + compositionalMultiphaseWellKernels::ColOffset::DCOMP + NC, + dRate, + jacobianFD.toViewConstSizes() ); + } + } + } ); + } ); + + // assemble the analytical jacobian + solver.resetStateToBeginningOfStep( domain ); + + residual.zero(); + jacobian.zero(); + assembleFunction( jacobian.toViewConstSizes(), residual.toView() ); + printCompareLocalMatrices( jacobian.toViewConst(), jacobianFD.toViewConst(), testName ); + compareLocalMatrices( jacobian.toViewConst(), jacobianFD.toViewConst(), relTol ); +} + +class CompositionalMultiphaseReservoirSolverTest : public ::testing::Test +{ +public: + + CompositionalMultiphaseReservoirSolverTest(): + state( std::make_unique< CommandLineOptions >( g_commandLineOptions ) ) + {} + +protected: + + void SetUp() override + { + setupProblemFromXML( state.getProblemManager(), xmlInput ); + solver = &state.getProblemManager().getPhysicsSolverManager().getGroup< CompositionalMultiphaseReservoirAndWells< CompositionalMultiphaseBase > >( "reservoirSystem" ); + + DomainPartition & domain = state.getProblemManager().getDomainPartition(); + + solver->setupSystem( domain, + solver->getDofManager(), + solver->getLocalMatrix(), + solver->getSystemRhs(), + solver->getSystemSolution() ); + + solver->wellSolver()->setupSystem( domain, + solver->wellSolver()->getDofManager(), + solver->wellSolver()->getLocalMatrix(), + solver->wellSolver()->getSystemRhs(), + solver->wellSolver()->getSystemSolution() ); + + solver->implicitStepSetup( time, dt, domain ); + + } + + static real64 constexpr time = 0.0; + static real64 constexpr dt = 1e4; + static real64 constexpr eps = std::numeric_limits< real64 >::epsilon(); + + GeosxState state; + CompositionalMultiphaseReservoirAndWells< CompositionalMultiphaseBase > * solver; +}; + +real64 constexpr CompositionalMultiphaseReservoirSolverTest::time; +real64 constexpr CompositionalMultiphaseReservoirSolverTest::dt; +real64 constexpr CompositionalMultiphaseReservoirSolverTest::eps; + + +TEST_F( CompositionalMultiphaseReservoirSolverTest, jacobianNumericalCheck_Perforation ) +{ + real64 const perturb = std::sqrt( eps ); + real64 const tol = 1e-1; // 10% error margin + + DomainPartition & domain = state.getProblemManager().getDomainPartition(); + + testWellEstimatorNumericalJacobian( *solver, domain, perturb, time, tol, "WellEstimator", + [&] ( CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) + { + + DofManager const & dofManager = solver->wellSolver()->getDofManager(); + solver->wellSolver()->forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &, + MeshLevel & mesh, + string_array const & regionNames ) + { + ElementRegionManager & elemManager = mesh.getElemManager(); + elemManager.forElementSubRegions< WellElementSubRegion >( regionNames, + [&]( localIndex const, + WellElementSubRegion & subRegion ) + { + // call assemble to fill the matrix and the rhs + solver->wellSolver()->assembleWellSystem( time, + dt, + elemManager, + subRegion, + dofManager, + localMatrix, + localRhs ); + +// apply boundary conditions to system + + solver->wellSolver()->applyWellBoundaryConditions( time, + dt, + elemManager, + subRegion, + dofManager, + localRhs, + localMatrix ); + } ); + } ); + + + + } ); +} + + +int main( int argc, char * * argv ) +{ + writeTableToFile( "co2flash.txt", co2flash ); + writeTableToFile( "pvtliquid.txt", pvtLiquid ); + writeTableToFile( "pvtgas.txt", pvtGas ); + ::testing::InitGoogleTest( &argc, argv ); + g_commandLineOptions = *geos::basicSetup( argc, argv ); + int const result = RUN_ALL_TESTS(); + geos::basicCleanup(); + removeFile( "co2flash.txt" ); + removeFile( "pvtliquid.txt" ); + removeFile( "pvtgas.txt" ); + + return result; +} diff --git a/src/coreComponents/integrationTests/wellsTests/testThermalReservoirCompositionalMultiphaseMSWells.cpp b/src/coreComponents/integrationTests/wellsTests/testThermalReservoirCompositionalMultiphaseMSWells.cpp index 98aa57a5c84..308e21b106e 100644 --- a/src/coreComponents/integrationTests/wellsTests/testThermalReservoirCompositionalMultiphaseMSWells.cpp +++ b/src/coreComponents/integrationTests/wellsTests/testThermalReservoirCompositionalMultiphaseMSWells.cpp @@ -108,15 +108,20 @@ char const * xmlInput = logLevel="2" type="injector" control="totalVolRate" - referenceElevation="-0.01" - targetBHP="1.45e7" enableCrossflow="0" useSurfaceConditions="1" surfacePressure="1.45e7" - surfaceTemperature="300.15" - targetTotalRate="0.001" - injectionTemperature="300.15" - injectionStream="{ 0.99, 0.01 }"/> + surfaceTemperature="300.15"> + + + @@ -537,7 +542,7 @@ void testNumericalJacobian( CompositionalMultiphaseReservoirAndWells< Compositio wellElemCompDens.move( hostMemorySpace, false ); arrayView1d< real64 > const & connRate = - subRegion.getField< fields::well::mixtureConnectionRate >(); + subRegion.getField< fields::well::connectionRate >(); connRate.move( hostMemorySpace, false ); // a) compute all the derivatives wrt to the pressure in WELL elem iwelem @@ -699,6 +704,7 @@ class CompositionalMultiphaseReservoirSolverTest : public ::testing::Test solver->getSystemSolution() ); solver->implicitStepSetup( time, dt, domain ); + solver->wellSolver()->initializeWells( domain, time ); } static real64 constexpr time = 0.0; diff --git a/src/coreComponents/integrationTests/wellsTests/testThermalReservoirCompositionalMultiphaseSSWells.cpp b/src/coreComponents/integrationTests/wellsTests/testThermalReservoirCompositionalMultiphaseSSWells.cpp index 76fa20d4cea..2e368bba5c7 100644 --- a/src/coreComponents/integrationTests/wellsTests/testThermalReservoirCompositionalMultiphaseSSWells.cpp +++ b/src/coreComponents/integrationTests/wellsTests/testThermalReservoirCompositionalMultiphaseSSWells.cpp @@ -109,15 +109,20 @@ char const * xmlInput = logLevel="2" type="injector" control="totalVolRate" - referenceElevation="-0.01" - targetBHP="1.45e7" enableCrossflow="0" useSurfaceConditions="1" surfacePressure="1.45e7" - surfaceTemperature="300.15" - targetTotalRate="0.001" - injectionTemperature="300.15" - injectionStream="{ 0.99, 0.01 }"/> + surfaceTemperature="300.15"> + + + @@ -539,7 +544,7 @@ void testNumericalJacobian( CompositionalMultiphaseReservoirAndWells< Compositio wellElemCompDens.move( hostMemorySpace, false ); arrayView1d< real64 > const & connRate = - subRegion.getField< fields::well::mixtureConnectionRate >(); + subRegion.getField< fields::well::connectionRate >(); connRate.move( hostMemorySpace, false ); // a) compute all the derivatives wrt to the pressure in WELL elem iwelem @@ -688,6 +693,7 @@ class CompositionalMultiphaseReservoirSolverTest : public ::testing::Test solver->getSystemSolution() ); solver->implicitStepSetup( time, dt, domain ); + solver->wellSolver()->initializeWells( domain, time ); } static real64 constexpr time = 0.0; diff --git a/src/coreComponents/linearAlgebra/utilities/ComponentMask.hpp b/src/coreComponents/linearAlgebra/utilities/ComponentMask.hpp index a27d1a37a64..42eab7c568e 100644 --- a/src/coreComponents/linearAlgebra/utilities/ComponentMask.hpp +++ b/src/coreComponents/linearAlgebra/utilities/ComponentMask.hpp @@ -92,10 +92,10 @@ class ComponentMask private: /// Number of bits in mask storage - static constexpr int NUM_BITS = internal::roundToNextPowerOfTwo( MAX_COMP ); + static constexpr int NUM_BITS = geos::internal::roundToNextPowerOfTwo( MAX_COMP ); /// Type used to represent the bit mask - using mask_t = typename internal::ComponentMaskType< NUM_BITS >::type; + using mask_t = typename geos::internal::ComponentMaskType< NUM_BITS >::type; public: diff --git a/src/coreComponents/mesh/WellElementRegion.hpp b/src/coreComponents/mesh/WellElementRegion.hpp index ee3e8be547b..d93a564aa9b 100644 --- a/src/coreComponents/mesh/WellElementRegion.hpp +++ b/src/coreComponents/mesh/WellElementRegion.hpp @@ -106,6 +106,12 @@ class WellElementRegion : public ElementRegionBase */ void setWellControlsName( string const & name ) { m_wellControlsName = name; } + /** + * @return name the name of the WellControls object + */ + string const & getWellControlsName() const { return m_wellControlsName; } + + /** * @brief Get the name of the subRegion. * @return the name of the subRegion object diff --git a/src/coreComponents/mesh/WellElementSubRegion.hpp b/src/coreComponents/mesh/WellElementSubRegion.hpp index 26089a42838..4be7cf48e3b 100644 --- a/src/coreComponents/mesh/WellElementSubRegion.hpp +++ b/src/coreComponents/mesh/WellElementSubRegion.hpp @@ -223,6 +223,14 @@ class WellElementSubRegion : public ElementSubRegionBase m_topRank = rank; } + /** + * @brief Get for the MPI rank that owns this well (i.e. the top segment). + * @return the rank that owns the top well element + */ + int getTopRank() const + { + return m_topRank; + } /** * @brief Check if well is owned by current rank * @return true if the well is owned by current rank, false otherwise diff --git a/src/coreComponents/physicsSolvers/SolverStatistics.hpp b/src/coreComponents/physicsSolvers/SolverStatistics.hpp index 13b2d5c0a90..0cc8ee74282 100644 --- a/src/coreComponents/physicsSolvers/SolverStatistics.hpp +++ b/src/coreComponents/physicsSolvers/SolverStatistics.hpp @@ -184,6 +184,11 @@ class IterationsStatistics : public dataRepository::Group void closeFile() { if( m_CSVOutputOpened ) { m_logStream.close(); m_CSVOutputOpened = false; } } + /** + * @brief Get the number of time steps + */ + integer const & getNumTimeSteps() const { return m_numTimeSteps; } + protected: /// Number of time steps diff --git a/src/coreComponents/physicsSolvers/fluidFlow/CMakeLists.txt b/src/coreComponents/physicsSolvers/fluidFlow/CMakeLists.txt index 6eb55e9775a..80ed8fce152 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/CMakeLists.txt +++ b/src/coreComponents/physicsSolvers/fluidFlow/CMakeLists.txt @@ -129,11 +129,21 @@ set( fluidFlowSolvers_headers wells/SinglePhaseWellFields.hpp wells/WellConstants.hpp wells/WellControls.hpp - wells/WellSolverBase.hpp - wells/WellSolverBaseFields.hpp + wells/WellConstraintsBase.hpp + wells/WellInjectionConstraint.hpp + wells/WellProductionConstraint.hpp + wells/WellBHPConstraints.hpp + wells/WellVolumeRateConstraint.hpp + wells/WellMassRateConstraint.hpp + wells/WellPhaseVolumeRateConstraint.hpp + wells/WellLiquidRateConstraint.hpp + wells/WellManager.hpp + wells/WellNewtonSolver.hpp wells/LogLevelsInfo.hpp wells/kernels/SinglePhaseWellKernels.hpp wells/kernels/CompositionalMultiphaseWellKernels.hpp + wells/kernels/CompositionalMultiphaseWellConstraintKernels.hpp + wells/kernels/SinglePhaseWellConstraintKernels.hpp proppantTransport/ProppantTransport.hpp proppantTransport/ProppantTransportFields.hpp proppantTransport/ProppantTransportKernels.hpp ) @@ -165,7 +175,16 @@ set( fluidFlowSolvers_sources wells/SinglePhaseWell.cpp wells/kernels/SinglePhaseWellKernels.cpp wells/WellControls.cpp - wells/WellSolverBase.cpp + wells/WellConstraintsBase.cpp + wells/WellInjectionConstraint.cpp + wells/WellProductionConstraint.cpp + wells/WellBHPConstraints.cpp + wells/WellVolumeRateConstraint.cpp + wells/WellMassRateConstraint.cpp + wells/WellPhaseVolumeRateConstraint.cpp + wells/WellLiquidRateConstraint.cpp + wells/WellManager.cpp + wells/WellNewtonSolver.cpp proppantTransport/ProppantTransport.cpp proppantTransport/ProppantTransportKernels.cpp ) diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/CompositionalMultiphaseWell.cpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/CompositionalMultiphaseWell.cpp index 28def34e770..9a0c5015173 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/wells/CompositionalMultiphaseWell.cpp +++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/CompositionalMultiphaseWell.cpp @@ -50,6 +50,16 @@ #include "physicsSolvers/fluidFlow/kernels/compositional/FluidUpdateKernel.hpp" #include "physicsSolvers/fluidFlow/CompositionalMultiphaseStatistics.hpp" +#include "physicsSolvers/fluidFlow/wells/WellBHPConstraints.hpp" + +#include "physicsSolvers/fluidFlow/wells/WellVolumeRateConstraint.hpp" +#include "physicsSolvers/fluidFlow/wells/WellPhaseVolumeRateConstraint.hpp" +#include "physicsSolvers/fluidFlow/wells/WellMassRateConstraint.hpp" +#include "physicsSolvers/fluidFlow/wells/WellLiquidRateConstraint.hpp" +#include "physicsSolvers/fluidFlow/wells/kernels/CompositionalMultiphaseWellConstraintKernels.hpp" +#include "physicsSolvers/multiphysics/CoupledReservoirAndWellKernels.hpp" + + #if defined( __INTEL_COMPILER ) #pragma GCC optimize "O0" #endif @@ -64,15 +74,14 @@ using namespace fields; CompositionalMultiphaseWell::CompositionalMultiphaseWell( const string & name, Group * const parent ) : - WellSolverBase( name, parent ), + WellControls( name, parent ), m_useMass( false ), m_useTotalMassEquation( 1 ), m_maxCompFracChange( 1.0 ), m_maxRelativePresChange( 0.2 ), m_maxAbsolutePresChange( -1 ), // disabled by default m_minScalingFactor( 0.01 ), - m_allowCompDensChopping( 1 ), - m_targetPhaseIndex( -1 ) + m_allowCompDensChopping( 1 ) { this->registerWrapper( viewKeyStruct::useMassFlagString(), &m_useMass ). setApplyDefaultValue( 0 ). @@ -123,7 +132,7 @@ CompositionalMultiphaseWell::CompositionalMultiphaseWell( const string & name, void CompositionalMultiphaseWell::postInputInitialization() { - WellSolverBase::postInputInitialization(); + WellControls::postInputInitialization(); GEOS_ERROR_IF_GT_MSG( m_maxCompFracChange, 1.0, getWrapperDataContext( viewKeyStruct::maxCompFracChangeString() ) << @@ -136,28 +145,21 @@ void CompositionalMultiphaseWell::postInputInitialization() getWrapperDataContext( viewKeyStruct::maxRelativeCompDensChangeString() ) << ": The maximum relative change in component density must be larger than 0.0" ); } - -void CompositionalMultiphaseWell::registerDataOnMesh( Group & meshBodies ) +void CompositionalMultiphaseWell::setConstitutiveNames( ElementSubRegionBase & subRegion ) const +{ + setConstitutiveName< MultiFluidBase >( subRegion, viewKeyStruct::fluidNamesString(), "multiphase fluid" ); +} +void CompositionalMultiphaseWell::registerWellDataOnMesh( WellElementSubRegion & subRegion ) { - WellSolverBase::registerDataOnMesh( meshBodies ); + DomainPartition const & domain = this->getGroupByPath< DomainPartition >( "/Problem/domain" ); ConstitutiveManager const & cm = domain.getConstitutiveManager(); - - forDiscretizationOnMeshTargets( meshBodies, [&]( string const &, - MeshLevel & mesh, - string_array const & regionNames ) + setConstitutiveNames ( subRegion ); + if( m_referenceFluidModelName.empty() ) { - mesh.getElemManager().forElementSubRegions( regionNames, - [&]( localIndex const, - ElementSubRegionBase & subRegion ) - { - if( m_referenceFluidModelName.empty() ) - { - m_referenceFluidModelName = getConstitutiveName< MultiFluidBase >( subRegion ); - } - } ); - } ); + m_referenceFluidModelName = getConstitutiveName< MultiFluidBase >( subRegion ); + } // 1. Set key dimensions of the problem // Empty check needed to avoid errors when running in schema generation mode. @@ -172,131 +174,107 @@ void CompositionalMultiphaseWell::registerDataOnMesh( Group & meshBodies ) // 1 pressure + NC compositions + temp if thermal m_numDofPerResElement = isThermal() ? m_numComponents + 2 : m_numComponents + 1; - // loop over the wells - forDiscretizationOnMeshTargets( meshBodies, [&] ( string const &, - MeshLevel & mesh, - string_array const & regionNames ) + + WellControls::registerWellDataOnMesh( subRegion ); + + + + string const & fluidName = getConstitutiveName< MultiFluidBase >( subRegion ); + MultiFluidBase const & fluid = subRegion.getConstitutiveModel< MultiFluidBase >( fluidName ); + + // The resizing of the arrays needs to happen here, before the call to initializePreSubGroups, + // to make sure that the dimensions are properly set before the timeHistoryOutput starts its initialization. + subRegion.registerField< well::pressure >( getName() ); + subRegion.registerField< well::pressure_n >( getName() ); + + subRegion.registerField< well::temperature >( getName() ); + if( isThermal() ) { + subRegion.registerField< well::temperature_n >( getName() ); + } - ElementRegionManager & elemManager = mesh.getElemManager(); + subRegion.registerField< well::gravityCoefficient >( getName() ); - elemManager.forElementSubRegions< WellElementSubRegion >( regionNames, - [&]( localIndex const, - WellElementSubRegion & subRegion ) - { - string const & fluidName = getConstitutiveName< MultiFluidBase >( subRegion ); - MultiFluidBase const & fluid = subRegion.getConstitutiveModel< MultiFluidBase >( fluidName ); - - // The resizing of the arrays needs to happen here, before the call to initializePreSubGroups, - // to make sure that the dimensions are properly set before the timeHistoryOutput starts its initialization. - - subRegion.registerField< well::globalCompDensity >( getName() ). - reference().resizeDimension< 1 >( m_numComponents ); - subRegion.registerField< well::globalCompDensity_n >( getName() ). - reference().resizeDimension< 1 >( m_numComponents ); - - subRegion.registerField< well::mixtureConnectionRate >( getName() ); - subRegion.registerField< well::mixtureConnectionRate_n >( getName() ); - - subRegion.registerField< well::globalCompFraction >( getName() ). - setDimLabels( 1, fluid.componentNames() ). - reference().resizeDimension< 1 >( m_numComponents ); - subRegion.registerField< well::dGlobalCompFraction_dGlobalCompDensity >( getName() ). - reference().resizeDimension< 1, 2 >( m_numComponents, m_numComponents ); - - subRegion.registerField< well::phaseVolumeFraction >( getName() ). - setDimLabels( 1, fluid.phaseNames() ). - reference().resizeDimension< 1 >( m_numPhases ); - subRegion.registerField< well::dPhaseVolumeFraction >( getName() ). - reference().resizeDimension< 1, 2 >( m_numPhases, m_numComponents + 2 ); // dP, dT, dC - - subRegion.registerField< well::totalMassDensity >( getName() ); - subRegion.registerField< well::dTotalMassDensity >( getName() ). - reference().resizeDimension< 1 >( m_numComponents +2 ); // dP, dT, dC - - subRegion.registerField< well::phaseVolumeFraction_n >( getName() ). - reference().resizeDimension< 1 >( m_numPhases ); - - subRegion.registerField< well::pressureScalingFactor >( getName() ); - subRegion.registerField< well::temperatureScalingFactor >( getName() ); - subRegion.registerField< well::globalCompDensityScalingFactor >( getName() ); - - PerforationData & perforationData = *subRegion.getPerforationData(); - perforationData.registerField< well::compPerforationRate >( getName() ). - reference().resizeDimension< 1 >( m_numComponents ); - - perforationData.registerField< well::dCompPerforationRate >( getName() ). - reference().resizeDimension< 1, 2, 3 >( 2, m_numComponents, m_numComponents+ 2 ); - if( fluid.isThermal() ) - { - perforationData.registerField< well::energyPerforationFlux >( getName() ); - perforationData.registerField< well::dEnergyPerforationFlux >( getName() ). - reference().resizeDimension< 1, 2 >( 2, m_numComponents+2 ); - } - WellControls & wellControls = getWellControls( subRegion ); - wellControls.registerWrapper< real64 >( viewKeyStruct::currentBHPString() ); + subRegion.registerField< well::globalCompDensity >( getName() ). + reference().resizeDimension< 1 >( m_numComponents ); + subRegion.registerField< well::globalCompDensity_n >( getName() ). + reference().resizeDimension< 1 >( m_numComponents ); - wellControls.registerWrapper< array1d< real64 > >( viewKeyStruct::dCurrentBHPString() ). - setSizedFromParent( 0 ). - reference().resizeDimension< 0 >( m_numComponents + 2 ); // dP, dT, dC + subRegion.registerField< well::connectionRate >( getName() ); + subRegion.registerField< well::connectionRate_n >( getName() ); - wellControls.registerWrapper< array1d< real64 > >( viewKeyStruct::currentPhaseVolRateString() ). - setSizedFromParent( 0 ). - reference().resizeDimension< 0 >( m_numPhases ); + subRegion.registerField< well::globalCompFraction >( getName() ). + setDimLabels( 1, fluid.componentNames() ). + reference().resizeDimension< 1 >( m_numComponents ); + subRegion.registerField< well::dGlobalCompFraction_dGlobalCompDensity >( getName() ). + reference().resizeDimension< 1, 2 >( m_numComponents, m_numComponents ); - wellControls.registerWrapper< array2d< real64 > >( viewKeyStruct::dCurrentPhaseVolRateString() ). - setSizedFromParent( 0 ). - reference().resizeDimension< 0, 1 >( m_numPhases, m_numComponents + 3 ); // dP, dT, dC, dQ + subRegion.registerField< well::phaseVolumeFraction >( getName() ). + setDimLabels( 1, fluid.phaseNames() ). + reference().resizeDimension< 1 >( m_numPhases ); + subRegion.registerField< well::dPhaseVolumeFraction >( getName() ). + reference().resizeDimension< 1, 2 >( m_numPhases, m_numComponents + 2 ); // dP, dT, dC - wellControls.registerWrapper< real64 >( viewKeyStruct::massDensityString() ); + subRegion.registerField< well::totalMassDensity >( getName() ); + subRegion.registerField< well::dTotalMassDensity >( getName() ). + reference().resizeDimension< 1 >( m_numComponents +2 ); // dP, dT, dC - wellControls.registerWrapper< real64 >( viewKeyStruct::currentTotalVolRateString() ); - wellControls.registerWrapper< array1d< real64 > >( viewKeyStruct::dCurrentTotalVolRateString() ). - setSizedFromParent( 0 ). - reference().resizeDimension< 0 >( m_numComponents + 3 ); // dP, dT, dC dQ + subRegion.registerField< well::phaseVolumeFraction_n >( getName() ). + reference().resizeDimension< 1 >( m_numPhases ); - wellControls.registerWrapper< real64 >( viewKeyStruct::massDensityString() ); + subRegion.registerField< well::pressureScalingFactor >( getName() ); + subRegion.registerField< well::temperatureScalingFactor >( getName() ); + subRegion.registerField< well::globalCompDensityScalingFactor >( getName() ); - wellControls.registerWrapper< real64 >( viewKeyStruct::currentMassRateString() ); + PerforationData & perforationData = *subRegion.getPerforationData(); - // write rates output header - // the rank that owns the reference well element is responsible - if( m_writeCSV > 0 && subRegion.isLocallyOwned() ) - { - string const fileName = GEOS_FMT( "{}/{}.csv", m_ratesOutputDir, wellControls.getName() ); - string const massUnit = m_useMass ? "kg" : "mol"; - integer const useSurfaceConditions = wellControls.useSurfaceConditions(); - string const conditionKey = useSurfaceConditions ? "surface" : "reservoir"; - string const unitKey = useSurfaceConditions ? "s" : "r"; - integer const numPhase = m_numPhases; - integer const numComp = m_numComponents; - // format: time,bhp,total_rate,total_vol_rate,phase0_vol_rate,phase1_vol_rate,... - makeDirsForPath( m_ratesOutputDir ); - GEOS_LOG( GEOS_FMT( "{}: Rates CSV generated at {}", getName(), fileName ) ); - std::ofstream outputFile( fileName ); - outputFile << "Time [s],dt[s],BHP [Pa],Total rate [" << massUnit << "/s],Total " << conditionKey << " volumetric rate [" << unitKey << "m3/s]"; - for( integer ip = 0; ip < numPhase; ++ip ) - { - outputFile << ",Phase" << ip << " " << conditionKey << " volumetric rate [" << unitKey << "m3/s]"; - } - for( integer ic = 0; ic < numComp; ++ic ) - { - outputFile << ",Component" << ic << " rate [" << massUnit << "/s]"; - } - outputFile << std::endl; - outputFile.close(); - } - } ); - } ); + perforationData.registerField< well::gravityCoefficient >( getName() ); + perforationData.registerField< well::compPerforationRate >( getName() ). + reference().resizeDimension< 1 >( m_numComponents ); + + perforationData.registerField< well::dCompPerforationRate >( getName() ). + reference().resizeDimension< 1, 2, 3 >( 2, m_numComponents, m_numComponents+ 2 ); + if( fluid.isThermal() ) + { + perforationData.registerField< well::energyPerforationFlux >( getName() ); + perforationData.registerField< well::dEnergyPerforationFlux >( getName() ). + reference().resizeDimension< 1, 2 >( 2, m_numComponents+2 ); + } + + m_writeCSV=1; + // write rates output header + // the rank that owns the reference well element is responsible + if( m_writeCSV > 0 && subRegion.isLocallyOwned() ) + { + string const fileName = GEOS_FMT( "{}/{}.csv", m_ratesOutputDir, getName() ); + string const massUnit = m_useMass ? "kg" : "mol"; + integer const useSurfaceConditions = this->useSurfaceConditions(); + string const conditionKey = useSurfaceConditions ? "surface" : "reservoir"; + string const unitKey = useSurfaceConditions ? "s" : "r"; + integer const numPhase = m_numPhases; + integer const numComp = m_numComponents; + // format: time,bhp,total_rate,total_vol_rate,phase0_vol_rate,phase1_vol_rate,... + makeDirsForPath( m_ratesOutputDir ); + GEOS_LOG( GEOS_FMT( "{}: Rates CSV generated at {}", getName(), fileName ) ); + std::ofstream outputFile( fileName ); + outputFile << "Time [s],dt[s],BHP [Pa],Total rate [" << massUnit << "/s],Total " << conditionKey << " volumetric rate [" << unitKey << "m3/s]"; + for( integer ip = 0; ip < numPhase; ++ip ) + { + outputFile << ",Phase" << ip << " " << conditionKey << " volumetric rate [" << unitKey << "m3/s]"; + } + for( integer ic = 0; ic < numComp; ++ic ) + { + outputFile << ",Component" << ic << " rate [" << massUnit << "/s]"; + } + outputFile << std::endl; + outputFile.close(); + } -} -void CompositionalMultiphaseWell::setConstitutiveNames( ElementSubRegionBase & subRegion ) const -{ - setConstitutiveName< MultiFluidBase >( subRegion, viewKeyStruct::fluidNamesString(), "multiphase fluid" ); } + namespace { @@ -340,15 +318,18 @@ void compareMulticomponentModels( MODEL1_TYPE const & lhs, MODEL2_TYPE const & r * @brief Checks if the WellControls parameters are within the fluid tables ranges * @param fluid the fluid to check */ -void CompositionalMultiphaseWell::validateWellControlsForFluid( WellControls const & wellControls, - MultiFluidBase const & fluid ) const +void CompositionalMultiphaseWell::validateFluidModel( + constitutive::MultiFluidBase const & fluid, constitutive::MultiFluidBase const & referenceFluid ) const { - if( wellControls.useSurfaceConditions() ) + + compareMultiphaseModels( fluid, referenceFluid ); + compareMulticomponentModels( fluid, referenceFluid ); + if( useSurfaceConditions() ) { try { - real64 const & surfaceTemp = wellControls.getSurfaceTemperature(); - real64 const & surfacePres = wellControls.getSurfacePressure(); + real64 const & surfaceTemp = getSurfaceTemperature(); + real64 const & surfacePres = getSurfacePressure(); fluid.checkTablesParameters( surfacePres, surfaceTemp ); } catch( SimulationError const & ex ) { @@ -361,99 +342,33 @@ void CompositionalMultiphaseWell::validateWellControlsForFluid( WellControls con } } -void CompositionalMultiphaseWell::validateConstitutiveModels( DomainPartition const & domain ) const -{ - GEOS_MARK_FUNCTION; - - ConstitutiveManager const & cm = domain.getConstitutiveManager(); - CompositionalMultiphaseBase const & flowSolver = getParent().getGroup< CompositionalMultiphaseBase >( getFlowSolverName() ); - string const referenceFluidName = flowSolver.referenceFluidModelName(); - MultiFluidBase const & referenceFluid = cm.getConstitutiveRelation< MultiFluidBase >( m_referenceFluidModelName ); - - forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &, - MeshLevel const & mesh, - string_array const & regionNames ) - { - - mesh.getElemManager().forElementSubRegions< WellElementSubRegion >( regionNames, [&]( localIndex const, - WellElementSubRegion const & subRegion ) - { - string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString() ); - MultiFluidBase const & fluid = getConstitutiveModel< MultiFluidBase >( subRegion, fluidName ); - compareMultiphaseModels( fluid, referenceFluid ); - compareMulticomponentModels( fluid, referenceFluid ); - - WellControls const & wellControls = getWellControls( subRegion ); - validateWellControlsForFluid( wellControls, fluid ); - } ); - - } ); -} - -void CompositionalMultiphaseWell::validateInjectionStreams( WellElementSubRegion const & subRegion ) const -{ - WellControls const & wellControls = getWellControls( subRegion ); - - // check well injection stream for injectors - if( wellControls.isInjector()) - { - arrayView1d< real64 const > const & injectionStream = wellControls.getInjectionStream(); - - integer const streamSize = injectionStream.size(); - GEOS_THROW_IF( ( streamSize == 0 ), - "WellControls " << wellControls.getName() << - " : Injection stream not specified for well " << subRegion.getName(), - InputError ); - GEOS_THROW_IF( ( streamSize != m_numComponents ), - "WellControls " << wellControls.getName() << - " : Injection stream for well " << subRegion.getName() << " should have " << - m_numComponents << " components.", - InputError ); - - real64 compFracSum = 0; - for( integer ic = 0; ic < m_numComponents; ++ic ) - { - real64 const compFrac = injectionStream[ic]; - GEOS_THROW_IF( ( compFrac < 0.0 ) || ( compFrac > 1.0 ), - "WellControls " << wellControls.getDataContext() << - ": Invalid injection stream for well " << subRegion.getName(), - InputError, wellControls.getDataContext() ); - compFracSum += compFrac; - } - GEOS_THROW_IF( ( compFracSum < 1.0 - std::numeric_limits< real64 >::epsilon() ) || - ( compFracSum > 1.0 + std::numeric_limits< real64 >::epsilon() ), - "WellControls " << wellControls.getDataContext() << - ": Invalid injection stream for well " << subRegion.getName(), - InputError, wellControls.getDataContext() ); - } -} - void CompositionalMultiphaseWell::validateWellConstraints( real64 const & time_n, real64 const & GEOS_UNUSED_PARAM( dt ), WellElementSubRegion const & subRegion ) { - WellControls & wellControls = getWellControls( subRegion ); - if( !wellControls.useSurfaceConditions() ) + GEOS_UNUSED_VAR( time_n ); + + if( !useSurfaceConditions() ) { - bool const useSeg =wellControls.referenceReservoirRegion().empty(); + bool const useSeg = getReferenceReservoirRegion().empty(); GEOS_WARNING_IF( useSeg, "WellControls " <( getFlowSolverName() ); + string const regionName = getReferenceReservoirRegion(); + CompositionalMultiphaseBase const & flowSolver = getParent().getParent().getGroup< CompositionalMultiphaseBase >( getFlowSolverName() ); string_array const & targetRegionsNames = flowSolver.getTargetRegionNames(); auto const pos = std::find( targetRegionsNames.begin(), targetRegionsNames.end(), regionName ); GEOS_ERROR_IF( pos == targetRegionsNames.end(), GEOS_FMT( "{}: Region {} is not a target of the reservoir solver and cannot be used for referenceReservoirRegion in WellControl {}.", - getDataContext(), regionName, wellControls.getName() ) ); + getDataContext(), regionName, getName() ) ); } @@ -461,135 +376,66 @@ void CompositionalMultiphaseWell::validateWellConstraints( real64 const & time_n string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString()); MultiFluidBase const & fluid = subRegion.getConstitutiveModel< MultiFluidBase >( fluidName ); - WellControls::Control const currentControl = wellControls.getControl(); - real64 const & targetTotalRate = wellControls.getTargetTotalRate( time_n ); - real64 const & targetPhaseRate = wellControls.getTargetPhaseRate( time_n ); - real64 const & targetMassRate = wellControls.getTargetMassRate( time_n ); - - GEOS_THROW_IF( wellControls.isInjector() && currentControl == WellControls::Control::PHASEVOLRATE, - "WellControls " << wellControls.getDataContext() << - ": Phase rate control is not available for injectors", - InputError, wellControls.getDataContext() ); - GEOS_THROW_IF( wellControls.isProducer() && currentControl == WellControls::Control::TOTALVOLRATE, - "WellControls " << wellControls.getDataContext() << - ": Total rate control is not available for producers", - InputError, wellControls.getDataContext() ); - - GEOS_THROW_IF( wellControls.isInjector() && targetTotalRate < 0.0, - "WellControls " << wellControls.getDataContext() << - ": Target total rate cannot be negative for injectors", - InputError, wellControls.getDataContext() ); - GEOS_THROW_IF( wellControls.isInjector() && !isZero( targetPhaseRate ), - "WellControls " << wellControls.getDataContext() << - ": Target phase rate cannot be used for injectors", - InputError, wellControls.getDataContext() ); - GEOS_THROW_IF( wellControls.isProducer() && !isZero( targetTotalRate ), - "WellControls " << wellControls.getDataContext() << - ": Target total rate cannot be used for producers", - InputError, wellControls.getDataContext() ); - GEOS_THROW_IF( wellControls.isProducer() && !isZero( targetMassRate ), - "WellControls " << wellControls.getDataContext() << - ": Target mass rate cannot be used for producers", - InputError, wellControls.getDataContext() ); - GEOS_THROW_IF( !m_useMass && !isZero( targetMassRate ), - "WellControls " << wellControls.getDataContext() << - ": Target mass rate cannot with useMass=0", - InputError, wellControls.getDataContext() ); - - // The user always provides positive rates, but these rates are later multiplied by -1 internally for producers - GEOS_THROW_IF( wellControls.isProducer() && targetPhaseRate > 0.0, - "WellControls " << wellControls.getDataContext() << - ": Target phase rate cannot be negative for producers", - InputError, wellControls.getDataContext() ); - GEOS_THROW_IF( wellControls.isProducer() && !isZero( targetTotalRate ), - "WellControls " << wellControls.getDataContext() << - ": Target total rate cannot be used for producers", - InputError, wellControls.getDataContext() ); - - // Find target phase index for phase rate constraint - for( integer ip = 0; ip < fluid.numFluidPhases(); ++ip ) + // tjb + forSubGroups< InjectionConstraint< PhaseVolumeRateConstraint >, ProductionConstraint< PhaseVolumeRateConstraint > >( [&]( auto & constraint ) { - if( fluid.phaseNames()[ip] == wellControls.getTargetPhaseName() ) - { - m_targetPhaseIndex = ip; - } - } - GEOS_THROW_IF( wellControls.isProducer() && m_targetPhaseIndex == -1, - "WellControls " << wellControls.getDataContext() << - ": Phase " << wellControls.getTargetPhaseName() << " not found for well control " << wellControls.getName(), - InputError, wellControls.getDataContext() ); + constraint.validatePhaseType( fluid ); + } ); + + + } void CompositionalMultiphaseWell::initializePostSubGroups() { - WellSolverBase::initializePostSubGroups(); + WellControls::initializePostSubGroups(); - DomainPartition & domain = this->getGroupByPath< DomainPartition >( "/Problem/domain" ); + //DomainPartition & domain = this->getGroupByPath< DomainPartition >( "/Problem/domain" ); - validateConstitutiveModels( domain ); + // tjbvalidateConstitutiveModels( domain ); - forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &, - MeshLevel & mesh, - string_array const & regionNames ) - { - mesh.getElemManager().forElementSubRegions< WellElementSubRegion >( regionNames, [&]( localIndex const, - WellElementSubRegion & subRegion ) - { - validateInjectionStreams( subRegion ); - } ); - } ); } void CompositionalMultiphaseWell::initializePostInitialConditionsPreSubGroups() { - WellSolverBase::initializePostInitialConditionsPreSubGroups(); - createSeparator(); + WellControls::initializePostInitialConditionsPreSubGroups(); + } -void CompositionalMultiphaseWell::postRestartInitialization() +void CompositionalMultiphaseWell::initializeWellPostInitialConditionsPreSubGroups( WellElementSubRegion & subRegion ) { - DomainPartition & domain = this->getGroupByPath< DomainPartition >( "/Problem/domain" ); - forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &, - MeshLevel & mesh, - string_array const & regionNames ) - { - // loop over the wells - mesh.getElemManager().forElementSubRegions< WellElementSubRegion >( regionNames, [&]( localIndex const, - WellElementSubRegion & subRegion ) - { - // setup fluid separator - WellControls & wellControls = getWellControls( subRegion ); - constitutive::MultiFluidBase & fluidSeparator = wellControls.getMultiFluidSeparator(); - fluidSeparator.allocateConstitutiveData( wellControls, 1 ); - fluidSeparator.resize( 1 ); - } ); - } ); + // set gravity coefficient + setGravCoef( subRegion, getParent().getParent().getReference< R1Tensor >( PhysicsSolverManager::viewKeyStruct::gravityVectorString() )); + + // setup fluid model + string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString() ); + constitutive::MultiFluidBase & fluid = subRegion.getConstitutiveModel< constitutive::MultiFluidBase >( fluidName ); + fluid.setMassFlag( m_useMass ); + createSeparator( subRegion ); +} +void CompositionalMultiphaseWell::postRestartInitialization( ) +{ + + // setup fluid separator + constitutive::MultiFluidBase & fluidSeparator = getMultiFluidSeparator(); + fluidSeparator.allocateConstitutiveData( *this, 1 ); + fluidSeparator.resize( 1 ); + } -void CompositionalMultiphaseWell::createSeparator() +void CompositionalMultiphaseWell::createSeparator( WellElementSubRegion & subRegion ) { - DomainPartition & domain = this->getGroupByPath< DomainPartition >( "/Problem/domain" ); - forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &, - MeshLevel & mesh, - string_array const & regionNames ) - { - // loop over the wells - mesh.getElemManager().forElementSubRegions< WellElementSubRegion >( regionNames, [&]( localIndex const, - WellElementSubRegion & subRegion ) - { - string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString() ); - MultiFluidBase & fluid = subRegion.getConstitutiveModel< MultiFluidBase >( fluidName ); - fluid.setMassFlag( m_useMass ); - // setup fluid separator - WellControls & wellControls = getWellControls( subRegion ); - string const fluidSeparatorName = wellControls.getName() + "Separator"; - std::unique_ptr< constitutive::ConstitutiveBase > fluidSeparatorPtr = fluid.deliverClone( fluidSeparatorName, &wellControls ); - fluidSeparatorPtr->allocateConstitutiveData( wellControls, 1 ); - fluidSeparatorPtr->resize( 1 ); - wellControls.setFluidSeparator( std::move( fluidSeparatorPtr )); - } ); - } ); + string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString() ); + MultiFluidBase & fluid = subRegion.getConstitutiveModel< MultiFluidBase >( fluidName ); + fluid.setMassFlag( m_useMass ); + // setup fluid separator + string const fluidSeparatorName = getName() + "Separator"; + std::unique_ptr< constitutive::ConstitutiveBase > fluidSeparatorPtr = fluid.deliverClone( fluidSeparatorName, this ); + fluidSeparatorPtr->allocateConstitutiveData( *this, 1 ); + fluidSeparatorPtr->resize( 1 ); + setFluidSeparator( std::move( fluidSeparatorPtr )); + } void CompositionalMultiphaseWell::updateGlobalComponentFraction( WellElementSubRegion & subRegion ) const { @@ -611,70 +457,41 @@ void CompositionalMultiphaseWell::updateBHPForConstraint( WellElementSubRegion & { return; } - using Deriv = constitutive::multifluid::DerivativeOffset; - integer const numComp = m_numComponents; localIndex const iwelemRef = subRegion.getTopWellElementIndex(); - string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString() ); - MultiFluidBase const & fluid = subRegion.getConstitutiveModel< MultiFluidBase >( fluidName ); - integer const isThermal = fluid.isThermal(); // subRegion data - arrayView1d< real64 const > const & pres = subRegion.getField< well::pressure >(); - arrayView1d< real64 > const & totalMassDens = subRegion.getField< well::totalMassDensity >(); - arrayView2d< real64, compflow::USD_FLUID_DC > const & dTotalMassDens = subRegion.getField< well::dTotalMassDensity >(); - arrayView1d< real64 const > const wellElemGravCoef = subRegion.getField< well::gravityCoefficient >(); // control data - WellControls & wellControls = getWellControls( subRegion ); - string const wellControlsName = wellControls.getName(); - real64 const & refGravCoef = wellControls.getReferenceGravityCoef(); + string const wellControlsName = getName(); + real64 const & refGravCoef = getReferenceGravityCoef(); real64 & currentBHP = - wellControls.getReference< real64 >( CompositionalMultiphaseWell::viewKeyStruct::currentBHPString() ); - arrayView1d< real64 > const & dCurrentBHP = - wellControls.getReference< array1d< real64 > >( CompositionalMultiphaseWell::viewKeyStruct::dCurrentBHPString() ); - - geos::internal::kernelLaunchSelectorCompThermSwitch( numComp, isThermal, [&] ( auto NC, auto ISTHERMAL ) + getReference< real64 >( CompositionalMultiphaseWell::viewKeyStruct::currentBHPString() ); + // bring everything back to host, capture the scalars by reference + forAll< serialPolicy >( 1, [ pres, + totalMassDens, + wellElemGravCoef, + ¤tBHP, + &iwelemRef, + &refGravCoef] ( localIndex const ) { - integer constexpr IS_THERMAL = ISTHERMAL(); - GEOS_UNUSED_VAR( NC ); - // bring everything back to host, capture the scalars by reference - forAll< serialPolicy >( 1, [&numComp, - pres, - totalMassDens, - dTotalMassDens, - wellElemGravCoef, - ¤tBHP, - &dCurrentBHP, - &iwelemRef, - &refGravCoef] ( localIndex const ) - { - real64 const diffGravCoef = refGravCoef - wellElemGravCoef[iwelemRef]; - currentBHP = pres[iwelemRef] + totalMassDens[iwelemRef] * diffGravCoef; - dCurrentBHP[Deriv::dP] = 1 + dTotalMassDens[iwelemRef][Deriv::dP] * diffGravCoef; - for( integer ic = 0; ic < numComp; ++ic ) - { - dCurrentBHP[Deriv::dC+ic] = dTotalMassDens[iwelemRef][Deriv::dC+ic] * diffGravCoef; - } - if constexpr ( IS_THERMAL ) - { - dCurrentBHP[Deriv::dT] = dTotalMassDens[iwelemRef][Deriv::dT] * diffGravCoef; - } - } ); + real64 const diffGravCoef = refGravCoef - wellElemGravCoef[iwelemRef]; + currentBHP = pres[iwelemRef] + totalMassDens[iwelemRef] * diffGravCoef; } ); + GEOS_LOG_LEVEL_BY_RANK( logInfo::BoundaryConditions, GEOS_FMT( "{}: BHP (at the specified reference elevation) = {} Pa", wellControlsName, currentBHP ) ); } -void CompositionalMultiphaseWell::updateVolRatesForConstraint( ElementRegionManager const & elemManager, WellElementSubRegion const & subRegion ) +void CompositionalMultiphaseWell::updateVolRatesForConstraint( WellElementSubRegion const & subRegion ) { GEOS_MARK_FUNCTION; @@ -684,250 +501,201 @@ void CompositionalMultiphaseWell::updateVolRatesForConstraint( ElementRegionMana return; } - integer constexpr maxNumComp = constitutive::MultiFluidBase::MAX_NUM_COMPONENTS; - integer const numComp = m_numComponents; + integer const numPhase = m_numPhases; localIndex const iwelemRef = subRegion.getTopWellElementIndex(); - WellControls & wellControls = getWellControls( subRegion ); - // subRegion data - arrayView1d< real64 const > const & pres = subRegion.getField< well::pressure >(); - arrayView1d< real64 const > const & temp = subRegion.getField< well::temperature >(); - arrayView1d< real64 const > const & connRate = subRegion.getField< well::mixtureConnectionRate >(); - - arrayView2d< real64 const, compflow::USD_COMP > const & compFrac = subRegion.getField< well::globalCompFraction >(); - arrayView3d< real64 const, compflow::USD_COMP_DC > const & dCompFrac_dCompDens = subRegion.getField< well::dGlobalCompFraction_dGlobalCompDensity >(); + arrayView1d< real64 const > const & connRate = subRegion.getField< well::connectionRate >(); // fluid data - constitutive::MultiFluidBase & fluidSeparator = wellControls.getMultiFluidSeparator(); - integer isThermal = fluidSeparator.isThermal(); - arrayView3d< real64 const, constitutive::multifluid::USD_PHASE > const & phaseFrac = fluidSeparator.phaseFraction(); - arrayView4d< real64 const, constitutive::multifluid::USD_PHASE_DC > const & dPhaseFrac = fluidSeparator.dPhaseFraction(); + constitutive::MultiFluidBase & fluidSeparator = getMultiFluidSeparator(); + arrayView3d< real64 const, constitutive::multifluid::USD_PHASE > const & phaseFrac = fluidSeparator.phaseFraction(); arrayView2d< real64 const, constitutive::multifluid::USD_FLUID > const & totalDens = fluidSeparator.totalDensity(); - arrayView3d< real64 const, constitutive::multifluid::USD_FLUID_DC > const & dTotalDens = fluidSeparator.dTotalDensity(); - arrayView3d< real64 const, constitutive::multifluid::USD_PHASE > const & phaseDens = fluidSeparator.phaseDensity(); - arrayView4d< real64 const, constitutive::multifluid::USD_PHASE_DC > const & dPhaseDens = fluidSeparator.dPhaseDensity(); // control data - string const wellControlsName = wellControls.getName(); - bool const logSurfaceCondition = isLogLevelActive< logInfo::BoundaryConditions >( wellControls.getLogLevel()); - string const massUnit = m_useMass ? "kg" : "mol"; + string const wellControlsName = getName(); - integer const useSurfaceConditions = wellControls.useSurfaceConditions(); - real64 flashPressure; - real64 flashTemperature; - if( useSurfaceConditions ) - { - // use surface conditions - flashPressure = wellControls.getSurfacePressure(); - flashTemperature = wellControls.getSurfaceTemperature(); - } - else + arrayView1d< real64 > const & currentPhaseVolRate = + getReference< array1d< real64 > >( viewKeyStruct::currentPhaseVolRateString() ); + + real64 & currentTotalVolRate = + getReference< real64 >( viewKeyStruct::currentTotalVolRateString() ); + + real64 & currentMassRate = + getReference< real64 >( viewKeyStruct::currentMassRateString() ); + + real64 & massDensity = + getReference< real64 >( viewKeyStruct::massDensityString() ); + + // bring everything back to host, capture the scalars by reference + forAll< serialPolicy >( 1, [&numPhase, + connRate, + totalDens, + phaseDens, + phaseFrac, + ¤tTotalVolRate, + currentPhaseVolRate, + ¤tMassRate, + &iwelemRef, + &massDensity] ( localIndex const ) { - if( !wellControls.referenceReservoirRegion().empty() ) - { - ElementRegionBase const & region = elemManager.getRegion( wellControls.referenceReservoirRegion()); - GEOS_ERROR_IF ( !region.hasWrapper( CompositionalMultiphaseStatistics::regionStatisticsName() ), - GEOS_FMT( "{}: WellControl {} referenceReservoirRegion field requires CompositionalMultiphaseStatistics to be configured for region {} ", - getDataContext(), wellControls.getName(), wellControls.referenceReservoirRegion() ) ); + // Step 1: update the total volume rate - CompositionalMultiphaseStatistics::RegionStatistics const & stats = region.getReference< CompositionalMultiphaseStatistics::RegionStatistics >( - CompositionalMultiphaseStatistics::regionStatisticsName() ); - wellControls.setRegionAveragePressure( stats.averagePressure ); - wellControls.setRegionAverageTemperature( stats.averageTemperature ); - GEOS_ERROR_IF( stats.averagePressure <= 0.0, - GEOS_FMT( "{}: No region average quantities computed. WellControl {} referenceReservoirRegion field requires CompositionalMultiphaseStatistics to be configured for region {} ", - getDataContext(), wellControls.getName(), wellControls.referenceReservoirRegion() )); - } - // If flashPressure is not set by region the value is defaulted to -1 and indicates to use top segment conditions - flashPressure = wellControls.getRegionAveragePressure(); - if( flashPressure < 0.0 ) - { - // region name not set, use segment conditions - flashPressure = pres[iwelemRef]; - flashTemperature = temp[iwelemRef]; - } - else + real64 const currentTotalRate = connRate[iwelemRef]; + // Assumes useMass is true + currentMassRate = currentTotalRate; + // Step 1.1: compute the inverse of the total density and derivatives + massDensity = totalDens[iwelemRef][0]; + real64 const totalDensInv = 1.0 / totalDens[iwelemRef][0]; + + // Step 1.2: divide the total mass/molar rate by the total density to get the total volumetric rate + currentTotalVolRate = currentTotalRate * totalDensInv; + + // Step 2: update the phase volume rate + for( integer ip = 0; ip < numPhase; ++ip ) { - // use reservoir region averages - flashTemperature = wellControls.getRegionAverageTemperature(); + // Step 2.1: compute the inverse of the (phase density * phase fraction) and derivatives + + // skip the rest of this function if phase ip is absent + bool const phaseExists = (phaseFrac[iwelemRef][0][ip] > 0); + if( !phaseExists ) + { + continue; + } + + real64 const phaseDensInv = 1.0 / phaseDens[iwelemRef][0][ip]; + real64 const phaseFracTimesPhaseDensInv = phaseFrac[iwelemRef][0][ip] * phaseDensInv; + + // Step 2.2: divide the total mass/molar rate by the (phase density * phase fraction) to get the phase volumetric rate + currentPhaseVolRate[ip] = currentTotalRate * phaseFracTimesPhaseDensInv; } + } ); + +} + +void CompositionalMultiphaseWell::calculateReferenceElementRates( WellElementSubRegion & subRegion ) +{ + GEOS_MARK_FUNCTION; + + // the rank that owns the reference well element is responsible for the calculations below. + if( !subRegion.isLocallyOwned() ) + { + return; } + + integer const numPhase = m_numPhases; + localIndex const iwelemRef = subRegion.getTopWellElementIndex(); + + + + // subRegion data + arrayView1d< real64 const > const & connRate = subRegion.getField< fields::well::connectionRate >(); + + // fluid data + constitutive::MultiFluidBase & fluidSeparator = getMultiFluidSeparator(); + arrayView3d< real64 const, constitutive::multifluid::USD_PHASE > const & phaseFrac = fluidSeparator.phaseFraction(); + arrayView2d< real64 const, constitutive::multifluid::USD_FLUID > const & totalDens = fluidSeparator.totalDensity(); + arrayView3d< real64 const, constitutive::multifluid::USD_PHASE > const & phaseDens = fluidSeparator.phaseDensity(); + + // control data + string const wellControlsName = getName(); + bool const logSurfaceCondition = isLogLevelActive< logInfo::BoundaryConditions >( getLogLevel()); + string const massUnit = m_useMass ? "kg" : "mol"; + + integer const useSurfCond = useSurfaceConditions(); + arrayView1d< real64 > const & currentPhaseVolRate = - wellControls.getReference< array1d< real64 > >( CompositionalMultiphaseWell::viewKeyStruct::currentPhaseVolRateString() ); - arrayView2d< real64 > const & dCurrentPhaseVolRate = - wellControls.getReference< array2d< real64 > >( CompositionalMultiphaseWell::viewKeyStruct::dCurrentPhaseVolRateString() ); + getReference< array1d< real64 > >( CompositionalMultiphaseWell::viewKeyStruct::currentPhaseVolRateString() ); real64 & currentTotalVolRate = - wellControls.getReference< real64 >( CompositionalMultiphaseWell::viewKeyStruct::currentTotalVolRateString() ); + getReference< real64 >( CompositionalMultiphaseWell::viewKeyStruct::currentTotalVolRateString() ); real64 & currentMassRate = - wellControls.getReference< real64 >( CompositionalMultiphaseWell::viewKeyStruct::currentMassRateString() ); - - arrayView1d< real64 > const & dCurrentTotalVolRate = - wellControls.getReference< array1d< real64 > >( CompositionalMultiphaseWell::viewKeyStruct::dCurrentTotalVolRateString() ); + getReference< real64 >( CompositionalMultiphaseWell::viewKeyStruct::currentMassRateString() ); real64 & massDensity = - wellControls.getReference< real64 >( CompositionalMultiphaseWell::viewKeyStruct::massDensityString() ); + getReference< real64 >( CompositionalMultiphaseWell::viewKeyStruct::massDensityString() ); + constitutive::constitutiveUpdatePassThru( fluidSeparator, [&] ( auto & castedFluidSeparator ) { // typename TYPEOFREF( castedFluid ) ::KernelWrapper fluidWrapper = castedFluid.createKernelWrapper(); typename TYPEOFREF( castedFluidSeparator ) ::KernelWrapper fluidSeparatorWrapper = castedFluidSeparator.createKernelWrapper(); - geos::internal::kernelLaunchSelectorCompThermSwitch( numComp, isThermal, [&] ( auto NC, auto ISTHERMAL ) + + // bring everything back to host, capture the scalars by reference + forAll< serialPolicy >( 1, [fluidSeparatorWrapper, + &numPhase, + connRate, + totalDens, + phaseDens, + phaseFrac, + logSurfaceCondition, + &useSurfCond, + ¤tTotalVolRate, + currentPhaseVolRate, + ¤tMassRate, + &iwelemRef, + &wellControlsName, + &massUnit, + &massDensity] ( localIndex const ) { - integer constexpr NUM_COMP = NC(); - integer constexpr IS_THERMAL = ISTHERMAL(); - using COFFSET_WJ = compositionalMultiphaseWellKernels::ColOffset_WellJac< NUM_COMP, IS_THERMAL >; - // bring everything back to host, capture the scalars by reference - forAll< serialPolicy >( 1, [&numComp, - &numPhase, - fluidSeparatorWrapper, - pres, - temp, - compFrac, - dCompFrac_dCompDens, - connRate, - totalDens, - dTotalDens, - phaseDens, - dPhaseDens, - phaseFrac, - dPhaseFrac, - logSurfaceCondition, - &useSurfaceConditions, - &flashPressure, - &flashTemperature, - ¤tTotalVolRate, - dCurrentTotalVolRate, - currentPhaseVolRate, - dCurrentPhaseVolRate, - ¤tMassRate, - &iwelemRef, - &wellControlsName, - &massUnit, - &massDensity] ( localIndex const ) - { - GEOS_UNUSED_VAR( massUnit ); - using Deriv = constitutive::multifluid::DerivativeOffset; - stackArray1d< real64, maxNumComp > work( numComp ); - // Step 1: evaluate the phase and total density in the reference element - - // We need to evaluate the density as follows: - // - Surface conditions: using the surface pressure provided by the user - // - Segment conditions: using the pressure in the top element - // - Reservoir conditions: using the average region pressure - if( useSurfaceConditions ) - { - // we need to compute the surface density - fluidSeparatorWrapper.update( iwelemRef, 0, flashPressure, flashTemperature, compFrac[iwelemRef] ); - if( logSurfaceCondition ) - { - GEOS_LOG_RANK( GEOS_FMT( "{}: surface density computed with P_surface = {} Pa and T_surface = {} K", - wellControlsName, flashPressure, flashTemperature ) ); - } -#ifdef GEOS_USE_HIP - GEOS_UNUSED_VAR( wellControlsName ); -#endif + GEOS_UNUSED_VAR( massUnit ); - } - else - { - fluidSeparatorWrapper.update( iwelemRef, 0, flashPressure, flashTemperature, compFrac[iwelemRef] ); - } - // Step 2: update the total volume rate - real64 const currentTotalRate = connRate[iwelemRef]; - // Assumes useMass is true - currentMassRate = currentTotalRate; - // Step 2.1: compute the inverse of the total density and derivatives - massDensity = totalDens[iwelemRef][0]; - real64 const totalDensInv = 1.0 / totalDens[iwelemRef][0]; + // Step 2: update the total volume rate - stackArray1d< real64, maxNumComp > dTotalDensInv_dCompDens( numComp ); - for( integer ic = 0; ic < numComp; ++ic ) - { - dTotalDensInv_dCompDens[ic] = -dTotalDens[iwelemRef][0][Deriv::dC+ic] * totalDensInv * totalDensInv; - } - applyChainRuleInPlace( numComp, dCompFrac_dCompDens[iwelemRef], dTotalDensInv_dCompDens, work.data() ); - - // Step 2.2: divide the total mass/molar rate by the total density to get the total volumetric rate - currentTotalVolRate = currentTotalRate * totalDensInv; - // Compute derivatives dP dT - real64 const dTotalDensInv_dPres = -dTotalDens[iwelemRef][0][Deriv::dP] * totalDensInv * totalDensInv; - dCurrentTotalVolRate[COFFSET_WJ::dP] = ( useSurfaceConditions == 0 ) * currentTotalRate * dTotalDensInv_dPres; - if constexpr ( IS_THERMAL ) - { - dCurrentTotalVolRate[COFFSET_WJ::dT] = ( useSurfaceConditions == 0 ) * currentTotalRate * -dTotalDens[iwelemRef][0][Deriv::dT] * totalDensInv * totalDensInv; - } + real64 const currentTotalRate = connRate[iwelemRef]; + // Assumes useMass is true + currentMassRate = currentTotalRate; + // Step 2.1: compute the inverse of the total density + massDensity = totalDens[iwelemRef][0]; + real64 const totalDensInv = 1.0 / totalDens[iwelemRef][0]; - if( logSurfaceCondition && useSurfaceConditions ) - { - GEOS_LOG_RANK( GEOS_FMT( "{}: total fluid density at surface conditions = {} {}/sm3, total rate = {} {}/s, total surface volumetric rate = {} sm3/s", - wellControlsName, totalDens[iwelemRef][0], massUnit, connRate[iwelemRef], massUnit, currentTotalVolRate ) ); - } - dCurrentTotalVolRate[COFFSET_WJ::dQ] = totalDensInv; - for( integer ic = 0; ic < numComp; ++ic ) - { - dCurrentTotalVolRate[COFFSET_WJ::dC+ic] = currentTotalRate * dTotalDensInv_dCompDens[ic]; - } + // Step 2.2: divide the total mass/molar rate by the total density to get the total volumetric rate + currentTotalVolRate = currentTotalRate * totalDensInv; - // Step 3: update the phase volume rate - for( integer ip = 0; ip < numPhase; ++ip ) - { - // Step 3.1: compute the inverse of the (phase density * phase fraction) and derivatives + if( logSurfaceCondition && useSurfCond ) + { + GEOS_LOG_RANK( GEOS_FMT( "{}: total fluid density at surface conditions = {} {}/sm3, total rate = {} {}/s, total surface volumetric rate = {} sm3/s", + wellControlsName, totalDens[iwelemRef][0], massUnit, connRate[iwelemRef], massUnit, currentTotalVolRate ) ); + } - // skip the rest of this function if phase ip is absent - bool const phaseExists = (phaseFrac[iwelemRef][0][ip] > 0); - if( !phaseExists ) - { - continue; - } + // Step 3: update the phase volume rate + for( integer ip = 0; ip < numPhase; ++ip ) + { - real64 const phaseDensInv = 1.0 / phaseDens[iwelemRef][0][ip]; - real64 const phaseFracTimesPhaseDensInv = phaseFrac[iwelemRef][0][ip] * phaseDensInv; - real64 const dPhaseFracTimesPhaseDensInv_dPres = dPhaseFrac[iwelemRef][0][ip][Deriv::dP] * phaseDensInv - - dPhaseDens[iwelemRef][0][ip][Deriv::dP] * phaseFracTimesPhaseDensInv * phaseDensInv; + // Step 3.1: compute the inverse of the (phase density * phase fraction) + // skip the rest of this function if phase ip is absent + bool const phaseExists = (phaseFrac[iwelemRef][0][ip] > 0); + if( !phaseExists ) + { + continue; + } - // Step 3.2: divide the total mass/molar rate by the (phase density * phase fraction) to get the phase volumetric rate - currentPhaseVolRate[ip] = currentTotalRate * phaseFracTimesPhaseDensInv; - dCurrentPhaseVolRate[ip][COFFSET_WJ::dP] = ( useSurfaceConditions == 0 ) * currentTotalRate * dPhaseFracTimesPhaseDensInv_dPres; - dCurrentPhaseVolRate[ip][COFFSET_WJ::dQ] = phaseFracTimesPhaseDensInv; - if constexpr (IS_THERMAL ) - { - real64 const dPhaseFracTimesPhaseDensInv_dTemp = dPhaseFrac[iwelemRef][0][ip][Deriv::dT] * phaseDensInv - - dPhaseDens[iwelemRef][0][ip][Deriv::dT] * phaseFracTimesPhaseDensInv * phaseDensInv; - dCurrentPhaseVolRate[ip][COFFSET_WJ::dT] = ( useSurfaceConditions == 0 ) * currentTotalRate * dPhaseFracTimesPhaseDensInv_dTemp; - } + real64 const phaseDensInv = 1.0 / phaseDens[iwelemRef][0][ip]; + real64 const phaseFracTimesPhaseDensInv = phaseFrac[iwelemRef][0][ip] * phaseDensInv; - for( integer ic = 0; ic < numComp; ++ic ) - { - dCurrentPhaseVolRate[ip][COFFSET_WJ::dC+ic] = -phaseFracTimesPhaseDensInv * dPhaseDens[iwelemRef][0][ip][Deriv::dC+ic] * phaseDensInv; - dCurrentPhaseVolRate[ip][COFFSET_WJ::dC+ic] += dPhaseFrac[iwelemRef][0][ip][Deriv::dC+ic] * phaseDensInv; - dCurrentPhaseVolRate[ip][COFFSET_WJ::dC+ic] *= currentTotalRate; - } - applyChainRuleInPlace( numComp, dCompFrac_dCompDens[iwelemRef], &dCurrentPhaseVolRate[ip][COFFSET_WJ::dC], work.data() ); + // Step 3.2: divide the total mass/molar rate by the (phase density * phase fraction) to get the phase volumetric rate + currentPhaseVolRate[ip] = currentTotalRate * phaseFracTimesPhaseDensInv; - if( logSurfaceCondition && useSurfaceConditions ) - { - GEOS_LOG_RANK( GEOS_FMT( "{}: density of phase {} at surface conditions = {} {}/sm3, phase surface volumetric rate = {} sm3/s", - wellControlsName, ip, phaseDens[iwelemRef][0][ip], massUnit, currentPhaseVolRate[ip] ) ); - } + if( logSurfaceCondition && useSurfCond ) + { + GEOS_LOG_RANK( GEOS_FMT( "{}: density of phase {} at surface conditions = {} {}/sm3, phase surface volumetric rate = {} sm3/s", + wellControlsName, ip, phaseDens[iwelemRef][0][ip], massUnit, currentPhaseVolRate[ip] ) ); } - } ); + } } ); } ); } - void CompositionalMultiphaseWell::updateFluidModel( WellElementSubRegion & subRegion ) { GEOS_MARK_FUNCTION; @@ -954,24 +722,134 @@ void CompositionalMultiphaseWell::updateFluidModel( WellElementSubRegion & subRe } -real64 CompositionalMultiphaseWell::updatePhaseVolumeFraction( WellElementSubRegion & subRegion ) const +void CompositionalMultiphaseWell::updateSeparator( ElementRegionManager const & elemManager, WellElementSubRegion & subRegion ) { GEOS_MARK_FUNCTION; - string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString() ); - MultiFluidBase & fluid = subRegion.getConstitutiveModel< MultiFluidBase >( fluidName ); + // the rank that owns the reference well element is responsible for the calculations below. + if( !subRegion.isLocallyOwned() ) + { + return; + } - real64 maxDeltaPhaseVolFrac = - m_isThermal ? - thermalCompositionalMultiphaseBaseKernels:: - PhaseVolumeFractionKernelFactory:: - createAndLaunch< parallelDevicePolicy<> >( m_numComponents, - m_numPhases, - subRegion, - fluid ) -: isothermalCompositionalMultiphaseBaseKernels:: - PhaseVolumeFractionKernelFactory:: - createAndLaunch< parallelDevicePolicy<> >( m_numComponents, + localIndex const iwelemRef = subRegion.getTopWellElementIndex(); + + // subRegion data + arrayView1d< real64 const > const & pres = subRegion.getField< fields::well::pressure >(); + arrayView1d< real64 const > const & temp = subRegion.getField< fields::well::temperature >(); + arrayView2d< real64 const, compflow::USD_COMP > const & compFrac = subRegion.getField< fields::well::globalCompFraction >(); + + + // fluid data + constitutive::MultiFluidBase & fluidSeparator = getMultiFluidSeparator(); + + // control data + + string const wellControlsName = getName(); + bool const logSurfaceCondition = isLogLevelActive< logInfo::BoundaryConditions >( getLogLevel()); + string const massUnit = m_useMass ? "kg" : "mol"; + + integer const useSurfaceCond = useSurfaceConditions(); + real64 flashPressure; + real64 flashTemperature; + if( useSurfaceCond ) + { + // use surface conditions + flashPressure = getSurfacePressure(); + flashTemperature = getSurfaceTemperature(); + } + else + { + if( !getReferenceReservoirRegion().empty() ) + { + ElementRegionBase const & region = elemManager.getRegion( getReferenceReservoirRegion() ); + GEOS_ERROR_IF ( !region.hasWrapper( CompositionalMultiphaseStatistics::regionStatisticsName()), + GEOS_FMT( "{}: WellControl {} referenceReservoirRegion field requires CompositionalMultiphaseStatistics to be configured for region {} ", + getDataContext(), getName(), getReferenceReservoirRegion() ) ); + + CompositionalMultiphaseStatistics::RegionStatistics const & stats = region.getReference< CompositionalMultiphaseStatistics::RegionStatistics >( + CompositionalMultiphaseStatistics::regionStatisticsName() ); + GEOS_ERROR_IF( stats.averagePressure <= 0.0, + GEOS_FMT( + "{}: No region average quantities computed. WellControl {} referenceReservoirRegion field requires CompositionalMultiphaseStatistics to be configured for region {} ", + getDataContext(), getName(), getReferenceReservoirRegion() )); + setRegionAveragePressure( stats.averagePressure ); + setRegionAverageTemperature( stats.averageTemperature ); + } + // If flashPressure is not set by region the value is defaulted to -1 and indicates to use top segment conditions + flashPressure = getRegionAveragePressure(); + if( flashPressure < 0.0 ) + { + // region name not set, use segment conditions + flashPressure = pres[iwelemRef]; + flashTemperature = temp[iwelemRef]; + } + else + { + // use reservoir region averages + flashTemperature = getRegionAverageTemperature(); + } + } + + constitutive::constitutiveUpdatePassThru( fluidSeparator, [&] ( auto & castedFluidSeparator ) + { + // typename TYPEOFREF( castedFluid ) ::KernelWrapper fluidWrapper = castedFluid.createKernelWrapper(); + typename TYPEOFREF( castedFluidSeparator ) ::KernelWrapper fluidSeparatorWrapper = castedFluidSeparator.createKernelWrapper(); + // bring everything back to host, capture the scalars by reference + forAll< serialPolicy >( 1, [fluidSeparatorWrapper, + wellControlsName, + useSurfaceCond, + flashPressure, + flashTemperature, + logSurfaceCondition, + iwelemRef, + pres, + temp, + compFrac] ( localIndex const ) + { + // - Surface conditions: using the surface pressure provided by the user + // - Reservoir conditions: using the pressure in the top element + if( useSurfaceCond ) + { + // we need to compute the surface density + //fluidWrapper.update( iwelemRef, 0, surfacePres, surfaceTemp, compFrac[iwelemRef] ); + fluidSeparatorWrapper.update( iwelemRef, 0, flashPressure, flashTemperature, compFrac[iwelemRef] ); + if( logSurfaceCondition ) + { + GEOS_LOG_RANK( GEOS_FMT( "{}: surface density computed with P_surface = {} Pa and T_surface = {} K", + wellControlsName, flashPressure, flashTemperature ) ); + } +#ifdef GEOS_USE_HIP + GEOS_UNUSED_VAR( wellControlsName ); +#endif + } + else + { + fluidSeparatorWrapper.update( iwelemRef, 0, flashPressure, flashTemperature, compFrac[iwelemRef] ); + } + } ); + } ); +} + + +real64 CompositionalMultiphaseWell::updatePhaseVolumeFraction( WellElementSubRegion & subRegion ) const +{ + GEOS_MARK_FUNCTION; + + string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString() ); + MultiFluidBase & fluid = subRegion.getConstitutiveModel< MultiFluidBase >( fluidName ); + + real64 maxDeltaPhaseVolFrac = + m_isThermal ? + thermalCompositionalMultiphaseBaseKernels:: + PhaseVolumeFractionKernelFactory:: + createAndLaunch< parallelDevicePolicy<> >( m_numComponents, + m_numPhases, + subRegion, + fluid ) +: isothermalCompositionalMultiphaseBaseKernels:: + PhaseVolumeFractionKernelFactory:: + createAndLaunch< parallelDevicePolicy<> >( m_numComponents, m_numPhases, subRegion, fluid ); @@ -1002,185 +880,273 @@ void CompositionalMultiphaseWell::updateTotalMassDensity( WellElementSubRegion & } -void CompositionalMultiphaseWell::updateState( DomainPartition & domain ) +real64 CompositionalMultiphaseWell::updateWellState( ElementRegionManager const & elemManager, WellElementSubRegion & subRegion ) { GEOS_MARK_FUNCTION; - real64 maxPhaseVolFrac = 0.0; - forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &, - MeshLevel & mesh, - string_array const & regionNames ) - { - ElementRegionManager & elemManager = mesh.getElemManager(); - elemManager.forElementSubRegions< WellElementSubRegion >( regionNames, [&]( localIndex const, - WellElementSubRegion & subRegion ) - { - WellControls & wellControls = getWellControls( subRegion ); - if( wellControls.getWellStatus() == WellControls::Status::OPEN ) - { - real64 const maxRegionPhaseVolFrac = updateSubRegionState( elemManager, subRegion ); - maxPhaseVolFrac = LvArray::math::max( maxRegionPhaseVolFrac, maxPhaseVolFrac ); - } - } ); - } ); - maxPhaseVolFrac = MpiWrapper::max( maxPhaseVolFrac ); - - GEOS_LOG_LEVEL_RANK_0( logInfo::Solution, - GEOS_FMT( " {}: Max well phase volume fraction change = {}", - getName(), fmt::format( "{:.{}f}", maxPhaseVolFrac, 4 ) ) ); + real64 maxPhaseVolFrac = updateSubRegionState( elemManager, subRegion ); + return maxPhaseVolFrac; } real64 CompositionalMultiphaseWell::updateSubRegionState( ElementRegionManager const & elemManager, WellElementSubRegion & subRegion ) { - // update properties - updateGlobalComponentFraction( subRegion ); + real64 maxPhaseVolChange=0.0; + + if( getWellState()) + { + + // update properties + updateGlobalComponentFraction( subRegion ); + + // update densities, phase fractions, phase volume fractions + + updateFluidModel( subRegion ); // Calculate fluid properties - // update volumetric rates for the well constraints - // note: this must be called before updateFluidModel - updateVolRatesForConstraint( elemManager, subRegion ); + updateSeparator( elemManager, subRegion ); // Calculate fluid properties at control conditions - // update densities, phase fractions, phase volume fractions + updateVolRatesForConstraint( subRegion ); // remove tjb ?? - updateFluidModel( subRegion ); // Calculate fluid properties; - real64 maxPhaseVolChange = updatePhaseVolumeFraction( subRegion ); - updateTotalMassDensity( subRegion ); - // update the current BHP pressure - updateBHPForConstraint( subRegion ); + maxPhaseVolChange = updatePhaseVolumeFraction( subRegion ); + updateTotalMassDensity( subRegion ); + + // Calculate the reference element rates + calculateReferenceElementRates( subRegion ); + + // update the current BHP + updateBHPForConstraint( subRegion ); + + // Broad case the updated well state to other ranks + real64 & currentBHP = + getReference< real64 >( CompositionalMultiphaseWell::viewKeyStruct::currentBHPString() ); + array1d< real64 > currentPhaseVolRate = + getReference< array1d< real64 > >( CompositionalMultiphaseWell::viewKeyStruct::currentPhaseVolRateString() ); + real64 & currentTotalVolRate = + getReference< real64 >( CompositionalMultiphaseWell::viewKeyStruct::currentTotalVolRateString() ); + real64 & currentMassRate = + getReference< real64 >( CompositionalMultiphaseWell::viewKeyStruct::currentMassRateString() ); + integer topRank = subRegion.getTopRank(); + MpiWrapper::broadcast( currentBHP, topRank ); + MpiWrapper::bcast( currentPhaseVolRate.data(), LvArray::integerConversion< int >( currentPhaseVolRate.size() ), topRank ); + MpiWrapper::broadcast( currentTotalVolRate, topRank ); + MpiWrapper::broadcast( currentMassRate, topRank ); + if( !subRegion.isLocallyOwned() ) + { + getReference< array1d< real64 > >( + CompositionalMultiphaseWell::viewKeyStruct::currentPhaseVolRateString() ) =currentPhaseVolRate; + + + } + + + } return maxPhaseVolChange; } -void CompositionalMultiphaseWell::initializeWells( DomainPartition & domain, real64 const & time_n ) +void CompositionalMultiphaseWell::initializeWell( DomainPartition & domain, MeshLevel & mesh, WellElementSubRegion & subRegion, real64 const & time_n ) { GEOS_MARK_FUNCTION; - + GEOS_UNUSED_VAR( domain ); integer const numComp = m_numComponents; integer const numPhase = m_numPhases; - // TODO: change the way we access the flowSolver here - CompositionalMultiphaseBase const & flowSolver = getParent().getGroup< CompositionalMultiphaseBase >( getFlowSolverName() ); - // loop over the wells - forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &, - MeshLevel & mesh, - string_array const & regionNames ) - { + // TODO: change the way we access the flowSolver here + ElementRegionManager const & elemManager = mesh.getElemManager(); - ElementRegionManager & elemManager = mesh.getElemManager(); - compositionalMultiphaseWellKernels::PresTempCompFracInitializationKernel::CompFlowAccessors - resCompFlowAccessors( mesh.getElemManager(), flowSolver.getName() ); - compositionalMultiphaseWellKernels::PresTempCompFracInitializationKernel::MultiFluidAccessors - resMultiFluidAccessors( mesh.getElemManager(), flowSolver.getName() ); + compositionalMultiphaseWellKernels::PresTempCompFracInitializationKernel::CompFlowAccessors + resCompFlowAccessors( elemManager, getFlowSolverName() ); + compositionalMultiphaseWellKernels::PresTempCompFracInitializationKernel::MultiFluidAccessors + resMultiFluidAccessors( elemManager, getFlowSolverName() ); - elemManager.forElementSubRegions< WellElementSubRegion >( regionNames, - [&]( localIndex const, - WellElementSubRegion & subRegion ) - { - WellControls & wellControls = getWellControls( subRegion ); - PerforationData const & perforationData = *subRegion.getPerforationData(); - arrayView2d< real64 const > const compPerfRate = perforationData.getField< fields::well::compPerforationRate >(); + PerforationData const & perforationData = *subRegion.getPerforationData(); + arrayView2d< real64 const > const compPerfRate = perforationData.getField< fields::well::compPerforationRate >(); - bool const hasNonZeroRate = MpiWrapper::max< integer >( hasNonZero( compPerfRate )); + bool const hasNonZeroRate = MpiWrapper::max< integer >( hasNonZero( compPerfRate )); - if( wellControls.isWellOpen() && !hasNonZeroRate ) + if( time_n <= 0.0 || ( isWellOpen( ) && !hasNonZeroRate ) ) + { + setWellState( true ); + if( getCurrentConstraint() == nullptr ) + { + // tjb needed for backward compatibility. and these 2 lists must be consistent + ConstraintTypeId inputControl = ConstraintTypeId( getInputControl()); + if( isProducer() ) { - // get well primary variables on well elements - arrayView1d< real64 > const & wellElemPressure = subRegion.getField< well::pressure >(); - arrayView1d< real64 > const & wellElemTemp = subRegion.getField< well::temperature >(); - arrayView2d< real64, compflow::USD_COMP > const & wellElemCompDens = subRegion.getField< well::globalCompDensity >(); - arrayView1d< real64 > const & connRate = subRegion.getField< well::mixtureConnectionRate >(); - - // get the info stored on well elements - arrayView2d< real64, compflow::USD_COMP > const & wellElemCompFrac = subRegion.getField< well::globalCompFraction >(); - arrayView1d< real64 const > const & wellElemGravCoef = subRegion.getField< well::gravityCoefficient >(); - - // get the element region, subregion, index - arrayView1d< localIndex const > const resElementRegion = perforationData.getField< perforation::reservoirElementRegion >(); - arrayView1d< localIndex const > const resElementSubRegion = perforationData.getField< perforation::reservoirElementSubRegion >(); - arrayView1d< localIndex const > const resElementIndex = perforationData.getField< perforation::reservoirElementIndex >(); - - arrayView1d< real64 const > const & perfGravCoef = perforationData.getField< fields::well::gravityCoefficient >(); - arrayView1d< integer const > const & perfStatus = perforationData.getField< fields::perforation::perforationStatus >(); - - // 1) Loop over all perforations to compute an average mixture density and component fraction - // 2) Initialize the reference pressure - // 3) Estimate the pressures in the well elements using the average density - compositionalMultiphaseWellKernels:: - PresTempCompFracInitializationKernel:: - launch( perforationData.size(), - subRegion.size(), - numComp, - numPhase, - wellControls, - 0.0, // initialization done at t = 0 - resCompFlowAccessors.get( flow::pressure{} ), - resCompFlowAccessors.get( flow::temperature{} ), - resCompFlowAccessors.get( flow::globalCompDensity{} ), - resCompFlowAccessors.get( flow::phaseVolumeFraction{} ), - resMultiFluidAccessors.get( fields::multifluid::phaseMassDensity{} ), - resElementRegion, - resElementSubRegion, - resElementIndex, - perfGravCoef, - perfStatus, - wellElemGravCoef, - wellElemPressure, - wellElemTemp, - wellElemCompFrac ); - - // get well secondary variables on well elements - string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString() ); - MultiFluidBase & fluid = getConstitutiveModel< MultiFluidBase >( subRegion, fluidName ); - arrayView3d< real64 const, constitutive::multifluid::USD_PHASE > const & wellElemPhaseDens = fluid.phaseDensity(); - arrayView2d< real64 const, constitutive::multifluid::USD_FLUID > const & wellElemTotalDens = fluid.totalDensity(); - - // 4) Back calculate component densities - constitutive::constitutiveUpdatePassThru( fluid, [&] ( auto & castedFluid ) + forSubGroups< MinimumBHPConstraint, ProductionConstraint< VolumeRateConstraint >, ProductionConstraint< MassRateConstraint >, + ProductionConstraint< PhaseVolumeRateConstraint > >( [&]( auto & constraint ) { - typename TYPEOFREF( castedFluid ) ::KernelWrapper fluidWrapper = castedFluid.createKernelWrapper(); - - thermalCompositionalMultiphaseBaseKernels:: - FluidUpdateKernel:: - launch< serialPolicy >( subRegion.size(), - fluidWrapper, - wellElemPressure, - wellElemTemp, - wellElemCompFrac ); + if( constraint.getControl() == inputControl ) + { + setCurrentConstraint( &constraint ); + setControl( static_cast< WellControls::Control >(inputControl) ); // tjb old + } + } ); + } + else + { - compositionalMultiphaseWellKernels:: - CompDensInitializationKernel::launch( subRegion.size(), - numComp, - wellElemCompFrac, - wellElemTotalDens, - wellElemCompDens ); - - // 5) Recompute the pressure-dependent properties - updateSubRegionState( elemManager, subRegion ); - - // 6) Estimate the well rates - // TODO: initialize rates using perforation rates - compositionalMultiphaseWellKernels:: - RateInitializationKernel:: - launch( subRegion.size(), - m_targetPhaseIndex, - wellControls, - time_n, // initialization done at time_n - wellElemPhaseDens, - wellElemTotalDens, - connRate ); + forSubGroups< MaximumBHPConstraint, InjectionConstraint< VolumeRateConstraint >, InjectionConstraint< MassRateConstraint >, + InjectionConstraint< PhaseVolumeRateConstraint > >( [&]( auto & constraint ) + { + if( constraint.getControl() == inputControl ) + { + setCurrentConstraint( &constraint ); + setControl( static_cast< WellControls::Control >(inputControl) ); // tjb old + } + } ); } + } + // get well primary variables on well elements + arrayView1d< real64 > const & wellElemPressure = subRegion.getField< well::pressure >(); + arrayView1d< real64 > const & wellElemTemp = subRegion.getField< well::temperature >(); + arrayView2d< real64, compflow::USD_COMP > const & wellElemCompDens = subRegion.getField< well::globalCompDensity >(); + arrayView1d< real64 > const & connRate = subRegion.getField< well::connectionRate >(); + + // get the info stored on well elements + arrayView2d< real64, compflow::USD_COMP > const & wellElemCompFrac = subRegion.getField< well::globalCompFraction >(); + arrayView1d< real64 const > const & wellElemGravCoef = subRegion.getField< well::gravityCoefficient >(); + + // get the element region, subregion, index + arrayView1d< localIndex const > const resElementRegion = perforationData.getField< perforation::reservoirElementRegion >(); + arrayView1d< localIndex const > const resElementSubRegion = perforationData.getField< perforation::reservoirElementSubRegion >(); + arrayView1d< localIndex const > const resElementIndex = perforationData.getField< perforation::reservoirElementIndex >(); + + arrayView1d< real64 const > const & perfGravCoef = perforationData.getField< fields::well::gravityCoefficient >(); + arrayView1d< integer const > const & perfStatus = perforationData.getField< fields::perforation::perforationStatus >(); + + // 1) Loop over all perforations to compute an average mixture density and component fraction + // 2) Initialize the reference pressure + // 3) Estimate the pressures in the well elements using the average density + compositionalMultiphaseWellKernels:: + PresTempCompFracInitializationKernel:: + launch( perforationData.size(), + subRegion.size(), + numComp, + numPhase, + *this, + 0.0, // initialization done at t = 0 + resCompFlowAccessors.get( flow::pressure{} ), + resCompFlowAccessors.get( flow::temperature{} ), + resCompFlowAccessors.get( flow::globalCompDensity{} ), + resCompFlowAccessors.get( flow::phaseVolumeFraction{} ), + resMultiFluidAccessors.get( fields::multifluid::phaseMassDensity{} ), + resElementRegion, + resElementSubRegion, + resElementIndex, + perfGravCoef, + perfStatus, + wellElemGravCoef, + wellElemPressure, + wellElemTemp, + wellElemCompFrac ); + + // get well secondary variables on well elements + string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString() ); + MultiFluidBase & fluid = getConstitutiveModel< MultiFluidBase >( subRegion, fluidName ); + arrayView3d< real64 const, constitutive::multifluid::USD_PHASE > const & wellElemPhaseDens = fluid.phaseDensity(); + arrayView2d< real64 const, constitutive::multifluid::USD_FLUID > const & wellElemTotalDens = fluid.totalDensity(); + + // 4) Back calculate component densities + constitutive::constitutiveUpdatePassThru( fluid, [&] ( auto & castedFluid ) + { + typename TYPEOFREF( castedFluid ) ::KernelWrapper fluidWrapper = castedFluid.createKernelWrapper(); + + thermalCompositionalMultiphaseBaseKernels:: + FluidUpdateKernel:: + launch< serialPolicy >( subRegion.size(), + fluidWrapper, + wellElemPressure, + wellElemTemp, + wellElemCompFrac ); } ); - } ); + compositionalMultiphaseWellKernels:: + CompDensInitializationKernel::launch( subRegion.size(), + numComp, + wellElemCompFrac, + wellElemTotalDens, + wellElemCompDens ); + + // 5) Recompute the pressure-dependent properties + + updateSubRegionState( elemManager, subRegion ); + + // 6) Estimate the well rates + // TODO: initialize rates using perforation rates + compositionalMultiphaseWellKernels:: + RateInitializationKernel:: + launch( subRegion.size(), + *this, + time_n, // initialization done at time_n + wellElemPhaseDens, + wellElemTotalDens, + connRate ); + + updateVolRatesForConstraint( subRegion ); + // Since this is a well manager class the rates need to be pushed into the WellControls class, which represnets the well + WellConstraintBase * constraint = getCurrentConstraint(); + constraint->setBHP ( getReference< real64 >( CompositionalMultiphaseWell::viewKeyStruct::currentBHPString() )); + constraint->setPhaseVolumeRates ( getReference< array1d< real64 > >( + CompositionalMultiphaseWell::viewKeyStruct::currentPhaseVolRateString() ) ); + constraint->setTotalVolumeRate ( getReference< real64 >( + CompositionalMultiphaseWell::viewKeyStruct::currentTotalVolRateString() )); + constraint->setMassRate( getReference< real64 >( CompositionalMultiphaseWell::viewKeyStruct::currentMassRateString() )); + // 7) Copy well / fluid dofs to "prop"_n variables + saveState( subRegion ); + } + else if( !hasNonZeroRate ) + { + setWellState( false ); + GEOS_LOG_RANK_0( "tjb shut wells "<< subRegion.getName()); + } + else + { + setWellState( true ); + // setup if restart + if( getCurrentConstraint() == nullptr ) + { + updateSubRegionState( elemManager, subRegion ); + if( isProducer() ) + { + forSubGroups< MinimumBHPConstraint, ProductionConstraint< VolumeRateConstraint >, ProductionConstraint< MassRateConstraint >, + ProductionConstraint< PhaseVolumeRateConstraint > >( [&]( + auto + & constraint ) + { + if( ConstraintTypeId( getControl()) == constraint.getControl() ) + { + setCurrentConstraint( &constraint ); + } + } ); + } + else + { + forSubGroups< MaximumBHPConstraint, InjectionConstraint< VolumeRateConstraint >, InjectionConstraint< MassRateConstraint >, InjectionConstraint< PhaseVolumeRateConstraint > >( [&]( + auto + & + constraint ) + { + if( ConstraintTypeId( getControl()) == constraint.getControl() ) + { + setCurrentConstraint( &constraint ); + } + } ); + } + } + + } } -void CompositionalMultiphaseWell::assembleFluxTerms( real64 const & time, - real64 const & dt, - DomainPartition & domain, - DofManager const & dofManager, - CRSMatrixView< real64, globalIndex const > const & localMatrix, - arrayView1d< real64 > const & localRhs ) + + +void CompositionalMultiphaseWell::assembleWellFluxTerms( real64 const & time, + real64 const & dt, + WellElementSubRegion const & subRegion, + DofManager const & dofManager, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) { GEOS_MARK_FUNCTION; GEOS_UNUSED_VAR( time ); @@ -1190,62 +1156,55 @@ void CompositionalMultiphaseWell::assembleFluxTerms( real64 const & time, kernelFlags.set( isothermalCompositionalMultiphaseBaseKernels::KernelFlags::TotalMassEquation ); string const wellDofKey = dofManager.getKey( wellElementDofName()); - forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &, - MeshLevel & mesh, - string_array const & regionNames ) + + + if( isWellOpen( ) && !m_keepVariablesConstantDuringInitStep ) { - mesh.getElemManager().forElementSubRegions< WellElementSubRegion >( regionNames, - [&]( localIndex const, - WellElementSubRegion & subRegion ) + string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString()); + MultiFluidBase const & fluid = getConstitutiveModel< MultiFluidBase >( subRegion, fluidName ); + int numComponents = fluid.numFluidComponents(); + + if( isThermal() ) { - WellControls const & well_controls = getWellControls( subRegion ); - if( well_controls.getWellStatus() == WellControls::Status::OPEN && !m_keepVariablesConstantDuringInitStep ) - { - string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString()); - MultiFluidBase const & fluid = getConstitutiveModel< MultiFluidBase >( subRegion, fluidName ); - int const numComponents = fluid.numFluidComponents(); + thermalCompositionalMultiphaseWellKernels:: + FaceBasedAssemblyKernelFactory:: + createAndLaunch< parallelDevicePolicy<> >( numComponents, + dt, + dofManager.rankOffset(), + kernelFlags, + wellDofKey, + *this, + subRegion, + fluid, + localMatrix, + localRhs ); + } + else + { + compositionalMultiphaseWellKernels:: + FaceBasedAssemblyKernelFactory:: + createAndLaunch< parallelDevicePolicy<> >( numComponents, + dt, + dofManager.rankOffset(), + kernelFlags, + wellDofKey, + *this, + subRegion, + localMatrix, + localRhs ); + } + } - if( isThermal() ) - { - thermalCompositionalMultiphaseWellKernels:: - FaceBasedAssemblyKernelFactory:: - createAndLaunch< parallelDevicePolicy<> >( numComponents, - dt, - dofManager.rankOffset(), - kernelFlags, - wellDofKey, - well_controls, - subRegion, - fluid, - localMatrix, - localRhs ); - } - else - { - compositionalMultiphaseWellKernels:: - FaceBasedAssemblyKernelFactory:: - createAndLaunch< parallelDevicePolicy<> >( numComponents, - dt, - dofManager.rankOffset(), - kernelFlags, - wellDofKey, - well_controls, - subRegion, - localMatrix, - localRhs ); - } - } - } ); - } ); } -void CompositionalMultiphaseWell::assembleAccumulationTerms( real64 const & time, - real64 const & dt, - DomainPartition & domain, - DofManager const & dofManager, - CRSMatrixView< real64, globalIndex const > const & localMatrix, - arrayView1d< real64 > const & localRhs ) + +void CompositionalMultiphaseWell::assembleWellAccumulationTerms( real64 const & time, + real64 const & dt, + WellElementSubRegion & subRegion, + DofManager const & dofManager, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) { GEOS_MARK_FUNCTION; GEOS_UNUSED_VAR( time ); @@ -1256,126 +1215,128 @@ void CompositionalMultiphaseWell::assembleAccumulationTerms( real64 const & time kernelFlags.set( isothermalCompositionalMultiphaseBaseKernels::KernelFlags::TotalMassEquation ); string const wellDofKey = dofManager.getKey( wellElementDofName() ); - forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &, - MeshLevel & mesh, - string_array const & regionNames ) + + string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString()); + MultiFluidBase const & fluid = getConstitutiveModel< MultiFluidBase >( subRegion, fluidName ); + integer const numPhases = fluid.numFluidPhases(); + integer const numComponents = fluid.numFluidComponents(); + + if( getWellStatus() == WellControls::Status::OPEN && !m_keepVariablesConstantDuringInitStep ) { - mesh.getElemManager().forElementSubRegions< WellElementSubRegion >( regionNames, - [&]( localIndex const, - WellElementSubRegion & subRegion ) + if( isThermal() ) + { + + thermalCompositionalMultiphaseWellKernels:: + ElementBasedAssemblyKernelFactory:: + createAndLaunch< parallelDevicePolicy<> >( numComponents, + numPhases, + thermalEffectsEnabled(), + isProducer(), + dofManager.rankOffset(), + kernelFlags, + wellDofKey, + subRegion, + fluid, + localMatrix, + localRhs ); + } + else + { + compositionalMultiphaseWellKernels:: + ElementBasedAssemblyKernelFactory:: + createAndLaunch< parallelDevicePolicy<> >( numComponents, + numPhases, + thermalEffectsEnabled(), + isProducer(), + dofManager.rankOffset(), + kernelFlags, + wellDofKey, + subRegion, + fluid, + localMatrix, + localRhs ); + } + // get the degrees of freedom and ghosting info + arrayView1d< globalIndex const > const & wellElemDofNumber = + subRegion.getReference< array1d< globalIndex > >( wellDofKey ); + arrayView1d< integer const > const wellElemGhostRank = subRegion.ghostRank(); + arrayView1d< integer const > const elemStatus = subRegion.getLocalWellElementStatus(); + arrayView1d< real64 > const mixConnRate = subRegion.getField< fields::well::connectionRate >(); + localIndex rank_offset = dofManager.rankOffset(); + forAll< parallelDevicePolicy<> >( subRegion.size(), [=] GEOS_HOST_DEVICE ( localIndex const ei ) { - string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString()); - MultiFluidBase const & fluid = getConstitutiveModel< MultiFluidBase >( subRegion, fluidName ); - integer const numPhases = fluid.numFluidPhases(); - integer const numComponents = fluid.numFluidComponents(); - WellControls const & wellControls = getWellControls( subRegion ); - if( wellControls.getWellStatus() == WellControls::Status::OPEN && !m_keepVariablesConstantDuringInitStep ) + if( wellElemGhostRank[ei] < 0 ) { - if( isThermal() ) + if( elemStatus[ei]==WellElementSubRegion::WellElemStatus::CLOSED ) { + mixConnRate[ei] = 0.0; + globalIndex const dofIndex = wellElemDofNumber[ei]; + localIndex const localRow = dofIndex - rank_offset; - thermalCompositionalMultiphaseWellKernels:: - ElementBasedAssemblyKernelFactory:: - createAndLaunch< parallelDevicePolicy<> >( numComponents, - numPhases, - wellControls.isProducer(), - dofManager.rankOffset(), - kernelFlags, - wellDofKey, - subRegion, - fluid, - localMatrix, - localRhs ); - } - else - { - compositionalMultiphaseWellKernels:: - ElementBasedAssemblyKernelFactory:: - createAndLaunch< parallelDevicePolicy<> >( numComponents, - numPhases, - wellControls.isProducer(), - dofManager.rankOffset(), - kernelFlags, - wellDofKey, - subRegion, - fluid, - localMatrix, - localRhs ); - } - // get the degrees of freedom and ghosting info - arrayView1d< globalIndex const > const & wellElemDofNumber = - subRegion.getReference< array1d< globalIndex > >( wellDofKey ); - arrayView1d< integer const > const wellElemGhostRank = subRegion.ghostRank(); - arrayView1d< integer const > const elemStatus = subRegion.getLocalWellElementStatus(); - arrayView1d< real64 > const mixConnRate = subRegion.getField< fields::well::mixtureConnectionRate >(); - localIndex rank_offset = dofManager.rankOffset(); - forAll< parallelDevicePolicy<> >( subRegion.size(), [=] GEOS_HOST_DEVICE ( localIndex const ei ) - { - if( wellElemGhostRank[ei] < 0 ) + real64 const unity = 1.0; + for( integer i=0; i < m_numDofPerWellElement; i++ ) { - if( elemStatus[ei]==WellElementSubRegion::WellElemStatus::CLOSED ) - { - mixConnRate[ei] = 0.0; - globalIndex const dofIndex = wellElemDofNumber[ei]; - localIndex const localRow = dofIndex - rank_offset; - - real64 const unity = 1.0; - for( integer i=0; i < m_numDofPerWellElement; i++ ) - { - globalIndex const rindex = localRow+i; - globalIndex const cindex =dofIndex + i; - localMatrix.template addToRow< serialAtomic >( rindex, - &cindex, - &unity, - 1 ); - localRhs[rindex] = 0.0; - } - } + globalIndex const rindex = localRow+i; + globalIndex const cindex =dofIndex + i; + localMatrix.template addToRow< serialAtomic >( rindex, + &cindex, + &unity, + 1 ); + localRhs[rindex] = 0.0; } - } ); + } } - else + } ); + + } + else + { + arrayView1d< globalIndex const > const & wellElemDofNumber = + subRegion.getReference< array1d< globalIndex > >( wellDofKey ); + arrayView1d< integer const > const wellElemGhostRank = subRegion.ghostRank(); + + arrayView1d< real64 > mixConnRate = subRegion.getField< fields::well::connectionRate >(); + localIndex rank_offset = dofManager.rankOffset(); + forAll< parallelDevicePolicy<> >( subRegion.size(), [=] GEOS_HOST_DEVICE ( localIndex const ei ) + { + if( wellElemGhostRank[ei] < 0 ) { - // get the degrees of freedom and ghosting info - arrayView1d< globalIndex const > const & wellElemDofNumber = - subRegion.getReference< array1d< globalIndex > >( wellDofKey ); - arrayView1d< integer const > const & wellElemGhostRank = subRegion.ghostRank(); - localIndex rank_offset = dofManager.rankOffset(); - forAll< parallelDevicePolicy<> >( subRegion.size(), [=] GEOS_HOST_DEVICE ( localIndex const ei ) + mixConnRate[ei] = 0.0; + globalIndex const dofIndex = wellElemDofNumber[ei]; + localIndex const localRow = dofIndex - rank_offset; + + real64 const unity = 1.0; + for( integer i=0; i < m_numDofPerWellElement; i++ ) { - if( wellElemGhostRank[ei] < 0 ) - { - globalIndex const dofIndex = wellElemDofNumber[ei]; - localIndex const localRow = dofIndex - rank_offset; - - real64 unity = 1.0; - for( integer i=0; i < m_numDofPerWellElement; i++ ) - { - globalIndex const rindex = localRow+i; - globalIndex const cindex =dofIndex + i; - localMatrix.template addToRow< serialAtomic >( rindex, - &cindex, - &unity, - 1 ); - localRhs[rindex] = 0.0; - } - } - } ); + globalIndex const rindex = localRow+i; + globalIndex const cindex =dofIndex + i; + localMatrix.template addToRow< serialAtomic >( rindex, + &cindex, + &unity, + 1 ); + localRhs[rindex] = 0.0; + } } - } ); // forElementSubRegions - } ); // forDiscretizationOnMeshTargets + } ); + // zero out current state constraint quantities + getReference< real64 >( CompositionalMultiphaseWell::viewKeyStruct::currentBHPString() )=0.0; + getReference< array1d< real64 > >( + CompositionalMultiphaseWell::viewKeyStruct::currentPhaseVolRateString() ).zero(); + getReference< real64 >( + CompositionalMultiphaseWell::viewKeyStruct::currentTotalVolRateString() )=0.0; + getReference< real64 >( CompositionalMultiphaseWell::viewKeyStruct::currentMassRateString() )=0.0; + } } - -real64 -CompositionalMultiphaseWell::calculateResidualNorm( real64 const & time_n, - real64 const & dt, - DomainPartition const & domain, - DofManager const & dofManager, - arrayView1d< real64 const > const & localRhs ) +array1d< real64 > +CompositionalMultiphaseWell::calculateLocalWellResidualNorm( real64 const & time_n, + real64 const & dt, + NonlinearSolverParameters const & nonlinearSolverParameters, + WellElementSubRegion const & subRegion, + DofManager const & dofManager, + arrayView1d< real64 const > const & localRhs ) { GEOS_MARK_FUNCTION; - integer numNorm = 1; // mass balance array1d< real64 > localResidualNorm; array1d< real64 > localResidualNormalizer; @@ -1391,85 +1352,127 @@ CompositionalMultiphaseWell::calculateResidualNorm( real64 const & time_n, globalIndex const rankOffset = dofManager.rankOffset(); string const wellDofKey = dofManager.getKey( wellElementDofName() ); - forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &, - MeshLevel const & mesh, - string_array const & regionNames ) - { - ElementRegionManager const & elemManager = mesh.getElemManager(); + string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString() ); + MultiFluidBase const & fluid = subRegion.getConstitutiveModel< MultiFluidBase >( fluidName ); - elemManager.forElementSubRegions< WellElementSubRegion >( regionNames, - [&]( localIndex const, - WellElementSubRegion const & subRegion ) + if( isWellOpen( ) ) + { + // step 1: compute the norm in the subRegion + if( isThermal() ) { - - - string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString() ); - MultiFluidBase const & fluid = subRegion.getConstitutiveModel< MultiFluidBase >( fluidName ); - - WellControls const & wellControls = getWellControls( subRegion ); - - // step 1: compute the norm in the subRegion - - if( isThermal() ) + real64 subRegionResidualNorm[2]{}; + + thermalCompositionalMultiphaseWellKernels::ResidualNormKernelFactory:: + createAndLaunch< parallelDevicePolicy<> >( m_numComponents, + rankOffset, + wellDofKey, + localRhs, + subRegion, + fluid, + *this, + time_n, + dt, + nonlinearSolverParameters.m_minNormalizer, + subRegionResidualNorm ); + // step 2: reduction across meshBodies/regions/subRegions + + for( integer i=0; i >( m_numComponents, - m_targetPhaseIndex, - rankOffset, - wellDofKey, - localRhs, - subRegion, - fluid, - wellControls, - time_n, - dt, - m_nonlinearSolverParameters.m_minNormalizer, - subRegionResidualNorm ); - // step 2: reduction across meshBodies/regions/subRegions - - for( integer i=0; i localResidualNorm[i] ) { - if( subRegionResidualNorm[i] > localResidualNorm[i] ) - { - localResidualNorm[i] = subRegionResidualNorm[i]; - } + localResidualNorm[i] = subRegionResidualNorm[i]; } - } - else + + } + else + { + real64 subRegionResidualNorm[1]{}; + compositionalMultiphaseWellKernels::ResidualNormKernelFactory:: + createAndLaunch< parallelDevicePolicy<> >( m_numComponents, + m_numDofPerWellElement, + rankOffset, + wellDofKey, + localRhs, + subRegion, + fluid, + *this, + time_n, + dt, + nonlinearSolverParameters.m_minNormalizer, + subRegionResidualNorm ); + + + + // step 2: reduction across meshBodies/regions/subRegions + + if( subRegionResidualNorm[0] > localResidualNorm[0] ) { - real64 subRegionResidualNorm[1]{}; - compositionalMultiphaseWellKernels::ResidualNormKernelFactory:: - createAndLaunch< parallelDevicePolicy<> >( m_numComponents, - numDofPerWellElement(), - m_targetPhaseIndex, - rankOffset, - wellDofKey, - localRhs, - subRegion, - fluid, - wellControls, - time_n, - dt, - m_nonlinearSolverParameters.m_minNormalizer, - subRegionResidualNorm ); - - - - // step 2: reduction across meshBodies/regions/subRegions - - if( subRegionResidualNorm[0] > localResidualNorm[0] ) - { - localResidualNorm[0] = subRegionResidualNorm[0]; - } + localResidualNorm[0] = subRegionResidualNorm[0]; } - } ); - } ); + } + } + else + { + for( integer i=0; i const & localRhs ) +{ + GEOS_MARK_FUNCTION; + integer numNorm = 1; // mass balance + array1d< real64 > localResidualNorm; + array1d< real64 > localResidualNormalizer; + + if( isThermal() ) + { + numNorm = 2; // mass balance and energy balance + } + localResidualNorm.resize( numNorm ); + localResidualNormalizer.resize( numNorm ); + + + //globalIndex const rankOffset = dofManager.rankOffset(); + string const wellDofKey = dofManager.getKey( wellElementDofName() ); + + + + //string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString() ); + //MultiFluidBase const & fluid = subRegion.getConstitutiveModel< MultiFluidBase >( fluidName ); + + if( isWellOpen( ) ) + { + localResidualNorm = calculateLocalWellResidualNorm( time_n, + dt, + nonlinearSolverParameters, + subRegion, + dofManager, + localRhs ); + + } + else + { + for( integer i=0; i const & localSolution ) +CompositionalMultiphaseWell::scalingForLocalSystemSolution( WellElementSubRegion & subRegion, + DofManager const & dofManager, + real64 & maxDeltaPres, + real64 & maxDeltaCompDens, + real64 & maxDeltaTemp, + real64 & minPresScalingFactor, + real64 & minCompDensScalingFactor, + real64 & minTempScalingFactor, + arrayView1d< real64 const > const & localSolution ) { GEOS_MARK_FUNCTION; string const wellDofKey = dofManager.getKey( wellElementDofName() ); real64 scalingFactor = 1.0; - real64 maxDeltaPres = 0.0, maxDeltaCompDens = 0.0, maxDeltaTemp = 0.0; - real64 minPresScalingFactor = 1.0, minCompDensScalingFactor = 1.0, minTempScalingFactor = 1.0; - - forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &, - MeshLevel & mesh, - string_array const & regionNames ) - { - mesh.getElemManager().forElementSubRegions( regionNames, - [&]( localIndex const, - ElementSubRegionBase & subRegion ) - { - arrayView1d< real64 const > const pressure = subRegion.getField< well::pressure >(); - arrayView1d< real64 const > const temperature = subRegion.getField< well::temperature >(); - arrayView2d< real64 const, compflow::USD_COMP > const compDens = subRegion.getField< well::globalCompDensity >(); - arrayView1d< real64 > pressureScalingFactor = subRegion.getField< well::pressureScalingFactor >(); - arrayView1d< real64 > temperatureScalingFactor = subRegion.getField< well::temperatureScalingFactor >(); - arrayView1d< real64 > compDensScalingFactor = subRegion.getField< well::globalCompDensityScalingFactor >(); - const integer temperatureOffset = m_numComponents+2; - auto const subRegionData = - m_isThermal + maxDeltaPres = 0.0; + maxDeltaCompDens = 0.0; + maxDeltaTemp = 0.0; + minPresScalingFactor = 1.0; + minCompDensScalingFactor = 1.0; + minTempScalingFactor = 1.0; + + arrayView1d< real64 const > const pressure = subRegion.getField< fields::well::pressure >(); + arrayView1d< real64 const > const temperature = subRegion.getField< fields::well::temperature >(); + arrayView2d< real64 const, compflow::USD_COMP > const compDens = subRegion.getField< fields::well::globalCompDensity >(); + arrayView1d< real64 > pressureScalingFactor = subRegion.getField< fields::well::pressureScalingFactor >(); + arrayView1d< real64 > temperatureScalingFactor = subRegion.getField< fields::well::temperatureScalingFactor >(); + arrayView1d< real64 > compDensScalingFactor = subRegion.getField< fields::well::globalCompDensityScalingFactor >(); + const integer temperatureOffset = m_numComponents+2; + auto const subRegionData = + m_isThermal ? thermalCompositionalMultiphaseBaseKernels:: - SolutionScalingKernelFactory:: - createAndLaunch< parallelDevicePolicy<> >( m_maxRelativePresChange, - m_maxAbsolutePresChange, - m_maxRelativeTempChange, - m_maxCompFracChange, - m_maxRelativeCompDensChange, - pressure, - temperature, - compDens, - pressureScalingFactor, - compDensScalingFactor, - temperatureScalingFactor, - dofManager.rankOffset(), - m_numComponents, - wellDofKey, - subRegion, - localSolution, - temperatureOffset ) + SolutionScalingKernelFactory:: + createAndLaunch< parallelDevicePolicy<> >( m_maxRelativePresChange, + m_maxAbsolutePresChange, + m_maxRelativeTempChange, + m_maxCompFracChange, + m_maxRelativeCompDensChange, + pressure, + temperature, + compDens, + pressureScalingFactor, + compDensScalingFactor, + temperatureScalingFactor, + dofManager.rankOffset(), + m_numComponents, + wellDofKey, + subRegion, + localSolution, + temperatureOffset ) : isothermalCompositionalMultiphaseBaseKernels:: - SolutionScalingKernelFactory:: - createAndLaunch< parallelDevicePolicy<> >( m_maxRelativePresChange, - m_maxAbsolutePresChange, - m_maxCompFracChange, - m_maxRelativeCompDensChange, - pressure, - compDens, - pressureScalingFactor, - compDensScalingFactor, - dofManager.rankOffset(), - m_numComponents, - wellDofKey, - subRegion, - localSolution ); - - - scalingFactor = std::min( subRegionData.localMinVal, scalingFactor ); - - maxDeltaPres = std::max( maxDeltaPres, subRegionData.localMaxDeltaPres ); - maxDeltaCompDens = std::max( maxDeltaCompDens, subRegionData.localMaxDeltaCompDens ); - maxDeltaTemp = std::max( maxDeltaTemp, subRegionData.localMaxDeltaTemp ); - minPresScalingFactor = std::min( minPresScalingFactor, subRegionData.localMinPresScalingFactor ); - minCompDensScalingFactor = std::min( minCompDensScalingFactor, subRegionData.localMinCompDensScalingFactor ); - minTempScalingFactor = std::min( minTempScalingFactor, subRegionData.localMinTempScalingFactor ); - } ); - } ); + SolutionScalingKernelFactory:: + createAndLaunch< parallelDevicePolicy<> >( m_maxRelativePresChange, + m_maxAbsolutePresChange, + m_maxCompFracChange, + m_maxRelativeCompDensChange, + pressure, + compDens, + pressureScalingFactor, + compDensScalingFactor, + dofManager.rankOffset(), + m_numComponents, + wellDofKey, + subRegion, + localSolution ); + + + scalingFactor = std::min( subRegionData.localMinVal, scalingFactor ); + + maxDeltaPres = std::max( maxDeltaPres, subRegionData.localMaxDeltaPres ); + maxDeltaCompDens = std::max( maxDeltaCompDens, subRegionData.localMaxDeltaCompDens ); + maxDeltaTemp = std::max( maxDeltaTemp, subRegionData.localMaxDeltaTemp ); + minPresScalingFactor = std::min( minPresScalingFactor, subRegionData.localMinPresScalingFactor ); + minCompDensScalingFactor = std::min( minCompDensScalingFactor, subRegionData.localMinCompDensScalingFactor ); + minTempScalingFactor = std::min( minTempScalingFactor, subRegionData.localMinTempScalingFactor ); + + scalingFactor = MpiWrapper::min( scalingFactor ); + maxDeltaPres = MpiWrapper::max( maxDeltaPres ); + maxDeltaCompDens = MpiWrapper::max( maxDeltaCompDens ); + minPresScalingFactor = MpiWrapper::min( minPresScalingFactor ); + minCompDensScalingFactor = MpiWrapper::min( minCompDensScalingFactor ); + + string const massUnit = m_useMass ? "kg/m3" : "mol/m3"; + GEOS_LOG_LEVEL_RANK_0( logInfo::Solution, + GEOS_FMT( " {}: Max well pressure change: {} Pa (before scaling)", + getName(), GEOS_FMT( "{:.{}f}", maxDeltaPres, 3 ) ) ); + GEOS_LOG_LEVEL_RANK_0( logInfo::Solution, + GEOS_FMT( " {}: Max well component density change: {} {} (before scaling)", + getName(), GEOS_FMT( "{:.{}f}", maxDeltaCompDens, 3 ), massUnit ) ); + + if( m_isThermal ) + { + maxDeltaTemp = MpiWrapper::max( maxDeltaTemp ); + minTempScalingFactor = MpiWrapper::min( minTempScalingFactor ); + GEOS_LOG_LEVEL_RANK_0( logInfo::Solution, + GEOS_FMT( " {}: Max well temperature change: {} K (before scaling)", + getName(), GEOS_FMT( "{:.{}f}", maxDeltaTemp, 3 ) ) ); + } + + GEOS_LOG_LEVEL_RANK_0( logInfo::Solution, + GEOS_FMT( " {}: Min well pressure scaling factor: {}", + getName(), minPresScalingFactor ) ); + GEOS_LOG_LEVEL_RANK_0( logInfo::Solution, + GEOS_FMT( " {}: Min well component density scaling factor: {}", + getName(), minCompDensScalingFactor ) ); + if( m_isThermal ) + { + GEOS_LOG_LEVEL_RANK_0( logInfo::Solution, + GEOS_FMT( " {}: Min well temperature scaling factor: {}", + getName(), minTempScalingFactor ) ); + } + + return LvArray::math::max( scalingFactor, m_minScalingFactor ); + +} +real64 +CompositionalMultiphaseWell::scalingForWellSystemSolution( WellElementSubRegion & subRegion, + DofManager const & dofManager, + arrayView1d< real64 const > const & localSolution ) +{ + GEOS_MARK_FUNCTION; + + string const wellDofKey = dofManager.getKey( wellElementDofName() ); - scalingFactor = MpiWrapper::min( scalingFactor ); - maxDeltaPres = MpiWrapper::max( maxDeltaPres ); - maxDeltaCompDens = MpiWrapper::max( maxDeltaCompDens ); - minPresScalingFactor = MpiWrapper::min( minPresScalingFactor ); - minCompDensScalingFactor = MpiWrapper::min( minCompDensScalingFactor ); + real64 maxDeltaPres = 0.0, maxDeltaCompDens = 0.0, maxDeltaTemp = 0.0; + real64 minPresScalingFactor = 1.0, minCompDensScalingFactor = 1.0, minTempScalingFactor = 1.0; + real64 scalingFactor =scalingForLocalSystemSolution( subRegion, + dofManager, + maxDeltaPres, + maxDeltaCompDens, + maxDeltaTemp, + minPresScalingFactor, + minCompDensScalingFactor, + minTempScalingFactor, + localSolution ); string const massUnit = m_useMass ? "kg/m3" : "mol/m3"; GEOS_LOG_LEVEL_RANK_0( logInfo::Solution, GEOS_FMT( " {}: Max well pressure change: {} Pa (before scaling)", @@ -1596,7 +1651,6 @@ CompositionalMultiphaseWell::scalingForSystemSolution( DomainPartition & domain, getName(), GEOS_FMT( "{:.{}f}", maxDeltaTemp, 3 ) ) ); } - GEOS_LOG_LEVEL_RANK_0( logInfo::Solution, GEOS_FMT( " {}: Min well pressure scaling factor: {}", getName(), minPresScalingFactor ) ); @@ -1610,94 +1664,90 @@ CompositionalMultiphaseWell::scalingForSystemSolution( DomainPartition & domain, getName(), minTempScalingFactor ) ); } - return LvArray::math::max( scalingFactor, m_minScalingFactor ); } + + bool -CompositionalMultiphaseWell::checkSystemSolution( DomainPartition & domain, - DofManager const & dofManager, - arrayView1d< real64 const > const & localSolution, - real64 const scalingFactor ) +CompositionalMultiphaseWell::checkWellSystemSolution( WellElementSubRegion & subRegion, + DofManager const & dofManager, + arrayView1d< real64 const > const & localSolution, + real64 const scalingFactor ) { GEOS_MARK_FUNCTION; string const wellDofKey = dofManager.getKey( wellElementDofName() ); integer localCheck = 1; + + real64 minPres = 0.0, minDens = 0.0, minTotalDens = 0.0; integer numNegPres = 0, numNegDens = 0, numNegTotalDens = 0; - forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &, - MeshLevel & mesh, - string_array const & regionNames ) - { - mesh.getElemManager().forElementSubRegions( regionNames, - [&]( localIndex const, - ElementSubRegionBase & subRegion ) - { - //integer const m_allowCompDensChopping(true); - integer const m_allowNegativePressure( false ); - compositionalMultiphaseUtilities::ScalingType const m_scalingType( compositionalMultiphaseUtilities::ScalingType::Global ); - arrayView1d< real64 const > const pressure = - subRegion.getField< well::pressure >(); - arrayView1d< real64 const > const temperature = - subRegion.getField< well::temperature >(); - arrayView2d< real64 const, compflow::USD_COMP > const compDens = - subRegion.getField< well::globalCompDensity >(); - arrayView1d< real64 > pressureScalingFactor = subRegion.getField< well::pressureScalingFactor >(); - arrayView1d< real64 > temperatureScalingFactor = subRegion.getField< well::temperatureScalingFactor >(); - arrayView1d< real64 > compDensScalingFactor = subRegion.getField< well::globalCompDensityScalingFactor >(); - - // check that pressure and component densities are non-negative - // for thermal, check that temperature is above 273.15 K - const integer temperatureOffset = m_numComponents+2; - auto const subRegionData = - m_isThermal + const std::string wellName = subRegion.getName(); + + //integer const m_allowCompDensChopping(true); + integer const m_allowNegativePressure( false ); + compositionalMultiphaseUtilities::ScalingType const m_scalingType( compositionalMultiphaseUtilities::ScalingType::Global ); + arrayView1d< real64 const > const pressure = + subRegion.getField< fields::well::pressure >(); + arrayView1d< real64 const > const temperature = + subRegion.getField< fields::well::temperature >(); + arrayView2d< real64 const, compflow::USD_COMP > const compDens = + subRegion.getField< fields::well::globalCompDensity >(); + arrayView1d< real64 > pressureScalingFactor = subRegion.getField< fields::well::pressureScalingFactor >(); + arrayView1d< real64 > temperatureScalingFactor = subRegion.getField< fields::well::temperatureScalingFactor >(); + arrayView1d< real64 > compDensScalingFactor = subRegion.getField< fields::well::globalCompDensityScalingFactor >(); + + // check that pressure and component densities are non-negative + // for thermal, check that temperature is above 273.15 K + const integer temperatureOffset = m_numComponents+2; + auto const subRegionData = + m_isThermal ? thermalCompositionalMultiphaseBaseKernels:: - SolutionCheckKernelFactory:: - createAndLaunch< parallelDevicePolicy<> >( m_allowCompDensChopping, - m_allowNegativePressure, - m_scalingType, - scalingFactor, - pressure, - temperature, - compDens, - pressureScalingFactor, - temperatureScalingFactor, - compDensScalingFactor, - dofManager.rankOffset(), - m_numComponents, - wellDofKey, - subRegion, - localSolution, - temperatureOffset ) + SolutionCheckKernelFactory:: + createAndLaunch< parallelDevicePolicy<> >( m_allowCompDensChopping, + m_allowNegativePressure, + m_scalingType, + scalingFactor, + pressure, + temperature, + compDens, + pressureScalingFactor, + temperatureScalingFactor, + compDensScalingFactor, + dofManager.rankOffset(), + m_numComponents, + wellDofKey, + subRegion, + localSolution, + temperatureOffset ) : isothermalCompositionalMultiphaseBaseKernels:: - SolutionCheckKernelFactory:: - createAndLaunch< parallelDevicePolicy<> >( m_allowCompDensChopping, - m_allowNegativePressure, - m_scalingType, - scalingFactor, - pressure, - compDens, - pressureScalingFactor, - compDensScalingFactor, - dofManager.rankOffset(), - m_numComponents, - wellDofKey, - subRegion, - localSolution ); - - localCheck = std::min( localCheck, subRegionData.localMinVal ); - - minPres = std::min( minPres, subRegionData.localMinPres ); - minDens = std::min( minDens, subRegionData.localMinDens ); - minTotalDens = std::min( minTotalDens, subRegionData.localMinTotalDens ); - numNegPres += subRegionData.localNumNegPressures; - numNegDens += subRegionData.localNumNegDens; - numNegTotalDens += subRegionData.localNumNegTotalDens; - } ); - } ); + SolutionCheckKernelFactory:: + createAndLaunch< parallelDevicePolicy<> >( m_allowCompDensChopping, + m_allowNegativePressure, + m_scalingType, + scalingFactor, + pressure, + compDens, + pressureScalingFactor, + compDensScalingFactor, + dofManager.rankOffset(), + m_numComponents, + wellDofKey, + subRegion, + localSolution ); + + localCheck = std::min( localCheck, subRegionData.localMinVal ); + + minPres = std::min( minPres, subRegionData.localMinPres ); + minDens = std::min( minDens, subRegionData.localMinDens ); + minTotalDens = std::min( minTotalDens, subRegionData.localMinTotalDens ); + numNegPres += subRegionData.localNumNegPressures; + numNegDens += subRegionData.localNumNegDens; + numNegTotalDens += subRegionData.localNumNegTotalDens; + minPres = MpiWrapper::min( minPres ); minDens = MpiWrapper::min( minDens ); @@ -1709,103 +1759,176 @@ CompositionalMultiphaseWell::checkSystemSolution( DomainPartition & domain, if( numNegPres > 0 ) GEOS_LOG_LEVEL_RANK_0( logInfo::Solution, GEOS_FMT( " {}: Number of negative well pressure values: {}, minimum value: {} Pa", - getName(), numNegPres, fmt::format( "{:.{}f}", minPres, 3 ) ) ); + subRegion.getName(), numNegPres, fmt::format( "{:.{}f}", minPres, 3 ) ) ); string const massUnit = m_useMass ? "kg/m3" : "mol/m3"; if( numNegDens > 0 ) GEOS_LOG_LEVEL_RANK_0( logInfo::Solution, GEOS_FMT( " {}: Number of negative well component density values: {}, minimum value: {} {} ", - getName(), numNegDens, fmt::format( "{:.{}f}", minDens, 3 ), massUnit ) ); + subRegion.getName(), numNegDens, fmt::format( "{:.{}f}", minDens, 3 ), massUnit ) ); if( minTotalDens > 0 ) GEOS_LOG_LEVEL_RANK_0( logInfo::Solution, GEOS_FMT( " {}: Number of negative total well density values: {}, minimum value: {} {} ", - getName(), minTotalDens, fmt::format( "{:.{}f}", minDens, 3 ), massUnit ) ); + subRegion.getName(), minTotalDens, fmt::format( "{:.{}f}", minDens, 3 ), massUnit ) ); + + return MpiWrapper::min( localCheck ); } -void CompositionalMultiphaseWell::computePerforationRates( real64 const & time_n, - real64 const & GEOS_UNUSED_PARAM( dt ), - DomainPartition & domain ) +void CompositionalMultiphaseWell::computeWellPerforationRates( real64 const & time_n, + real64 const & GEOS_UNUSED_PARAM( dt ), + ElementRegionManager & elemManager, + WellElementSubRegion & subRegion ) { GEOS_MARK_FUNCTION; GEOS_UNUSED_VAR( time_n ); - forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &, - MeshLevel & mesh, - string_array const & regionNames ) + //CompositionalMultiphaseBase const & flowSolver = getParent().getParent().getGroup< CompositionalMultiphaseBase >( getFlowSolverName() ); + + PerforationData * const perforationData = subRegion.getPerforationData(); + + if( isWellOpen() && !m_keepVariablesConstantDuringInitStep ) { - // TODO: change the way we access the flowSolver here - CompositionalMultiphaseBase const & flowSolver = getParent().getGroup< CompositionalMultiphaseBase >( getFlowSolverName() ); - ElementRegionManager & elemManager = mesh.getElemManager(); + string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString() ); + MultiFluidBase const & fluid = getConstitutiveModel< MultiFluidBase >( subRegion, fluidName ); + bool isThermal = fluid.isThermal(); - elemManager.forElementSubRegions< WellElementSubRegion >( regionNames, [&]( localIndex const, - WellElementSubRegion & subRegion ) + if( isThermal ) { - PerforationData * const perforationData = subRegion.getPerforationData(); - WellControls const & wellControls = getWellControls( subRegion ); - if( wellControls.getWellStatus() == WellControls::Status::OPEN && !m_keepVariablesConstantDuringInitStep ) - { - string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString() ); - MultiFluidBase const & fluid = getConstitutiveModel< MultiFluidBase >( subRegion, fluidName ); - bool const isThermal = fluid.isThermal(); - - if( isThermal ) - { - thermalPerforationFluxKernels:: - PerforationFluxKernelFactory:: - createAndLaunch< parallelDevicePolicy<> >( m_numComponents, - m_numPhases, - flowSolver.getName(), - perforationData, - subRegion, - fluid, - elemManager, - wellControls.isInjector(), - wellControls.isCrossflowEnabled() ); - } - else - { - isothermalPerforationFluxKernels:: - PerforationFluxKernelFactory:: - createAndLaunch< parallelDevicePolicy<> >( m_numComponents, - m_numPhases, - flowSolver.getName(), - perforationData, - subRegion, - elemManager, - wellControls.isInjector(), - wellControls.isCrossflowEnabled() ); - } - } - else + thermalPerforationFluxKernels:: + PerforationFluxKernelFactory:: + createAndLaunch< parallelDevicePolicy<> >( m_numComponents, + m_numPhases, + getFlowSolverName(), + perforationData, + subRegion, + fluid, + elemManager, + isInjector(), + isCrossflowEnabled()); + } + else + { + isothermalPerforationFluxKernels:: + PerforationFluxKernelFactory:: + createAndLaunch< parallelDevicePolicy<> >( m_numComponents, + m_numPhases, + getFlowSolverName(), + perforationData, + subRegion, + elemManager, + isInjector(), + isCrossflowEnabled() ); + } + } + else + { + // Zero completion flow rate + arrayView2d< real64 > const compPerfRate = perforationData->getField< fields::well::compPerforationRate >(); + for( integer iperf=0; iperfsize(); iperf++ ) + { + for( integer ic = 0; ic < m_numComponents; ++ic ) { - // Zero completion flow rate - arrayView2d< real64 > const compPerfRate = perforationData->getField< well::compPerforationRate >(); - for( integer iperf=0; iperfsize(); iperf++ ) - { - for( integer ic = 0; ic < m_numComponents; ++ic ) - { - compPerfRate[iperf][ic] = 0.0; - } - } + compPerfRate[iperf][ic] = 0.0; } - } ); + } + } - } ); } - void -CompositionalMultiphaseWell::applySystemSolution( DofManager const & dofManager, - arrayView1d< real64 const > const & localSolution, - real64 const scalingFactor, - real64 const dt, - DomainPartition & domain ) +CompositionalMultiphaseWell::applyWellBoundaryConditions( real64 const time_n, + real64 const dt, + ElementRegionManager & elemManager, + WellElementSubRegion & subRegion, + DofManager const & dofManager, + arrayView1d< real64 > const & localRhs, + CRSMatrixView< real64, globalIndex const > const & localMatrix ) { + GEOS_UNUSED_VAR( elemManager ); + GEOS_UNUSED_VAR( time_n ); + + using namespace compositionalMultiphaseUtilities; + + BitFlags< isothermalCompositionalMultiphaseBaseKernels::KernelFlags > kernelFlags; + if( useTotalMassEquation() ) + kernelFlags.set( isothermalCompositionalMultiphaseBaseKernels::KernelFlags::TotalMassEquation ); + + integer const numComps = numFluidComponents(); + + globalIndex const rankOffset = dofManager.rankOffset(); + + + string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString() ); + MultiFluidBase const & fluid = subRegion.getConstitutiveModel< MultiFluidBase >( fluidName ); + + // if the well is shut, we neglect reservoir-well flow that may occur despite the zero rate + // therefore, we do not want to compute perforation rates and we simply assume they are zero + + //bool const detectCrossflow = + // ( wellControls.isInjector() ) && wellControls.isCrossflowEnabled() && + // getLogLevel() >= 1; // since detect crossflow requires communication, we detect it only if the logLevel is sufficiently high + + if( !isWellOpen( ) ) + { + return; + } + + PerforationData const * const perforationData = subRegion.getPerforationData(); + + // get the degrees of freedom + string const wellDofKey = dofManager.getKey( wellElementDofName() ); + + if( isThermal ( ) ) + { + coupledReservoirAndWellKernels:: + ThermalCompositionalMultiPhaseWellFluxKernelFactory:: + createAndLaunch< parallelDevicePolicy<> >( numComps, + *this, + isProducer(), + dt, + rankOffset, + wellDofKey, + subRegion, + perforationData, + fluid, + kernelFlags, + localRhs, + localMatrix ); + + } + else + { + coupledReservoirAndWellKernels:: + IsothermalCompositionalMultiPhaseWellFluxKernelFactory:: + createAndLaunch< parallelDevicePolicy<> >( numComps, + dt, + rankOffset, + wellDofKey, + subRegion, + perforationData, + fluid, + localRhs, + localMatrix, + kernelFlags ); + } + +} + +void +CompositionalMultiphaseWell::applyWellSystemSolution( DofManager const & dofManager, + arrayView1d< real64 const > const & localSolution, + real64 const scalingFactor, + real64 const dt, + DomainPartition & domain, + MeshLevel & mesh, + WellElementSubRegion & subRegion ) +{ + GEOS_UNUSED_VAR( domain ); DofManager::CompMask pressureMask( m_numDofPerWellElement, 0, 1 ); DofManager::CompMask componentMask( m_numDofPerWellElement, 1, numFluidComponents()+1 ); DofManager::CompMask connRateMask( m_numDofPerWellElement, numFluidComponents()+1, numFluidComponents()+2 ); @@ -1813,513 +1936,517 @@ CompositionalMultiphaseWell::applySystemSolution( DofManager const & dofManager, // update all the fields using the global damping coefficients dofManager.addVectorToField( localSolution, wellElementDofName(), - well::pressure::key(), + fields::well::pressure::key(), scalingFactor, pressureMask ); dofManager.addVectorToField( localSolution, wellElementDofName(), - well::globalCompDensity::key(), + fields::well::globalCompDensity::key(), scalingFactor, componentMask ); dofManager.addVectorToField( localSolution, wellElementDofName(), - well::mixtureConnectionRate::key(), + fields::well::connectionRate::key(), scalingFactor, connRateMask ); if( isThermal() ) { DofManager::CompMask temperatureMask( m_numDofPerWellElement, numFluidComponents()+2, numFluidComponents()+3 ); - dofManager.addVectorToField( localSolution, wellElementDofName(), - well::temperature::key(), + fields::well::temperature::key(), scalingFactor, temperatureMask ); } + +#if 1 // if component density chopping is allowed, some component densities may be negative after the update // these negative component densities are set to zero in this function if( m_allowCompDensChopping ) { - chopNegativeDensities( domain ); + chopNegativeDensities( subRegion ); } - - forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &, - MeshLevel & mesh, - string_array const & regionNames ) +#endif + std::cout << getTargetRegionNames()[0] << " Well after applyWellSystemSolution: " << std::endl; + // synchronize + FieldIdentifiers fieldsToBeSync; + if( isThermal() ) { - // synchronize - FieldIdentifiers fieldsToBeSync; - if( isThermal() ) - { - fieldsToBeSync.addElementFields( { well::pressure::key(), - well::globalCompDensity::key(), - well::mixtureConnectionRate::key(), - well::temperature::key() }, - regionNames ); - } - else - { - fieldsToBeSync.addElementFields( { well::pressure::key(), - well::globalCompDensity::key(), - well::mixtureConnectionRate::key() }, - regionNames ); - } - CommunicationTools::getInstance().synchronizeFields( fieldsToBeSync, - mesh, - domain.getNeighbors(), - true ); - } ); - + fieldsToBeSync.addElementFields( { fields::well::pressure::key(), + fields::well::globalCompDensity::key(), + fields::well::connectionRate::key(), + fields::well::temperature::key() }, + getTargetRegionNames() ); + } + else + { + fieldsToBeSync.addElementFields( { fields::well::pressure::key(), + fields::well::globalCompDensity::key(), + fields::well::connectionRate::key() }, + getTargetRegionNames() ); + } + CommunicationTools::getInstance().synchronizeFields( fieldsToBeSync, + mesh, + domain.getNeighbors(), + true ); } -void CompositionalMultiphaseWell::chopNegativeDensities( DomainPartition & domain ) + + +void CompositionalMultiphaseWell::chopNegativeDensities( WellElementSubRegion & subRegion ) { integer const numComp = m_numComponents; - forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &, - MeshLevel & mesh, - string_array const & regionNames ) - { - - ElementRegionManager & elemManager = mesh.getElemManager(); - elemManager.forElementSubRegions< WellElementSubRegion >( regionNames, - [&]( localIndex const, - WellElementSubRegion & subRegion ) - { - arrayView1d< integer const > const & wellElemGhostRank = subRegion.ghostRank(); + arrayView1d< integer const > const & wellElemGhostRank = subRegion.ghostRank(); - arrayView2d< real64, compflow::USD_COMP > const & wellElemCompDens = - subRegion.getField< well::globalCompDensity >(); + arrayView2d< real64, compflow::USD_COMP > const & wellElemCompDens = + subRegion.getField< fields::well::globalCompDensity >(); - forAll< parallelDevicePolicy<> >( subRegion.size(), [=] GEOS_HOST_DEVICE ( localIndex const iwelem ) + forAll< parallelDevicePolicy<> >( subRegion.size(), [=] GEOS_HOST_DEVICE ( localIndex const iwelem ) + { + if( wellElemGhostRank[iwelem] < 0 ) + { + for( integer ic = 0; ic < numComp; ++ic ) { - if( wellElemGhostRank[iwelem] < 0 ) + // we allowed for some densities to be slightly negative in CheckSystemSolution + // if the new density is negative, chop back to zero + if( wellElemCompDens[iwelem][ic] < 0 ) { - for( integer ic = 0; ic < numComp; ++ic ) - { - // we allowed for some densities to be slightly negative in CheckSystemSolution - // if the new density is negative, chop back to zero - if( wellElemCompDens[iwelem][ic] < 0 ) - { - wellElemCompDens[iwelem][ic] = 0; - } - } + wellElemCompDens[iwelem][ic] = 0.0; } - } ); - } ); - + } + } } ); + } -void CompositionalMultiphaseWell::resetStateToBeginningOfStep( DomainPartition & domain ) +void CompositionalMultiphaseWell::resetStateToBeginningOfStep( ElementRegionManager const & elemManager, WellElementSubRegion & subRegion ) { - forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &, - MeshLevel & mesh, - string_array const & regionNames ) + + // get a reference to the primary variables on well elements + arrayView1d< real64 > const & wellElemPressure = + subRegion.getField< well::pressure >(); + arrayView1d< real64 const > const & wellElemPressure_n = + subRegion.getField< well::pressure_n >(); + wellElemPressure.setValues< parallelDevicePolicy<> >( wellElemPressure_n ); + + if( isThermal() ) + { + // get a reference to the primary variables on well elements + arrayView1d< real64 > const & wellElemTemperature = + subRegion.getField< well::temperature >(); + arrayView1d< real64 const > const & wellElemTemperature_n = + subRegion.getField< well::temperature_n >(); + wellElemTemperature.setValues< parallelDevicePolicy<> >( wellElemTemperature_n ); + } + arrayView2d< real64, compflow::USD_COMP > const & wellElemGlobalCompDensity = + subRegion.getField< well::globalCompDensity >(); + arrayView2d< real64 const, compflow::USD_COMP > const & wellElemGlobalCompDensity_n = + subRegion.getField< well::globalCompDensity_n >(); + wellElemGlobalCompDensity.setValues< parallelDevicePolicy<> >( wellElemGlobalCompDensity_n ); + + arrayView1d< real64 > const & connRate = + subRegion.getField< well::connectionRate >(); + arrayView1d< real64 const > const & connRate_n = + subRegion.getField< well::connectionRate_n >(); + connRate.setValues< parallelDevicePolicy<> >( connRate_n ); + + + if( isWellOpen( ) ) { + updateSubRegionState( elemManager, subRegion ); + } + +} + +void CompositionalMultiphaseWell::assembleWellConstraintTerms( real64 const & time_n, + real64 const & GEOS_UNUSED_PARAM( dt ), + WellElementSubRegion const & subRegion, + DofManager const & dofManager, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) +{ + GEOS_MARK_FUNCTION; + + + // the rank that owns the reference well element is responsible for the calculations below. - ElementRegionManager & elemManager = mesh.getElemManager(); + if( !subRegion.isLocallyOwned() || !( getWellStatus() == WellControls::Status::OPEN )) + { + return; + } - elemManager.forElementSubRegions< WellElementSubRegion >( regionNames, - [&]( localIndex const, - WellElementSubRegion & subRegion ) + if( isProducer() ) + { + forSubGroups< MinimumBHPConstraint, ProductionConstraint< PhaseVolumeRateConstraint >, ProductionConstraint< MassRateConstraint >, ProductionConstraint< VolumeRateConstraint >, + ProductionConstraint< LiquidRateConstraint > + >( [&]( auto & constraint ) { - // get a reference to the primary variables on well elements - arrayView1d< real64 > const & wellElemPressure = - subRegion.getField< well::pressure >(); - arrayView1d< real64 const > const & wellElemPressure_n = - subRegion.getField< well::pressure_n >(); - wellElemPressure.setValues< parallelDevicePolicy<> >( wellElemPressure_n ); - - if( isThermal() ) + // Need to use name since there could be multiple constraints of the same type + if( constraint.getName() == getCurrentConstraint()->getName()) { - // get a reference to the primary variables on well elements - arrayView1d< real64 > const & wellElemTemperature = - subRegion.getField< well::temperature >(); - arrayView1d< real64 const > const & wellElemTemperature_n = - subRegion.getField< well::temperature_n >(); - wellElemTemperature.setValues< parallelDevicePolicy<> >( wellElemTemperature_n ); + // found limiting constraint + constitutive::MultiFluidBase & fluidSeparator = getMultiFluidSeparator(); + integer isThermal = fluidSeparator.isThermal(); + integer const numComp = fluidSeparator.numFluidComponents(); + geos::internal::kernelLaunchSelectorCompThermSwitch( numComp, isThermal, [&] ( auto NC, auto ISTHERMAL ) + { + integer constexpr NUM_COMP = NC(); + integer constexpr IS_THERMAL = ISTHERMAL(); + + wellConstraintKernels::ConstraintHelper< NUM_COMP, IS_THERMAL >::assembleConstraintEquation( time_n, + *this, + constraint, + subRegion, + dofManager.getKey( wellElementDofName() ), + dofManager.rankOffset(), + localMatrix, + localRhs ); + } ); } - arrayView2d< real64, compflow::USD_COMP > const & wellElemGlobalCompDensity = - subRegion.getField< well::globalCompDensity >(); - arrayView2d< real64 const, compflow::USD_COMP > const & wellElemGlobalCompDensity_n = - subRegion.getField< well::globalCompDensity_n >(); - wellElemGlobalCompDensity.setValues< parallelDevicePolicy<> >( wellElemGlobalCompDensity_n ); - - arrayView1d< real64 > const & connRate = - subRegion.getField< well::mixtureConnectionRate >(); - arrayView1d< real64 const > const & connRate_n = - subRegion.getField< well::mixtureConnectionRate_n >(); - connRate.setValues< parallelDevicePolicy<> >( connRate_n ); - WellControls & wellControls = getWellControls( subRegion ); - - if( wellControls.isWellOpen( ) ) + } ); + } + else + { + forSubGroups< MaximumBHPConstraint, InjectionConstraint< PhaseVolumeRateConstraint >, InjectionConstraint< MassRateConstraint >, + InjectionConstraint< VolumeRateConstraint >, + InjectionConstraint< LiquidRateConstraint > + >( [&]( auto & constraint ) + { + if( constraint.getName() == getCurrentConstraint()->getName()) { - updateSubRegionState( elemManager, subRegion ); + // found limiting constraint + constitutive::MultiFluidBase & fluidSeparator = getMultiFluidSeparator(); + integer isThermal = fluidSeparator.isThermal(); + integer const numComp = fluidSeparator.numFluidComponents(); + geos::internal::kernelLaunchSelectorCompThermSwitch( numComp, isThermal, [&] ( auto NC, auto ISTHERMAL ) + { + integer constexpr NUM_COMP = NC(); + integer constexpr IS_THERMAL = ISTHERMAL(); + + wellConstraintKernels::ConstraintHelper< NUM_COMP, IS_THERMAL >::assembleConstraintEquation( time_n, + *this, + constraint, + subRegion, + dofManager.getKey( wellElementDofName() ), + dofManager.rankOffset(), + localMatrix, + localRhs ); + } ); } } ); - } ); + } } -void CompositionalMultiphaseWell::assemblePressureRelations( real64 const & time_n, - real64 const & GEOS_UNUSED_PARAM( dt ), - DomainPartition const & domain, - DofManager const & dofManager, - CRSMatrixView< real64, globalIndex const > const & localMatrix, - arrayView1d< real64 > const & localRhs ) +void CompositionalMultiphaseWell::assembleWellPressureRelations( real64 const & GEOS_UNUSED_PARAM( time_n ), + real64 const & GEOS_UNUSED_PARAM( dt ), + WellElementSubRegion const & subRegion, + DofManager const & dofManager, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) { GEOS_MARK_FUNCTION; - forDiscretizationOnMeshTargets ( domain.getMeshBodies(), [&] ( string const &, - MeshLevel const & mesh, - string_array const & regionNames ) + if( isWellOpen( ) && !m_keepVariablesConstantDuringInitStep ) { + string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString() ); + MultiFluidBase const & fluid = getConstitutiveModel< MultiFluidBase >( subRegion, fluidName ); + bool const isThermal = fluid.isThermal(); + // get the degrees of freedom, depth info, next welem index + string const wellDofKey = dofManager.getKey( wellElementDofName() ); + arrayView1d< globalIndex const > const & wellElemDofNumber = + subRegion.getReference< array1d< globalIndex > >( wellDofKey ); + arrayView1d< real64 const > const & wellElemGravCoef = + subRegion.getField< well::gravityCoefficient >(); + arrayView1d< localIndex const > const & nextWellElemIndex = + subRegion.getReference< array1d< localIndex > >( WellElementSubRegion::viewKeyStruct::nextWellElementIndexString() ); + + // get primary variables on well elements + arrayView1d< real64 const > const & wellElemPres = + subRegion.getField< well::pressure >(); + + // get total mass density on well elements (for potential calculations) + arrayView1d< real64 const > const & wellElemTotalMassDens = + subRegion.getField< well::totalMassDensity >(); + arrayView2d< real64 const, compflow::USD_FLUID_DC > const & dWellElemTotalMassDens = + subRegion.getField< well::dTotalMassDensity >(); + + // segment status + arrayView1d< integer const > const elemStatus =subRegion.getLocalWellElementStatus(); + + bool controlHasSwitched = false; + isothermalCompositionalMultiphaseBaseKernels:: + KernelLaunchSelectorCompTherm< compositionalMultiphaseWellKernels::PressureRelationKernel > + ( numFluidComponents(), + isThermal, + subRegion.size(), + dofManager.rankOffset(), + elemStatus, + wellElemDofNumber, + wellElemGravCoef, + nextWellElemIndex, + wellElemPres, + wellElemTotalMassDens, + dWellElemTotalMassDens, + controlHasSwitched, + localMatrix, + localRhs ); - ElementRegionManager const & elemManager = mesh.getElemManager(); + } - elemManager.forElementSubRegions< WellElementSubRegion >( regionNames, - [&]( localIndex const, - WellElementSubRegion const & subRegion ) - { +} - WellControls & wellControls = getWellControls( subRegion ); - if( wellControls.isWellOpen( ) && !m_keepVariablesConstantDuringInitStep ) - { - string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString() ); - MultiFluidBase const & fluid = getConstitutiveModel< MultiFluidBase >( subRegion, fluidName ); - bool const isThermal = fluid.isThermal(); - // get the degrees of freedom, depth info, next welem index - string const wellDofKey = dofManager.getKey( wellElementDofName() ); - arrayView1d< globalIndex const > const & wellElemDofNumber = - subRegion.getReference< array1d< globalIndex > >( wellDofKey ); - arrayView1d< real64 const > const & wellElemGravCoef = - subRegion.getField< well::gravityCoefficient >(); - arrayView1d< localIndex const > const & nextWellElemIndex = - subRegion.getReference< array1d< localIndex > >( WellElementSubRegion::viewKeyStruct::nextWellElementIndexString() ); - - // get primary variables on well elements - arrayView1d< real64 const > const & wellElemPres = - subRegion.getField< well::pressure >(); - - // get total mass density on well elements (for potential calculations) - arrayView1d< real64 const > const & wellElemTotalMassDens = - subRegion.getField< well::totalMassDensity >(); - arrayView2d< real64 const, compflow::USD_FLUID_DC > const & dWellElemTotalMassDens = - subRegion.getField< well::dTotalMassDensity >(); - - // segment status - arrayView1d< integer const > const elemStatus =subRegion.getLocalWellElementStatus(); - - bool controlHasSwitched = false; - isothermalCompositionalMultiphaseBaseKernels:: - KernelLaunchSelectorCompTherm< compositionalMultiphaseWellKernels::PressureRelationKernel > - ( numFluidComponents(), - isThermal, - subRegion.size(), - dofManager.rankOffset(), - subRegion.isLocallyOwned(), - subRegion.getTopWellElementIndex(), - m_targetPhaseIndex, - wellControls, - time_n, // controls evaluated with BHP/rate of the beginning of step - elemStatus, - wellElemDofNumber, - wellElemGravCoef, - nextWellElemIndex, - wellElemPres, - wellElemTotalMassDens, - dWellElemTotalMassDens, - controlHasSwitched, - localMatrix, - localRhs ); - - if( controlHasSwitched ) - { - // TODO: move the switch logic into wellControls - // TODO: implement a more general switch when more then two constraints per well type are allowed - if( wellControls.getControl() == WellControls::Control::BHP ) - { - if( wellControls.isProducer() ) - { - wellControls.switchToPhaseRateControl( wellControls.getTargetPhaseRate( time_n ) ); - GEOS_LOG_LEVEL_RANK_0( logInfo::WellControl, - GEOS_FMT( "Control switch for well {} from BHP constraint to phase volumetric rate constraint", subRegion.getName() ) ); - } - else if( wellControls.getInputControl() == WellControls::Control::MASSRATE ) - { - wellControls.switchToMassRateControl( wellControls.getTargetMassRate( time_n ) ); - GEOS_LOG_LEVEL_RANK_0( logInfo::WellControl, - GEOS_FMT( "Control switch for well {} from BHP constraint to mass rate constraint", subRegion.getName()) ); - } - else - { - wellControls.switchToTotalRateControl( wellControls.getTargetTotalRate( time_n ) ); - GEOS_LOG_LEVEL_RANK_0( logInfo::WellControl, - GEOS_FMT( "Control switch for well {} from BHP constraint to total volumetric rate constraint", subRegion.getName()) ); - } - } - else - { - wellControls.switchToBHPControl( wellControls.getTargetBHP( time_n ) ); - GEOS_LOG_LEVEL_RANK_0( logInfo::WellControl, - GEOS_FMT( "Control switch for well {} from rate constraint to BHP constraint", subRegion.getName() ) ); - } - } +void CompositionalMultiphaseWell::saveState( WellElementSubRegion & subRegion ) +{ - // If a well is opened and then timestep is cut resulting in the well being shut, if the well is opened - // the well initialization code requires control type to by synced - integer owner = -1; - // Only subregion owner evaluates well control and control changes need to be broadcast to all ranks - if( subRegion.isLocallyOwned() ) - { - owner = MpiWrapper::commRank( MPI_COMM_GEOS ); - } - owner = MpiWrapper::max( owner ); - WellControls::Control wellControl = wellControls.getControl(); - MpiWrapper::broadcast( wellControl, owner ); - wellControls.setControl( wellControl ); - } + // get a reference to the primary variables on well elements + arrayView1d< real64 const > const & wellElemPressure = + subRegion.getField< fields::well::pressure >(); + arrayView2d< real64 const, compflow::USD_COMP > const & wellElemGlobalCompDensity = + subRegion.getField< fields::well::globalCompDensity >(); + arrayView1d< real64 const > const & wellElemTemperature = + subRegion.getField< fields::well::temperature >(); + + arrayView1d< real64 > const & wellElemPressure_n = + subRegion.getField< fields::well::pressure_n >(); + wellElemPressure_n.setValues< parallelDevicePolicy<> >( wellElemPressure ); + + if( isThermal() ) + { + + arrayView1d< real64 > const & wellElemTemperature_n = + subRegion.getField< fields::well::temperature_n >(); + wellElemTemperature_n.setValues< parallelDevicePolicy<> >( wellElemTemperature ); + } + + arrayView2d< real64, compflow::USD_COMP > const & wellElemGlobalCompDensity_n = + subRegion.getField< fields::well::globalCompDensity_n >(); + wellElemGlobalCompDensity_n.setValues< parallelDevicePolicy<> >( wellElemGlobalCompDensity ); + + arrayView1d< real64 const > const & connRate = + subRegion.getField< fields::well::connectionRate >(); + arrayView1d< real64 > const & connRate_n = + subRegion.getField< fields::well::connectionRate_n >(); + connRate_n.setValues< parallelDevicePolicy<> >( connRate ); + + arrayView2d< real64 const, compflow::USD_PHASE > const wellElemPhaseVolFrac = + subRegion.getField< fields::well::phaseVolumeFraction >(); + arrayView2d< real64, compflow::USD_PHASE > const wellElemPhaseVolFrac_n = + subRegion.getField< fields::well::phaseVolumeFraction_n >(); + wellElemPhaseVolFrac_n.setValues< parallelDevicePolicy<> >( wellElemPhaseVolFrac ); + + string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString() ); + MultiFluidBase const & fluid = getConstitutiveModel< MultiFluidBase >( subRegion, fluidName ); + fluid.saveConvergedState(); + - } ); - } ); } void CompositionalMultiphaseWell::implicitStepSetup( real64 const & time_n, real64 const & dt, - DomainPartition & domain ) + ElementRegionManager & elemManager, + WellElementSubRegion & subRegion ) { - WellSolverBase::implicitStepSetup( time_n, dt, domain ); + WellControls::implicitStepSetup( time_n, dt, elemManager, subRegion ); - forDiscretizationOnMeshTargets ( domain.getMeshBodies(), [&] ( string const &, - MeshLevel & mesh, - string_array const & regionNames ) + if( isWellOpen() ) { + // get a reference to the primary variables on well elements + arrayView1d< real64 const > const & wellElemPressure = + subRegion.getField< fields::well::pressure >(); + arrayView2d< real64 const, compflow::USD_COMP > const & wellElemGlobalCompDensity = + subRegion.getField< fields::well::globalCompDensity >(); + arrayView1d< real64 const > const & wellElemTemperature = + subRegion.getField< fields::well::temperature >(); + + arrayView1d< real64 > const & wellElemPressure_n = + subRegion.getField< fields::well::pressure_n >(); + wellElemPressure_n.setValues< parallelDevicePolicy<> >( wellElemPressure ); - ElementRegionManager & elemManager = mesh.getElemManager(); - - elemManager.forElementSubRegions< WellElementSubRegion >( regionNames, - [&]( localIndex const, - WellElementSubRegion & subRegion ) + if( isThermal() ) { + arrayView1d< real64 > const & wellElemTemperature_n = + subRegion.getField< fields::well::temperature_n >(); + wellElemTemperature_n.setValues< parallelDevicePolicy<> >( wellElemTemperature ); + } - WellControls & wellControls = getWellControls( subRegion ); - if( wellControls.isWellOpen() ) - { - // get a reference to the primary variables on well elements - arrayView1d< real64 const > const & wellElemPressure = - subRegion.getField< fields::well::pressure >(); - arrayView2d< real64 const, compflow::USD_COMP > const & wellElemGlobalCompDensity = - subRegion.getField< fields::well::globalCompDensity >(); - arrayView1d< real64 const > const & wellElemTemperature = - subRegion.getField< fields::well::temperature >(); - - arrayView1d< real64 > const & wellElemPressure_n = - subRegion.getField< fields::well::pressure_n >(); - wellElemPressure_n.setValues< parallelDevicePolicy<> >( wellElemPressure ); - - if( isThermal() ) - { - arrayView1d< real64 > const & wellElemTemperature_n = - subRegion.getField< fields::well::temperature_n >(); - wellElemTemperature_n.setValues< parallelDevicePolicy<> >( wellElemTemperature ); - } + arrayView2d< real64, compflow::USD_COMP > const & wellElemGlobalCompDensity_n = + subRegion.getField< fields::well::globalCompDensity_n >(); + wellElemGlobalCompDensity_n.setValues< parallelDevicePolicy<> >( wellElemGlobalCompDensity ); - arrayView2d< real64, compflow::USD_COMP > const & wellElemGlobalCompDensity_n = - subRegion.getField< fields::well::globalCompDensity_n >(); - wellElemGlobalCompDensity_n.setValues< parallelDevicePolicy<> >( wellElemGlobalCompDensity ); + arrayView1d< real64 const > const & connRate = + subRegion.getField< fields::well::connectionRate >(); + arrayView1d< real64 > const & connRate_n = + subRegion.getField< fields::well::connectionRate_n >(); + connRate_n.setValues< parallelDevicePolicy<> >( connRate ); - arrayView1d< real64 const > const & connRate = - subRegion.getField< fields::well::mixtureConnectionRate >(); - arrayView1d< real64 > const & connRate_n = - subRegion.getField< fields::well::mixtureConnectionRate_n >(); - connRate_n.setValues< parallelDevicePolicy<> >( connRate ); + arrayView2d< real64 const, compflow::USD_PHASE > const wellElemPhaseVolFrac = + subRegion.getField< fields::well::phaseVolumeFraction >(); + arrayView2d< real64, compflow::USD_PHASE > const wellElemPhaseVolFrac_n = + subRegion.getField< fields::well::phaseVolumeFraction_n >(); + wellElemPhaseVolFrac_n.setValues< parallelDevicePolicy<> >( wellElemPhaseVolFrac ); - arrayView2d< real64 const, compflow::USD_PHASE > const wellElemPhaseVolFrac = - subRegion.getField< fields::well::phaseVolumeFraction >(); - arrayView2d< real64, compflow::USD_PHASE > const wellElemPhaseVolFrac_n = - subRegion.getField< fields::well::phaseVolumeFraction_n >(); - wellElemPhaseVolFrac_n.setValues< parallelDevicePolicy<> >( wellElemPhaseVolFrac ); + string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString() ); + MultiFluidBase const & fluid = getConstitutiveModel< MultiFluidBase >( subRegion, fluidName ); + fluid.saveConvergedState(); - string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString() ); - MultiFluidBase const & fluid = getConstitutiveModel< MultiFluidBase >( subRegion, fluidName ); - fluid.saveConvergedState(); + validateWellConstraints( time_n, dt, subRegion ); - validateWellConstraints( time_n, dt, subRegion ); + updateSubRegionState( elemManager, subRegion ); + } - updateSubRegionState( elemManager, subRegion ); - } - } ) - ; - } ); } void CompositionalMultiphaseWell::implicitStepComplete( real64 const & time_n, real64 const & dt, - DomainPartition & domain ) + WellElementSubRegion const & subRegion ) { - WellSolverBase::implicitStepComplete( time_n, dt, domain ); - - if( getLogLevel() > 0 ) - { - printRates( time_n, dt, domain ); - } + printRates( time_n, dt, subRegion ); } void CompositionalMultiphaseWell::printRates( real64 const & time_n, real64 const & dt, - DomainPartition & domain ) + WellElementSubRegion const & subRegion ) { - forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &, - MeshLevel & mesh, - string_array const & regionNames ) - { - ElementRegionManager & elemManager = mesh.getElemManager(); + integer const numPhase = m_numPhases; + integer const numComp = m_numComponents; + integer const numPerf = subRegion.getPerforationData()->size(); - elemManager.forElementSubRegions< WellElementSubRegion >( regionNames, - [&]( localIndex const, - WellElementSubRegion & subRegion ) - { - integer const numPhase = m_numPhases; - integer const numComp = m_numComponents; - integer const numPerf = subRegion.getPerforationData()->size(); - // control data - WellControls const & wellControls = getWellControls( subRegion ); - stdVector< double > compRate( numComp, 0.0 ); - if( m_writeCSV > 0 && wellControls.isWellOpen( ) ) - { - arrayView2d< real64 > const compPerfRate = subRegion.getPerforationData()->getField< fields::well::compPerforationRate >(); + stdVector< double > compRate( numComp, 0.0 ); + if( m_writeCSV > 0 && isWellOpen( ) ) + { + arrayView2d< real64 const > const & compPerfRate = subRegion.getPerforationData()->getField< fields::well::compPerforationRate >(); - // bring everything back to host, capture the scalars by reference - forAll< serialPolicy >( 1, [&numComp, - &numPerf, - compPerfRate, - &compRate] ( localIndex const ) - { - for( integer ic = 0; ic < numComp; ++ic ) - { - for( integer iperf = 0; iperf < numPerf; iperf++ ) - { - compRate[ic] += compPerfRate[iperf][ic]; - } - } - } ); - for( integer ic = 0; ic < numComp; ++ic ) + // bring everything back to host, capture the scalars by reference + forAll< serialPolicy >( 1, [&numComp, + &numPerf, + compPerfRate, + &compRate] ( localIndex const ) + { + for( integer ic = 0; ic < numComp; ++ic ) + { + for( integer iperf = 0; iperf < numPerf; iperf++ ) { - compRate[ic] = MpiWrapper::sum( compRate[ic] ); + compRate[ic] += compPerfRate[iperf][ic]; } } + } ); + for( integer ic = 0; ic < numComp; ++ic ) + { + compRate[ic] = MpiWrapper::sum( compRate[ic] ); + } + } - // the rank that owns the reference well element is responsible for the calculations below. - if( !subRegion.isLocallyOwned() ) - { - return; - } + // the rank that owns the reference well element is responsible for the calculations below. + if( !subRegion.isLocallyOwned() ) + { + return; + } + + string const wellControlsName = getName(); - string const wellControlsName = wellControls.getName(); + // format: time,total_rate,total_vol_rate,phase0_vol_rate,phase1_vol_rate,... + std::ofstream outputFile; + if( m_writeCSV > 0 ) + { + outputFile.open( m_ratesOutputDir + "/" + wellControlsName + ".csv", std::ios_base::app ); + outputFile << time_n << "," << dt; + } - // format: time,total_rate,total_vol_rate,phase0_vol_rate,phase1_vol_rate,... - std::ofstream outputFile; - if( m_writeCSV > 0 ) + if( getWellStatus() == WellControls::Status::CLOSED ) + { + GEOS_LOG( GEOS_FMT( "{}: well is shut", wellControlsName ) ); + if( outputFile.is_open()) + { + // print all zeros in the rates file + outputFile << ",0.0,0.0,0.0"; + for( integer ip = 0; ip < numPhase; ++ip ) { - outputFile.open( m_ratesOutputDir + "/" + wellControlsName + ".csv", std::ios_base::app ); - outputFile << time_n << "," << dt; + outputFile << ",0.0"; } - - if( wellControls.getWellStatus() == WellControls::Status::CLOSED ) + for( integer ic = 0; ic < numComp; ++ic ) { - GEOS_LOG( GEOS_FMT( "{}: well is shut", wellControlsName ) ); - if( outputFile.is_open()) - { - // print all zeros in the rates file - outputFile << ",0.0,0.0,0.0"; - for( integer ip = 0; ip < numPhase; ++ip ) - { - outputFile << ",0.0"; - } - for( integer ic = 0; ic < numComp; ++ic ) - { - outputFile << ",0.0"; - } - outputFile << std::endl; - outputFile.close(); - } - return; + outputFile << ",0.0"; } + outputFile << std::endl; + outputFile.close(); + } + return; + } + + localIndex const iwelemRef = subRegion.getTopWellElementIndex(); + string const massUnit = m_useMass ? "kg" : "mol"; + + // subRegion data - localIndex const iwelemRef = subRegion.getTopWellElementIndex(); - string const massUnit = m_useMass ? "kg" : "mol"; - - // subRegion data - - arrayView1d< real64 const > const & connRate = - subRegion.getField< well::mixtureConnectionRate >(); - - integer const useSurfaceConditions = wellControls.useSurfaceConditions(); - - real64 const & currentBHP = - wellControls.getReference< real64 >( CompositionalMultiphaseWell::viewKeyStruct::currentBHPString() ); - arrayView1d< real64 const > const & currentPhaseVolRate = - wellControls.getReference< array1d< real64 > >( CompositionalMultiphaseWell::viewKeyStruct::currentPhaseVolRateString() ); - real64 const & currentTotalVolRate = - wellControls.getReference< real64 >( CompositionalMultiphaseWell::viewKeyStruct::currentTotalVolRateString() ); - - // bring everything back to host, capture the scalars by reference - forAll< serialPolicy >( 1, [&numPhase, - &numComp, - &useSurfaceConditions, - ¤tBHP, - connRate, - ¤tTotalVolRate, - currentPhaseVolRate, - &compRate, - &iwelemRef, - &wellControlsName, - &massUnit, - &outputFile] ( localIndex const ) + arrayView1d< real64 const > const & connRate = + subRegion.getField< well::connectionRate >(); + + integer const useSurfaceCond = useSurfaceConditions(); + + real64 const & currentBHP = + getReference< real64 >( CompositionalMultiphaseWell::viewKeyStruct::currentBHPString() ); + arrayView1d< real64 const > const & currentPhaseVolRate = + getReference< array1d< real64 > >( CompositionalMultiphaseWell::viewKeyStruct::currentPhaseVolRateString() ); + real64 const & currentTotalVolRate = + getReference< real64 >( CompositionalMultiphaseWell::viewKeyStruct::currentTotalVolRateString() ); + + // bring everything back to host, capture the scalars by reference + forAll< serialPolicy >( 1, [&numPhase, + &numComp, + &useSurfaceCond, + ¤tBHP, + connRate, + ¤tTotalVolRate, + currentPhaseVolRate, + &compRate, + &iwelemRef, + &wellControlsName, + &massUnit, + &outputFile] ( localIndex const ) + { + string const conditionKey = useSurfaceCond ? "surface" : "reservoir"; + string const unitKey = useSurfaceCond ? "s" : "r"; + + real64 const currentTotalRate = connRate[iwelemRef]; + GEOS_LOG( GEOS_FMT( "{}: BHP (at the specified reference elevation): {} Pa", + wellControlsName, currentBHP ) ); + GEOS_LOG( GEOS_FMT( "{}: Total rate: {} {}/s; total {} volumetric rate: {} {}m3/s", + wellControlsName, currentTotalRate, massUnit, conditionKey, currentTotalVolRate, unitKey ) ); + for( integer ip = 0; ip < numPhase; ++ip ) + GEOS_LOG( GEOS_FMT( "{}: Phase {} {} volumetric rate: {} {}m3/s", + wellControlsName, ip, conditionKey, currentPhaseVolRate[ip], unitKey ) ); + if( outputFile.is_open()) + { + outputFile << "," << currentBHP; + outputFile << "," << currentTotalRate << "," << currentTotalVolRate; + for( integer ip = 0; ip < numPhase; ++ip ) { - string const conditionKey = useSurfaceConditions ? "surface" : "reservoir"; - string const unitKey = useSurfaceConditions ? "s" : "r"; - - real64 const currentTotalRate = connRate[iwelemRef]; - GEOS_LOG( GEOS_FMT( "{}: BHP (at the specified reference elevation): {} Pa", - wellControlsName, currentBHP ) ); - GEOS_LOG( GEOS_FMT( "{}: Total rate: {} {}/s; total {} volumetric rate: {} {}m3/s", - wellControlsName, currentTotalRate, massUnit, conditionKey, currentTotalVolRate, unitKey ) ); - for( integer ip = 0; ip < numPhase; ++ip ) - GEOS_LOG( GEOS_FMT( "{}: Phase {} {} volumetric rate: {} {}m3/s", - wellControlsName, ip, conditionKey, currentPhaseVolRate[ip], unitKey ) ); - if( outputFile.is_open()) - { - outputFile << "," << currentBHP; - outputFile << "," << currentTotalRate << "," << currentTotalVolRate; - for( integer ip = 0; ip < numPhase; ++ip ) - { - outputFile << "," << currentPhaseVolRate[ip]; - } - for( integer ic = 0; ic < numComp; ++ic ) - { - outputFile << "," << compRate[ic]; - } - outputFile << std::endl; - outputFile.close(); - } - } ); - } ); + outputFile << "," << currentPhaseVolRate[ip]; + } + for( integer ic = 0; ic < numComp; ++ic ) + { + outputFile << "," << compRate[ic]; + } + outputFile << std::endl; + outputFile.close(); + } } ); + } -REGISTER_CATALOG_ENTRY( PhysicsSolverBase, CompositionalMultiphaseWell, string const &, Group * const ) -} // namespace geos + +} // namespace geos diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/CompositionalMultiphaseWell.hpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/CompositionalMultiphaseWell.hpp index cb8ed9f1a6f..f76050f7bf7 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/wells/CompositionalMultiphaseWell.hpp +++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/CompositionalMultiphaseWell.hpp @@ -22,9 +22,11 @@ #include "constitutive/fluid/multifluid/Layouts.hpp" #include "constitutive/relativePermeability/Layouts.hpp" -#include "physicsSolvers/fluidFlow/wells/WellSolverBase.hpp" + #include "physicsSolvers/fluidFlow/CompositionalMultiphaseBase.hpp" +#include "physicsSolvers/fluidFlow/wells/WellConstraintsBase.hpp" +#include "physicsSolvers/fluidFlow/wells/WellControls.hpp" namespace geos { @@ -39,7 +41,7 @@ class MultiFluidBase; * * A compositional multiphase well solver */ -class CompositionalMultiphaseWell : public WellSolverBase +class CompositionalMultiphaseWell : public WellControls { public: @@ -57,8 +59,8 @@ class CompositionalMultiphaseWell : public WellSolverBase /// deleted copy constructor CompositionalMultiphaseWell( CompositionalMultiphaseWell const & ) = delete; - /// default move constructor - CompositionalMultiphaseWell( CompositionalMultiphaseWell && ) = default; + /// deleted move constructor + CompositionalMultiphaseWell( CompositionalMultiphaseWell && ) = delete; /// deleted assignment operator CompositionalMultiphaseWell & operator=( CompositionalMultiphaseWell const & ) = delete; @@ -71,64 +73,168 @@ class CompositionalMultiphaseWell : public WellSolverBase */ virtual ~CompositionalMultiphaseWell() override = default; + + virtual void registerWellDataOnMesh( WellElementSubRegion & subRegion ) override; /** - * @brief name of the node manager in the object catalog - * @return string that contains the catalog name to generate a new NodeManager object through the object catalog. + * @defgroup WellManager Interface Functions + * + * These functions provide the primary interface that is required for derived classes + * The "Well" versions apply to individual well subRegions, whereas the others apply to all wells */ - static string catalogName() { return "CompositionalMultiphaseWell"; } + /**@{*/ /** - * @copydoc PhysicsSolverBase::getCatalogName() + * * @brief Initialize well for the beginning of a simulation or restart + * @param domain the domain + * @param mesh the mesh level + * @param subRegion the well subRegion */ - string getCatalogName() const override { return catalogName(); } + virtual void initializeWell( DomainPartition & domain, MeshLevel & mesh, WellElementSubRegion & subRegion, real64 const & time_n ) override; + + virtual void initializeWellPostInitialConditionsPreSubGroups( WellElementSubRegion & subRegion ) override; - virtual void registerDataOnMesh( Group & meshBodies ) override; + virtual bool isCompositional() const override { return true; } + /** + * @copydoc WellControls::assembleWellAccumulationTerms() + */ + virtual void assembleWellAccumulationTerms( real64 const & time, + real64 const & dt, + WellElementSubRegion & subRegion, + DofManager const & dofManager, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) override; + /** + * @copydoc WellControls::assembleWellPressureRelations() + */ + virtual void assembleWellPressureRelations( real64 const & time_n, + real64 const & dt, + WellElementSubRegion const & subRegion, + DofManager const & dofManager, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) override; + + /** + * @copydoc WellControls::assembleWellConstraintTerms() + */ + virtual void assembleWellConstraintTerms( real64 const & time_n, + real64 const & dt, + WellElementSubRegion const & subRegion, + DofManager const & dofManager, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) override; + /** + * @copydoc WellControls::computeWellPerforationRates() + */ + virtual void computeWellPerforationRates( real64 const & time_n, + real64 const & GEOS_UNUSED_PARAM( dt ), + ElementRegionManager & elemManager, + WellElementSubRegion & subRegion ) override; + + /** + * @copydoc WellControls::assembleFluxTerms() + */ + virtual void assembleWellFluxTerms( real64 const & time, + real64 const & dt, + WellElementSubRegion const & subRegion, + DofManager const & dofManager, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) override; + /**@}*/ /** * @defgroup Solver Interface Functions * * These functions provide the primary interface that is required for derived classes + * The "Well" versions apply to individual well subRegions, whereas the others apply to all wells */ /**@{*/ + /** + * @copydoc WellControls::calculateResidualNorm() + */ + + virtual array1d< real64 > + calculateLocalWellResidualNorm( real64 const & time_n, + real64 const & dt, + NonlinearSolverParameters const & nonlinearSolverParameters, + WellElementSubRegion const & subRegion, + DofManager const & dofManager, + arrayView1d< real64 const > const & localRhs ) override; - virtual real64 - calculateResidualNorm( real64 const & time_n, - real64 const & dt, - DomainPartition const & domain, - DofManager const & dofManager, - arrayView1d< real64 const > const & localRhs ) override; virtual real64 - scalingForSystemSolution( DomainPartition & domain, - DofManager const & dofManager, - arrayView1d< real64 const > const & localSolution ) override; + calculateWellResidualNorm( real64 const & time_n, + real64 const & dt, + NonlinearSolverParameters const & nonlinearSolverParameters, + WellElementSubRegion const & subRegion, + DofManager const & dofManager, + arrayView1d< real64 const > const & localRhs ) override; - virtual bool - checkSystemSolution( DomainPartition & domain, - DofManager const & dofManager, - arrayView1d< real64 const > const & localSolution, - real64 const scalingFactor ) override; + /** + * @copydoc WellControls::scalingForSystemSolution() + */ + real64 scalingForLocalSystemSolution ( WellElementSubRegion & subRegion, + DofManager const & dofManager, + real64 & maxDeltaPres, + real64 & maxDeltaCompDens, + real64 & maxDeltaTemp, + real64 & minPresScalingFactor, + real64 & minCompDensScalingFactor, + real64 & minTempScalingFactor, + arrayView1d< real64 const > const & localSolution ); + + virtual real64 scalingForWellSystemSolution( WellElementSubRegion & subRegion, + DofManager const & dofManager, + arrayView1d< real64 const > const & localSolution ) override; - virtual void - applySystemSolution( DofManager const & dofManager, - arrayView1d< real64 const > const & localSolution, - real64 const scalingFactor, - real64 const dt, - DomainPartition & domain ) override; + /** + * @copydoc WellControls::checkSystemSolution() + */ + virtual bool + checkWellSystemSolution( WellElementSubRegion & subRegion, + DofManager const & dofManager, + arrayView1d< real64 const > const & localSolution, + real64 const scalingFactor ) override; - virtual void - resetStateToBeginningOfStep( DomainPartition & domain ) override; + /** + * @copydoc WellControls::applyWellSystemSolution() + */ virtual void - implicitStepSetup( real64 const & time, - real64 const & dt, - DomainPartition & domain ) override; + applyWellSystemSolution( DofManager const & dofManager, + arrayView1d< real64 const > const & localSolution, + real64 const scalingFactor, + real64 const dt, + DomainPartition & domain, + MeshLevel & mesh, + WellElementSubRegion & subRegion ) override; + + virtual void applyWellBoundaryConditions( real64 const time_n, + real64 const dt, + ElementRegionManager & elemManager, + WellElementSubRegion & subRegion, + DofManager const & dofManager, + arrayView1d< real64 > const & localRhs, + CRSMatrixView< real64, globalIndex const > const & localMatrix ) override; + + + virtual void resetStateToBeginningOfStep( ElementRegionManager const & elemManager, WellElementSubRegion & subRegion ) override; + + virtual void implicitStepSetup( real64 const & time_n, + real64 const & GEOS_UNUSED_PARAM( dt ), + ElementRegionManager & elemManager, + WellElementSubRegion & subRegion ) override; virtual void implicitStepComplete( real64 const & time, real64 const & dt, - DomainPartition & domain ) override; + WellElementSubRegion const & subRegion ) override; + + virtual void printRates( real64 const & time_n, + real64 const & dt, + WellElementSubRegion const & subRegion ) override; + + virtual real64 updateSubRegionState( ElementRegionManager const & elemManager, WellElementSubRegion & subRegion ) override; /**@}*/ @@ -140,10 +246,9 @@ class CompositionalMultiphaseWell : public WellSolverBase /** * @brief Recompute the volumetric rates that are used in the well constraints - * @param elemManager the well region manager containing the well * @param subRegion the well subregion containing all the primary and dependent fields */ - void updateVolRatesForConstraint( ElementRegionManager const & elemManager, WellElementSubRegion const & subRegion ); + void updateVolRatesForConstraint( WellElementSubRegion const & subRegion ); /** * @brief Recompute the current BHP pressure @@ -159,6 +264,22 @@ class CompositionalMultiphaseWell : public WellSolverBase */ void updateFluidModel( WellElementSubRegion & subRegion ); + /** + * @brief Update well separator using current values of pressure and composition at the reference + * element + * @param elemManager the element region manager + + */ + void updateSeparator( ElementRegionManager const & elemManager, WellElementSubRegion & subRegion ); + + /** + * @brief Calculate well rates at reference element + * @param subRegion the well subregion containing all the primary and dependent fields + * @param targetIndex the targetIndex of the subRegion + */ + + void calculateReferenceElementRates( WellElementSubRegion & subRegion ); + /** * @brief Recompute phase volume fractions (saturations) from constitutive and primary variables * @param subRegion the well subregion containing all the primary and dependent fields @@ -172,20 +293,12 @@ class CompositionalMultiphaseWell : public WellSolverBase */ void updateTotalMassDensity( WellElementSubRegion & subRegion ) const; - /** - * @brief Recompute the perforation rates for all the wells - * @param domain the domain containing the mesh and fields - */ - virtual void computePerforationRates( real64 const & time_n, - real64 const & dt, DomainPartition & domain ) override; - /** * @brief Recompute all dependent quantities from primary variables (including constitutive models) * @param subRegion the well subregion containing all the primary and dependent fields */ - virtual void updateState( DomainPartition & domain ) override; + virtual real64 updateWellState( ElementRegionManager const & elemManager, WellElementSubRegion & subRegion ) override; - virtual real64 updateSubRegionState( ElementRegionManager const & elemManager, WellElementSubRegion & subRegion ) override; virtual string wellElementDofName() const override { return viewKeyStruct::dofFieldString(); } @@ -197,61 +310,18 @@ class CompositionalMultiphaseWell : public WellSolverBase integer useTotalMassEquation() const { return m_useTotalMassEquation; } - /** - * @brief assembles the flux terms for all connections between well elements - * @param time_n previous time value - * @param dt time step - * @param domain the physical domain object - * @param dofManager degree-of-freedom manager associated with the linear system - * @param matrix the system matrix - * @param rhs the system right-hand side vector - */ - - virtual void assembleFluxTerms( real64 const & time_n, - real64 const & dt, - DomainPartition & domain, - DofManager const & dofManager, - CRSMatrixView< real64, globalIndex const > const & localMatrix, - arrayView1d< real64 > const & localRhs )override; - /** - * @brief assembles the accumulation term for all the well elements - * @param domain the physical domain object - * @param dofManager degree-of-freedom manager associated with the linear system - * @param matrix the system matrix - * @param rhs the system right-hand side vector - */ - virtual void assembleAccumulationTerms( real64 const & time_n, - real64 const & dt, - DomainPartition & domain, - DofManager const & dofManager, - CRSMatrixView< real64, globalIndex const > const & localMatrix, - arrayView1d< real64 > const & localRhs ) override; - - /** - * @brief assembles the pressure relations at all connections between well elements except at the well head - * @param time_n time at the beginning of the time step - * @param dt the time step size - * @param domain the physical domain object - * @param dofManager degree-of-freedom manager associated with the linear system - * @param matrix the system matrix - * @param rhs the system right-hand side vector - */ - virtual void assemblePressureRelations( real64 const & time_n, - real64 const & dt, - DomainPartition const & domain, - DofManager const & dofManager, - CRSMatrixView< real64, globalIndex const > const & localMatrix, - arrayView1d< real64 > const & localRhs ) override; - /** * @brief Sets all the negative component densities (if any) to zero. * @param domain the physical domain object */ void chopNegativeDensities( DomainPartition & domain ); - struct viewKeyStruct : WellSolverBase::viewKeyStruct + void chopNegativeDensities( WellElementSubRegion & subRegion ); + + + struct viewKeyStruct : WellControls::viewKeyStruct { - static constexpr char const * dofFieldString() { return "compositionalWellVars"; } + static constexpr char const * dofFieldString() { return "wellVars"; } // inputs @@ -271,36 +341,7 @@ class CompositionalMultiphaseWell : public WellSolverBase static constexpr char const * allowLocalCompDensChoppingString() { return CompositionalMultiphaseBase::viewKeyStruct::allowLocalCompDensChoppingString(); } - // control data (not registered on the mesh) - - static constexpr char const * massDensityString() { return "massDensity";} - - static constexpr char const * currentBHPString() { return "currentBHP"; } - static constexpr char const * dCurrentBHPString() { return "dCurrentBHP"; } - - static constexpr char const * dCurrentBHP_dPresString() { return "dCurrentBHP_dPres"; } - static constexpr char const * dCurrentBHP_dCompDensString() { return "dCurrentBHP_dCompDens"; } - - static constexpr char const * currentPhaseVolRateString() { return "currentPhaseVolumetricRate"; } - static constexpr char const * dCurrentPhaseVolRateString() { return "dCurrentPhaseVolumetricRate"; } - - - static constexpr char const * dCurrentPhaseVolRate_dPresString() { return "dCurrentPhaseVolumetricRate_dPres"; } - static constexpr char const * dCurrentPhaseVolRate_dCompDensString() { return "dCurrentPhaseVolumetricRate_dCompDens"; } - - static constexpr char const * dCurrentPhaseVolRate_dRateString() { return "dCurrentPhaseVolumetricRate_dRate"; } - - static constexpr char const * currentTotalVolRateString() { return "currentTotalVolumetricRate"; } - static constexpr char const * dCurrentTotalVolRateString() { return "dCurrentTotalVolumetricRate"; } - - static constexpr char const * currentMassRateString() { return "currentMassRate"; } - - static constexpr char const * dCurrentTotalVolRate_dPresString() { return "dCurrentTotalVolumetricRate_dPres"; } - - static constexpr char const * dCurrentTotalVolRate_dCompDensString() { return "dCurrentTotalVolumetricRate_dCompDens"; } - - static constexpr char const * dCurrentTotalVolRate_dRateString() { return "dCurrentTotalVolumetricRate_dRate"; } } viewKeysCompMultiphaseWell; @@ -312,30 +353,19 @@ class CompositionalMultiphaseWell : public WellSolverBase virtual void initializePostInitialConditionsPreSubGroups() override; - virtual void postRestartInitialization() override final; - /* - * @brief Utility function that checks the consistency of the constitutive models - * @param[in] domain the domain partition + void saveState( WellElementSubRegion & subRegion ); + virtual void postRestartInitialization( ) override; + + /** + * @brief Checks fluild model compatibility and validity + * @param[in] fluid the fluid to check + * @param[in] referenceFluid the reference fluid model * @detail * This function will produce an error if one of the well constitutive models * is incompatible with the corresponding models in reservoir * regions connected to that particular well. */ - void validateConstitutiveModels( DomainPartition const & domain ) const; - - /** - * @brief Checks if the WellControls parameters are within the fluid tables ranges - * @param fluid the fluid to check - */ - void validateWellControlsForFluid( WellControls const & wellControls, - constitutive::MultiFluidBase const & fluid ) const; - - /** - * @brief Checks injection streams for validity (compositions sum to one) - * @param subRegion the well subRegion - */ - void validateInjectionStreams( WellElementSubRegion const & subRegion ) const; - + void validateFluidModel( constitutive::MultiFluidBase const & fluid, constitutive::MultiFluidBase const & referenceFluid )const; /** * @brief Make sure that the well constraints are compatible * @param time_n the time at the beginning of the time step @@ -350,24 +380,15 @@ class CompositionalMultiphaseWell : public WellSolverBase /** * @brief Create well separator */ - void createSeparator(); + virtual void createSeparator( WellElementSubRegion & subRegion ) override; - void printRates( real64 const & time_n, - real64 const & dt, - DomainPartition & domain ) override; -private: - /** - * @brief Initialize all the primary and secondary variables in all the wells - * @param domain the domain containing the well manager to access individual wells - */ - void initializeWells( DomainPartition & domain, real64 const & time_n ) override; +private: virtual void setConstitutiveNames( ElementSubRegionBase & subRegion ) const override; - /// flag indicating whether mass or molar formulation should be used integer m_useMass; @@ -395,11 +416,6 @@ class CompositionalMultiphaseWell : public WellSolverBase /// flag indicating whether local (cell-wise) chopping of negative compositions is allowed integer m_allowCompDensChopping; - /// index of the target phase, used to impose the phase rate constraint - localIndex m_targetPhaseIndex; - - - }; } // namespace geos diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/CompositionalMultiphaseWellFields.hpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/CompositionalMultiphaseWellFields.hpp index 3ca423cd7ac..4abd716ffc4 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/wells/CompositionalMultiphaseWellFields.hpp +++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/CompositionalMultiphaseWellFields.hpp @@ -58,21 +58,7 @@ DECLARE_FIELD( globalCompDensity_n, WRITE_AND_READ, "Global component density at the previous converged time step" ); -DECLARE_FIELD( mixtureConnectionRate, - "wellElementMixtureConnectionRate", - array1d< real64 >, - 0, - LEVEL_0, - WRITE_AND_READ, - "Mixture connection rate" ); -DECLARE_FIELD( mixtureConnectionRate_n, - "wellElementMixtureConnectionRate_n", - array1d< real64 >, - 0, - NOPLOT, - WRITE_AND_READ, - "Mixture connection rate at the previous converged time step" ); DECLARE_FIELD( globalCompFraction, "globalCompFraction", diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/SinglePhaseWell.cpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/SinglePhaseWell.cpp index 54716e387d4..8e501bccc0e 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/wells/SinglePhaseWell.cpp +++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/SinglePhaseWell.cpp @@ -37,14 +37,13 @@ #include "physicsSolvers/fluidFlow/wells/WellFields.hpp" #include "physicsSolvers/fluidFlow/wells/WellSolverBaseFields.hpp" #include "physicsSolvers/fluidFlow/wells/SinglePhaseWellFields.hpp" -#include "physicsSolvers/fluidFlow/wells/WellControls.hpp" #include "physicsSolvers/fluidFlow/wells/kernels/SinglePhaseWellKernels.hpp" #include "physicsSolvers/fluidFlow/wells/kernels/ThermalSinglePhaseWellKernels.hpp" #include "physicsSolvers/fluidFlow/wells/kernels/SinglePhasePerforationFluxKernels.hpp" #include "physicsSolvers/fluidFlow/kernels/singlePhase/FluidUpdateKernel.hpp" #include "physicsSolvers/fluidFlow/kernels/singlePhase/SolutionCheckKernel.hpp" #include "physicsSolvers/fluidFlow/SinglePhaseStatistics.hpp" - +#include "physicsSolvers/fluidFlow/wells/kernels/SinglePhaseWellConstraintKernels.hpp" namespace geos { @@ -55,7 +54,7 @@ using namespace singlePhaseWellKernels; SinglePhaseWell::SinglePhaseWell( const string & name, Group * const parent ): - WellSolverBase( name, parent ) + WellControls( name, parent ) { m_numDofPerWellElement = 2; m_numDofPerResElement = 1; @@ -68,64 +67,58 @@ SinglePhaseWell::SinglePhaseWell( const string & name, setDescription( "Flag indicating if negative pressure is allowed" ); } -void SinglePhaseWell::registerDataOnMesh( Group & meshBodies ) +void SinglePhaseWell::registerWellDataOnMesh( WellElementSubRegion & subRegion ) { - WellSolverBase::registerDataOnMesh( meshBodies ); - // loop over the wells - forDiscretizationOnMeshTargets( meshBodies, [&] ( string const &, - MeshLevel & mesh, - string_array const & regionNames ) + setConstitutiveNames ( subRegion ); + WellControls::registerDataOnMesh( subRegion ); + //DomainPartition const & domain = this->getGroupByPath< DomainPartition >( "/Problem/domain" ); + //ConstitutiveManager const & cm = domain.getConstitutiveManager(); + if( m_referenceFluidModelName.empty() ) { + m_referenceFluidModelName = getConstitutiveName< SingleFluidBase >( subRegion ); + } + subRegion.registerField< well::pressure >( getName() ); + subRegion.registerField< well::pressure_n >( getName() ); - ElementRegionManager & elemManager = mesh.getElemManager(); - - elemManager.forElementSubRegions< WellElementSubRegion >( regionNames, - [&]( localIndex const, - WellElementSubRegion & subRegion ) - { - subRegion.registerField< well::connectionRate_n >( getName() ); - subRegion.registerField< well::connectionRate >( getName() ); - - PerforationData & perforationData = *subRegion.getPerforationData(); - perforationData.registerField< well::perforationRate >( getName() ); - perforationData.registerField< well::dPerforationRate >( getName() ). - reference().resizeDimension< 1, 2 >( 2, 2 ); - if( isThermal() ) - { - perforationData.registerField< well::energyPerforationFlux >( getName() ); - perforationData.registerField< well::dEnergyPerforationFlux >( getName() ). - reference().resizeDimension< 1, 2 >( 2, 2 ); - } - - WellControls & wellControls = getWellControls( subRegion ); - wellControls.registerWrapper< real64 >( viewKeyStruct::currentBHPString() ); - - wellControls.registerWrapper< array1d< real64 > >( viewKeyStruct::dCurrentBHPString() ). - setSizedFromParent( 0 ). - reference().resizeDimension< 0 >( 2 + isThermal() ); // dP, dT , dQ + subRegion.registerField< well::temperature >( getName() ); + if( isThermal() ) + { + subRegion.registerField< well::temperature_n >( getName() ); + } + subRegion.registerField< well::connectionRate_n >( getName() ); + subRegion.registerField< well::connectionRate >( getName() ); + subRegion.registerField< well::gravityCoefficient >( getName() ); + PerforationData & perforationData = *subRegion.getPerforationData(); + perforationData.registerField< well::gravityCoefficient >( getName() ); + + perforationData.registerField< well::perforationRate >( getName() ); + perforationData.registerField< well::dPerforationRate >( getName() ). + reference().resizeDimension< 1, 2 >( 2, 2 ); + if( isThermal() ) + { + perforationData.registerField< well::energyPerforationFlux >( getName() ); + perforationData.registerField< well::dEnergyPerforationFlux >( getName() ). + reference().resizeDimension< 1, 2 >( 2, 2 ); + perforationData.registerField< well::gravityCoefficient >( getName() ); + } - wellControls.registerWrapper< real64 >( viewKeyStruct::currentVolRateString() ); - wellControls.registerWrapper< array1d< real64 > >( viewKeyStruct::dCurrentVolRateString() ). - setSizedFromParent( 0 ). - reference().resizeDimension< 0 >( 2 + isThermal() ); // dP, dT, dQ + // dP, dT, dQ + m_writeCSV=1; + // write rates output header + if( m_writeCSV > 0 && subRegion.isLocallyOwned()) + { + string const fileName = GEOS_FMT( "{}/{}.csv", m_ratesOutputDir, getName() ); + string const conditionKey = useSurfaceConditions() ? "surface" : "reservoir"; + string const unitKey = useSurfaceConditions() ? "s" : "r"; + // format: time,bhp,total_rate,total_vol_rate + makeDirsForPath( m_ratesOutputDir ); + GEOS_LOG( GEOS_FMT( "{}: Rates CSV generated at {}", getName(), fileName ) ); + std::ofstream outputFile( fileName ); + outputFile << "Time [s],BHP [Pa],Total rate [kg/s],Total " << conditionKey << " volumetric rate ["< 0 && subRegion.isLocallyOwned()) - { - string const fileName = GEOS_FMT( "{}/{}.csv", m_ratesOutputDir, wellControls.getName() ); - integer const useSurfaceConditions = wellControls.useSurfaceConditions(); - string const conditionKey = useSurfaceConditions ? "surface" : "reservoir"; - string const unitKey = useSurfaceConditions ? "s" : "r"; - // format: time,bhp,total_rate,total_vol_rate - makeDirsForPath( m_ratesOutputDir ); - GEOS_LOG( GEOS_FMT( "{}: Rates CSV generated at {}", getName(), fileName ) ); - std::ofstream outputFile( fileName ); - outputFile << "Time [s],BHP [Pa],Total rate [kg/s],Total " << conditionKey << " volumetric rate ["<( getFlowSolverName() ); string_array const & targetRegionsNames = flowSolver.getTargetRegionNames(); auto const pos = std::find( targetRegionsNames.begin(), targetRegionsNames.end(), regionName ); GEOS_ERROR_IF( pos == targetRegionsNames.end(), GEOS_FMT( "{}: Region {} is not a target of the reservoir solver and cannot be used for referenceReservoirRegion in WellControl {}.", - getDataContext(), regionName, wellControls.getName() ) ); + getDataContext(), regionName, getName() ) ); } } - WellControls::Control currentControl = wellControls.getControl(); - real64 const targetTotalRate = wellControls.getTargetTotalRate( time_n ); - real64 const targetPhaseRate = wellControls.getTargetPhaseRate( time_n ); - GEOS_THROW_IF( currentControl == WellControls::Control::PHASEVOLRATE, - "WellControls " << wellControls.getDataContext() << - ": Phase rate control is not available for SinglePhaseWell", - InputError, wellControls.getDataContext() ); - // The user always provides positive rates, but these rates are later multiplied by -1 internally for producers - GEOS_THROW_IF( ( ( wellControls.isInjector() && targetTotalRate < 0.0 ) || - ( wellControls.isProducer() && targetTotalRate > 0.0) ), - "WellControls " << wellControls.getDataContext() << - ": Target total rate cannot be negative", - InputError, wellControls.getDataContext() ); - GEOS_THROW_IF( !isZero( targetPhaseRate ), - "WellControls " << wellControls.getDataContext() << - ": Target phase rate cannot be used for SinglePhaseWell", - InputError, wellControls.getDataContext() ); +} + +void SinglePhaseWell::initializeWellPostInitialConditionsPreSubGroups( WellElementSubRegion & subRegion ) +{ + + // set gravity coefficient + setGravCoef( subRegion, getParent().getParent().getReference< R1Tensor >( PhysicsSolverManager::viewKeyStruct::gravityVectorString() )); + + // setup fluid model + createSeparator( subRegion ); +} +void SinglePhaseWell::initializePostInitialConditionsPreSubGroups() +{ + WellControls::initializePostInitialConditionsPreSubGroups(); +} +void SinglePhaseWell::postRestartInitialization( ) +{ + // setup fluid separator + constitutive::SingleFluidBase & fluidSeparator = getSingleFluidSeparator(); + fluidSeparator.allocateConstitutiveData( *this, 1 ); + fluidSeparator.resize( 1 ); + +} +void SinglePhaseWell::createSeparator( WellElementSubRegion & subRegion ) +{ + + string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString() ); + SingleFluidBase & fluid = subRegion.getConstitutiveModel< SingleFluidBase >( fluidName ); + // setup fluid separator + + string const fluidSeparatorName = getName() + "Separator"; + std::unique_ptr< constitutive::ConstitutiveBase > fluidSeparatorPtr = fluid.deliverClone( fluidSeparatorName, this ); + fluidSeparatorPtr->allocateConstitutiveData( *this, 1 ); + fluidSeparatorPtr->resize( 1 ); + setFluidSeparator( std::move( fluidSeparatorPtr )); + } void SinglePhaseWell::updateBHPForConstraint( WellElementSubRegion & subRegion ) @@ -213,45 +226,33 @@ void SinglePhaseWell::updateBHPForConstraint( WellElementSubRegion & subRegion ) // control data - WellControls & wellControls = getWellControls( subRegion ); - string const wellControlsName = wellControls.getName(); - real64 const & refGravCoef = wellControls.getReferenceGravityCoef(); + string const wellControlsName = getName(); + real64 const & refGravCoef = getReferenceGravityCoef(); real64 & currentBHP = - wellControls.getReference< real64 >( SinglePhaseWell::viewKeyStruct::currentBHPString() ); - arrayView1d< real64 > const & dCurrentBHP = - wellControls.getReference< array1d< real64 > >( SinglePhaseWell::viewKeyStruct::dCurrentBHPString() ); - - geos::internal::kernelLaunchSelectorThermalSwitch( isThermal(), [&] ( auto ISTHERMAL ) + getReference< real64 >( WellControls::viewKeyStruct::currentBHPString() ); + + // bring everything back to host, capture the scalars by reference + forAll< serialPolicy >( 1, [pres, + dens, + dDens, + wellElemGravCoef, + ¤tBHP, + &iwelemRef, + &refGravCoef] ( localIndex const ) { - integer constexpr IS_THERMAL = ISTHERMAL(); - // bring everything back to host, capture the scalars by reference - forAll< serialPolicy >( 1, [pres, - dens, - dDens, - wellElemGravCoef, - ¤tBHP, - &dCurrentBHP, - &iwelemRef, - &refGravCoef] ( localIndex const ) - { - real64 const diffGravCoef = refGravCoef - wellElemGravCoef[iwelemRef]; - currentBHP = pres[iwelemRef] + dens[iwelemRef][0] * diffGravCoef; - dCurrentBHP[DerivOffset::dP] = 1.0 + dDens[iwelemRef][0][DerivOffset::dP] *diffGravCoef; - if constexpr ( IS_THERMAL ) - { - dCurrentBHP[DerivOffset::dT] = dDens[iwelemRef][0][DerivOffset::dT] * diffGravCoef; - } - } ); + real64 const diffGravCoef = refGravCoef - wellElemGravCoef[iwelemRef]; + currentBHP = pres[iwelemRef] + dens[iwelemRef][0] * diffGravCoef; } ); + GEOS_LOG_LEVEL_BY_RANK( logInfo::WellControl, GEOS_FMT( "{}: The BHP (at the specified reference elevation) = {} Pa", wellControlsName, currentBHP ) ); } -void SinglePhaseWell::updateVolRateForConstraint( ElementRegionManager const & elemManager, WellElementSubRegion & subRegion ) +void SinglePhaseWell::calculateReferenceElementRates( WellElementSubRegion & subRegion ) { GEOS_MARK_FUNCTION; @@ -265,81 +266,124 @@ void SinglePhaseWell::updateVolRateForConstraint( ElementRegionManager const & e // subRegion data - arrayView1d< real64 const > const pres = - subRegion.getField< well::pressure >(); - arrayView1d< real64 const > const & connRate = subRegion.getField< well::connectionRate >(); + + // control data + + // fluid data + constitutive::SingleFluidBase & fluidSeparator = getSingleFluidSeparator(); + arrayView2d< real64 const, constitutive::singlefluid::USD_FLUID > const & dens = fluidSeparator.density(); + + real64 & currentVolRate = + getReference< real64 >( WellControls::viewKeyStruct::currentVolRateString() ); + + // bring everything back to host, capture the scalars by reference + forAll< serialPolicy >( 1, [connRate, + dens, + ¤tVolRate, + &iwelemRef] ( localIndex const ) + { + real64 const densInv = 1.0 / dens[iwelemRef][0]; + currentVolRate = connRate[iwelemRef] * densInv; + // tjb compute mass + } ); + + +} + +void SinglePhaseWell::updateFluidModel( WellElementSubRegion & subRegion ) const +{ + GEOS_MARK_FUNCTION; + + arrayView1d< real64 const > const pres = subRegion.getField< well::pressure >(); + arrayView1d< real64 const > const temp = subRegion.getField< well::temperature >(); string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString() ); SingleFluidBase & fluid = subRegion.getConstitutiveModel< SingleFluidBase >( fluidName ); - arrayView2d< real64 const, constitutive::singlefluid::USD_FLUID > const & dens = fluid.density(); - arrayView3d< real64 const, constitutive::singlefluid::USD_FLUID_DER > const & dDens = fluid.dDensity(); + + constitutiveUpdatePassThru( fluid, [&]( auto & castedFluid ) + { + typename TYPEOFREF( castedFluid ) ::KernelWrapper fluidWrapper = castedFluid.createKernelWrapper(); + singlePhaseBaseKernels::FluidUpdateKernel::launch( fluidWrapper, pres, temp ); + } ); +} +void SinglePhaseWell::updateSeparator( ElementRegionManager const & elemManager, WellElementSubRegion & subRegion ) +{ + GEOS_MARK_FUNCTION; + + // the rank that owns the reference well element is responsible for the calculations below. + if( !subRegion.isLocallyOwned() ) + { + return; + } + + localIndex const iwelemRef = subRegion.getTopWellElementIndex(); + + // subRegion data + arrayView1d< real64 const > const pres = + subRegion.getField< well::pressure >(); // control data - WellControls & wellControls = getWellControls( subRegion ); - string const wellControlsName = wellControls.getName(); - bool const logSurfaceCondition = isLogLevelActive< logInfo::WellControl >( wellControls.getLogLevel()); - integer const useSurfaceConditions = wellControls.useSurfaceConditions(); + string const wellControlsName = getName(); + bool const logSurfaceCondition = isLogLevelActive< logInfo::WellControl >( getLogLevel()); + integer const useSurfaceCond = useSurfaceConditions(); + + // fluid data + constitutive::SingleFluidBase & fluidSeparator = getSingleFluidSeparator(); + arrayView2d< real64 const, constitutive::singlefluid::USD_FLUID > const & dens = fluidSeparator.density(); + real64 flashPressure; - if( useSurfaceConditions ) + if( useSurfaceCond ) { // use surface conditions - flashPressure = wellControls.getSurfacePressure(); + flashPressure = getSurfacePressure(); } else { - if( !wellControls.referenceReservoirRegion().empty() ) + if( !getReferenceReservoirRegion().empty() ) { - ElementRegionBase const & region = elemManager.getRegion( wellControls.referenceReservoirRegion() ); - GEOS_ERROR_IF ( !region.hasWrapper( SinglePhaseStatistics::regionStatisticsName()), + ElementRegionBase const & region = elemManager.getRegion( getReferenceReservoirRegion()); + GEOS_ERROR_IF ( !region.hasWrapper( SinglePhaseStatistics::regionStatisticsName() ), GEOS_FMT( "{}: WellControl {} referenceReservoirRegion field requires SinglePhaseStatistics to be configured for region {} ", - getDataContext(), wellControls.getName(), wellControls.referenceReservoirRegion() ) ); + getDataContext(), getName(), getReferenceReservoirRegion() ) ); - SinglePhaseStatistics::RegionStatistics const & stats = region.getReference< SinglePhaseStatistics::RegionStatistics >( SinglePhaseStatistics::regionStatisticsName() ); + SinglePhaseStatistics::RegionStatistics const & stats = region.getReference< SinglePhaseStatistics::RegionStatistics >( + SinglePhaseStatistics::regionStatisticsName() ); + setRegionAveragePressure( stats.averagePressure ); + setRegionAverageTemperature( stats.averageTemperature ); GEOS_ERROR_IF( stats.averagePressure <= 0.0, - GEOS_FMT( - "{}: No region average quantities computed. WellControl {} referenceReservoirRegion field requires SinglePhaseStatistics to be configured for region {} ", - getDataContext(), wellControls.getName(), wellControls.referenceReservoirRegion() )); - wellControls.setRegionAveragePressure( stats.averagePressure ); - wellControls.setRegionAverageTemperature( stats.averageTemperature ); + GEOS_FMT( "{}: No region average quantities computed. WellControl {} referenceReservoirRegion field requires CompositionalMultiphaseStatistics to be configured for region {} ", + getDataContext(), getName(), getReferenceReservoirRegion() )); + } // use region conditions - flashPressure = wellControls.getRegionAveragePressure(); + flashPressure = getRegionAveragePressure(); if( flashPressure < 0.0 ) { // use segment conditions flashPressure = pres[iwelemRef]; } } - real64 & currentVolRate = - wellControls.getReference< real64 >( SinglePhaseWell::viewKeyStruct::currentVolRateString() ); - - arrayView1d< real64 > const & dCurrentVolRate = - wellControls.getReference< array1d< real64 > >( SinglePhaseWell::viewKeyStruct::dCurrentVolRateString() ); - constitutiveUpdatePassThru( fluid, [&]( auto & castedFluid ) + constitutiveUpdatePassThru( fluidSeparator, [&]( auto & castedFluid ) { - typename TYPEOFREF( castedFluid ) ::KernelWrapper fluidWrapper = castedFluid.createKernelWrapper(); + typename TYPEOFREF( castedFluid ) ::KernelWrapper fluidSeparatorWrapper = castedFluid.createKernelWrapper(); geos::internal::kernelLaunchSelectorThermalSwitch( isThermal(), [&] ( auto ISTHERMAL ) { integer constexpr IS_THERMAL = ISTHERMAL(); - using COFFSET_WJ = singlePhaseWellKernels::ColOffset_WellJac< IS_THERMAL >; + GEOS_UNUSED_VAR( IS_THERMAL ); // bring everything back to host, capture the scalars by reference - forAll< serialPolicy >( 1, [fluidWrapper, + forAll< serialPolicy >( 1, [fluidSeparatorWrapper, pres, - connRate, dens, - dDens, logSurfaceCondition, - &useSurfaceConditions, + &useSurfaceCond, &flashPressure, - ¤tVolRate, - dCurrentVolRate, &iwelemRef, &wellControlsName] ( localIndex const ) { @@ -347,10 +391,10 @@ void SinglePhaseWell::updateVolRateForConstraint( ElementRegionManager const & e // - Surface conditions: using the surface pressure provided by the user // - Reservoir conditions: using the pressure in the top element - if( useSurfaceConditions ) + if( useSurfaceCond ) { // we need to compute the surface density - fluidWrapper.update( iwelemRef, 0, flashPressure ); + fluidSeparatorWrapper.update( iwelemRef, 0, flashPressure ); if( logSurfaceCondition ) { @@ -365,230 +409,258 @@ void SinglePhaseWell::updateVolRateForConstraint( ElementRegionManager const & e } else { - real64 const refPres = pres[iwelemRef]; - fluidWrapper.update( iwelemRef, 0, refPres ); - } - - real64 const densInv = 1.0 / dens[iwelemRef][0]; - currentVolRate = connRate[iwelemRef] * densInv; - - dCurrentVolRate[COFFSET_WJ::dP] = -( useSurfaceConditions == 0 ) * dDens[iwelemRef][0][DerivOffset::dP] * currentVolRate * densInv; - dCurrentVolRate[COFFSET_WJ::dQ] = densInv; - if constexpr ( IS_THERMAL ) - { - dCurrentVolRate[COFFSET_WJ::dT] = -( useSurfaceConditions == 0 ) * dDens[iwelemRef][0][DerivOffset::dT] * currentVolRate * densInv; - } - if( logSurfaceCondition && useSurfaceConditions ) - { - GEOS_LOG_RANK( GEOS_FMT( "{}: total fluid density at surface conditions = {} kg/sm3, total rate = {} kg/s, total surface volumetric rate = {} sm3/s", - wellControlsName, dens[iwelemRef][0], connRate[iwelemRef], currentVolRate ) ); + fluidSeparatorWrapper.update( iwelemRef, 0, flashPressure ); } } ); } ); } ); } -void SinglePhaseWell::updateFluidModel( WellElementSubRegion & subRegion ) const +real64 SinglePhaseWell::updateSubRegionState( ElementRegionManager const & elemManager, WellElementSubRegion & subRegion ) { - GEOS_MARK_FUNCTION; - - arrayView1d< real64 const > const pres = subRegion.getField< well::pressure >(); - arrayView1d< real64 const > const temp = subRegion.getField< well::temperature >(); - string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString() ); - SingleFluidBase & fluid = subRegion.getConstitutiveModel< SingleFluidBase >( fluidName ); - - constitutiveUpdatePassThru( fluid, [&]( auto & castedFluid ) + if( getWellState()) { - typename TYPEOFREF( castedFluid ) ::KernelWrapper fluidWrapper = castedFluid.createKernelWrapper(); - singlePhaseBaseKernels::FluidUpdateKernel::launch( fluidWrapper, pres, temp ); - } ); -} -real64 SinglePhaseWell::updateSubRegionState( ElementRegionManager const & elemManager, WellElementSubRegion & subRegion ) -{ - // update volumetric rates for the well constraints - // Warning! This must be called before updating the fluid model - updateVolRateForConstraint( elemManager, subRegion ); + // update volumetric rates for the well constraints + // Warning! This must be called before updating the fluid model + //calculateReferenceElementRates( subRegion ); - // update density in the well elements - updateFluidModel( subRegion ); + // update density in the well elements + updateFluidModel( subRegion ); + updateSeparator( elemManager, subRegion ); // Calculate fluid properties at control conditions - // update the current BHP - updateBHPForConstraint( subRegion ); + // Calculate the reference element rates + calculateReferenceElementRates( subRegion ); + // update the current BHP + updateBHPForConstraint( subRegion ); - // note: the perforation rates are updated separately - return 0.0; // change in phasevolume fraction doesnt apply -} -void SinglePhaseWell::initializeWells( DomainPartition & domain, real64 const & time_n ) + } + return 0.0; // change in phasevolume fraction doesnt apply +} +void SinglePhaseWell::initializeWell( DomainPartition & domain, MeshLevel & mesh, WellElementSubRegion & subRegion, real64 const & time_n ) { - GEOS_MARK_FUNCTION; - GEOS_UNUSED_VAR( time_n ); + GEOS_UNUSED_VAR( domain ); - // loop over the wells - forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &, - MeshLevel & meshLevel, - string_array const & regionNames ) + PerforationData const & perforationData = *subRegion.getPerforationData(); + ElementRegionManager const & elemManager = mesh.getElemManager(); + // get the info stored on well elements + arrayView1d< real64 const > const wellElemGravCoef = + subRegion.getField< well::gravityCoefficient >(); + + // get well primary variables on well elements + arrayView1d< real64 > const wellElemPressure = + subRegion.getField< well::pressure >(); + arrayView1d< real64 > const connRate = + subRegion.getField< well::connectionRate >(); + arrayView1d< real64 > const wellElemTemperature = + subRegion.getField< well::temperature >(); + // get the element region, subregion, index + arrayView1d< localIndex const > const resElementRegion = + perforationData.getField< perforation::reservoirElementRegion >(); + arrayView1d< localIndex const > const resElementSubRegion = + perforationData.getField< perforation::reservoirElementSubRegion >(); + arrayView1d< localIndex const > const resElementIndex = + perforationData.getField< perforation::reservoirElementIndex >(); + + arrayView1d< real64 const > const & perfGravCoef = + perforationData.getField< well::gravityCoefficient >(); + + bool const hasNonZeroRate = MpiWrapper::max< integer >( hasNonZero( connRate )); + + if( time_n <= 0.0 || ( isWellOpen() && !hasNonZeroRate ) ) { - ElementRegionManager & elemManager = meshLevel.getElemManager(); + setWellState( true ); + if( getCurrentConstraint() == nullptr ) + { + if( isProducer() ) + { + forSubGroups< MinimumBHPConstraint, ProductionConstraint< VolumeRateConstraint >, ProductionConstraint< MassRateConstraint >, + ProductionConstraint< PhaseVolumeRateConstraint > >( [&]( auto & constraint ) + { + if( ConstraintTypeId( getControl()) == constraint.getControl() ) + { + setCurrentConstraint( &constraint ); + setControl( static_cast< WellControls::Control >(constraint.getControl()) ); // tjb old + } + } ); + } + else + { + forSubGroups< MaximumBHPConstraint, InjectionConstraint< VolumeRateConstraint >, InjectionConstraint< MassRateConstraint >, + InjectionConstraint< PhaseVolumeRateConstraint > >( [&]( auto & constraint ) + { + if( ConstraintTypeId( getControl()) == constraint.getControl() ) + { + setCurrentConstraint( &constraint ); + setControl( static_cast< WellControls::Control >(constraint.getControl()) ); // tjb old + } + } ); + } + } - elemManager.forElementSubRegions< WellElementSubRegion >( regionNames, - [&]( localIndex const, - WellElementSubRegion & subRegion ) + PresTempInitializationKernel::SinglePhaseFlowAccessors resSinglePhaseFlowAccessors( elemManager, getFlowSolverName()); + PresTempInitializationKernel::SingleFluidAccessors resSingleFluidAccessors( elemManager, getFlowSolverName() ); + + // 1) Loop over all perforations to compute an average density + // 2) Initialize the reference pressure + // 3) Estimate the pressures in the well elements using the average density + PresTempInitializationKernel:: + launch( isThermal(), + perforationData.size(), + subRegion.size(), + perforationData.getNumPerforationsGlobal(), + *this, + 0.0, // initialization done at t = 0 + resSinglePhaseFlowAccessors.get( flow::pressure{} ), + resSinglePhaseFlowAccessors.get( flow::temperature{} ), + resSingleFluidAccessors.get( fields::singlefluid::density{} ), + resElementRegion, + resElementSubRegion, + resElementIndex, + perfGravCoef, + wellElemGravCoef, + wellElemPressure, + wellElemTemperature ); + + // 4) Recompute the pressure-dependent properties + // Note: I am leaving that here because I would like to use the perforationRates (computed in UpdateState) + // to better initialize the rates + updateSubRegionState( elemManager, subRegion ); + + string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString() ); + SingleFluidBase & fluid = subRegion.getConstitutiveModel< SingleFluidBase >( fluidName ); + arrayView2d< real64 const, constitutive::singlefluid::USD_FLUID > const & wellElemDens = fluid.density(); + + // 5) Estimate the well rates + RateInitializationKernel::launch( subRegion.size(), + *this, + 0.0, // initialization done at t = 0 + wellElemDens, + connRate ); + + calculateReferenceElementRates( subRegion ); + WellConstraintBase * constraint = getCurrentConstraint(); + constraint->setBHP ( getReference< real64 >( WellControls::viewKeyStruct::currentBHPString() )); + constraint->setTotalVolumeRate ( getReference< real64 >( + WellControls::viewKeyStruct::currentVolRateString() )); + //constraint->setMassRate( wellControls.getReference< real64 >( WellControls::viewKeyStruct::currentMassRateString() )); + // 7) Copy well / fluid dofs to "prop"_n variables + saveState( subRegion ); + } + else if( !hasNonZeroRate ) + { + setWellState( false ); + GEOS_LOG_RANK_0( "tjb shut wells "<< subRegion.getName()); + } + else + { + setWellState( true ); + // setup for restart + if( getCurrentConstraint() == nullptr ) { - WellControls const & wellControls = getWellControls( subRegion ); - PerforationData const & perforationData = *subRegion.getPerforationData(); - - // get the info stored on well elements - arrayView1d< real64 const > const wellElemGravCoef = - subRegion.getField< well::gravityCoefficient >(); - - // get well primary variables on well elements - arrayView1d< real64 > const wellElemPressure = - subRegion.getField< well::pressure >(); - arrayView1d< real64 > const connRate = - subRegion.getField< well::connectionRate >(); - arrayView1d< real64 > const wellElemTemperature = - subRegion.getField< well::temperature >(); - // get the element region, subregion, index - arrayView1d< localIndex const > const resElementRegion = - perforationData.getField< perforation::reservoirElementRegion >(); - arrayView1d< localIndex const > const resElementSubRegion = - perforationData.getField< perforation::reservoirElementSubRegion >(); - arrayView1d< localIndex const > const resElementIndex = - perforationData.getField< perforation::reservoirElementIndex >(); - - arrayView1d< real64 const > const & perfGravCoef = - perforationData.getField< well::gravityCoefficient >(); - - bool const hasNonZeroRate = MpiWrapper::max< integer >( hasNonZero( connRate )); - - if( wellControls.isWellOpen() && !hasNonZeroRate ) + updateSubRegionState( elemManager, subRegion ); + if( isProducer() ) { - // TODO: change the way we access the flowSolver here - SinglePhaseBase const & flowSolver = getParent().getGroup< SinglePhaseBase >( getFlowSolverName() ); - PresTempInitializationKernel::SinglePhaseFlowAccessors resSinglePhaseFlowAccessors( meshLevel.getElemManager(), flowSolver.getName() ); - PresTempInitializationKernel::SingleFluidAccessors resSingleFluidAccessors( meshLevel.getElemManager(), flowSolver.getName() ); - - // 1) Loop over all perforations to compute an average density - // 2) Initialize the reference pressure - // 3) Estimate the pressures in the well elements using the average density - PresTempInitializationKernel:: - launch( isThermal(), - perforationData.size(), - subRegion.size(), - perforationData.getNumPerforationsGlobal(), - wellControls, - 0.0, // initialization done at t = 0 - resSinglePhaseFlowAccessors.get( flow::pressure{} ), - resSinglePhaseFlowAccessors.get( flow::temperature{} ), - resSingleFluidAccessors.get( fields::singlefluid::density{} ), - resElementRegion, - resElementSubRegion, - resElementIndex, - perfGravCoef, - wellElemGravCoef, - wellElemPressure, - wellElemTemperature ); - - // 4) Recompute the pressure-dependent properties - // Note: I am leaving that here because I would like to use the perforationRates (computed in UpdateState) - // to better initialize the rates - updateSubRegionState( elemManager, subRegion ); - - string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString() ); - SingleFluidBase & fluid = subRegion.getConstitutiveModel< SingleFluidBase >( fluidName ); - arrayView2d< real64 const, constitutive::singlefluid::USD_FLUID > const & wellElemDens = fluid.density(); - - // 5) Estimate the well rates - RateInitializationKernel::launch( subRegion.size(), - wellControls, - 0.0, // initialization done at t = 0 - wellElemDens, - connRate ); + forSubGroups< MinimumBHPConstraint, ProductionConstraint< VolumeRateConstraint >, ProductionConstraint< MassRateConstraint >, + ProductionConstraint< PhaseVolumeRateConstraint > >( [&]( + auto + & constraint ) + { + if( ConstraintTypeId( getControl()) == constraint.getControl() ) + { + setCurrentConstraint( &constraint ); + } + } ); } + else + { + forSubGroups< MaximumBHPConstraint, InjectionConstraint< VolumeRateConstraint >, InjectionConstraint< MassRateConstraint >, InjectionConstraint< PhaseVolumeRateConstraint > >( [&]( + auto + & + constraint ) + { + if( ConstraintTypeId( getControl()) == constraint.getControl() ) + { + setCurrentConstraint( &constraint ); + } + } ); + } + } - } ); + } - } ); } -void SinglePhaseWell::shutDownWell( real64 const time_n, - DomainPartition const & domain, + +void SinglePhaseWell::shutDownWell( WellElementSubRegion & subRegion, DofManager const & dofManager, CRSMatrixView< real64, globalIndex const > const & localMatrix, arrayView1d< real64 > const & localRhs ) { GEOS_MARK_FUNCTION; - GEOS_UNUSED_VAR( time_n ); string const wellDofKey = dofManager.getKey( wellElementDofName() ); - forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &, - MeshLevel const & mesh, - string_array const & regionNames ) - { - ElementRegionManager const & elemManager = mesh.getElemManager(); - elemManager.forElementSubRegions< WellElementSubRegion >( regionNames, - [&]( localIndex const, - WellElementSubRegion const & subRegion ) - { - - // if the well is open, we don't have to do anything, so we just return - WellControls const & wellControls = getWellControls( subRegion ); - if( wellControls.isWellOpen( ) ) - { - return; - } + if( isWellOpen( ) ) + { + return; + } - globalIndex const rankOffset = dofManager.rankOffset(); + globalIndex const rankOffset = dofManager.rankOffset(); - arrayView1d< integer const > const ghostRank = - subRegion.getReference< array1d< integer > >( ObjectManagerBase::viewKeyStruct::ghostRankString() ); - arrayView1d< globalIndex const > const dofNumber = - subRegion.getReference< array1d< globalIndex > >( wellDofKey ); + arrayView1d< integer const > const ghostRank = + subRegion.getReference< array1d< integer > >( ObjectManagerBase::viewKeyStruct::ghostRankString() ); + arrayView1d< globalIndex const > const dofNumber = + subRegion.getReference< array1d< globalIndex > >( wellDofKey ); - arrayView1d< real64 const > const pres = - subRegion.getField< fields::well::pressure >(); - arrayView1d< real64 const > const connRate = - subRegion.getField< fields::well::connectionRate >(); + arrayView1d< real64 const > const pres = + subRegion.getField< fields::well::pressure >(); + arrayView1d< real64 const > const connRate = + subRegion.getField< fields::well::connectionRate >(); - forAll< parallelDevicePolicy<> >( subRegion.size(), [=] GEOS_HOST_DEVICE ( localIndex const ei ) - { - if( ghostRank[ei] >= 0 ) - { - return; - } + forAll< parallelDevicePolicy<> >( subRegion.size(), [=] GEOS_HOST_DEVICE ( localIndex const ei ) + { + if( ghostRank[ei] >= 0 ) + { + return; + } - globalIndex const dofIndex = dofNumber[ei]; - localIndex const localRow = dofIndex - rankOffset; - real64 rhsValue; - - // 4.1. Apply pressure value to the matrix/rhs - FieldSpecificationEqual::SpecifyFieldValue( dofIndex, - rankOffset, - localMatrix, - rhsValue, - pres[ei], // freeze the current pressure value - pres[ei] ); - localRhs[localRow] = rhsValue; - - // 4.2. Apply rate value to the matrix/rhs - FieldSpecificationEqual::SpecifyFieldValue( dofIndex + 1, - rankOffset, - localMatrix, - rhsValue, - connRate[ei], // freeze the current pressure value - connRate[ei] ); - localRhs[localRow + 1] = rhsValue; + globalIndex const dofIndex = dofNumber[ei]; + localIndex const localRow = dofIndex - rankOffset; + real64 rhsValue; + + // 4.1. Apply pressure value to the matrix/rhs + FieldSpecificationEqual::SpecifyFieldValue( dofIndex, + rankOffset, + localMatrix, + rhsValue, + pres[ei], // freeze the current pressure value + pres[ei] ); + localRhs[localRow] = rhsValue; + + // 4.2. Apply rate value to the matrix/rhs + FieldSpecificationEqual::SpecifyFieldValue( dofIndex + 1, + rankOffset, + localMatrix, + rhsValue, + connRate[ei], // freeze the current pressure value + connRate[ei] ); + localRhs[localRow + 1] = rhsValue; - } ); - } ); } ); + } +real64 SinglePhaseWell::updateWellState( ElementRegionManager const & elemManager, WellElementSubRegion & subRegion ) +{ + GEOS_MARK_FUNCTION; + updateSubRegionState( elemManager, subRegion ); + return 0.0; +} +#if 0 void SinglePhaseWell::assembleSystem( real64 const time, real64 const dt, DomainPartition & domain, @@ -598,11 +670,38 @@ void SinglePhaseWell::assembleSystem( real64 const time, { string const wellDofKey = dofManager.getKey( wellElementDofName()); + + // selects constraints one of 2 ways + // wellEstimator flag set to 0 => orginal logic rates are computed during update state and constraints are selected every newton + // iteration + // wellEstimator flag > 0 => well esitmator solved for each constraint and then selects the constraint + // => estimator solve only performed first "wellEstimator" iterations + NonlinearSolverParameters const & nonlinearParams = getNonlinearSolverParameters(); + selectWellConstraint( time, dt, nonlinearParams.m_numNewtonIterations, domain ); + + // assemble the accumulation term in the mass balance equations assembleAccumulationTerms( time, dt, domain, dofManager, localMatrix, localRhs ); // then assemble the pressure relations between well elements - assemblePressureRelations( time, dt, domain, dofManager, localMatrix, localRhs ); + //assemblePressureRelations( time, dt, domain, dofManager, localMatrix, localRhs ); + { + forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &, + MeshLevel & mesh, + string_array const & regionNames ) + { + ElementRegionManager & elementRegionManager = mesh.getElemManager(); + elementRegionManager.forElementRegions< WellElementRegion >( regionNames, + [&]( localIndex const, + WellElementRegion & region ) + { + WellElementSubRegion & subRegion = region.getGroup( ElementRegionBase::viewKeyStruct::elementSubRegions() ) + .getGroup< WellElementSubRegion >( region.getSubRegionName() ); + + assembleWellConstraintTerms( time, dt, subRegion, dofManager, localMatrix.toViewConstSizes(), localRhs ); + } ); + } ); + } // then compute the perforation rates (later assembled by the coupled solver) computePerforationRates( time, dt, domain ); @@ -614,298 +713,333 @@ void SinglePhaseWell::assembleSystem( real64 const time, // then apply a special treatment to the wells that are shut shutDownWell( time, domain, dofManager, localMatrix, localRhs ); } +#endif -void SinglePhaseWell::assembleFluxTerms( real64 const & time_n, - real64 const & dt, - DomainPartition & domain, - DofManager const & dofManager, - CRSMatrixView< real64, globalIndex const > const & localMatrix, - arrayView1d< real64 > const & localRhs ) +void SinglePhaseWell::assembleWellFluxTerms( real64 const & time, + real64 const & dt, + WellElementSubRegion const & subRegion, + DofManager const & dofManager, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) { GEOS_MARK_FUNCTION; - GEOS_UNUSED_VAR( time_n ); - GEOS_UNUSED_VAR( dt ); + GEOS_UNUSED_VAR( time ); - // loop over the wells - forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &, - MeshLevel const & mesh, - string_array const & regionNames ) + // get a reference to the degree-of-freedom numbers + string const wellDofKey = dofManager.getKey( wellElementDofName() ); + + if( isThermal() ) { + string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString() ); + SingleFluidBase const & fluid = subRegion.getConstitutiveModel< SingleFluidBase >( fluidName ); + thermalSinglePhaseWellKernels:: + FaceBasedAssemblyKernelFactory:: + createAndLaunch< parallelDevicePolicy<> >( dt, + dofManager.rankOffset(), + wellDofKey, + *this, + subRegion, + fluid, + localMatrix, + localRhs ); + } + else + { + singlePhaseWellKernels:: + FaceBasedAssemblyKernelFactory:: + createAndLaunch< parallelDevicePolicy<> >( dt, + dofManager.rankOffset(), + wellDofKey, + *this, + subRegion, + localMatrix, + localRhs ); + } - ElementRegionManager const & elemManager = mesh.getElemManager(); +} - elemManager.forElementSubRegions< WellElementSubRegion >( regionNames, - [&]( localIndex const, - WellElementSubRegion const & subRegion ) - { - WellControls const & wellControls = getWellControls( subRegion ); - // get a reference to the degree-of-freedom numbers - string const wellDofKey = dofManager.getKey( wellElementDofName() ); +void SinglePhaseWell::assembleWellConstraintTerms( real64 const & time_n, + real64 const & GEOS_UNUSED_PARAM( dt ), + WellElementSubRegion const & subRegion, + DofManager const & dofManager, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) +{ + GEOS_MARK_FUNCTION; + - if( isThermal() ) - { - string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString() ); - SingleFluidBase const & fluid = subRegion.getConstitutiveModel< SingleFluidBase >( fluidName ); - thermalSinglePhaseWellKernels:: - FaceBasedAssemblyKernelFactory:: - createAndLaunch< parallelDevicePolicy<> >( dt, - dofManager.rankOffset(), - wellDofKey, - wellControls, - subRegion, - fluid, - localMatrix, - localRhs ); - } - else + // the rank that owns the reference well element is responsible for the calculations below. + + if( !subRegion.isLocallyOwned() || !( getWellStatus() == WellControls::Status::OPEN )) + { + return; + } + { + // tjb wellControls.forSubGroups< BHPConstraint, MassConstraint, VolumeRateConstraint >( [&]( auto & constraint ) + forSubGroups< BHPConstraint, InjectionConstraint< VolumeRateConstraint >, ProductionConstraint< VolumeRateConstraint > >( [&]( auto & constraint ) + { + if( constraint.getName() == getCurrentConstraint()->getName()) { - singlePhaseWellKernels:: - FaceBasedAssemblyKernelFactory:: - createAndLaunch< parallelDevicePolicy<> >( dt, - dofManager.rankOffset(), - wellDofKey, - wellControls, - subRegion, - localMatrix, - localRhs ); + // found limiting constraint + + // fluid data + constitutive::SingleFluidBase & fluidSeparator = getSingleFluidSeparator(); + integer isThermal = fluidSeparator.isThermal(); + + geos::internal::kernelLaunchSelectorThermalSwitch( isThermal, [&] ( auto ISTHERMAL ) + { + integer constexpr IS_THERMAL = ISTHERMAL(); + + singlePhaseWellConstraintKernels::ConstraintHelper< IS_THERMAL >::assembleConstraintEquation( time_n, + *this, + constraint, + subRegion, + dofManager.getKey( wellElementDofName() ), + dofManager.rankOffset(), + localMatrix, + localRhs ); + } ); } } ); + } - } ); } -void SinglePhaseWell::assemblePressureRelations( real64 const & time_n, - real64 const & GEOS_UNUSED_PARAM( dt ), - DomainPartition const & domain, - DofManager const & dofManager, - CRSMatrixView< real64, globalIndex const > const & localMatrix, - arrayView1d< real64 > const & localRhs ) +void SinglePhaseWell::assembleWellPressureRelations( real64 const & GEOS_UNUSED_PARAM( time_n ), + real64 const & GEOS_UNUSED_PARAM( dt ), + WellElementSubRegion const & subRegion, + DofManager const & dofManager, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) { - GEOS_MARK_FUNCTION; - forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &, - MeshLevel const & mesh, - string_array const & regionNames ) - { + // get the degrees of freedom numbers, depth, next well elem index + string const wellDofKey = dofManager.getKey( wellElementDofName() ); + arrayView1d< globalIndex const > const & wellElemDofNumber = + subRegion.getReference< array1d< globalIndex > >( wellDofKey ); + arrayView1d< real64 const > const & wellElemGravCoef = + subRegion.getField< well::gravityCoefficient >(); + arrayView1d< localIndex const > const & nextWellElemIndex = + subRegion.getReference< array1d< localIndex > >( WellElementSubRegion::viewKeyStruct::nextWellElementIndexString() ); - ElementRegionManager const & elemManager = mesh.getElemManager(); + // get primary variables on well elements + arrayView1d< real64 const > const & wellElemPressure = + subRegion.getField< well::pressure >(); + // get well constitutive data + string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString() ); + SingleFluidBase const & fluid = subRegion.getConstitutiveModel< SingleFluidBase >( fluidName ); + arrayView2d< real64 const, constitutive::singlefluid::USD_FLUID > const & wellElemDensity = fluid.density(); + arrayView3d< real64 const, constitutive::singlefluid::USD_FLUID_DER > const & dWellElemDensity = fluid.dDensity(); - elemManager.forElementSubRegions< WellElementSubRegion >( regionNames, - [&]( localIndex const, - WellElementSubRegion const & subRegion ) - { + geos::internal::kernelLaunchSelectorThermalSwitch( isThermal(), [&] ( auto ISTHERMAL ) + { + PressureRelationKernel::launch< ISTHERMAL >( subRegion.size(), + dofManager.rankOffset(), + wellElemDofNumber, + wellElemGravCoef, + nextWellElemIndex, + wellElemPressure, + wellElemDensity, + dWellElemDensity, + localMatrix, + localRhs ); + } ); - WellControls & wellControls = getWellControls( subRegion ); +} - // get the degrees of freedom numbers, depth, next well elem index - string const wellDofKey = dofManager.getKey( wellElementDofName() ); - arrayView1d< globalIndex const > const & wellElemDofNumber = - subRegion.getReference< array1d< globalIndex > >( wellDofKey ); - arrayView1d< real64 const > const & wellElemGravCoef = - subRegion.getField< well::gravityCoefficient >(); - arrayView1d< localIndex const > const & nextWellElemIndex = - subRegion.getReference< array1d< localIndex > >( WellElementSubRegion::viewKeyStruct::nextWellElementIndexString() ); +void SinglePhaseWell::assembleWellAccumulationTerms( real64 const & time, + real64 const & dt, + WellElementSubRegion & subRegion, + DofManager const & dofManager, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) - // get primary variables on well elements - arrayView1d< real64 const > const & wellElemPressure = - subRegion.getField< well::pressure >(); +{ + GEOS_UNUSED_VAR( time ); + GEOS_UNUSED_VAR( dt ); + // get a reference to the degree-of-freedom numbers + string const wellElemDofKey = dofManager.getKey( wellElementDofName() ); - // get well constitutive data - string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString() ); - SingleFluidBase const & fluid = subRegion.getConstitutiveModel< SingleFluidBase >( fluidName ); - arrayView2d< real64 const, constitutive::singlefluid::USD_FLUID > const & wellElemDensity = fluid.density(); - arrayView3d< real64 const, constitutive::singlefluid::USD_FLUID_DER > const & dWellElemDensity = fluid.dDensity(); - geos::internal::kernelLaunchSelectorThermalSwitch( isThermal(), [&] ( auto ISTHERMAL ) + string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString() ); + SingleFluidBase const & fluid = subRegion.getConstitutiveModel< SingleFluidBase >( fluidName ); + if( getWellStatus() == WellControls::Status::OPEN && !m_keepVariablesConstantDuringInitStep ) + { + if( isThermal() ) + { + thermalSinglePhaseWellKernels:: + ElementBasedAssemblyKernelFactory:: + createAndLaunch< parallelDevicePolicy<> >( isProducer(), + dofManager.rankOffset(), + wellElemDofKey, + subRegion, + fluid, + localMatrix, + localRhs ); + } + else + { + singlePhaseWellKernels:: + ElementBasedAssemblyKernelFactory:: + createAndLaunch< parallelDevicePolicy<> >( dofManager.rankOffset(), + wellElemDofKey, + subRegion, + fluid, + localMatrix, + localRhs ); + } + // get the degrees of freedom and ghosting info + arrayView1d< globalIndex const > const & wellElemDofNumber = + subRegion.getReference< array1d< globalIndex > >( wellElemDofKey ); + arrayView1d< integer const > const wellElemGhostRank = subRegion.ghostRank(); + arrayView1d< integer const > const elemStatus = subRegion.getLocalWellElementStatus(); + + arrayView1d< real64 > connRate = subRegion.getField< fields::well::connectionRate >(); + localIndex rank_offset = dofManager.rankOffset(); + forAll< parallelDevicePolicy<> >( subRegion.size(), [=] GEOS_HOST_DEVICE ( localIndex const ei ) + { + if( wellElemGhostRank[ei] < 0 ) { - localIndex controlHasSwitched=0; - controlHasSwitched = PressureRelationKernel::launch< ISTHERMAL >( subRegion.size(), - dofManager.rankOffset(), - subRegion.isLocallyOwned(), - subRegion.getTopWellElementIndex(), - wellControls, - time_n, - wellElemDofNumber, - wellElemGravCoef, - nextWellElemIndex, - wellElemPressure, - wellElemDensity, - dWellElemDensity, - localMatrix, - localRhs ); - - if( controlHasSwitched == 1 ) + if( elemStatus[ei]==WellElementSubRegion::WellElemStatus::CLOSED ) { - // Note: if BHP control is not viable, we switch to TOTALVOLRATE - // if TOTALVOLRATE is not viable, we switch to BHP + connRate[ei] = 0.0; + globalIndex const dofIndex = wellElemDofNumber[ei]; + localIndex const localRow = dofIndex - rank_offset; - if( wellControls.getControl() == WellControls::Control::BHP ) - { - wellControls.switchToTotalRateControl( wellControls.getTargetTotalRate( time_n ) ); - GEOS_LOG_LEVEL_RANK_0( logInfo::WellControl, - GEOS_FMT( "Control switch for well {} from BHP constraint to rate constraint", subRegion.getName()) ); - } - else + real64 const unity = 1.0; + for( integer i=0; i < m_numDofPerWellElement; i++ ) { - wellControls.switchToBHPControl( wellControls.getTargetBHP( time_n ) ); - GEOS_LOG_LEVEL_RANK_0( logInfo::WellControl, - GEOS_FMT( "Control switch for well {} from rate constraint to BHP constraint", subRegion.getName()) ); + globalIndex const rindex = localRow+i; + globalIndex const cindex =dofIndex + i; + localMatrix.template addToRow< serialAtomic >( rindex, + &cindex, + &unity, + 1 ); + localRhs[rindex] = 0.0; } } - } ); - + } } ); - } ); -} - -void SinglePhaseWell::assembleAccumulationTerms( real64 const & time_n, - real64 const & dt, - DomainPartition & domain, - DofManager const & dofManager, - CRSMatrixView< real64, globalIndex const > const & localMatrix, - arrayView1d< real64 > const & localRhs ) -{ - GEOS_MARK_FUNCTION; - GEOS_UNUSED_VAR( time_n ); - GEOS_UNUSED_VAR( dt ); - forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &, - MeshLevel const & mesh, - string_array const & regionNames ) + } + else { - - ElementRegionManager const & elemManager = mesh.getElemManager(); - - elemManager.forElementSubRegions< WellElementSubRegion >( regionNames, - [&]( localIndex const, - WellElementSubRegion const & subRegion ) + // Zero accumulation contribution + arrayView1d< globalIndex const > const & wellElemDofNumber = + subRegion.getReference< array1d< globalIndex > >( wellElemDofKey ); + arrayView1d< integer const > const wellElemGhostRank = subRegion.ghostRank(); + + arrayView1d< real64 > connRate = subRegion.getField< fields::well::connectionRate >(); + localIndex rank_offset = dofManager.rankOffset(); + forAll< parallelDevicePolicy<> >( subRegion.size(), [=] GEOS_HOST_DEVICE ( localIndex const ei ) { - - // get a reference to the degree-of-freedom numbers - string const wellElemDofKey = dofManager.getKey( wellElementDofName() ); - - WellControls const & wellControls = getWellControls( subRegion ); - string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString() ); - SingleFluidBase const & fluid = subRegion.getConstitutiveModel< SingleFluidBase >( fluidName ); - - if( isThermal() ) - { - thermalSinglePhaseWellKernels:: - ElementBasedAssemblyKernelFactory:: - createAndLaunch< parallelDevicePolicy<> >( wellControls.isProducer(), - dofManager.rankOffset(), - wellElemDofKey, - subRegion, - fluid, - localMatrix, - localRhs ); - } - else + if( wellElemGhostRank[ei] < 0 ) { - singlePhaseWellKernels:: - ElementBasedAssemblyKernelFactory:: - createAndLaunch< parallelDevicePolicy<> >( dofManager.rankOffset(), - wellElemDofKey, - subRegion, - fluid, - localMatrix, - localRhs ); + connRate[ei] = 0.0; + globalIndex const dofIndex = wellElemDofNumber[ei]; + localIndex const localRow = dofIndex - rank_offset; + + real64 const unity = 1.0; + for( integer i=0; i < m_numDofPerWellElement; i++ ) + { + globalIndex const rindex = localRow+i; + globalIndex const cindex =dofIndex + i; + localMatrix.template addToRow< serialAtomic >( rindex, + &cindex, + &unity, + 1 ); + localRhs[rindex] = 0.0; + } } } ); - } ); - // then assemble the volume balance equations - assembleVolumeBalanceTerms( domain, dofManager, localMatrix, localRhs ); + // zero out current state constraint quantities + getReference< real64 >( WellControls::viewKeyStruct::currentBHPString() ) = 0.0; + getReference< real64 >( WellControls::viewKeyStruct::currentVolRateString() )=0.0; + getReference< real64 >( WellControls::viewKeyStruct::currentVolRateString() )=0.0; + } } -void SinglePhaseWell::assembleVolumeBalanceTerms( DomainPartition const & GEOS_UNUSED_PARAM( domain ), - DofManager const & GEOS_UNUSED_PARAM( dofManager ), - CRSMatrixView< real64, globalIndex const > const & GEOS_UNUSED_PARAM( localMatrix ), - arrayView1d< real64 > const & GEOS_UNUSED_PARAM( localRhs ) ) -{ - // not implemented for single phase flow -} -void SinglePhaseWell::computePerforationRates( real64 const & time_n, - real64 const & dt, DomainPartition & domain ) + +void SinglePhaseWell::computeWellPerforationRates( real64 const & time_n, + real64 const & GEOS_UNUSED_PARAM( dt ), + ElementRegionManager & elemManager, + WellElementSubRegion & subRegion ) { GEOS_MARK_FUNCTION; GEOS_UNUSED_VAR( time_n ); - GEOS_UNUSED_VAR( dt ); - forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &, - MeshLevel & mesh, - string_array const & regionNames ) - { + // get the well data + PerforationData * const perforationData = subRegion.getPerforationData(); - // TODO: change the way we access the flowSolver here - SinglePhaseBase const & flowSolver = getParent().getGroup< SinglePhaseBase >( getFlowSolverName() ); - PerforationKernel::SinglePhaseFlowAccessors resSinglePhaseFlowAccessors( mesh.getElemManager(), flowSolver.getName() ); - PerforationKernel::SingleFluidAccessors resSingleFluidAccessors( mesh.getElemManager(), flowSolver.getName() ); - ElementRegionManager & elemManager = mesh.getElemManager(); - elemManager.forElementSubRegions< WellElementSubRegion >( regionNames, [&]( localIndex const, - WellElementSubRegion & subRegion ) - { - - // get the well data - PerforationData * const perforationData = subRegion.getPerforationData(); - WellControls const & wellControls = getWellControls( subRegion ); - if( wellControls.isWellOpen() && !m_keepVariablesConstantDuringInitStep ) - { + if( isWellOpen() && !m_keepVariablesConstantDuringInitStep ) + { - string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString() ); - SingleFluidBase const & fluid = getConstitutiveModel< SingleFluidBase >( subRegion, fluidName ); + string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString() ); + SingleFluidBase const & fluid = getConstitutiveModel< SingleFluidBase >( subRegion, fluidName ); - if( isThermal() ) - { - thermalSinglePhasePerforationFluxKernels:: - PerforationFluxKernelFactory:: - createAndLaunch< parallelDevicePolicy<> >( flowSolver.getName(), - perforationData, - subRegion, - fluid, - elemManager ); - } - else - { - isothermalSinglePhasePerforationFluxKernels:: - PerforationFluxKernelFactory:: - createAndLaunch< parallelDevicePolicy<> >( flowSolver.getName(), - perforationData, - subRegion, - fluid, - elemManager ); - } - } - else - { - // Zero completion flow rate - arrayView1d< real64 > const perfRate = perforationData->getField< fields::well::perforationRate >(); - for( integer iperf=0; iperfsize(); iperf++ ) - { - perfRate[iperf] = 0.0; - } - } - } ); - } ); + if( isThermal() ) + { + thermalSinglePhasePerforationFluxKernels:: + PerforationFluxKernelFactory:: + createAndLaunch< parallelDevicePolicy<> >( getFlowSolverName(), + perforationData, + subRegion, + fluid, + elemManager ); + } + else + { + isothermalSinglePhasePerforationFluxKernels:: + PerforationFluxKernelFactory:: + createAndLaunch< parallelDevicePolicy<> >( getFlowSolverName(), + perforationData, + subRegion, + fluid, + elemManager ); + } + } + else + { + // Zero completion flow rate + arrayView1d< real64 > const perfRate = perforationData->getField< fields::well::perforationRate >(); + for( integer iperf=0; iperfsize(); iperf++ ) + { + perfRate[iperf] = 0.0; + } + } } real64 -SinglePhaseWell::calculateResidualNorm( real64 const & time_n, - real64 const & dt, - DomainPartition const & domain, - DofManager const & dofManager, - arrayView1d< real64 const > const & localRhs ) +SinglePhaseWell::scalingForWellSystemSolution( WellElementSubRegion & subRegion, + DofManager const & dofManager, + arrayView1d< real64 const > const & localSolution ) { GEOS_MARK_FUNCTION; - integer numNorm = 1; // mass balance + GEOS_UNUSED_VAR( subRegion ); + GEOS_UNUSED_VAR( dofManager ); + GEOS_UNUSED_VAR( localSolution ); + + return 1.0; +} +array1d< real64 > +SinglePhaseWell::calculateLocalWellResidualNorm( real64 const & time_n, + real64 const & dt, + NonlinearSolverParameters const & nonlinearSolverParameters, + WellElementSubRegion const & subRegion, + DofManager const & dofManager, + arrayView1d< real64 const > const & localRhs ) +{ + GEOS_MARK_FUNCTION; + integer numNorm = 1; // mass balance array1d< real64 > localResidualNorm; array1d< real64 > localResidualNormalizer; if( isThermal() ) { - numNorm = 2; // mass balance and energy balance + numNorm = 2; // mass balance and energy balance } localResidualNorm.resize( numNorm ); localResidualNormalizer.resize( numNorm ); @@ -914,74 +1048,101 @@ SinglePhaseWell::calculateResidualNorm( real64 const & time_n, globalIndex const rankOffset = dofManager.rankOffset(); string const wellDofKey = dofManager.getKey( wellElementDofName() ); - forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &, - MeshLevel const & mesh, - string_array const & regionNames ) - { - ElementRegionManager const & elemManager = mesh.getElemManager(); - - elemManager.forElementSubRegions< WellElementSubRegion >( regionNames, - [&]( localIndex const, - WellElementSubRegion const & subRegion ) - { + string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString() ); + SingleFluidBase const & fluid = subRegion.getConstitutiveModel< SingleFluidBase >( fluidName ); - string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString() ); - SingleFluidBase const & fluid = subRegion.getConstitutiveModel< SingleFluidBase >( fluidName ); - WellControls const & wellControls = getWellControls( subRegion ); - // step 1: compute the norm in the subRegion - if( isThermal() ) + if( isWellOpen() ) + { + // step 1: compute the norm in the subRegion + if( isThermal() ) + { + real64 subRegionResidualNorm[2]{}; + thermalSinglePhaseWellKernels::ResidualNormKernelFactory:: + createAndLaunch< parallelDevicePolicy<> >( rankOffset, + wellDofKey, + localRhs, + subRegion, + fluid, + *this, + time_n, + dt, + nonlinearSolverParameters.m_minNormalizer, + subRegionResidualNorm ); + // step 2: reduction across meshBodies/regions/subRegions + + for( integer i=0; i >( rankOffset, - wellDofKey, - localRhs, - subRegion, - fluid, - wellControls, - time_n, - dt, - m_nonlinearSolverParameters.m_minNormalizer, - subRegionResidualNorm ); - // step 2: reduction across meshBodies/regions/subRegions - - for( integer i=0; i localResidualNorm[i] ) { - if( subRegionResidualNorm[i] > localResidualNorm[i] ) - { - localResidualNorm[i] = subRegionResidualNorm[i]; - } + localResidualNorm[i] = subRegionResidualNorm[i]; } } - else + } + else + { + real64 subRegionResidualNorm[1]{}; + ResidualNormKernelFactory:: + createAndLaunch< parallelDevicePolicy<> >( rankOffset, + wellDofKey, + localRhs, + subRegion, + fluid, + *this, + time_n, + dt, + nonlinearSolverParameters.m_minNormalizer, + subRegionResidualNorm ); + + // step 2: reduction across meshBodies/regions/subRegions + + if( subRegionResidualNorm[0] > localResidualNorm[0] ) { - real64 subRegionResidualNorm[1]{}; - ResidualNormKernelFactory:: - createAndLaunch< parallelDevicePolicy<> >( rankOffset, - wellDofKey, - localRhs, - subRegion, - fluid, - wellControls, - time_n, - dt, - m_nonlinearSolverParameters.m_minNormalizer, - subRegionResidualNorm ); - - // step 2: reduction across meshBodies/regions/subRegions - - if( subRegionResidualNorm[0] > localResidualNorm[0] ) - { - localResidualNorm[0] = subRegionResidualNorm[0]; - } + localResidualNorm[0] = subRegionResidualNorm[0]; } - } ); - } ); + } + } + return localResidualNorm; + + +} + +real64 +SinglePhaseWell::calculateWellResidualNorm( real64 const & time_n, + real64 const & dt, + NonlinearSolverParameters const & nonlinearSolverParameters, + WellElementSubRegion const & subRegion, + DofManager const & dofManager, + arrayView1d< real64 const > const & localRhs ) +{ + GEOS_MARK_FUNCTION; + integer numNorm = 1; // mass balance + array1d< real64 > localResidualNorm; + array1d< real64 > localResidualNormalizer; + if( isThermal() ) + { + numNorm = 2; // mass balance and energy balance + } + localResidualNorm.resize( numNorm ); + localResidualNormalizer.resize( numNorm ); + + + //globalIndex const rankOffset = dofManager.rankOffset(); + string const wellDofKey = dofManager.getKey( wellElementDofName() ); + + if( isWellOpen() ) + { + localResidualNorm = calculateLocalWellResidualNorm( time_n, + dt, + nonlinearSolverParameters, + subRegion, + dofManager, + localRhs ); + } real64 resNorm=localResidualNorm[0]; if( isThermal() ) { @@ -993,8 +1154,8 @@ SinglePhaseWell::calculateResidualNorm( real64 const & time_n, GEOS_LOG_LEVEL_RANK_0_NLR( logInfo::ResidualNorm, GEOS_FMT( " ( R{} ) = ( {:4.2e} ) ( Renergy ) = ( {:4.2e} )", coupledSolverAttributePrefix(), globalResidualNorm[0], globalResidualNorm[1] )); - getConvergenceStats().setResidualValue( GEOS_FMT( "R{}", coupledSolverAttributePrefix()), globalResidualNorm[0] ); - getConvergenceStats().setResidualValue( "Renergy", globalResidualNorm[1] ); + //getConvergenceStats().setResidualValue( GEOS_FMT( "R{}", coupledSolverAttributePrefix()), globalResidualNorm[0] ); + //getConvergenceStats().setResidualValue( "Renergy", globalResidualNorm[1] ); } else { @@ -1002,16 +1163,15 @@ SinglePhaseWell::calculateResidualNorm( real64 const & time_n, GEOS_LOG_LEVEL_RANK_0_NLR( logInfo::ResidualNorm, GEOS_FMT( " ( R{} ) = ( {:4.2e} )", coupledSolverAttributePrefix(), resNorm )); - getConvergenceStats().setResidualValue( GEOS_FMT( "R{}", coupledSolverAttributePrefix()), resNorm ); + //getConvergenceStats().setResidualValue( GEOS_FMT( "R{}", coupledSolverAttributePrefix()), resNorm ); } return resNorm; } - -bool SinglePhaseWell::checkSystemSolution( DomainPartition & domain, - DofManager const & dofManager, - arrayView1d< real64 const > const & localSolution, - real64 const scalingFactor ) +bool SinglePhaseWell::checkWellSystemSolution( WellElementSubRegion & subRegion, + DofManager const & dofManager, + arrayView1d< real64 const > const & localSolution, + real64 const scalingFactor ) { GEOS_MARK_FUNCTION; @@ -1019,36 +1179,24 @@ bool SinglePhaseWell::checkSystemSolution( DomainPartition & domain, integer numNegativePressures = 0; real64 minPressure = 0.0; - forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &, - MeshLevel const & mesh, - string_array const & regionNames ) - { - ElementRegionManager const & elemManager = mesh.getElemManager(); + globalIndex const rankOffset = dofManager.rankOffset(); + // get the degree of freedom numbers on well elements + arrayView1d< globalIndex const > const & dofNumber = + subRegion.getReference< array1d< globalIndex > >( wellDofKey ); + arrayView1d< integer const > const & ghostRank = subRegion.ghostRank(); - elemManager.forElementSubRegions< WellElementSubRegion >( regionNames, - [&]( localIndex const, - WellElementSubRegion const & subRegion ) + // get a reference to the primary variables on well elements + arrayView1d< real64 const > const & pres = + subRegion.getField< well::pressure >(); + + auto const statistics = + singlePhaseBaseKernels::SolutionCheckKernel:: + launch< parallelDevicePolicy<> >( localSolution, rankOffset, dofNumber, ghostRank, pres, scalingFactor ); + + numNegativePressures += statistics.first; + minPressure = std::min( minPressure, statistics.second ); - { - globalIndex const rankOffset = dofManager.rankOffset(); - // get the degree of freedom numbers on well elements - arrayView1d< globalIndex const > const & dofNumber = - subRegion.getReference< array1d< globalIndex > >( wellDofKey ); - arrayView1d< integer const > const & ghostRank = subRegion.ghostRank(); - - // get a reference to the primary variables on well elements - arrayView1d< real64 const > const & pres = - subRegion.getField< well::pressure >(); - - auto const statistics = - singlePhaseBaseKernels::SolutionCheckKernel:: - launch< parallelDevicePolicy<> >( localSolution, rankOffset, dofNumber, ghostRank, pres, scalingFactor ); - - numNegativePressures += statistics.first; - minPressure = std::min( minPressure, statistics.second ); - } ); - } ); numNegativePressures = MpiWrapper::sum( numNegativePressures ); @@ -1062,14 +1210,19 @@ bool SinglePhaseWell::checkSystemSolution( DomainPartition & domain, return (m_allowNegativePressure || numNegativePressures == 0) ? 1 : 0; } + + void -SinglePhaseWell::applySystemSolution( DofManager const & dofManager, - arrayView1d< real64 const > const & localSolution, - real64 const scalingFactor, - real64 const dt, - DomainPartition & domain ) +SinglePhaseWell::applyWellSystemSolution( DofManager const & dofManager, + arrayView1d< real64 const > const & localSolution, + real64 const scalingFactor, + real64 const dt, + DomainPartition & domain, + MeshLevel & mesh, + WellElementSubRegion & subRegion ) { GEOS_UNUSED_VAR( dt ); + GEOS_UNUSED_VAR( subRegion ); DofManager::CompMask pressureMask( m_numDofPerWellElement, 0, 1 ); DofManager::CompMask connRateMask( m_numDofPerWellElement, 1, 2 ); dofManager.addVectorToField( localSolution, @@ -1096,213 +1249,195 @@ SinglePhaseWell::applySystemSolution( DofManager const & dofManager, } - forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &, - MeshLevel & mesh, - string_array const & regionNames ) + + FieldIdentifiers fieldsToBeSync; + if( isThermal() ) { - FieldIdentifiers fieldsToBeSync; - if( isThermal() ) - { - fieldsToBeSync.addElementFields( { well::pressure::key(), - well::connectionRate::key(), - well::temperature::key() }, - regionNames ); - } - else - { - fieldsToBeSync.addElementFields( { well::pressure::key(), - well::connectionRate::key() }, - regionNames ); - } - CommunicationTools::getInstance().synchronizeFields( fieldsToBeSync, - mesh, - domain.getNeighbors(), - true ); - } ); + fieldsToBeSync.addElementFields( { well::pressure::key(), + well::connectionRate::key(), + well::temperature::key() }, + getTargetRegionNames() ); + } + else + { + fieldsToBeSync.addElementFields( { well::pressure::key(), + well::connectionRate::key() }, + getTargetRegionNames() ); + } + CommunicationTools::getInstance().synchronizeFields( fieldsToBeSync, + mesh, + domain.getNeighbors(), + true ); + } -void SinglePhaseWell::resetStateToBeginningOfStep( DomainPartition & domain ) + +void SinglePhaseWell::resetStateToBeginningOfStep( ElementRegionManager const & elemManager, WellElementSubRegion & subRegion ) { - forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &, - MeshLevel & mesh, - string_array const & regionNames ) + + // get a reference to the primary variables on well elements + arrayView1d< real64 > const & wellElemPressure = + subRegion.getField< well::pressure >(); + arrayView1d< real64 const > const & wellElemPressure_n = + subRegion.getField< well::pressure_n >(); + wellElemPressure.setValues< parallelDevicePolicy<> >( wellElemPressure_n ); + + if( isThermal() ) { - ElementRegionManager & elemManager = mesh.getElemManager(); + arrayView1d< real64 > const & wellElemTemperature = + subRegion.getField< fields::well::temperature >(); + arrayView1d< real64 const > const & wellElemTemperature_n = + subRegion.getField< fields::well::temperature_n >(); + wellElemTemperature.setValues< parallelDevicePolicy<> >( wellElemTemperature_n ); + } + arrayView1d< real64 > const & connRate = + subRegion.getField< well::connectionRate >(); + arrayView1d< real64 const > const & connRate_n = + subRegion.getField< well::connectionRate_n >(); + connRate.setValues< parallelDevicePolicy<> >( connRate_n ); - elemManager.forElementSubRegions< WellElementSubRegion >( regionNames, - [&]( localIndex const, - WellElementSubRegion & subRegion ) - { - // get a reference to the primary variables on well elements - arrayView1d< real64 > const & wellElemPressure = - subRegion.getField< well::pressure >(); - arrayView1d< real64 const > const & wellElemPressure_n = - subRegion.getField< well::pressure_n >(); - wellElemPressure.setValues< parallelDevicePolicy<> >( wellElemPressure_n ); - - if( isThermal() ) - { - arrayView1d< real64 > const & wellElemTemperature = - subRegion.getField< fields::well::temperature >(); - arrayView1d< real64 const > const & wellElemTemperature_n = - subRegion.getField< fields::well::temperature_n >(); - wellElemTemperature.setValues< parallelDevicePolicy<> >( wellElemTemperature_n ); - } - arrayView1d< real64 > const & connRate = - subRegion.getField< well::connectionRate >(); - arrayView1d< real64 const > const & connRate_n = - subRegion.getField< well::connectionRate_n >(); - connRate.setValues< parallelDevicePolicy<> >( connRate_n ); + updateSubRegionState( elemManager, subRegion ); - updateSubRegionState( elemManager, subRegion ); - } ); - } ); } +void SinglePhaseWell::saveState( WellElementSubRegion & subRegion ) +{ + arrayView1d< real64 const > const wellElemPressure = subRegion.getField< well::pressure >(); + arrayView1d< real64 > const wellElemPressure_n = subRegion.getField< well::pressure_n >(); + wellElemPressure_n.setValues< parallelDevicePolicy<> >( wellElemPressure ); + + if( isThermal() ) + { + arrayView1d< real64 const > const wellElemTemperature = subRegion.getField< well::temperature >(); + arrayView1d< real64 > const wellElemTemperature_n = subRegion.getField< well::temperature_n >(); + wellElemTemperature_n.setValues< parallelDevicePolicy<> >( wellElemTemperature ); + } + arrayView1d< real64 const > const connRate = subRegion.getField< well::connectionRate >(); + arrayView1d< real64 > const connRate_n = subRegion.getField< well::connectionRate_n >(); + connRate_n.setValues< parallelDevicePolicy<> >( connRate ); -void SinglePhaseWell::implicitStepSetup( real64 const & time, + SingleFluidBase const & fluid = + getConstitutiveModel< SingleFluidBase >( subRegion, subRegion.getReference< string >( viewKeyStruct::fluidNamesString() ) ); + fluid.saveConvergedState(); +} + +void SinglePhaseWell::implicitStepSetup( real64 const & time_n, real64 const & dt, - DomainPartition & domain ) + ElementRegionManager & elemManager, + WellElementSubRegion & subRegion ) { - WellSolverBase::implicitStepSetup( time, dt, domain ); + GEOS_MARK_FUNCTION; + WellControls::implicitStepSetup( time_n, dt, elemManager, subRegion ); + arrayView1d< real64 const > const wellElemPressure = subRegion.getField< well::pressure >(); + arrayView1d< real64 > const wellElemPressure_n = subRegion.getField< well::pressure_n >(); + wellElemPressure_n.setValues< parallelDevicePolicy<> >( wellElemPressure ); - forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &, - MeshLevel & mesh, - string_array const & regionNames ) + if( isThermal() ) { + arrayView1d< real64 const > const wellElemTemperature = subRegion.getField< well::temperature >(); + arrayView1d< real64 > const wellElemTemperature_n = subRegion.getField< well::temperature_n >(); + wellElemTemperature_n.setValues< parallelDevicePolicy<> >( wellElemTemperature ); + } + arrayView1d< real64 const > const connRate = subRegion.getField< well::connectionRate >(); + arrayView1d< real64 > const connRate_n = subRegion.getField< well::connectionRate_n >(); + connRate_n.setValues< parallelDevicePolicy<> >( connRate ); - ElementRegionManager & elemManager = mesh.getElemManager(); - elemManager.forElementSubRegions< WellElementSubRegion >( regionNames, - [&]( localIndex const, - WellElementSubRegion & subRegion ) - { - arrayView1d< real64 const > const wellElemPressure = subRegion.getField< well::pressure >(); - arrayView1d< real64 > const wellElemPressure_n = subRegion.getField< well::pressure_n >(); - wellElemPressure_n.setValues< parallelDevicePolicy<> >( wellElemPressure ); + SingleFluidBase const & fluid = + getConstitutiveModel< SingleFluidBase >( subRegion, subRegion.getReference< string >( viewKeyStruct::fluidNamesString() ) ); + fluid.saveConvergedState(); - if( isThermal() ) - { - arrayView1d< real64 const > const wellElemTemperature = subRegion.getField< well::temperature >(); - arrayView1d< real64 > const wellElemTemperature_n = subRegion.getField< well::temperature_n >(); - wellElemTemperature_n.setValues< parallelDevicePolicy<> >( wellElemTemperature ); - } - arrayView1d< real64 const > const connRate = subRegion.getField< well::connectionRate >(); - arrayView1d< real64 > const connRate_n = subRegion.getField< well::connectionRate_n >(); - connRate_n.setValues< parallelDevicePolicy<> >( connRate ); + validateWellConstraints( time_n, dt, subRegion ); - SingleFluidBase const & fluid = - getConstitutiveModel< SingleFluidBase >( subRegion, subRegion.getReference< string >( viewKeyStruct::fluidNamesString() ) ); - fluid.saveConvergedState(); + updateSubRegionState( elemManager, subRegion ); - validateWellConstraints( time, dt, subRegion ); - - updateSubRegionState( elemManager, subRegion ); - } ); - } ); } void SinglePhaseWell::implicitStepComplete( real64 const & time_n, real64 const & dt, - DomainPartition & domain ) + WellElementSubRegion const & subRegion ) { - WellSolverBase::implicitStepComplete( time_n, dt, domain ); - - if( getLogLevel() > 0 ) - { - printRates( time_n, dt, domain ); - } + printRates( time_n, dt, subRegion ); } void SinglePhaseWell::printRates( real64 const & time_n, - real64 const & GEOS_UNUSED_PARAM( dt ), - DomainPartition & domain ) + real64 const & dt, + WellElementSubRegion const & subRegion ) { - forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &, - MeshLevel & mesh, - string_array const & regionNames ) - { - - ElementRegionManager & elemManager = mesh.getElemManager(); - - elemManager.forElementSubRegions< WellElementSubRegion >( regionNames, - [&]( localIndex const, - WellElementSubRegion & subRegion ) - { - - // the rank that owns the reference well element is responsible for the calculations below. - if( !subRegion.isLocallyOwned() ) - { - return; - } - localIndex const iwelemRef = subRegion.getTopWellElementIndex(); + GEOS_UNUSED_VAR( dt ); // FIX THIS tjb + // the rank that owns the reference well element is responsible for the calculations below. + if( !subRegion.isLocallyOwned() ) + { + return; + } - // subRegion data + localIndex const iwelemRef = subRegion.getTopWellElementIndex(); - arrayView1d< real64 const > const & connRate = - subRegion.getField< well::connectionRate >(); + // subRegion data - // control data + arrayView1d< real64 const > const & connRate = + subRegion.getField< well::connectionRate >(); - WellControls const & wellControls = getWellControls( subRegion ); - string const wellControlsName = wellControls.getName(); + // control data - // format: time,total_rate,total_vol_rate - std::ofstream outputFile; - if( m_writeCSV > 0 ) - { - outputFile.open( m_ratesOutputDir + "/" + wellControlsName + ".csv", std::ios_base::app ); - outputFile << time_n; - } - if( !wellControls.isWellOpen() ) - { - GEOS_LOG( GEOS_FMT( "{}: well is shut", wellControlsName ) ); - if( outputFile.is_open()) - { - // print all zeros in the rates file - outputFile << ",0.0,0.0,0.0" << std::endl; - outputFile.close(); - } - return; - } + string const wellControlsName = getName(); - integer const useSurfaceConditions = wellControls.useSurfaceConditions(); + // format: time,total_rate,total_vol_rate + std::ofstream outputFile; + if( m_writeCSV > 0 ) + { + outputFile.open( m_ratesOutputDir + "/" + wellControlsName + ".csv", std::ios_base::app ); + outputFile << time_n; + } - real64 const & currentBHP = - wellControls.getReference< real64 >( SinglePhaseWell::viewKeyStruct::currentBHPString() ); - real64 const & currentTotalVolRate = - wellControls.getReference< real64 >( SinglePhaseWell::viewKeyStruct::currentVolRateString() ); + if( !isWellOpen() ) + { + GEOS_LOG( GEOS_FMT( "{}: well is shut", wellControlsName ) ); + if( outputFile.is_open()) + { + // print all zeros in the rates file + outputFile << ",0.0,0.0,0.0" << std::endl; + outputFile.close(); + } + return; + } - // bring everything back to host, capture the scalars by reference - forAll< serialPolicy >( 1, [&useSurfaceConditions, - ¤tBHP, - connRate, - ¤tTotalVolRate, - &iwelemRef, - &wellControlsName, - &outputFile] ( localIndex const ) - { - string const conditionKey = useSurfaceConditions ? "surface" : "reservoir"; - string const unitKey = useSurfaceConditions ? "s" : "r"; - - real64 const currentTotalRate = connRate[iwelemRef]; - GEOS_LOG( GEOS_FMT( "{}: BHP (at the specified reference elevation): {} Pa", - wellControlsName, currentBHP ) ); - GEOS_LOG( GEOS_FMT( "{}: Total rate: {} kg/s; total {} volumetric rate: {} {}m3/s", - wellControlsName, currentTotalRate, conditionKey, currentTotalVolRate, unitKey ) ); - if( outputFile.is_open()) - { - outputFile << "," << currentBHP; - outputFile << "," << currentTotalRate << "," << currentTotalVolRate << std::endl; - outputFile.close(); - } - } ); - } ); + integer const useSurfaceCond = useSurfaceConditions(); + + real64 const & currentBHP = + getReference< real64 >( WellControls::viewKeyStruct::currentBHPString() ); + real64 const & currentTotalVolRate = + getReference< real64 >( WellControls::viewKeyStruct::currentVolRateString() ); + + // bring everything back to host, capture the scalars by reference + forAll< serialPolicy >( 1, [&useSurfaceCond, + ¤tBHP, + connRate, + ¤tTotalVolRate, + &iwelemRef, + &wellControlsName, + &outputFile] ( localIndex const ) + { + string const conditionKey = useSurfaceCond ? "surface" : "reservoir"; + string const unitKey = useSurfaceCond ? "s" : "r"; + + real64 const currentTotalRate = connRate[iwelemRef]; + GEOS_LOG( GEOS_FMT( "{}: BHP (at the specified reference elevation): {} Pa", + wellControlsName, currentBHP ) ); + GEOS_LOG( GEOS_FMT( "{}: Total rate: {} kg/s; total {} volumetric rate: {} {}m3/s", + wellControlsName, currentTotalRate, conditionKey, currentTotalVolRate, unitKey ) ); + if( outputFile.is_open()) + { + outputFile << "," << currentBHP; + outputFile << "," << currentTotalRate << "," << currentTotalVolRate << std::endl; + outputFile.close(); + } } ); } -REGISTER_CATALOG_ENTRY( PhysicsSolverBase, SinglePhaseWell, string const &, Group * const ) + + }// namespace geos diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/SinglePhaseWell.hpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/SinglePhaseWell.hpp index 8c07e223fb7..a77226289dc 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/wells/SinglePhaseWell.hpp +++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/SinglePhaseWell.hpp @@ -20,7 +20,7 @@ #ifndef GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_SINGLEPHASEWELL_HPP_ #define GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_SINGLEPHASEWELL_HPP_ -#include "WellSolverBase.hpp" +#include "physicsSolvers/fluidFlow/wells/WellControls.hpp" #include "constitutive/fluid/singlefluid/SingleFluidLayouts.hpp" @@ -43,7 +43,7 @@ class WellElementSubRegion; * * A single-phase well solver */ -class SinglePhaseWell : public WellSolverBase +class SinglePhaseWell : public WellControls { public: @@ -63,7 +63,7 @@ class SinglePhaseWell : public WellSolverBase SinglePhaseWell( SinglePhaseWell const & ) = delete; /// default move constructor - SinglePhaseWell( SinglePhaseWell && ) = default; + SinglePhaseWell( SinglePhaseWell && ) = delete; /// deleted assignment operator SinglePhaseWell & operator=( SinglePhaseWell const & ) = delete; @@ -76,57 +76,150 @@ class SinglePhaseWell : public WellSolverBase */ virtual ~SinglePhaseWell() override = default; + void registerWellDataOnMesh( WellElementSubRegion & subRegion ) override; + + /** + * @defgroup WellManager Interface Functions + * + * These functions provide the primary interface that is required for derived classes + * The "Well" versions apply to individual well subRegions, whereas the others apply to all wells + */ + /**@{*/ + /** + * * @brief Initialize well for the beginning of a simulation or restart + * @param domain the domain + * @param mesh the mesh level + * @param subRegion the well subRegion + * @param time_n the current time + */ + virtual void initializeWell( DomainPartition & domain, MeshLevel & mesh, WellElementSubRegion & subRegion, real64 const & time_n )override; + + virtual void initializeWellPostInitialConditionsPreSubGroups( WellElementSubRegion & subRegion )override; + + virtual bool isCompositional() const override { return false; } /** - * @brief name of the node manager in the object catalog - * @return string that contains the catalog name to generate a new NodeManager object through the object catalog. + * @copydoc WellControls::assembleWellAccumulationTerms() */ - static string catalogName() { return "SinglePhaseWell"; } + virtual void assembleWellAccumulationTerms( real64 const & time, + real64 const & dt, + WellElementSubRegion & subRegion, + DofManager const & dofManager, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) override; /** - * @copydoc PhysicsSolverBase::getCatalogName() + * @copydoc WellControls::assembleWellConstraintTerms() */ - string getCatalogName() const override { return catalogName(); } + virtual void assembleWellPressureRelations( real64 const & time_n, + real64 const & dt, + WellElementSubRegion const & subRegion, + DofManager const & dofManager, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) override; - virtual void registerDataOnMesh( Group & meshBodies ) override; + /** + * @copydoc WellControls::assembleWellConstraintTerms() + */ + virtual void assembleWellConstraintTerms( real64 const & time_n, + real64 const & dt, + WellElementSubRegion const & subRegion, + DofManager const & dofManager, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) override; + + /** + * @copydoc WellControls::computeWellPerforationRates() + */ + virtual void computeWellPerforationRates( real64 const & time_n, + real64 const & GEOS_UNUSED_PARAM( dt ), + ElementRegionManager & elemManager, + WellElementSubRegion & subRegion ) override; + + /** + * @copydoc WellControls::assembleFluxTerms() + */ + virtual void assembleWellFluxTerms( real64 const & time, + real64 const & dt, + WellElementSubRegion const & subRegion, + DofManager const & dofManager, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) override; + /**@}*/ /** * @defgroup Solver Interface Functions * * These functions provide the primary interface that is required for derived classes + * The "Well" versions apply to individual well subRegions, whereas the others apply to all wells */ /**@{*/ - virtual real64 - calculateResidualNorm( real64 const & time_n, - real64 const & dt, - DomainPartition const & domain, - DofManager const & dofManager, - arrayView1d< real64 const > const & localRhs ) override; + virtual array1d< real64 > + calculateLocalWellResidualNorm( real64 const & time_n, + real64 const & dt, + NonlinearSolverParameters const & nonlinearSolverParameters, + WellElementSubRegion const & subRegion, + DofManager const & dofManager, + arrayView1d< real64 const > const & localRhs )override; - virtual bool - checkSystemSolution( DomainPartition & domain, - DofManager const & dofManager, - arrayView1d< real64 const > const & localSolution, - real64 const scalingFactor ) override; - virtual void - applySystemSolution( DofManager const & dofManager, - arrayView1d< real64 const > const & localSolution, - real64 const scalingFactor, - real64 const dt, - DomainPartition & domain ) override; + virtual real64 + calculateWellResidualNorm( real64 const & time_n, + real64 const & dt, + NonlinearSolverParameters const & nonlinearSolverParameters, + WellElementSubRegion const & subRegion, + DofManager const & dofManager, + arrayView1d< real64 const > const & localRhs ) override; + + virtual real64 scalingForWellSystemSolution( WellElementSubRegion & subRegion, + DofManager const & dofManager, + arrayView1d< real64 const > const & localSolution ) override; + /** + * @copydoc WellControls::checkSystemSolution() + */ - virtual void - resetStateToBeginningOfStep( DomainPartition & domain ) override; + virtual bool + checkWellSystemSolution( WellElementSubRegion & subRegion, + DofManager const & dofManager, + arrayView1d< real64 const > const & localSolution, + real64 const scalingFactor ) override; + /** + * @copydoc WellControls::applyWellSystemSolution() + */ virtual void - implicitStepSetup( real64 const & time, - real64 const & dt, - DomainPartition & domain ) override; + applyWellSystemSolution( DofManager const & dofManager, + arrayView1d< real64 const > const & localSolution, + real64 const scalingFactor, + real64 const dt, + DomainPartition & domain, + MeshLevel & mesh, + WellElementSubRegion & subRegion ) override; + + virtual void applyWellBoundaryConditions ( real64 const GEOS_UNUSED_PARAM( time_n ), + real64 const GEOS_UNUSED_PARAM( dt ), + ElementRegionManager & GEOS_UNUSED_PARAM( elemManager ), + WellElementSubRegion & GEOS_UNUSED_PARAM( subRegion ), + DofManager const & GEOS_UNUSED_PARAM( dofManager ), + arrayView1d< real64 > const & GEOS_UNUSED_PARAM( localRhs ), + CRSMatrixView< real64, globalIndex const > const & GEOS_UNUSED_PARAM( localMatrix ) )override {}; + + virtual void resetStateToBeginningOfStep( ElementRegionManager const & elemManager, WellElementSubRegion & subRegion ) override; + + virtual void implicitStepSetup( real64 const & time_n, + real64 const & GEOS_UNUSED_PARAM( dt ), + ElementRegionManager & elemManager, + WellElementSubRegion & subRegion )override; virtual void implicitStepComplete( real64 const & time, real64 const & dt, - DomainPartition & domain ) override; + WellElementSubRegion const & subRegion ) override; + + virtual void printRates( real64 const & time_n, + real64 const & dt, + WellElementSubRegion const & subRegion ) override; + + virtual real64 updateSubRegionState( ElementRegionManager const & elemManager, WellElementSubRegion & subRegion ) override; /**@}*/ @@ -134,7 +227,7 @@ class SinglePhaseWell : public WellSolverBase virtual string resElementDofName() const override; - virtual localIndex numFluidComponents() const override { return 1; } + virtual localIndex numFluidComponents() const override { return 0; } virtual localIndex numFluidPhases() const override { return 1; } @@ -143,7 +236,7 @@ class SinglePhaseWell : public WellSolverBase * @param elemManager the well region manager * @param subRegion the well subregion containing all the primary and dependent fields */ - virtual void updateVolRateForConstraint( ElementRegionManager const & elemManager, WellElementSubRegion & subRegion ); + virtual void calculateReferenceElementRates( WellElementSubRegion & subRegion ); /** * @brief Recompute the BHP pressure that is used in the well constraints @@ -156,124 +249,53 @@ class SinglePhaseWell : public WellSolverBase * @param subRegion the well subRegion containing the well elements and their associated fields */ virtual void updateFluidModel( WellElementSubRegion & subRegion ) const; - /** - * @brief Recompute the perforation rates for all the wells - * @param domain the domain containing the mesh and fields + * @brief Update separator model state + * @param elemManager the element region manager + * @param subRegion the well subRegion containing the separator */ - virtual void computePerforationRates( real64 const & time_n, - real64 const & dt, DomainPartition & domain ) override; + void updateSeparator( ElementRegionManager const & elemManager, WellElementSubRegion & subRegion ); /** - * @brief Recompute all dependent quantities from primary variables (including constitutive models) - * @param elemManager the elemManager containing the well - * @param subRegion the well subRegion containing the well elements and their associated fields - */ - virtual real64 updateSubRegionState( ElementRegionManager const & elemManager, WellElementSubRegion & subRegion ) override; + * @brief Recompute all dependent quantities from primary variables (including constitutive + * models) - /** - * @brief function to assemble the linear system matrix and rhs - * @param time the time at the beginning of the step - * @param dt the desired timestep - * @param domain the domain partition - * @param dofManager degree-of-freedom manager associated with the linear system - * @param matrix the system matrix - * @param rhs the system right-hand side vector + * @param + * @param subRegion the well subRegion containing the well elements and their associated */ - virtual void assembleSystem( real64 const time, - real64 const dt, - DomainPartition & domain, - DofManager const & dofManager, - CRSMatrixView< real64, globalIndex const > const & localMatrix, - arrayView1d< real64 > const & localRhs ) override; + virtual real64 updateWellState( ElementRegionManager const & elemManager, WellElementSubRegion & subRegion ) override; - /** - * @brief assembles the flux terms for all connections between well elements - * @param time_n previous time value - * @param dt time step - * @param domain the physical domain object - * @param dofManager degree-of-freedom manager associated with the linear system - * @param matrix the system matrix - * @param rhs the system right-hand side vector - */ - virtual void assembleFluxTerms( real64 const & time_n, - real64 const & dt, - DomainPartition & domain, - DofManager const & dofManager, - CRSMatrixView< real64, globalIndex const > const & localMatrix, - arrayView1d< real64 > const & localRhs ) override; /** - * @brief assembles the accumulation term for all the well elements - * @param domain the physical domain object - * @param dofManager degree-of-freedom manager associated with the linear system - * @param matrix the system matrix - * @param rhs the system right-hand side vector - */ - virtual void assembleAccumulationTerms( real64 const & time_n, - real64 const & dt, DomainPartition & domain, - DofManager const & dofManager, - CRSMatrixView< real64, globalIndex const > const & localMatrix, - arrayView1d< real64 > const & localRhs ) override; - - /** - * @brief assembles the volume balance terms for all well elements - * @param domain the physical domain object - * @param dofManager degree-of-freedom manager associated with the linear system - * @param matrix the system matrix - * @param rhs the system right-hand side vector - */ - void assembleVolumeBalanceTerms( DomainPartition const & domain, - DofManager const & dofManager, - CRSMatrixView< real64, globalIndex const > const & localMatrix, - arrayView1d< real64 > const & localRhs ); - - /** - * @brief assembles the pressure relations at all connections between well elements except at the well head - * @param time_n time at the beginning of the time step - * @param dt the time step size - * @param domain the physical domain object - * @param dofManager degree-of-freedom manager associated with the linear system - * @param matrix the system matrix - * @param rhs the system right-hand side vector + * @brief Recompute all dependent quantities from primary variables (including constitutive + * models) + * @param elemManager the element region manager + * @param subRegion the well subRegion containing the well elements and their associated fields */ - virtual void assemblePressureRelations( real64 const & time_n, - real64 const & dt, - DomainPartition const & domain, - DofManager const & dofManager, - CRSMatrixView< real64, globalIndex const > const & localMatrix, - arrayView1d< real64 > const & localRhs ) override; - /* * @brief apply a special treatment to the wells that are shut - * @param time_n the time at the previous converged time step - * @param domain the physical domain object + * @param dofManager degree-of-freedom manager associated with the linear system * @param matrix the system matrix * @param rhs the system right-hand side vector */ - void shutDownWell( real64 const time_n, - DomainPartition const & domain, + void shutDownWell( WellElementSubRegion & subRegion, DofManager const & dofManager, CRSMatrixView< real64, globalIndex const > const & localMatrix, arrayView1d< real64 > const & localRhs ); - struct viewKeyStruct : WellSolverBase::viewKeyStruct + struct viewKeyStruct : WellControls::viewKeyStruct { - static constexpr char const * dofFieldString() { return "singlePhaseWellVars"; } + static constexpr char const * dofFieldString() { return "wellVars"; } - // control data (not registered on the mesh) - static constexpr char const * currentBHPString() { return "currentBHP"; } - static constexpr char const * dCurrentBHPString() { return "dCurrentBHP"; } - static constexpr char const * currentVolRateString() { return "currentVolumetricRate"; } - static constexpr char const * dCurrentVolRateString() { return "dCurrentVolRate"; } }; protected: - void printRates( real64 const & time_n, - real64 const & dt, - DomainPartition & domain ) override; + virtual void initializePostInitialConditionsPreSubGroups() override; + + void saveState( WellElementSubRegion & subRegion ); + virtual void postRestartInitialization( )override; /// flag if negative pressure is allowed integer m_allowNegativePressure; @@ -281,11 +303,6 @@ class SinglePhaseWell : public WellSolverBase virtual void setConstitutiveNames( ElementSubRegionBase & subRegion ) const override; - /** - * @brief Initialize all the primary and secondary variables in all the wells - * @param domain the domain containing the well manager to access individual wells - */ - void initializeWells( DomainPartition & domain, real64 const & time_n ) override; /** * @brief Make sure that the well constraints are compatible @@ -295,7 +312,15 @@ class SinglePhaseWell : public WellSolverBase */ virtual void validateWellConstraints( real64 const & time_n, real64 const & dt, - WellElementSubRegion const & subRegion ) override; + WellElementSubRegion const & subRegion + ) override; + + + + /** + * @brief Create well separator + */ + virtual void createSeparator( WellElementSubRegion & subRegion ) override; }; diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/SinglePhaseWellFields.hpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/SinglePhaseWellFields.hpp index 2cd21f32866..9e0d5472826 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/wells/SinglePhaseWellFields.hpp +++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/SinglePhaseWellFields.hpp @@ -33,21 +33,6 @@ namespace fields namespace well { -DECLARE_FIELD( connectionRate, - "connectionRate", - array1d< real64 >, - 0, - LEVEL_0, - WRITE_AND_READ, - "Connection rate" ); - -DECLARE_FIELD( connectionRate_n, - "connectionRate_n", - array1d< real64 >, - 0, - NOPLOT, - WRITE_AND_READ, - "Connection rate at the previous converged time step" ); DECLARE_FIELD( density_n, "density_n", diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/WellBHPConstraints.cpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellBHPConstraints.cpp new file mode 100644 index 00000000000..70f6e79b86a --- /dev/null +++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellBHPConstraints.cpp @@ -0,0 +1,116 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * SPDX-License-Identifier: LGPL-2.1-only + * + * Copyright (c) 2016-2024 Lawrence Livermore National Security LLC + * Copyright (c) 2018-2024 TotalEnergies + * Copyright (c) 2018-2024 The Board of Trustees of the Leland Stanford Junior University + * Copyright (c) 2023-2024 Chevron + * Copyright (c) 2019- GEOS/GEOSX Contributors + * All rights reserved + * + * See top level LICENSE, COPYRIGHT, CONTRIBUTORS, NOTICE, and ACKNOWLEDGEMENTS files for details. + * ------------------------------------------------------------------------------------------------------------ + */ + +/* + * @file WellBHPConstraints.cpp + */ + +#include "LogLevelsInfo.hpp" +#include "WellBHPConstraints.hpp" +#include "WellConstants.hpp" +#include "dataRepository/InputFlags.hpp" +#include "functions/FunctionManager.hpp" + + +namespace geos +{ + +using namespace dataRepository; + +BHPConstraint::BHPConstraint( string const & name, Group * const parent ) + : WellConstraintBase( name, parent ), + m_refElevation( 0.0 ), + m_refGravCoef( 0.0 ) +{ + setInputFlags( InputFlags::OPTIONAL_NONUNIQUE ); + + registerWrapper( viewKeyStruct::targetBHPString(), &m_constraintValue ). + setDefaultValue( 0.0 ). + setInputFlag( InputFlags::OPTIONAL ). + setRestartFlags( RestartFlags::WRITE_AND_READ ). + setDescription( "Minimun bottom-hole production pressure [Pa]" ); + + registerWrapper( viewKeyStruct::refElevString(), &m_refElevation ). + setDefaultValue( -1 ). + setInputFlag( InputFlags::REQUIRED ). + setDescription( "Reference elevation where BHP control is enforced [m]" ); + +} + + +BHPConstraint::~BHPConstraint() +{} + +void BHPConstraint::postInputInitialization() +{ + + WellConstraintBase::postInputInitialization(); + +} + +MinimumBHPConstraint::MinimumBHPConstraint( string const & name, Group * const parent ) + : BHPConstraint( name, parent ) +{ + setInputFlags( InputFlags::OPTIONAL_NONUNIQUE ); + + registerWrapper( viewKeyStruct::targetBHPString(), &m_constraintValue ). + setDefaultValue( 0.0 ). + setInputFlag( InputFlags::OPTIONAL ). + setRestartFlags( RestartFlags::WRITE_AND_READ ). + setDescription( "Minimun bottom-hole production pressure [Pa]" ); +} + + +MinimumBHPConstraint::~MinimumBHPConstraint() +{} + +void MinimumBHPConstraint::postInputInitialization() +{ + + BHPConstraint::postInputInitialization(); + +} + +bool MinimumBHPConstraint::checkViolation( WellConstraintBase const & currentConstraint, real64 const & currentTime ) const +{ + return currentConstraint.bottomHolePressure() < getConstraintValue( currentTime ); +} + +MaximumBHPConstraint::MaximumBHPConstraint( string const & name, Group * const parent ) + : BHPConstraint( name, parent ) +{ + setInputFlags( InputFlags::OPTIONAL_NONUNIQUE ); + +} + + +MaximumBHPConstraint::~MaximumBHPConstraint() +{} + +void MaximumBHPConstraint::postInputInitialization() +{ + // Validate value and table options + BHPConstraint::postInputInitialization(); + +} +bool MaximumBHPConstraint::checkViolation( WellConstraintBase const & currentConstraint, real64 const & currentTime ) const +{ + return currentConstraint.bottomHolePressure() > getConstraintValue( currentTime ); +} + +REGISTER_CATALOG_ENTRY( WellConstraintBase, MinimumBHPConstraint, string const &, Group * const ) +REGISTER_CATALOG_ENTRY( WellConstraintBase, MaximumBHPConstraint, string const &, Group * const ) + +} //namespace geos diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/WellBHPConstraints.hpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellBHPConstraints.hpp new file mode 100644 index 00000000000..3002d303e39 --- /dev/null +++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellBHPConstraints.hpp @@ -0,0 +1,334 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * SPDX-License-Identifier: LGPL-2.1-only + * + * Copyright (c) 2016-2024 Lawrence Livermore National Security LLC + * Copyright (c) 2018-2024 TotalEnergies + * Copyright (c) 2018-2024 The Board of Trustees of the Leland Stanford Junior University + * Copyright (c) 2023-2024 Chevron + * Copyright (c) 2019- GEOS/GEOSX Contributors + * All rights reserved + * + * See top level LICENSE, COPYRIGHT, CONTRIBUTORS, NOTICE, and ACKNOWLEDGEMENTS files for details. + * ------------------------------------------------------------------------------------------------------------ + */ + +/* + * @file WellBHPConstraints.hpp + */ + + +#ifndef GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_WELLBHPCONSTRAINTS_HPP +#define GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_WELLBHPCONSTRAINTS_HPP + +#include "common/format/EnumStrings.hpp" +#include "dataRepository/Group.hpp" +#include "functions/TableFunction.hpp" +#include "WellConstraintsBase.hpp" +namespace geos +{ + +/** + * @class BHPConstraint + * @brief This class describes a minimum pressure constraint used to control a injection well. + */ +class BHPConstraint : public WellConstraintBase +{ +public: + + /** + * @name Constructor / Destructor + */ + ///@{ + + /** + * @brief Constructor for WellControls Objects. + * @param[in] name the name of this instantiation of WellControls in the repository + * @param[in] parent the parent group of this instantiation of WellControls + */ + explicit BHPConstraint( string const & name, dataRepository::Group * const parent ); + + + /** + * @brief Default destructor. + */ + ~BHPConstraint() override; + + /** + * @brief Deleted default constructor. + */ + BHPConstraint() = delete; + + /** + * @brief Deleted copy constructor. + */ + BHPConstraint( BHPConstraint const & ) = delete; + + /** + * @brief Deleted move constructor. + */ + BHPConstraint( BHPConstraint && ) = delete; + + /** + * @brief Deleted assignment operator. + * @return a reference to a constraint object + */ + BHPConstraint & operator=( BHPConstraint const & ) = delete; + + /** + * @brief Deleted move operator. + * @return a reference to a constraint object + */ + BHPConstraint & operator=( BHPConstraint && ) = delete; + + ///@} + + /** + * @name Getters / Setters + */ + ///@{ + + // Temp interface - tjb + virtual ConstraintTypeId getControl() const override { return ConstraintTypeId::BHP; }; + + ///@} + /** + * @brief Struct to serve as a container for variable strings and keys. + * @struct viewKeyStruct + */ + struct viewKeyStruct + { + /// String key for the well target BHP + static constexpr char const * targetBHPString() { return "targetBHP"; } + /// String key for the well reference elevation (for BHP control) + static constexpr char const * refElevString() { return "referenceElevation"; } + } + viewKeysWellBHPConstraint; + + //virtual bool checkViolation( WellConstraintBase const & currentConstraint, real64 const & currentTime ) const override; + + /** + * @brief Getter for the reference elevation where the BHP control is enforced + * @return the reference elevation + */ + real64 getReferenceElevation() const { return m_refElevation; } + + /** + * @brief Set the reference elevation where the BHP control is enforced + * @return the reference elevation + */ + void setReferenceElevation( real64 const & refElevation ) { m_refElevation=refElevation; } + + /** + * @brief Getter for the reference gravity coefficient + * @return the reference gravity coefficient + */ + real64 getReferenceGravityCoef() const { return m_refGravCoef; } + + /** + * @brief Setter for the reference gravity + */ + void setReferenceGravityCoef( real64 const & refGravCoef ) { m_refGravCoef = refGravCoef; } + +protected: + + virtual void postInputInitialization() override; + + /// Reference elevation + real64 m_refElevation; + + /// Gravity coefficient of the reference elevation + real64 m_refGravCoef; + +}; + +/** + * @class MinimumBHPConstraint + * @brief This class describes a minimum pressure constraint used to control a injection well. + */ +class MinimumBHPConstraint : public BHPConstraint +{ +public: + + /** + * @name Constructor / Destructor + */ + ///@{ + + /** + * @brief Constructor for WellControls Objects. + * @param[in] name the name of this instantiation of WellControls in the repository + * @param[in] parent the parent group of this instantiation of WellControls + */ + explicit MinimumBHPConstraint( string const & name, dataRepository::Group * const parent ); + + + /** + * @brief Default destructor. + */ + ~MinimumBHPConstraint() override; + + /** + * @brief Deleted default constructor. + */ + MinimumBHPConstraint() = delete; + + /** + * @brief Deleted copy constructor. + */ + MinimumBHPConstraint( MinimumBHPConstraint const & ) = delete; + + /** + * @brief Deleted move constructor. + */ + MinimumBHPConstraint( MinimumBHPConstraint && ) = delete; + + /** + * @brief Deleted assignment operator. + * @return a reference to a constraint object + */ + MinimumBHPConstraint & operator=( MinimumBHPConstraint const & ) = delete; + + /** + * @brief Deleted move operator. + * @return a reference to a constraint object + */ + MinimumBHPConstraint & operator=( MinimumBHPConstraint && ) = delete; + + ///@} + + /** + * @brief name of the node manager in the object catalog + * @return string that contains the catalog name to generate a new Constraint object through the object catalog. + */ + static string catalogName() + { + return "MinimumBHPConstraint"; + } + virtual string getCatalogName() const override { return catalogName(); } + /** + * @name Getters / Setters + */ + ///@{ + + + /** + * @brief Struct to serve as a container for variable strings and keys. + * @struct viewKeyStruct + */ + struct viewKeyStruct + { + /// String key for the well target BHP + static constexpr char const * targetBHPString() { return "targetBHP"; } + } + viewKeysWellBHPConstraint; + + virtual bool checkViolation( WellConstraintBase const & currentConstraint, real64 const & currentTime ) const override; + +protected: + + virtual void postInputInitialization() override; + + +}; + +/** + * @class WellMinimumBHPConstraint + * @brief This class describes a maximum pressure constraint used to control a injection well. + */ +class MaximumBHPConstraint : public BHPConstraint +{ +public: + + /** + * @name Constructor / Destructor + */ + ///@{ + + /** + * @brief Constructor for WellControls Objects. + * @param[in] name the name of this instantiation of WellControls in the repository + * @param[in] parent the parent group of this instantiation of WellControls + */ + explicit MaximumBHPConstraint( string const & name, dataRepository::Group * const parent ); + + + /** + * @brief Default destructor. + */ + ~MaximumBHPConstraint() override; + + /** + * @brief Deleted default constructor. + */ + MaximumBHPConstraint() = delete; + + /** + * @brief Deleted copy constructor. + */ + MaximumBHPConstraint( MaximumBHPConstraint const & ) = delete; + + /** + * @brief Deleted move constructor. + */ + MaximumBHPConstraint( MaximumBHPConstraint && ) = delete; + + /** + * @brief Deleted assignment operator. + * @return a reference to a constraint object + */ + MaximumBHPConstraint & operator=( MaximumBHPConstraint const & ) = delete; + + /** + * @brief Deleted move operator. + * @return a reference to a constraint object + */ + MaximumBHPConstraint & operator=( MaximumBHPConstraint && ) = delete; + + ///@} + + /** + * @brief name of the node manager in the object catalog + * @return string that contains the catalog name to generate a new Constraint object through the object catalog. + */ + static string catalogName() + { + return "MaximumBHPConstraint"; + } + virtual string getCatalogName() const override { return catalogName(); } + /** + * @name Getters / Setters + */ + ///@{ + // Temp interface - tjb + virtual ConstraintTypeId getControl() const override { return ConstraintTypeId::BHP; }; + + + ///@} + /** + * @brief Struct to serve as a container for variable strings and keys. + * @struct viewKeyStruct + */ + struct viewKeyStruct + { + /// String key for the well target BHP + static constexpr char const * targetBHPString() { return "targetBHP"; } + } + viewKeysWellBHPConstraint; + + virtual bool checkViolation( WellConstraintBase const & currentConstraint, real64 const & currentTime ) const override; +protected: + + virtual void postInputInitialization() override; + + + +private: + + +}; + + +} //namespace geos + +#endif //GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_WELLBHPCONSTRAINTS_HPP diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/WellConstants.hpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellConstants.hpp index 58b8b421c3a..83eadb7c2ff 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/wells/WellConstants.hpp +++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellConstants.hpp @@ -36,6 +36,11 @@ struct WellConstants static constexpr real64 defaultInjectorBHP = 1.01325e8; }; +enum class WellTypes : integer +{ + PRODUCER, /**< A production well */ + INJECTOR /**< An injection well */ +}; } //namespace geos #endif //GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_WELLCONSTANTS_HPP diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/WellConstraintsBase.cpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellConstraintsBase.cpp new file mode 100644 index 00000000000..5ea4fc74d42 --- /dev/null +++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellConstraintsBase.cpp @@ -0,0 +1,141 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * SPDX-License-Identifier: LGPL-2.1-only + * + * Copyright (c) 2016-2024 Lawrence Livermore National Security LLC + * Copyright (c) 2018-2024 TotalEnergies + * Copyright (c) 2018-2024 The Board of Trustees of the Leland Stanford Junior University + * Copyright (c) 2023-2024 Chevron + * Copyright (c) 2019- GEOS/GEOSX Contributors + * All rights reserved + * + * See top level LICENSE, COPYRIGHT, CONTRIBUTORS, NOTICE, and ACKNOWLEDGEMENTS files for details. + * ------------------------------------------------------------------------------------------------------------ + */ + +/* + * @file WellConstraintBase.cpp + */ + +#include "LogLevelsInfo.hpp" +#include "WellConstraintsBase.hpp" +#include "WellConstants.hpp" +#include "dataRepository/InputFlags.hpp" +#include "functions/FunctionManager.hpp" + + +namespace geos +{ + +using namespace dataRepository; + +// Provide a properly-typed static catalog for WellConstraintBase so that +// CatalogInterface< WellConstraintBase, ... >::getCatalog() can return +// a catalog of CatalogInterface objects instead of +// inheriting Group::getCatalog() which returns a catalog of Group entries. +WellConstraintBase::CatalogInterface::CatalogType & WellConstraintBase::getCatalog() +{ + static WellConstraintBase::CatalogInterface::CatalogType catalog; + return catalog; +} + +namespace +{ + + +#if 0 +/// Utility function to create a one-value table internally when not provided by the user +TableFunction * createConstraintScheduleTable( string const & tableName, + real64 const & constantValue ) +{ + array1d< array1d< real64 > > timeCoord; + timeCoord.resize( 1 ); + timeCoord[0].emplace_back( 0 ); + array1d< real64 > constantValueArray; + constantValueArray.emplace_back( constantValue ); + + FunctionManager & functionManager = FunctionManager::getInstance(); + TableFunction * table = dynamicCast< TableFunction * >( functionManager.createChild( TableFunction::catalogName(), tableName )); + table->setTableCoordinates( timeCoord, { units::Time } ); + table->setTableValues( constantValueArray ); + table->setInterpolationMethod( TableFunction::InterpolationType::Lower ); + return table; +} +#endif + + +} + +WellConstraintBase::WellConstraintBase( string const & name, Group * const parent ) + : Group( name, parent ), + m_isConstraintActive( 1 ), + m_useScheduleTable( false ), + m_constraintValue( 0 ), + m_constraintScheduleTable( nullptr ), + m_rateSign( 1.0 ) // Default to positive rate sign for injection, set to -1.0 for production wells + +{ + setInputFlags( InputFlags::OPTIONAL_NONUNIQUE ); + + registerWrapper( viewKeyStruct::constraintScheduleTableNameString(), &m_constraintScheduleTableName ). + setRTTypeName( rtTypes::CustomTypes::groupNameRef ). + setInputFlag( InputFlags::OPTIONAL ). + setDescription( "Name of the well constraint schedule table when the constraint value is a time dependent function. \n" ); + + registerWrapper( viewKeyStruct::constraintActiveString(), &m_isConstraintActive ). + setDefaultValue( 1 ). + setInputFlag( InputFlags::OPTIONAL ). + setDescription( "Flag to enable constraint. Currently only supported for injectors: \n" + " - If the flag is set to 1, constraint included in boundary condition selection. \n" + " - If the flag is set to 0, constraint excluded from boundary condition selection." ); + +} + + +WellConstraintBase::~WellConstraintBase() +{} + + +void WellConstraintBase::postInputInitialization() +{ + + GEOS_THROW_IF( ((m_constraintValue > 0.0 && !m_constraintScheduleTableName.empty())|| (!(m_constraintValue > 0.0) && m_constraintScheduleTableName.empty())), + this->getDataContext() << ": You have provided redundant information for well constraint value ." << + " A constraint value and table of constraint values cannot be specified together", + InputError ); + + // Create time-dependent constraint table + if( !m_constraintScheduleTableName.empty() ) + { + FunctionManager & functionManager = FunctionManager::getInstance(); + m_constraintScheduleTable = &(functionManager.getGroup< TableFunction const >( m_constraintScheduleTableName )); + + GEOS_THROW_IF( m_constraintScheduleTable->getInterpolationMethod() != TableFunction::InterpolationType::Lower, + this->getName() << " " << this->getDataContext() << ": The interpolation method for the schedule table " + << m_constraintScheduleTable->getName() << " should be TableFunction::InterpolationType::Lower", + InputError ); + } + +} + +void WellConstraintBase::setNextDtFromTables( real64 const currentTime, real64 & nextDt ) +{ + setNextDtFromTable( m_constraintScheduleTable, currentTime, nextDt ); +} + +void WellConstraintBase::setNextDtFromTable( TableFunction const * table, real64 const currentTime, real64 & nextDt ) +{ + if( table ) + { + // small epsilon to make sure we land on the other side of table interval and pick up the right rate + real64 const eps = 1e-6; + real64 const dtLimit = (table->getCoord( ¤tTime, 0, TableFunction::InterpolationType::Upper ) - currentTime) * ( 1.0 + eps ); + if( dtLimit > eps && dtLimit < nextDt ) + { + nextDt = dtLimit; + } + } +} + + +} //namespace geos diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/WellConstraintsBase.hpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellConstraintsBase.hpp new file mode 100644 index 00000000000..8668c44d3a8 --- /dev/null +++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellConstraintsBase.hpp @@ -0,0 +1,291 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * SPDX-License-Identifier: LGPL-2.1-only + * + * Copyright (c) 2016-2024 Lawrence Livermore National Security LLC + * Copyright (c) 2018-2024 TotalEnergies + * Copyright (c) 2018-2024 The Board of Trustees of the Leland Stanford Junior University + * Copyright (c) 2023-2024 Chevron + * Copyright (c) 2019- GEOS/GEOSX Contributors + * All rights reserved + * + * See top level LICENSE, COPYRIGHT, CONTRIBUTORS, NOTICE, and ACKNOWLEDGEMENTS files for details. + * ------------------------------------------------------------------------------------------------------------ + */ + +/* + * @file WellConstraintBase.hpp + */ + + +#ifndef GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_WELLCONSTRAINTBASE_HPP +#define GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_WELLCONSTRAINTBASE_HPP + +#include "common/format/EnumStrings.hpp" + +#include "functions/TableFunction.hpp" +#include "dataRepository/Group.hpp" +namespace geos +{ + + + +enum class ConstraintTypeId : integer +{ + BHP, /**< The well operates at a specified bottom hole pressure (BHP) */ + PHASEVOLRATE, /**< The well operates at a specified phase volumetric flow rate */ + TOTALVOLRATE, /**< The well operates at a specified total volumetric flow rate */ + MASSRATE, /**; + + /// Get the singleton catalog for WellConstraintBase + static CatalogInterface::CatalogType & getCatalog(); + + /** + * @brief function to return the catalog name of the derived class + * @return a string that contains the catalog name of the derived class + */ + virtual string getCatalogName() const = 0; + /** + * @name Constructor / Destructor + */ + ///@{ + + /** + * @brief Constructor for WellControls Objects. + * @param[in] name the name of this instantiation of WellControls in the repository + * @param[in] parent the parent group of this instantiation of WellControls + */ + explicit WellConstraintBase( string const & name, dataRepository::Group * const parent ); + + + /** + * @brief Default destructor. + */ + ~WellConstraintBase() override; + + /** + * @brief Deleted default constructor. + */ + WellConstraintBase() = delete; + + /** + * @brief Deleted copy constructor. + */ + WellConstraintBase( WellConstraintBase const & ) = delete; + + /** + * @brief Deleted move constructor. + */ + WellConstraintBase( WellConstraintBase && ) = delete; + + /** + * @brief Deleted assignment operator. + * @return a reference to a constraint object + */ + WellConstraintBase & operator=( WellConstraintBase const & ) = delete; + + /** + * @brief Deleted move operator. + * @return a reference to a constraint object + */ + WellConstraintBase & operator=( WellConstraintBase && ) = delete; + + ///@} + + + /** + * @name Getters / Setters + */ + ///@{ + + // Temp interface - tjb + virtual ConstraintTypeId getControl() const = 0; + + /** + * @brief Defines whether the constraint should be evaluated or not + * @brief Some workflows require the well model to define a constraint + * @brief of similar type to user defined constraints. For example, + * @brief rate constraints to evaluated WHP constraints. + * @return true if the constraint is active, false otherwise + */ + bool isConstraintActive( ) const { return m_isConstraintActive; } + + /** + * @brief Sets constraint active status + * @param[in] constraintActive true if the constraint is active, false otherwise + */ + bool setConstraintActive( bool const & constraintActive ) { return m_isConstraintActive=constraintActive; } + + /** + * @brief Sets constraint value + * @param[in] constraint value + */ + void setConstraintValue( real64 const & constraintValue ) + { + m_constraintValue = constraintValue; + } + + /** + * @brief Get the target bottom hole pressure value. + * @return a value for the target bottom hole pressure + */ + real64 getConstraintValue( real64 const & currentTime ) const + { + if( m_constraintScheduleTableName.empty() ) + { + return m_rateSign*m_constraintValue; + } + + return m_rateSign*m_constraintScheduleTable->evaluate( ¤tTime ); + } + + ///@} + + /** + * @brief Struct to serve as a container for variable strings and keys. + * @struct viewKeyStruct + */ + struct viewKeyStruct + { + /// string key for schedule table name + static constexpr char const * constraintScheduleTableNameString() { return "constraintScheduleTableName"; } + + /// String key for the well constraint value + static constexpr char const * constraintValueString() { return "constraintValue"; } + + /// String key for the well constraint active flag + static constexpr char const * constraintActiveString() { return "constraintActive"; } + + } + /// ViewKey struct for the WellControls class + viewKeysWellConstraint; + + // Quantities computed from well constraint solve with this boundary condition + // This needs to be somewhere else tjb + void setBHP( real64 bhp ){ m_BHP=bhp;}; + void setPhaseVolumeRates( array1d< real64 > const & phaseVolumeRates ) { m_phaseVolumeRates = phaseVolumeRates; }; + void setTotalVolumeRate( real64 totalVolumeRate ){ m_totalVolumeRate = totalVolumeRate; }; + void setMassRate( real64 massRate ){ m_massRate = massRate; }; + + /** + * @brief Getter for the bottom hole pressure + * @return bottom hole pressure + */ + real64 bottomHolePressure() const { return m_BHP; } + + /** + * @brief Getter for the phase volume rates + * @return an arrayView1d storing the phase volume rates + */ + arrayView1d< real64 const > phaseVolumeRates() const { return m_phaseVolumeRates; } + + /** + * @brief Getter for the total volume rate + * @return mass rate + */ + real64 totalVolumeRate() const { return m_totalVolumeRate; } + + /** + * @brief Getter for the liquid rate + * @return liquid rate + */ + real64 liquidRate() const { return m_liquidRate; } + + /** + * @brief Getter for the mass rate + * @return mass rate + */ + real64 massRate() const { return m_massRate; } + + // endof This needs to be somewhere else tjb + /** + * @brief Check if this constraint is violated + * @return true if limiting constraint, false otherwise + */ + virtual bool checkViolation( WellConstraintBase const & currentConstraint, real64 const & currentTime ) const = 0; + +protected: + + virtual void postInputInitialization() override; + + /** + * @brief set next time step based on tables intervals + * @param[in] currentTime the current time + * @param[inout] nextDt the time step + */ + void setNextDtFromTables( real64 const currentTime, real64 & nextDt ); + + +protected: + + /// Constraint status + integer m_isConstraintActive; + + /// Flag to indicate whether a schedule table should be generated for constraint value; + bool m_useScheduleTable; + + /// Constraint value + real64 m_constraintValue; + + void setNextDtFromTable( TableFunction const * table, real64 const currentTime, real64 & nextDt ); + + /// Constraint schedule table name + string m_constraintScheduleTableName; + + /// Constraint values versus time + TableFunction const * m_constraintScheduleTable; + + // Quantities computed from well constraint solve with this boundary condition + + // botton hole pressure + real64 m_BHP; + + // phase rates + array1d< real64 > m_phaseVolumeRates; + + // liquid rate + real64 m_liquidRate; + + // total volume rate + real64 m_totalVolumeRate; + + // mass rate + real64 m_massRate; + + /// Rate sign. +1 for injector, -1 for producer + real64 m_rateSign; +}; + + +} //namespace geos + +#endif //GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_WELLCONSTRAINTBASE_HPP diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/WellControls.cpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellControls.cpp index d0d20deb642..e634f36d5cd 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/wells/WellControls.cpp +++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellControls.cpp @@ -22,8 +22,11 @@ #include "WellConstants.hpp" #include "dataRepository/InputFlags.hpp" #include "functions/FunctionManager.hpp" +#include "mesh/PerforationFields.hpp" +#include "fileIO/Outputs/OutputBase.hpp" +#include "physicsSolvers/fluidFlow/wells/WellFields.hpp" - +#include "functions/FunctionManager.hpp" namespace geos { @@ -32,26 +35,32 @@ using namespace dataRepository; WellControls::WellControls( string const & name, Group * const parent ) : Group( name, parent ), m_type( Type::PRODUCER ), - m_refElevation( 0.0 ), - m_refGravCoef( 0.0 ), + m_numPhases( 0 ), + m_numComponents( 0 ), + m_numDofPerWellElement( 0 ), + m_numDofPerResElement( 0 ), + m_isThermal( 0 ), + m_keepVariablesConstantDuringInitStep( false ), + //m_ratesOutputDir( joinPath( OutputBase::getOutputDirectory(), "_rates" ) ), + m_ratesOutputDir( joinPath( OutputBase::getOutputDirectory(), parent->getName() + "_rates" ) ), m_inputControl( Control::UNINITIALIZED ), m_currentControl( Control::UNINITIALIZED ), - m_targetBHP( 0.0 ), - m_targetTotalRate( 0.0 ), - m_targetPhaseRate( 0.0 ), - m_targetMassRate( 0.0 ), m_useSurfaceConditions( 0 ), - m_surfacePres( 0.0 ), - m_surfaceTemp( 0.0 ), + m_surfacePres( -1.0 ), + m_surfaceTemp( -1.0 ), m_isCrossflowEnabled( 1 ), - m_initialPressureCoefficient( 0.1 ), - m_rateSign( -1.0 ), - m_targetTotalRateTable( nullptr ), - m_targetPhaseRateTable( nullptr ), - m_targetBHPTable( nullptr ), - m_statusTable( nullptr ), + m_initialPressureCoefficient( 0.5 ), + m_currentConstraint( nullptr ), m_wellStatus( WellControls::Status::OPEN ), - m_regionAveragePressure( -1 ) + m_wellOpen( false ), + m_statusTable( nullptr ), + m_regionAveragePressure( -1 ), + m_estimateSolution( 0 ), + m_enableIsoThermalEstimator( 0 ), + /// Nonlinear solver parameters + m_wellNewtonSolver( viewKeyStruct::wellNewtonSolverString(), this ), + m_estimatorDoFManager( name ), + m_dofManagerInitialized( false ) { setInputFlags( InputFlags::OPTIONAL_NONUNIQUE ); @@ -59,61 +68,26 @@ WellControls::WellControls( string const & name, Group * const parent ) setInputFlag( InputFlags::REQUIRED ). setDescription( "Well type. Valid options:\n* " + EnumStrings< Type >::concat( "\n* " ) ); - registerWrapper( viewKeyStruct::inputControlString(), &m_inputControl ). - setInputFlag( InputFlags::REQUIRED ). - setDescription( "Well control. Valid options:\n* " + EnumStrings< Control >::concat( "\n* " ) ); + + + this->registerWrapper( viewKeyStruct::writeCSVFlagString(), &m_writeCSV ). + setApplyDefaultValue( 0 ). + setInputFlag( dataRepository::InputFlags::OPTIONAL ). + setDescription( "When set to 1, write the rates into a CSV file." ); + + this->registerWrapper( viewKeyStruct::timeStepFromTablesFlagString(), &m_timeStepFromTables ). + setApplyDefaultValue( 0 ). + setInputFlag( dataRepository::InputFlags::OPTIONAL ). + setDescription( "Choose time step to honor rates/bhp tables time intervals" ); registerWrapper( viewKeyStruct::currentControlString(), &m_currentControl ). setDefaultValue( Control::UNINITIALIZED ). setInputFlag( InputFlags::FALSE ). setDescription( "Current well control" ); - registerWrapper( viewKeyStruct::targetBHPString(), &m_targetBHP ). - setDefaultValue( 0.0 ). - setInputFlag( InputFlags::OPTIONAL ). - setRestartFlags( RestartFlags::WRITE_AND_READ ). - setDescription( "The target bottom-hole pressure [Pa] for the well." ); - - registerWrapper( viewKeyStruct::targetTotalRateString(), &m_targetTotalRate ). - setDefaultValue( 0.0 ). - setInputFlag( InputFlags::OPTIONAL ). - setRestartFlags( RestartFlags::WRITE_AND_READ ). - setDescription( "Target total volumetric rate (if useSurfaceConditions: [surface m^3/s]; else [reservoir m^3/s])" ); - - registerWrapper( viewKeyStruct::targetPhaseRateString(), &m_targetPhaseRate ). - setDefaultValue( 0.0 ). - setInputFlag( InputFlags::OPTIONAL ). - setRestartFlags( RestartFlags::WRITE_AND_READ ). - setDescription( "Target phase volumetric rate (if useSurfaceConditions: [surface m^3/s]; else [reservoir m^3/s])" ); - - registerWrapper( viewKeyStruct::targetMassRateString(), &m_targetMassRate ). - setDefaultValue( 0.0 ). - setInputFlag( InputFlags::OPTIONAL ). - setRestartFlags( RestartFlags::WRITE_AND_READ ). - setDescription( "Target Mass Rate rate ( [kg^3/s])" ); - - registerWrapper( viewKeyStruct::targetPhaseNameString(), &m_targetPhaseName ). - setRTTypeName( rtTypes::CustomTypes::groupNameRef ). - setDefaultValue( "" ). - setInputFlag( InputFlags::OPTIONAL ). - setRestartFlags( RestartFlags::WRITE_AND_READ ). - setDescription( "Name of the target phase" ); - - registerWrapper( viewKeyStruct::refElevString(), &m_refElevation ). - setDefaultValue( -1 ). + registerWrapper( viewKeyStruct::inputControlString(), &m_inputControl ). setInputFlag( InputFlags::REQUIRED ). - setDescription( "Reference elevation where BHP control is enforced [m]" ); - - registerWrapper( viewKeyStruct::injectionStreamString(), &m_injectionStream ). - setDefaultValue( -1 ). - setSizedFromParent( 0 ). - setInputFlag( InputFlags::OPTIONAL ). - setDescription( "Defines the global component fractions of the injected fluid." ); - - registerWrapper( viewKeyStruct::injectionTemperatureString(), &m_injectionTemperature ). - setDefaultValue( -1 ). - setInputFlag( InputFlags::OPTIONAL ). - setDescription( "Temperature of the injection stream [K]" ); + setDescription( "Well control. Valid options:\n* " + EnumStrings< Control >::concat( "\n* " ) ); registerWrapper( viewKeyStruct::useSurfaceConditionsString(), &m_useSurfaceConditions ). setDefaultValue( 0 ). @@ -155,31 +129,13 @@ WellControls::WellControls( string const & name, Group * const parent ) " - Injector pressure at reference depth initialized as: (1+initialPressureCoefficient)*reservoirPressureAtClosestPerforation + density*g*( zRef - zPerf ) \n" " - Producer pressure at reference depth initialized as: (1-initialPressureCoefficient)*reservoirPressureAtClosestPerforation + density*g*( zRef - zPerf ) " ); - registerWrapper( viewKeyStruct::targetBHPTableNameString(), &m_targetBHPTableName ). - setRTTypeName( rtTypes::CustomTypes::groupNameRef ). + this->registerWrapper( viewKeyStruct::estimateWellSolutionString(), &m_estimateSolution ). + setApplyDefaultValue( 0 ). setInputFlag( InputFlags::OPTIONAL ). - setDescription( "Name of the BHP table when the rate is a time dependent function" ); + setDescription( "Flag to esitmate well solution prior to coupled reservoir and well solve." ); - registerWrapper( viewKeyStruct::targetTotalRateTableNameString(), &m_targetTotalRateTableName ). - setRTTypeName( rtTypes::CustomTypes::groupNameRef ). - setInputFlag( InputFlags::OPTIONAL ). - setDescription( "Name of the total rate table when the rate is a time dependent function" ); - registerWrapper( viewKeyStruct::targetPhaseRateTableNameString(), &m_targetPhaseRateTableName ). - setRTTypeName( rtTypes::CustomTypes::groupNameRef ). - setInputFlag( InputFlags::OPTIONAL ). - setDescription( "Name of the phase rate table when the rate is a time dependent function" ); - - registerWrapper( viewKeyStruct::targetMassRateTableNameString(), &m_targetMassRateTableName ). - setRTTypeName( rtTypes::CustomTypes::groupNameRef ). - setInputFlag( InputFlags::OPTIONAL ). - setDescription( "Name of the mass rate table when the rate is a time dependent function" ); - - registerWrapper( viewKeyStruct::statusTableNameString(), &m_statusTableName ). - setRTTypeName( rtTypes::CustomTypes::groupNameRef ). - setInputFlag( InputFlags::OPTIONAL ). - setDescription( "Name of the well status table when the status of the well is a time dependent function. \n" - "If the status function evaluates to a positive value at the current time, the well will be open otherwise the well will be shut." ); + registerGroup( viewKeyStruct::wellNewtonSolverString(), &m_wellNewtonSolver ); addLogLevel< logInfo::WellControl >(); } @@ -188,28 +144,85 @@ WellControls::WellControls( string const & name, Group * const parent ) WellControls::~WellControls() {} -void WellControls::switchToBHPControl( real64 const & val ) +Group * WellControls::createChild( string const & childKey, string const & childName ) { - m_currentControl = Control::BHP; - m_targetBHP = val; -} - -void WellControls::switchToTotalRateControl( real64 const & val ) -{ - m_currentControl = Control::TOTALVOLRATE; - m_targetTotalRate = val; -} + //Group * baseChild = Group::createChild( childKey, childName ); + //if( baseChild != nullptr ) + //{ + // return baseChild; + //} + //GEOS_LOG_RANK_0( GEOS_FMT( "{}: adding {} {}", getName(), childKey, childName ) ); + ////const auto childTypes = { viewKeyStruct::perforationString() }; + //GEOS_ERROR_IF( childKey != viewKeyStruct::perforationString(), + // CatalogInterface::unknownTypeError( childKey, getDataContext(), childTypes ) ); + + Group * child = nullptr; + if( childKey == viewKeyStruct::minimumBHPConstraintString() ) + { + MinimumBHPConstraint & bhpConstraint = registerGroup< MinimumBHPConstraint >( childName ); + m_minBHPConstraint = &bhpConstraint; + child = &bhpConstraint; + } + else if( childKey == viewKeyStruct::maximumBHPConstraintString() ) + { + MaximumBHPConstraint & bhpConstraint = registerGroup< MaximumBHPConstraint >( childName ); + m_maxBHPConstraint = &bhpConstraint; + child = &bhpConstraint; + } + else if( childKey == viewKeyStruct::productionPhaseVolumeRateConstraintString() ) + { + ProductionConstraint< PhaseVolumeRateConstraint > & phaseConstraint = registerGroup< ProductionConstraint< PhaseVolumeRateConstraint > >( childName ); + m_productionRateConstraintList.emplace_back( &phaseConstraint ); + child = &phaseConstraint; + } + else if( childKey == viewKeyStruct::injectionPhaseVolumeRateConstraint() ) + { + InjectionConstraint< PhaseVolumeRateConstraint > & phaseConstraint = registerGroup< InjectionConstraint< PhaseVolumeRateConstraint > >( childName ); + m_injectionRateConstraintList.emplace_back( &phaseConstraint ); + child = &phaseConstraint; + } + else if( childKey == viewKeyStruct::productionVolumeRateConstraint() ) + { + ProductionConstraint< VolumeRateConstraint > & volConstraint = registerGroup< ProductionConstraint< VolumeRateConstraint > >( childName ); + m_productionRateConstraintList.emplace_back( &volConstraint ); + child = &volConstraint; + } + else if( childKey == viewKeyStruct::injectionVolumeRateConstraint() ) + { + InjectionConstraint< VolumeRateConstraint > & volConstraint = registerGroup< InjectionConstraint< VolumeRateConstraint > >( childName ); + m_injectionRateConstraintList.emplace_back( &volConstraint ); + child = &volConstraint; + } + else if( childKey == viewKeyStruct::productionMassRateConstraint() ) + { + ProductionConstraint< MassRateConstraint > & massConstraint = registerGroup< ProductionConstraint< MassRateConstraint > >( childName ); + m_productionRateConstraintList.emplace_back( &massConstraint ); + child = &massConstraint; -void WellControls::switchToPhaseRateControl( real64 const & val ) -{ - m_currentControl = Control::PHASEVOLRATE; - m_targetPhaseRate = val; + } + else if( childKey == viewKeyStruct::injectionMassRateConstraint() ) + { + InjectionConstraint< MassRateConstraint > & massConstraint = registerGroup< InjectionConstraint< MassRateConstraint > >( childName ); + m_injectionRateConstraintList.emplace_back( &massConstraint ); + child = &massConstraint; + } + else if( childKey == viewKeyStruct::productionLiquidRateConstraint() ) + { + ProductionConstraint< LiquidRateConstraint > & liquidConstraint = registerGroup< ProductionConstraint< LiquidRateConstraint > >( childName ); + m_productionRateConstraintList.emplace_back( &liquidConstraint ); + child = &liquidConstraint; + } + return child; } -void WellControls::switchToMassRateControl( real64 const & val ) +void WellControls::expandObjectCatalogs() { - m_currentControl = Control::MASSRATE; - m_targetMassRate = val; + // During schema generation, register one of each type derived from ConstitutiveBase here + for( auto & catalogIter: WellConstraintBase::getCatalog()) + { + createChild( catalogIter.first, catalogIter.first ); + } + // tjbcreateChild( WellNewtonSolver::catalogName(), WellNewtonSolver::catalogName() ); } namespace @@ -234,9 +247,28 @@ TableFunction * createWellTable( string const & tableName, } } +void WellControls::registerWellDataOnMesh( WellElementSubRegion & subRegion ) +{ + std::string const & regionName = subRegion.getName(); + std::string addrWithMask( regionName ); + std::size_t pos = addrWithMask.find( "UniqueSubRegion" ); + std::string addr = addrWithMask.substr( 0, pos ); + m_targetRegionNames.push_back( addr ); + + registerWrapper< real64 >( viewKeyStruct::currentBHPString() ); + registerWrapper< real64 >( viewKeyStruct::currentVolRateString() ); + + registerWrapper< array1d< real64 > >( viewKeyStruct::currentPhaseVolRateString() ). + setSizedFromParent( 0 ). + reference().resizeDimension< 0 >( m_numPhases ); + registerWrapper< real64 >( viewKeyStruct::massDensityString() ); + registerWrapper< real64 >( viewKeyStruct::currentTotalVolRateString() ); + registerWrapper< real64 >( viewKeyStruct::currentMassRateString() ); +} void WellControls::postInputInitialization() { + Group::postInputInitialization(); // 0) Assign the value of the current well control // When the simulation starts from a restart file, we don't want to use the inputControl, // because the control may have switched in the simulation that generated the restart @@ -250,119 +282,27 @@ void WellControls::postInputInitialization() m_currentControl = m_inputControl; } - // 1.a) check target BHP - GEOS_THROW_IF( m_targetBHP < 0, - getWrapperDataContext( viewKeyStruct::targetBHPString() ) << - ": Target bottom-hole pressure is negative", - InputError, getWrapperDataContext( viewKeyStruct::targetBHPString() ) ); - - // 1.b) check target rates - GEOS_THROW_IF( m_targetTotalRate < 0, - getWrapperDataContext( viewKeyStruct::targetTotalRateString() ) << ": Target rate is negative", - InputError, getWrapperDataContext( viewKeyStruct::targetTotalRateString() ) ); - - GEOS_THROW_IF( m_targetPhaseRate < 0, - getWrapperDataContext( viewKeyStruct::targetPhaseRateString() ) << ": Target oil rate is negative", - InputError, getWrapperDataContext( viewKeyStruct::targetPhaseRateString() ) ); - - GEOS_THROW_IF( m_targetMassRate < 0, - getWrapperDataContext( viewKeyStruct::targetMassRateString() ) << ": Target mass rate is negative", - InputError, getWrapperDataContext( viewKeyStruct::targetMassRateString() ) ); - - GEOS_THROW_IF( (m_injectionStream.empty() && m_injectionTemperature >= 0) || - (!m_injectionStream.empty() && m_injectionTemperature < 0), - "WellControls " << getDataContext() << ": Both " - << viewKeyStruct::injectionStreamString() << " and " << viewKeyStruct::injectionTemperatureString() - << " must be specified for multiphase simulations", - InputError, getDataContext() ); - - // 1.c) Set the multiplier for the rates - if( isProducer() ) - { - m_rateSign = -1.0; - } - else - { - m_rateSign = 1.0; - } - - // 2) check injection stream - if( !m_injectionStream.empty()) - { - real64 sum = 0.0; - for( localIndex ic = 0; ic < m_injectionStream.size(); ++ic ) - { - GEOS_ERROR_IF( m_injectionStream[ic] < 0.0 || m_injectionStream[ic] > 1.0, - getWrapperDataContext( viewKeyStruct::injectionStreamString() ) << ": Invalid injection stream", - getWrapperDataContext( viewKeyStruct::injectionStreamString() ) ); - sum += m_injectionStream[ic]; - } - GEOS_THROW_IF( LvArray::math::abs( 1.0 - sum ) > std::numeric_limits< real64 >::epsilon(), - getWrapperDataContext( viewKeyStruct::injectionStreamString() ) << ": Invalid injection stream", - InputError, getWrapperDataContext( viewKeyStruct::injectionStreamString() ) ); - } // 3) check the flag for surface / reservoir conditions GEOS_THROW_IF( m_useSurfaceConditions != 0 && m_useSurfaceConditions != 1, getWrapperDataContext( viewKeyStruct::useSurfaceConditionsString() ) << ": The flag to select surface/reservoir conditions must be equal to 0 or 1", InputError, getWrapperDataContext( viewKeyStruct::useSurfaceConditionsString() ) ); - // 4) check that at least one rate constraint has been defined - GEOS_THROW_IF( ((m_targetPhaseRate <= 0.0 && m_targetPhaseRateTableName.empty()) && - (m_targetMassRate <= 0.0 && m_targetMassRateTableName.empty()) && - (m_targetTotalRate <= 0.0 && m_targetTotalRateTableName.empty())), - "WellControls " << getDataContext() << ": You need to specify a phase, mass, or total rate constraint. \n" << - "The phase rate constraint can be specified using " << - "either " << viewKeyStruct::targetPhaseRateString() << - " or " << viewKeyStruct::targetPhaseRateTableNameString() << ".\n" << - "The total rate constraint can be specified using " << - "either " << viewKeyStruct::targetTotalRateString() << - " or " << viewKeyStruct::targetTotalRateTableNameString()<< - "The mass rate constraint can be specified using " << - "either " << viewKeyStruct::targetMassRateString() << - " or " << viewKeyStruct::targetMassRateTableNameString(), - InputError, getDataContext() ); + // tjb add more constraint validation + // 1) liquid rate - phase names consistent with fluild model + // 2) at least one bhp and one rate constraint defined + // 3) constraint type and well type compatibility - // 5) check whether redundant information has been provided - GEOS_THROW_IF( ((m_targetPhaseRate > 0.0 && !m_targetPhaseRateTableName.empty())), - "WellControls " << getDataContext() << ": You have provided redundant information for well phase rate." << - " The keywords " << viewKeyStruct::targetPhaseRateString() << " and " << viewKeyStruct::targetPhaseRateTableNameString() << " cannot be specified together", - InputError, getDataContext() ); + //GEOS_THROW_IF( ((m_targetMassRate > 0.0 && m_useSurfaceConditions==0)), + // "WellControls " << getDataContext() << ": Option only valid if useSurfaceConditions set to 1", + // InputError ); - GEOS_THROW_IF( ((m_targetTotalRate > 0.0 && !m_targetTotalRateTableName.empty())), - "WellControls " << getDataContext() << ": You have provided redundant information for well total rate." << - " The keywords " << viewKeyStruct::targetTotalRateString() << " and " << viewKeyStruct::targetTotalRateTableNameString() << " cannot be specified together", - InputError, getDataContext() ); - GEOS_THROW_IF( ((m_targetBHP > 0.0 && !m_targetBHPTableName.empty())), - "WellControls " << getDataContext() << ": You have provided redundant information for well BHP." << - " The keywords " << viewKeyStruct::targetBHPString() << " and " << viewKeyStruct::targetBHPTableNameString() << " cannot be specified together", - InputError, getDataContext() ); - - GEOS_THROW_IF( ((m_targetMassRate > 0.0 && !m_targetMassRateTableName.empty())), - "WellControls " << getDataContext() << ": You have provided redundant information for well mass rate." << - " The keywords " << viewKeyStruct::targetMassRateString() << " and " << viewKeyStruct::targetMassRateTableNameString() << " cannot be specified together", - InputError, getDataContext() ); - - GEOS_THROW_IF( ((m_targetMassRate > 0.0 && m_useSurfaceConditions==0)), - "WellControls " << getDataContext() << ": Option only valid if useSurfaceConditions set to 1", - InputError, getDataContext() ); - - // 6.1) If the well is under BHP control then the BHP must be specified. - // Otherwise the BHP will be set to a default value. - if( m_currentControl == Control::BHP ) - { - GEOS_THROW_IF( ((m_targetBHP <= 0.0 && m_targetBHPTableName.empty())), - "WellControls " << getDataContext() << ": You have to provide well BHP by specifying either " - << viewKeyStruct::targetBHPString() << " or " << viewKeyStruct::targetBHPTableNameString(), - InputError, getDataContext() ); - } - else if( m_targetBHP <= 0.0 && m_targetBHPTableName.empty() ) - { - m_targetBHP = isProducer() ? WellConstants::defaultProducerBHP : WellConstants::defaultInjectorBHP; - GEOS_LOG_LEVEL_RANK_0( logInfo::WellControl, - GEOS_FMT( "WellControls {}: Setting {} to default value {}", getDataContext(), viewKeyStruct::targetBHPString(), m_targetBHP )); - } + // 8) Make sure that the initial pressure coefficient is positive + GEOS_THROW_IF( m_initialPressureCoefficient < 0, + getWrapperDataContext( viewKeyStruct::initialPressureCoefficientString() ) << + ": This tuning coefficient is negative", + InputError ); // 6.2) Check incoherent information @@ -378,79 +318,6 @@ void WellControls::postInputInitialization() << EnumStrings< Control >::toString( Control::MASSRATE ), InputError, getDataContext() ); - // 8) Make sure that the initial pressure coefficient is positive - GEOS_THROW_IF( m_initialPressureCoefficient < 0, - getWrapperDataContext( viewKeyStruct::initialPressureCoefficientString() ) << - ": This tuning coefficient is negative", - InputError, getWrapperDataContext( viewKeyStruct::initialPressureCoefficientString() ) ); - - - // 9) Create time-dependent BHP table - if( m_targetBHPTableName.empty() ) - { - m_targetBHPTableName = getName()+"_ConstantBHP_table"; - m_targetBHPTable = createWellTable( m_targetBHPTableName, m_targetBHP ); - } - else - { - FunctionManager & functionManager = FunctionManager::getInstance(); - m_targetBHPTable = &(functionManager.getGroup< TableFunction const >( m_targetBHPTableName )); - - GEOS_THROW_IF( m_targetBHPTable->getInterpolationMethod() != TableFunction::InterpolationType::Lower, - "WellControls " << getDataContext() << ": The interpolation method for the time-dependent BHP table " - << m_targetBHPTable->getName() << " should be TableFunction::InterpolationType::Lower", - InputError, getDataContext() ); - } - - // 10) Create time-dependent total rate table - if( m_targetTotalRateTableName.empty() ) - { - m_targetTotalRateTableName = getName()+"_ConstantTotalRate_table"; - m_targetTotalRateTable = createWellTable( m_targetTotalRateTableName, m_targetTotalRate ); - } - else - { - FunctionManager & functionManager = FunctionManager::getInstance(); - m_targetTotalRateTable = &(functionManager.getGroup< TableFunction const >( m_targetTotalRateTableName )); - - GEOS_THROW_IF( m_targetTotalRateTable->getInterpolationMethod() != TableFunction::InterpolationType::Lower, - "WellControls " << getDataContext() << ": The interpolation method for the time-dependent total rate table " - << m_targetTotalRateTable->getName() << " should be TableFunction::InterpolationType::Lower", - InputError, getDataContext() ); - } - - // 11) Create time-dependent phase rate table - if( m_targetPhaseRateTableName.empty() ) - { - m_targetPhaseRateTableName = getName()+"_ConstantPhaseRate_table"; - m_targetPhaseRateTable = createWellTable( m_targetPhaseRateTableName, m_targetPhaseRate ); - } - else - { - FunctionManager & functionManager = FunctionManager::getInstance(); - m_targetPhaseRateTable = &(functionManager.getGroup< TableFunction const >( m_targetPhaseRateTableName )); - - GEOS_THROW_IF( m_targetPhaseRateTable->getInterpolationMethod() != TableFunction::InterpolationType::Lower, - "WellControls " << getDataContext() << ": The interpolation method for the time-dependent phase rate table " - << m_targetPhaseRateTable->getName() << " should be TableFunction::InterpolationType::Lower", - InputError, getDataContext() ); - } - // Create time-dependent mass rate table - if( m_targetMassRateTableName.empty() ) - { - m_targetMassRateTableName = getName()+"_ConstantMassRate_table"; - m_targetMassRateTable = createWellTable( m_targetMassRateTableName, m_targetMassRate ); - } - else - { - FunctionManager & functionManager = FunctionManager::getInstance(); - m_targetMassRateTable = &(functionManager.getGroup< TableFunction const >( m_targetMassRateTableName )); - - GEOS_THROW_IF( m_targetMassRateTable->getInterpolationMethod() != TableFunction::InterpolationType::Lower, - "WellControls " << getDataContext() << ": The interpolation method for the time-dependent mass rate table " - << m_targetMassRateTable->getName() << " should be TableFunction::InterpolationType::Lower", - InputError, getDataContext() ); - } // 12) Create the time-dependent well status table if( m_statusTableName.empty()) { @@ -475,37 +342,107 @@ void WellControls::postInputInitialization() } } - +void WellControls::postRestartInitialization( ) +{} void WellControls::setWellStatus( real64 const & currentTime, WellControls::Status status ) { m_wellStatus = status; if( m_wellStatus == WellControls::Status::OPEN ) { - - if( isZero( getTargetTotalRate( currentTime ) ) && isZero( getTargetPhaseRate( currentTime ) ) - && isZero( getTargetMassRate( currentTime ) ) ) + if( isProducer()) { - m_wellStatus = WellControls::Status::CLOSED; + std::vector< WellConstraintBase * > const constraints = getProdRateConstraints(); + for( auto const & constraint : constraints ) + { + if( isZero( constraint->getConstraintValue( currentTime ) ) ) + { + m_wellStatus = WellControls::Status::CLOSED; + break; + } + } + } + else + { + std::vector< WellConstraintBase * > const constraints = getInjRateConstraints(); + for( auto const & constraint : constraints ) + { + if( isZero( constraint->getConstraintValue( currentTime ) ) ) + { + m_wellStatus = WellControls::Status::CLOSED; + break; + } + } } + if( m_statusTable->evaluate( ¤tTime ) < LvArray::NumericLimits< real64 >::epsilon ) { m_wellStatus = WellControls::Status::CLOSED; } } } +real64 WellControls::setNextDt( real64 const & currentTime, + real64 const & currentDt, + WellElementSubRegion & subRegion ) +{ + real64 nextDt = currentDt; + real64 nextDt_perf=nextDt; + // Find min dt from perf status tables + PerforationData & perforationData = *subRegion.getPerforationData(); + string_array const & perfStatusTableName = perforationData.getPerfStatusTableName(); + FunctionManager & functionManager = FunctionManager::getInstance(); + // Get dt for local perforations + for( integer i=0; i( perfStatusTableName[i] ); + setNextDtFromTable( tableFunction, currentTime, nextDt_perf ); + } + nextDt = MpiWrapper::min< real64 >( nextDt_perf ); + // Find min dt including rate and status tables + real64 const nextDt_orig = nextDt; + setNextDtFromTables( currentTime, nextDt ); + //if( m_nonlinearSolverParameters.getLogLevel() > 0 && nextDt < nextDt_orig ) + if( getLogLevel() > 0 && nextDt < nextDt_orig ) + GEOS_LOG_RANK_0( GEOS_FMT( "{}: next time step based on tables coordinates = {}", getName(), nextDt )); + return nextDt; +} bool WellControls::isWellOpen() const { return getWellStatus() == WellControls::Status::OPEN; } +void WellControls::setWellState( bool open ) +{ + m_wellOpen = open; +} + +bool WellControls::getWellState() const +{ + return m_wellOpen; +} void WellControls::setNextDtFromTables( real64 const & currentTime, real64 & nextDt ) { - WellControls::setNextDtFromTable( m_targetBHPTable, currentTime, nextDt ); - WellControls::setNextDtFromTable( m_targetMassRateTable, currentTime, nextDt ); - WellControls::setNextDtFromTable( m_targetPhaseRateTable, currentTime, nextDt ); - WellControls::setNextDtFromTable( m_targetTotalRateTable, currentTime, nextDt ); + if( isProducer() ) + { + if( getMinBHPConstraint() != nullptr ) + { + getMinBHPConstraint()->setNextDtFromTables( currentTime, nextDt ); + } + for( auto const & constraint : m_productionRateConstraintList ) + { + constraint->setNextDtFromTables( currentTime, nextDt ); + } + } + else + { + getMaxBHPConstraint()->setNextDtFromTables( currentTime, nextDt ); + for( auto const & constraint : m_injectionRateConstraintList ) + { + constraint->setNextDtFromTables( currentTime, nextDt ); + } + } + WellControls::setNextDtFromTable( m_statusTable, currentTime, nextDt ); } @@ -523,4 +460,618 @@ void WellControls::setNextDtFromTable( TableFunction const * table, real64 const } } +real64 WellControls::getTargetBHP( real64 const & targetTime ) const +{ + if( isProducer()) + { + return m_minBHPConstraint->getConstraintValue( targetTime ); + } + return m_maxBHPConstraint->getConstraintValue( targetTime ); +} + + +real64 WellControls::getInjectionTemperature() const +{ + real64 injectionTemperature = 0.0; + this->forInjectionConstraints< InjectionConstraint< PhaseVolumeRateConstraint >, InjectionConstraint< VolumeRateConstraint >, InjectionConstraint< MassRateConstraint > >( [&] ( auto & constraint ) + { + if( constraint.isConstraintActive()) + { + injectionTemperature = constraint.getInjectionTemperature(); + return; + } + } ); + return injectionTemperature; +} + + +arrayView1d< real64 const > WellControls::getInjectionStream() const +{ + arrayView1d< real64 const > injectionStream; + forInjectionConstraints< InjectionConstraint< PhaseVolumeRateConstraint >, InjectionConstraint< VolumeRateConstraint >, InjectionConstraint< MassRateConstraint > >( [&] ( auto & constraint ) + { + if( constraint.isConstraintActive() ) + { + injectionStream = constraint.getInjectionStream(); + return; + } + } ); + + return injectionStream; +} + +integer WellControls::getConstraintPhaseIndex() const +{ + integer phaseIndex = -1; + + if( isProducer() ) + { + forProductionConstraints< ProductionConstraint< PhaseVolumeRateConstraint > >( [&] ( auto & constraint ) + { + if( constraint.isConstraintActive() ) + { + phaseIndex = constraint.getPhaseIndex(); + } + } ); + } + else + { + forInjectionConstraints< InjectionConstraint< PhaseVolumeRateConstraint > >( [&] ( auto & constraint ) + { + if( constraint.isConstraintActive() ) + { + phaseIndex = constraint.getPhaseIndex(); + } + } ); + } + + return phaseIndex; +} + +real64 WellControls::getReferenceElevation() const +{ + if( isProducer () ) + { + return getMinBHPConstraint()->getReferenceElevation(); + } + return getMaxBHPConstraint()->getReferenceElevation(); +} + +void WellControls::implicitStepSetup( real64 const & time_n, + real64 const & GEOS_UNUSED_PARAM( dt ), + ElementRegionManager & elemManager, + WellElementSubRegion & subRegion ) +{ + + GEOS_UNUSED_VAR( elemManager ); + // Set perforation status + setPerforationStatus( time_n, subRegion ); +} +void WellControls::setPerforationStatus( real64 const & time_n, WellElementSubRegion & subRegion ) +{ + FunctionManager & functionManager = FunctionManager::getInstance(); + + // Set perforation status + + PerforationData & perforationData = *subRegion.getPerforationData(); + string_array const & perfStatusTableName = perforationData.getPerfStatusTableName(); + arrayView1d< integer > perfStatus = perforationData.getLocalPerfStatus(); + // for now set to open + for( integer i=0; i( perfStatusTableName[i] ); + perfStatus[i]=PerforationData::PerforationStatus::OPEN; + if( tableFunction->evaluate( &time_n ) < LvArray::NumericLimits< real64 >::epsilon ) + { + perfStatus[i]=PerforationData::PerforationStatus::CLOSED; + } + } + + array1d< localIndex > const perfWellElemIndex = perforationData.getField< fields::perforation::wellElementIndex >(); + // global index local elements (size == subregion.size) + arrayView1d< globalIndex const > globalWellElementIndex = subRegion.getGlobalWellElementIndex(); + + arrayView1d< integer const > const elemGhostRank = subRegion.ghostRank(); + array1d< integer > & currentStatus = subRegion.getWellElementStatus(); + // Local elements + array1d< integer > & localElemStatus = subRegion.getWellLocalElementStatus(); + + integer numLocalElements = subRegion.getNumLocalElements(); + array1d< integer > segStatus( numLocalElements ); + + // Local perforations + for( integer j = 0; j < perforationData.size(); j++ ) + { + localIndex const iwelem = perfWellElemIndex[j]; + if( elemGhostRank[iwelem] < 0 ) + { + if( perfStatus[j] ) + { + segStatus[iwelem] +=1; + } + } + } + // Broadcast segment status so all cores have same well status + subRegion.setElementStatus( segStatus ); + integer numOpenElements = 0; + array1d< integer > const & updatedStatus = subRegion.getWellElementStatus(); + for( integer i=0; i0 ? setWellStatus( time_n, WellControls::Status::OPEN ) : setWellStatus( time_n, WellControls::Status::CLOSED ); + + + // Set local well element status array + for( integer i=0; i const wellElemLocation = subRegion.getElementCenter(); + arrayView1d< real64 > const wellElemGravCoef = subRegion.getField< fields::well::gravityCoefficient >(); + + arrayView2d< real64 const > const perfLocation = perforationData.getField< fields::perforation::location >(); + arrayView1d< real64 > const perfGravCoef = perforationData.getField< fields::well::gravityCoefficient >(); + + forAll< serialPolicy >( perforationData.size(), [=]( localIndex const iperf ) + { + // precompute the depth of the perforations + perfGravCoef[iperf] = LvArray::tensorOps::AiBi< 3 >( perfLocation[iperf], gravVector ); + } ); + + forAll< serialPolicy >( subRegion.size(), [=]( localIndex const iwelem ) + { + // precompute the depth of the well elements + wellElemGravCoef[iwelem] = LvArray::tensorOps::AiBi< 3 >( wellElemLocation[iwelem], gravVector ); + } ); + + forSubGroups< BHPConstraint >( [&]( auto & constraint ) + { + // set the reference well element where the BHP control is applied + real64 const refElev1 = constraint.getReferenceElevation(); + constraint.setReferenceGravityCoef( refElev1 * gravVector[2] ); + } ); + + // set the reference well element where the BHP control is applied + setReferenceGravityCoef( refElev * gravVector[2] ); // tjb remove +} + +void WellControls::selectWellConstraint( real64 const & time_n, + real64 const & dt, + integer const cycleNumber, + integer const coupledIterationNumber, + DomainPartition & domain, + MeshLevel & meshLevel, + ElementRegionManager & elemManager, + WellElementSubRegion & subRegion, + DofManager const & dofManager ) + +{ + + // Well state estimated from reservoir conditions + if( isWellOpen() ) + { + if( !getWellState() ) + { + setWellState( 1 ); + + initializeWell( domain, meshLevel, subRegion, time_n ); + } + } + else + { + setWellState( 0 ); + } + + + bool useEstimator = m_estimateSolution && (coupledIterationNumber < m_wellNewtonSolver.getNumActiveCoupledIterations() ); + + + + if( getWellState()) + { + if( useEstimator ) + { + // Estimate well solution prior to coupled solve + evaluateConstraints( time_n, + dt, + cycleNumber, + coupledIterationNumber, + domain, + meshLevel, + elemManager, + subRegion, + dofManager ); + } + else + { + // Evaluate well constraints based on current solution + evaluateConstraints( time_n, + subRegion ); + } + + // If a well is opened and then timestep is cut resulting in the well being shut, if the well is opened + // the well initialization code requires control type to by synced + integer owner = -1; + // Only subregion owner evaluates well control and control changes need to be broadcast to all ranks + if( subRegion.isLocallyOwned() ) + { + owner = MpiWrapper::commRank( MPI_COMM_GEOS ); + } + owner = MpiWrapper::max( owner ); + WellControls::Control wellControl = getControl(); + MpiWrapper::broadcast( wellControl, owner ); + setControl( wellControl ); + } + + +} + +bool WellControls::evaluateConstraints( real64 const & time_n, + WellElementSubRegion & subRegion ) +{ + + // create list of all constraints to process + std::vector< WellConstraintBase * > constraintList; + if( isProducer() ) + { + constraintList = getProdRateConstraints(); + // Solve minimum bhp constraint first + constraintList.insert( constraintList.begin(), getMinBHPConstraint() ); + } + else + { + constraintList = getInjRateConstraints(); + // Solve maximum bhp constraint first; + constraintList.insert( constraintList.begin(), getMaxBHPConstraint() ); + } + // Get current constraint + WellConstraintBase * limitingConstraint = nullptr; + for( auto & constraint : constraintList ) + { + if( constraint->getName() == getCurrentConstraint()->getName()) + { + limitingConstraint = constraint; + // tjb. this is likely not needed. set in update state + constraint->setBHP ( getReference< real64 >( viewKeyStruct::currentBHPString() )); + if( isCompositional()) + { + constraint->setPhaseVolumeRates ( getReference< array1d< real64 > >( + viewKeyStruct::currentPhaseVolRateString() ) ); + constraint->setTotalVolumeRate ( getReference< real64 >( + viewKeyStruct::currentTotalVolRateString() )); + constraint->setMassRate( getReference< real64 >( viewKeyStruct::currentMassRateString() )); + } + else + { + constraint->setBHP ( getReference< real64 >( viewKeyStruct::currentBHPString() )); + constraint->setTotalVolumeRate ( getReference< real64 >( + viewKeyStruct::currentVolRateString() )); + } + GEOS_LOG_RANK_IF ( getLogLevel() > 4 && subRegion.isLocallyOwned(), + " Well " << subRegion.getName() << " Limiting Constraint " << limitingConstraint->getName() << " " << limitingConstraint->bottomHolePressure() << " " << + limitingConstraint->phaseVolumeRates() << " " << + limitingConstraint->totalVolumeRate() << " " << limitingConstraint->massRate()); + } + } + constraintList.erase( std::find( constraintList.begin(), constraintList.end(), limitingConstraint ) ); + + // Check current against other constraints + for( auto & constraint : constraintList ) + { + + if( limitingConstraint->getName() != constraint->getName()) + { + if( constraint->checkViolation( *limitingConstraint, time_n ) ) + { + limitingConstraint = constraint; + setControl( static_cast< WellControls::Control >(constraint->getControl()) ); // tjb old + setCurrentConstraint( constraint ); + constraint->setBHP ( getReference< real64 >( viewKeyStruct::currentBHPString() )); + if( isCompositional()) + { + constraint->setPhaseVolumeRates ( getReference< array1d< real64 > >( + viewKeyStruct::currentPhaseVolRateString() ) ); + constraint->setTotalVolumeRate ( getReference< real64 >( + viewKeyStruct::currentTotalVolRateString() )); + constraint->setMassRate( getReference< real64 >( viewKeyStruct::currentMassRateString() )); + } + else + { + constraint->setBHP ( getReference< real64 >( viewKeyStruct::currentBHPString() )); + constraint->setTotalVolumeRate ( getReference< real64 >( + viewKeyStruct::currentVolRateString() )); + } + GEOS_LOG_RANK_IF ( subRegion.isLocallyOwned(), + " Well " << subRegion.getName() << " Control switch " << constraint->getName() << " " << constraint->getConstraintValue( time_n ) ); + } + } + } + GEOS_LOG_RANK_IF ( getLogLevel() > 4 && subRegion.isLocallyOwned(), + " Well " << subRegion.getName() << " Limiting Constraint " << limitingConstraint->getName() << " " << limitingConstraint->bottomHolePressure() << " " << + limitingConstraint->phaseVolumeRates() << " " << + limitingConstraint->totalVolumeRate() << " " << limitingConstraint->massRate()); + + return true; +} + +bool WellControls::evaluateConstraints( real64 const & time_n, + real64 const & dt, + integer const cycleNumber, + integer const coupledIterationNumber, + DomainPartition & domain, + MeshLevel & mesh, + ElementRegionManager & elemManager, + WellElementSubRegion & subRegion, + DofManager const & dofManager ) +{ + + + // create list of all constraints to solve + // note that initializeWells sets the initial constraint + // tjb reactive control schema to allow use to set if needed + std::vector< WellConstraintBase * > constraintList; + WellConstraintBase * limitingConstraint = getCurrentConstraint(); + if( isProducer() ) + { + constraintList = getProdRateConstraints(); + if( limitingConstraint->getControl() != ConstraintTypeId::BHP ) + { + { // remove from list and add BHP constraint + auto it = std::find( constraintList.begin(), constraintList.end(), limitingConstraint ); + if( it != constraintList.end() ) + { + constraintList.erase( it ); + } + if( getMinBHPConstraint()->isConstraintActive() ) + { + constraintList.push_back( getMinBHPConstraint() ); + } + constraintList.insert( constraintList.begin(), limitingConstraint ); + } + } + // Solve minimum bhp constraint first + if( false && getMinBHPConstraint()->isConstraintActive() ) + { + // this is related to WHP option which introduces a new BHP constraint + limitingConstraint = getMinBHPConstraint(); + } + else if( limitingConstraint == nullptr ) + { + limitingConstraint = constraintList[0]; + } + } + else + { + constraintList = getInjRateConstraints(); + // remove the limiting constraint from the list if present + { + if( limitingConstraint->getControl() != ConstraintTypeId::BHP ) + { // remove from list and add BHP constraint + //auto it = std::find( constraintList.begin(), constraintList.end(), limitingConstraint ); + //if( it != constraintList.end() ) + //{ + // constraintList.erase( it ); + //} + constraintList.push_back( getMaxBHPConstraint() ); + } + } + } + if( isoThermalEstimatorEnabled() ) + { + enableThermalEffects( false ); + solveConstraint ( limitingConstraint, time_n, + dt, + cycleNumber, + coupledIterationNumber, + domain, + mesh, + elemManager, + subRegion, + dofManager ); + enableThermalEffects( true ); + } + solveConstraint ( limitingConstraint, time_n, + dt, + cycleNumber, + coupledIterationNumber, + domain, + mesh, + elemManager, + subRegion, + dofManager ); + + for( auto const & constraint : constraintList ) + { + GEOS_LOG_RANK_IF ( getLogLevel() > 4 && subRegion.isLocallyOwned(), + " Well " << subRegion.getName() << " Constraint " << constraint->getName() << " active " << constraint->isConstraintActive() << + " value " << constraint->getConstraintValue( time_n ) ); + if( constraint->isConstraintActive() && constraint->checkViolation( *limitingConstraint, time_n )) + { + limitingConstraint=constraint; + setControl( static_cast< WellControls::Control >(constraint->getControl()) ); // tjb old + setCurrentConstraint( limitingConstraint ); + GEOS_LOG_RANK_IF ( getLogLevel() > 4 && subRegion.isLocallyOwned(), + " Well " << subRegion.getName() << " New Limiting Constraint " << constraint->getName() << " active " << constraint->isConstraintActive() << + " value " << constraint->getConstraintValue( time_n ) ); + solveConstraint ( constraint, time_n, + dt, + cycleNumber, + coupledIterationNumber, + domain, + mesh, + elemManager, + subRegion, + dofManager ); + // tjb. this is likely not needed. set in update state + constraint->setBHP ( getReference< real64 >( viewKeyStruct::currentBHPString() )); + if( isCompositional()) + { + constraint->setPhaseVolumeRates ( getReference< array1d< real64 > >( + viewKeyStruct::currentPhaseVolRateString() ) ); + constraint->setTotalVolumeRate ( getReference< real64 >( + viewKeyStruct::currentTotalVolRateString() )); + constraint->setMassRate( getReference< real64 >( viewKeyStruct::currentMassRateString() )); + } + else + { + constraint->setBHP ( getReference< real64 >( viewKeyStruct::currentBHPString() )); + constraint->setTotalVolumeRate ( getReference< real64 >( + viewKeyStruct::currentVolRateString() )); + } + + } + } + solveConstraint ( limitingConstraint, time_n, + dt, + cycleNumber, + coupledIterationNumber, + domain, + mesh, + elemManager, + subRegion, + dofManager ); + if( isCompositional()) + { + limitingConstraint->setPhaseVolumeRates ( getReference< array1d< real64 > >( + viewKeyStruct::currentPhaseVolRateString() ) ); + limitingConstraint->setTotalVolumeRate ( getReference< real64 >( + viewKeyStruct::currentTotalVolRateString() )); + limitingConstraint->setMassRate( getReference< real64 >( viewKeyStruct::currentMassRateString() )); + } + else + { + limitingConstraint->setBHP ( getReference< real64 >( viewKeyStruct::currentBHPString() )); + limitingConstraint->setTotalVolumeRate( getReference< real64 >( viewKeyStruct::currentVolRateString() )); + } + GEOS_LOG_RANK_IF ( getLogLevel() > 4 && subRegion.isLocallyOwned(), + " Well " << subRegion.getName() << " Limiting Constraint " << limitingConstraint->getName() << " " << limitingConstraint->bottomHolePressure() << " " << limitingConstraint->phaseVolumeRates() << " " << + limitingConstraint->totalVolumeRate() << " " << limitingConstraint->massRate()); + + return true; +} + +void WellControls::setupWellDofs( DomainPartition & domain, WellElementRegion & wellElementRegion, + string const & meshBodyName, MeshLevel const & meshLevel ) +{ + if( !m_dofManagerInitialized ) + { + m_dofManagerInitialized=true; + m_wellNewtonSolver.setupSystem( *this, domain, meshBodyName, meshLevel, wellElementRegion ); + + } +} + + +void WellControls::solveConstraint( WellConstraintBase *constraint, + real64 const & time_n, + real64 const & dt, + integer const cycleNumber, + integer const coupledIterationNumber, + DomainPartition & domain, + MeshLevel & mesh, + ElementRegionManager & elemManager, + WellElementSubRegion & subRegion, + DofManager const & dofManager ) +{ + GEOS_UNUSED_VAR( dt ); + GEOS_UNUSED_VAR( cycleNumber ); + GEOS_UNUSED_VAR( domain ); + GEOS_UNUSED_VAR( mesh ); + GEOS_UNUSED_VAR( elemManager ); + GEOS_UNUSED_VAR( dofManager ); + + bool useEstimator = coupledIterationNumber < estimateSolution(); + if( useEstimator ) + { + + if( getLogLevel() > 4 ) + { + GEOS_LOG_RANK_0( "Well " <getName() << " value " << constraint->getConstraintValue( time_n ) << " active " << + constraint->isConstraintActive() ); + } + if( constraint->isConstraintActive() ) + { + setControl( static_cast< WellControls::Control >(constraint->getControl()) ); // tjb old + setCurrentConstraint( constraint ); + // If a well is opened and then timestep is cut resulting in the well being shut, if the well is opened +// the well initialization code requires control type to by synced + integer owner = -1; +// Only subregion owner evaluates well control and control changes need to be broadcast to all ranks + if( subRegion.isLocallyOwned() ) + { + owner = MpiWrapper::commRank( MPI_COMM_GEOS ); + } + owner = MpiWrapper::max( owner ); + WellControls::Control wellControl = getControl(); + MpiWrapper::broadcast( wellControl, owner ); + setControl( wellControl ); + + m_wellNewtonSolver.solveNonlinearSystem( *this, time_n, + dt, + cycleNumber, + domain, + mesh, + elemManager, + subRegion ); + + // Store computed well quantities for this constraint + constraint->setBHP ( getReference< real64 >( viewKeyStruct::currentBHPString() )); + if( isCompositional() ) + { + + constraint->setPhaseVolumeRates ( getReference< array1d< real64 > >( + viewKeyStruct::currentPhaseVolRateString() ) ); + constraint->setTotalVolumeRate ( getReference< real64 >( + viewKeyStruct::currentTotalVolRateString() )); + constraint->setMassRate( getReference< real64 >( viewKeyStruct::currentMassRateString() )); + } + else + { + constraint->setBHP ( getReference< real64 >( viewKeyStruct::currentBHPString() )); + constraint->setTotalVolumeRate ( getReference< real64 >( + viewKeyStruct::currentVolRateString() )); + } + if( getLogLevel() > 4 ) + { + GEOS_LOG_RANK_0( "Well " <getName() << " bhp " << constraint->bottomHolePressure() << " phaseVolRate " << + constraint->phaseVolumeRates() << " totalVolRate " << constraint->totalVolumeRate() << " massRate " << constraint->massRate()); + } + } + + + } + +} + +void +WellControls::assembleSystem( real64 const & time_n, + real64 const & dt, + integer const cycleNumber, + ElementRegionManager & elemManager, + WellElementSubRegion & subRegion, + DofManager const & dofManager, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) +{ + GEOS_UNUSED_VAR( cycleNumber ); + assembleWellAccumulationTerms( time_n, dt, subRegion, dofManager, localMatrix.toViewConstSizes(), localRhs ); + + assembleWellConstraintTerms( time_n, dt, subRegion, dofManager, localMatrix.toViewConstSizes(), localRhs ); + + assembleWellPressureRelations( time_n, dt, subRegion, dofManager, localMatrix.toViewConstSizes(), localRhs ); + computeWellPerforationRates( time_n, dt, elemManager, subRegion ); + assembleWellFluxTerms( time_n, dt, subRegion, dofManager, localMatrix.toViewConstSizes(), localRhs ); + +} } //namespace geos diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/WellControls.hpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellControls.hpp index 301c3c42f7c..eafd4c67a85 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/wells/WellControls.hpp +++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellControls.hpp @@ -20,12 +20,23 @@ #ifndef GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_WELLCONTROLS_HPP #define GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_WELLCONTROLS_HPP - +#include "physicsSolvers/PhysicsSolverBase.hpp" #include "common/format/EnumStrings.hpp" #include "dataRepository/Group.hpp" #include "functions/TableFunction.hpp" #include "constitutive/fluid/multifluid/MultiFluidBase.hpp" +#include "physicsSolvers/fluidFlow/wells/WellInjectionConstraint.hpp" +#include "physicsSolvers/fluidFlow/wells/WellProductionConstraint.hpp" +#include "physicsSolvers/fluidFlow/wells/WellBHPConstraints.hpp" +#include "physicsSolvers/fluidFlow/wells/WellVolumeRateConstraint.hpp" +#include "physicsSolvers/fluidFlow/wells/WellPhaseVolumeRateConstraint.hpp" +#include "physicsSolvers/fluidFlow/wells/WellMassRateConstraint.hpp" +#include "physicsSolvers/fluidFlow/wells/WellLiquidRateConstraint.hpp" +#include "constitutive/fluid/multifluid/MultiFluidBase.hpp" +#include "constitutive/fluid/singlefluid/SingleFluidBase.hpp" +#include "physicsSolvers/fluidFlow/wells/WellNewtonSolver.hpp" + namespace geos { namespace dataRepository @@ -41,12 +52,12 @@ static constexpr auto wellControls = "WellControls"; * @class WellControls * @brief This class describes the controls used to operate a well. */ -class WellControls : public dataRepository::Group +class WellControls : public dataRepository::Group //public PhysicsSolverBase { public: /** Type of wells - * Either producer or injector. + * Either producer or injector */ enum class Type : integer { @@ -123,34 +134,399 @@ class WellControls : public dataRepository::Group ///@} + + /// String used to form the solverName used to register single-physics solvers in CoupledSolver + static string coupledSolverAttributePrefix() { return "well"; } + /** + * @brief Create a new geometric object (box, plane, etc) as a child of this group. + * @param childKey the catalog key of the new geometric object to create + * @param childName the name of the new geometric object in the repository + * @return the group child + */ + virtual Group * createChild( string const & childKey, string const & childName ) override; + /// Expand catalog for schema generation + + virtual void expandObjectCatalogs() override; + + virtual void registerWellDataOnMesh( WellElementSubRegion & subRegion ) = 0; + virtual void setConstitutiveNames( ElementSubRegionBase & subRegion ) const = 0; + /** + * @brief Create well separator + */ + virtual void createSeparator( WellElementSubRegion & subRegion ) = 0; + + /** + * @defgroup WellManager Interface Functions + * + * These functions provide the primary interface that is required for derived classes + * The "Well" versions apply to individual well subRegions, whereas the others apply to all wells + */ + /**@{*/ + + virtual void validateWellConstraints( real64 const & time_n, + real64 const & dt, + WellElementSubRegion const & subRegion ) = 0; + + virtual bool isCompositional() const = 0; + /** + * * @brief Initialize well for the beginning of a simulation or restart + * @param domain the domain + * @param mesh the mesh level + * @param subRegion the well subRegion + * @param time_n the current time + */ + virtual void initializeWell( DomainPartition & domain, MeshLevel & mesh, WellElementSubRegion & subRegion, real64 const & time_n ) = 0; + /** + * @brief function to set the next time step size + * @param[in] currentTime the current time + * @param[in] currentDt the current time step size + * @param[in] domain the domain object + * @return the prescribed time step size + */ + real64 setNextDt( real64 const & currentTime, + real64 const & currentDt, + WellElementSubRegion & subRegion ); + // Bring the base class implicitStepSetup into scope to avoid hiding the overloaded virtual function + + + virtual void implicitStepSetup( real64 const & time_n, + real64 const & GEOS_UNUSED_PARAM( dt ), + ElementRegionManager & elemManager, + WellElementSubRegion & subRegion ) = 0; + + virtual void + implicitStepComplete( real64 const & time, + real64 const & dt, + WellElementSubRegion const & subRegion ) = 0; + virtual real64 updateSubRegionState( ElementRegionManager const & elemManager, WellElementSubRegion & subRegion ) = 0; + + /** + * @brief Function to evaluate well constraints after applying the solution update + * @param time_n the time at the beginning of the time step + * @param subRegion the well subRegion + * @return true if all constraints are satisfied, false otherwise + */ + bool evaluateConstraints( real64 const & time_n, + WellElementSubRegion & subRegion ); + + /** + * @brief Function to evaluate well constraints after applying the solution update + * @param time_n the time at the beginning of the time step + * @param subRegion the well subRegion + * @return true if all constraints are satisfied, false otherwise + */ + bool evaluateConstraints( real64 const & time_n, + real64 const & dt, + integer const cycleNumber, + integer const coupledIterationNumber, + DomainPartition & domain, + MeshLevel & mesh, + ElementRegionManager & elemManager, + WellElementSubRegion & subRegion, + DofManager const & dofManager ); + + void solveConstraint( WellConstraintBase *constraint, + real64 const & time_n, + real64 const & dt, + integer const cycleNumber, + integer const coupledIterationNumber, + DomainPartition & domain, + MeshLevel & mesh, + ElementRegionManager & elemManager, + WellElementSubRegion & subRegion, + DofManager const & dofManager ); + + void assembleSystem( real64 const & time_n, + real64 const & dt, + integer const cycleNumber, + ElementRegionManager & elemManager, + WellElementSubRegion & subRegion, + DofManager const & dofManager, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ); + /** + * @brief assembles the accumulation term for an individual well + * @param time_n time at the beginning of the time step + * @param dt the time step size + * @param subRegion the well subregion containing all the primary and dependent fields + * @param dofManager degree-of-freedom manager associated with the linear system + * @param matrix the system matrix + * @param rhs the system right-hand side vector + */ + virtual void assembleWellAccumulationTerms( real64 const & time, + real64 const & dt, + WellElementSubRegion & subRegion, + DofManager const & dofManager, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) = 0; + /** + * @brief assembles the well momentum terms for an individual well + * @param time_n time at the beginning of the time step + * @param dt the time step size + * @param subRegion the well subregion containing all the primary and dependent fields + * @param dofManager degree-of-freedom manager associated with the linear system + * @param matrix the system matrix + * @param rhs the system right-hand side vector + */ + virtual void assembleWellPressureRelations( real64 const & time_n, + real64 const & dt, + WellElementSubRegion const & subRegion, + DofManager const & dofManager, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) = 0; + /** + * @brief assembles the well constraint terms for an individual well + * @param time_n time at the beginning of the time step + * @param dt the time step size + * @param subRegion the well subregion containing all the primary and dependent fields + * @param dofManager degree-of-freedom manager associated with the linear system + * @param matrix the system matrix + * @param rhs the system right-hand side vector + */ + virtual void assembleWellConstraintTerms( real64 const & time_n, + real64 const & dt, + WellElementSubRegion const & subRegion, + DofManager const & dofManager, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) = 0; + + /** + * @brief Recompute the perforation rates for all the wells + * @param time_n the time at the beginning of the time step + * @param dt the time step size + * @param elemManager the element region manager + * @param subRegion the well subregion containing all the primary and dependent fields + */ + virtual void computeWellPerforationRates( real64 const & time_n, + real64 const & GEOS_UNUSED_PARAM( dt ), + ElementRegionManager & elemManager, + WellElementSubRegion & subRegion ) = 0; + + /** + * @brief assembles the flux terms for individual well for all connections between well elements + * @param time_n previous time value + * @param dt time step + * @param subRegion the well subregion containing all the primary and dependent fields + * @param dofManager degree-of-freedom manager associated with the linear system + * @param matrix the system matrix + * @param rhs the system right-hand side vector + */ + virtual void assembleWellFluxTerms( real64 const & time, + real64 const & dt, + WellElementSubRegion const & subRegion, + DofManager const & dofManager, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) = 0; + virtual real64 + calculateWellResidualNorm( real64 const & time_n, + real64 const & dt, + NonlinearSolverParameters const & nonlinearSolverParameters, + WellElementSubRegion const & subRegion, + DofManager const & dofManager, + arrayView1d< real64 const > const & localRhs ) = 0; + + virtual array1d< real64 > + calculateLocalWellResidualNorm( real64 const & time_n, + real64 const & dt, + NonlinearSolverParameters const & nonlinearSolverParameters, + WellElementSubRegion const & subRegion, + DofManager const & dofManager, + arrayView1d< real64 const > const & localRhs ) = 0; + + virtual real64 + scalingForWellSystemSolution( WellElementSubRegion & subRegion, + DofManager const & dofManager, + arrayView1d< real64 const > const & localSolution ) = 0; + + virtual bool + checkWellSystemSolution( WellElementSubRegion & subRegion, + DofManager const & dofManager, + arrayView1d< real64 const > const & localSolution, + real64 const scalingFactor ) = 0; + + virtual void + applyWellSystemSolution( DofManager const & dofManager, + arrayView1d< real64 const > const & localSolution, + real64 const scalingFactor, + real64 const dt, + DomainPartition & domain, + MeshLevel & mesh, + WellElementSubRegion & subRegion ) = 0; + + virtual void applyWellBoundaryConditions( real64 const time_n, + real64 const dt, + ElementRegionManager & elemManager, + WellElementSubRegion & subRegion, + DofManager const & dofManager, + arrayView1d< real64 > const & localRhs, + CRSMatrixView< real64, globalIndex const > const & localMatrix ) = 0; + /** + * @brief Recompute all dependent quantities from primary variables (including constitutive models) + * @param subRegion the well subregion containing all the primary and dependent fields + */ + virtual real64 updateWellState( ElementRegionManager const & elemManager, WellElementSubRegion & subRegion ) = 0; + + /** + * @brief Reset the well state to the beginning of the time step + * @param subRegion the well subregion containing all the primary and dependent fields + */ + virtual void resetStateToBeginningOfStep( ElementRegionManager const & elemManager, WellElementSubRegion & subRegion ) = 0; + + virtual void postInputInitialization() override; + + virtual void initializeWellPostInitialConditionsPreSubGroups( WellElementSubRegion & subRegion ) = 0; + virtual void printRates( real64 const & time_n, + real64 const & dt, + WellElementSubRegion const & subRegion ) = 0; + /**@}*/ + /** + * @brief Apply a given functor to a container if the container can be + * cast to one of the specified types. + * @tparam CASTTYPE the first type that will be used in the attempted casting of container + * @tparam CASTTYPES a variadic list of types that will be used in the attempted casting of container + * @tparam CONTAINERTYPE the type of container + * @tparam LAMBDA the type of lambda function to call in the function + * @param[in] container a pointer to the container which will be passed to the lambda function + * @param[in] lambda the lambda function to call in the function + * @return a boolean to indicate whether the lambda was successfully applied to the container. + */ + template< typename T0, typename T1, typename ... CASTTYPES, typename CONTAINERTYPE, typename LAMBDA > + static bool applyLambdaToContainer( CONTAINERTYPE container, LAMBDA && lambda ) + { + using Pointee = std::remove_pointer_t< std::remove_reference_t< CONTAINERTYPE > >; + using T = std::conditional_t< std::is_const< Pointee >::value, T0 const, T0 >; + T * const castedContainer = dynamic_cast< T * >( container ); + + if( castedContainer != nullptr ) + { + lambda( *castedContainer ); + return true; + } + + return applyLambdaToContainer< T1, CASTTYPES... >( container, std::forward< LAMBDA >( lambda ) ); + } + + // Base case: no more types to try + template< typename CONTAINERTYPE, typename LAMBDA > + static bool applyLambdaToContainer( CONTAINERTYPE /*container*/, LAMBDA && /*lambda*/ ) + { + return false; + } + + // Single-type overload: try only T0 and stop + template< typename T0, typename CONTAINERTYPE, typename LAMBDA > + static bool applyLambdaToContainer( CONTAINERTYPE container, LAMBDA && lambda ) + { + using Pointee = std::remove_pointer_t< std::remove_reference_t< CONTAINERTYPE > >; + using T = std::conditional_t< std::is_const< Pointee >::value, T0 const, T0 >; + T * const castedContainer = dynamic_cast< T * >( container ); + + if( castedContainer != nullptr ) + { + lambda( *castedContainer ); + return true; + } + + return false; + } + + + /** + * @copydoc forInjectionConstraints(LAMBDA &&) + */ + template< typename GROUPTYPE = Group, typename ... GROUPTYPES, typename LAMBDA > + void forInjectionConstraints( LAMBDA && lambda ) const + { + for( auto const * constraintIter : m_injectionRateConstraintList ) + { + applyLambdaToContainer< GROUPTYPE, GROUPTYPES... >( constraintIter, [&]( auto const & castedSubGroup ) + { + lambda( castedSubGroup ); + } ); + } + } + + // non-const overload + template< typename GROUPTYPE = Group, typename ... GROUPTYPES, typename LAMBDA > + void forInjectionConstraints( LAMBDA && lambda ) + { + for( auto * constraintIter : m_injectionRateConstraintList ) + { + applyLambdaToContainer< GROUPTYPE, GROUPTYPES... >( constraintIter, [&]( auto & castedSubGroup ) + { + lambda( castedSubGroup ); + } ); + } + } + /** + * @copydoc forProductionConstraints(LAMBDA &&) + */ + template< typename GROUPTYPE = Group, typename ... GROUPTYPES, typename LAMBDA > + void forProductionConstraints( LAMBDA && lambda ) const + { + for( auto const * constraintIter : m_productionRateConstraintList ) + { + applyLambdaToContainer< GROUPTYPE, GROUPTYPES... >( constraintIter, [&]( auto const & castedSubGroup ) + { + lambda( castedSubGroup ); + } ); + } + } + + // non-const overload + template< typename GROUPTYPE = Group, typename ... GROUPTYPES, typename LAMBDA > + void forProductionConstraints( LAMBDA && lambda ) + { + for( auto * constraintIter : m_productionRateConstraintList ) + { + applyLambdaToContainer< GROUPTYPE, GROUPTYPES... >( constraintIter, [&]( auto & castedSubGroup ) + { + lambda( castedSubGroup ); + } ); + } + } /** * @name Getters / Setters */ ///@{ /** - * @brief Set the control type to BHP and set a numerical value for the control. - * @param[in] val value for the BHP control + * @brief Get the Constitutive Name object + * + * @tparam CONSTITUTIVE_BASE_TYPE the base type of the constitutive model. + * @param subRegion the element subregion on which the constitutive model is registered + * @return the name name of the constitutive model of type CONSTITUTIVE_BASE_TYPE registered on the subregion. */ - void switchToBHPControl( real64 const & val ); + template< typename CONSTITUTIVE_BASE_TYPE > + static string getConstitutiveName( ElementSubRegionBase const & subRegion ); /** - * @brief Set the control type to total rate and set a numerical value for the control. - * @param[in] val value for the total volumetric rate + * @brief Register wrapper with given name and store constitutive model name on the subregion + * + * @tparam CONSTITUTIVE the base type of the constitutive model. + * @param subRegion the subregion on which the constitutive model is registered. + * @param wrapperName the wrapper name to register. + * @param constitutiveType the type description of the constitutive model. */ - void switchToTotalRateControl( real64 const & val ); + template< typename CONSTITUTIVE > + void setConstitutiveName( ElementSubRegionBase & subRegion, string const & wrapperName, string const & constitutiveType ) const; /** - * @brief Set the control type to mass rate and set a numerical value for the control. - * @param[in] val value for the mass rate + * @brief return the list of target regions + * @return the array of region names */ - void switchToMassRateControl( real64 const & val ); + string_array const & getTargetRegionNames() const {return m_targetRegionNames;} + /** + * @brief Get the control type for the well. + * @return the Control enum enforced at the well + */ + std::string getFlowSolverName() const { return m_flowSolverName; } /** - * @brief Set the control type to phase rate and set a numerical value for the control. - * @param[in] val value for the phase volumetric rate + * @brief Set the control type for the well. + * @param[in] flowSolverName the name of the flow solver */ - void switchToPhaseRateControl( real64 const & val ); + void setFlowSolverName( const std::string & flowSolverName ) { m_flowSolverName = flowSolverName; } /** * @brief Get the control type for the well. @@ -171,10 +547,10 @@ class WellControls : public dataRepository::Group Control getInputControl() const { return m_inputControl; } /** - * @brief Getter for the reference elevation where the BHP control is enforced - * @return the reference elevation + * @brief getter for esitmator switch + * @return True if estimate well solution */ - real64 getReferenceElevation() const { return m_refElevation; } + integer estimateSolution() const { return m_estimateSolution; } /** * @brief Getter for the reference gravity coefficient @@ -187,60 +563,38 @@ class WellControls : public dataRepository::Group */ void setReferenceGravityCoef( real64 const & refGravCoef ) { m_refGravCoef = refGravCoef; } - /** - * @brief Get the target bottom hole pressure value. - * @return a value for the target bottom hole pressure + * @brief Returns the target bottom hole pressure value. + * @param[in] targetTime time at which to evaluate the constraint + * @return the injector maximum bottom hole pressure or producer minimum bottom hole pressure */ - real64 getTargetBHP( real64 const & currentTime ) const - { - return m_targetBHPTable->evaluate( ¤tTime ); - } + real64 getTargetBHP( real64 const & targetTime ) const; - /** - * @brief Get the target total rate - * @return the target total rate - */ - real64 getTargetTotalRate( real64 const & currentTime ) const - { - return m_rateSign * m_targetTotalRateTable->evaluate( ¤tTime ); - } /** - * @brief Get the target phase rate - * @return the target phase rate + * @brief Const accessor for the temperature of the injection stream + * @return the temperature of the injection stream */ - real64 getTargetPhaseRate( real64 const & currentTime ) const - { - return m_rateSign * m_targetPhaseRateTable->evaluate( ¤tTime ); - } + real64 getInjectionTemperature() const; /** - * @brief Get the target mass rate - * @return the target mass rate + * @brief Const accessor for the injection stream + * @return the injection stream */ - real64 getTargetMassRate( real64 const & currentTime ) const - { - return m_rateSign * m_targetMassRateTable->evaluate( ¤tTime ); - } + arrayView1d< real64 const > getInjectionStream() const; /** - * @brief Get the target phase name - * @return the target phase name + * @brief Const accessor for the phase constraint index + * @return phase index associated with phase constraint */ - const string & getTargetPhaseName() const { return m_targetPhaseName; } + integer getConstraintPhaseIndex() const; /** - * @brief Const accessor for the composition of the injection stream - * @return a global component fraction vector + * @brief Return the reference elvation where pressure constraint is measured + * @return vertical location of constraint */ - arrayView1d< real64 const > getInjectionStream() const { return m_injectionStream; } + real64 getReferenceElevation() const; - /** - * @brief Const accessor for the temperature of the injection stream - * @return the temperature of the injection stream - */ - real64 getInjectionTemperature() const { return m_injectionTemperature; } /** * @brief Getter for the flag specifying whether we check rates at surface or reservoir conditions @@ -252,7 +606,7 @@ class WellControls : public dataRepository::Group * @brief Getter for the reservoir region associated with reservoir volume constraint * @return name of reservoir region */ - string referenceReservoirRegion() const { return m_referenceReservoirRegion; } + string getReferenceReservoirRegion() const { return m_referenceReservoirRegion; } /** * @brief Getter for the surface pressure when m_useSurfaceConditions == 1 @@ -278,12 +632,49 @@ class WellControls : public dataRepository::Group */ bool isProducer() const { return ( m_type == Type::PRODUCER ); } + /** + * @brief getter for iso/thermal switch + * @return True if thermal + */ + integer isThermal() const { return m_isThermal; } + + /** + * @brief setter for iso/thermal switch + * @param[in] isThermal + */ + + void setThermal( bool isThermal ) { m_isThermal=isThermal; } /** * @brief Is the well open (or shut) at currentTime, status initalized in WellSolverBase::implicitStepSetup * @return a boolean */ bool isWellOpen() const; + /** + * @brief Set the well state + * @param[in] open boolean + */ + void setWellState( bool open ); + /** + * @brief Get the well state + * @return a boolean + */ + bool getWellState() const; + + + /** + * @brief Set the current consrtaint + * @param[in] currentConstraint pointer to constraint + */ + void setCurrentConstraint( WellConstraintBase * currentConstraint ) { m_currentConstraint = currentConstraint;} + /** + * @brief Get the current consrtaint + * @return pointer to constraint + */ + WellConstraintBase * getCurrentConstraint() { return m_currentConstraint; } + WellConstraintBase const * getCurrentConstraint() const { return m_currentConstraint; } + + /** * @brief Getter for the flag to enable crossflow * @return the flag deciding whether crossflow is allowed or not @@ -302,6 +693,17 @@ class WellControls : public dataRepository::Group * @param[inout] nextDt the time step */ void setNextDtFromTables( real64 const & currentTime, real64 & nextDt ); + /** + * @brief Utility function to keep the well variables during a time step (used in + * poromechanics simulations) + * @param[in] keepVariablesConstantDuringInitStep flag to tell the solver to freeze its + * primary variables during a time step + * @detail This function is meant to be called by a specific task before/after the + * initialization step + */ + void setKeepVariablesConstantDuringInitStep( bool const keepVariablesConstantDuringInitStep ) + { m_keepVariablesConstantDuringInitStep = keepVariablesConstantDuringInitStep; } + /** * @brief setter for multi fluid separator @@ -314,6 +716,12 @@ class WellControls : public dataRepository::Group */ constitutive::MultiFluidBase & getMultiFluidSeparator() { return dynamicCast< constitutive::MultiFluidBase & >( *m_fluidSeparatorPtr ); } + /** + * @brief Getter for single fluid separator + * @return reference to separator + */ + constitutive::SingleFluidBase & getSingleFluidSeparator() { return dynamicCast< constitutive::SingleFluidBase & >( *m_fluidSeparatorPtr ); } + /** * @brief Getter for the reservoir average pressure when m_useSurfaceConditions == 0 * @return the pressure @@ -350,36 +758,54 @@ class WellControls : public dataRepository::Group * @return a Status */ WellControls::Status getWellStatus () const { return m_wellStatus; } + + + ///@} + + virtual string wellElementDofName() const = 0; + + virtual string resElementDofName() const = 0; + + /** + * @brief getter for the number of degrees of freedom per well element + * @return the number of dofs + */ + localIndex numDofPerWellElement() const { return m_numDofPerWellElement; } + + /** + * @brief getter for the number of degrees of freedom per mesh element + * @return the number of dofs + */ + localIndex numDofPerResElement() const { return m_numDofPerResElement; } + + + virtual localIndex numFluidComponents() const = 0; + + virtual localIndex numFluidPhases() const = 0; /** * @brief Struct to serve as a container for variable strings and keys. * @struct viewKeyStruct */ struct viewKeyStruct { + /// String key for the fluid model names + static constexpr char const * fluidNamesString() { return "fluidNames"; } + /// String key for the write CSV flag + static constexpr char const * writeCSVFlagString() { return "writeCSV"; } + static constexpr char const * timeStepFromTablesFlagString() { return "timeStepFromTables"; } +/// @return string for the targetRegions wrapper + static constexpr char const * targetRegionsString() { return "targetRegions"; } + /// String key for the well reference elevation (for BHP control) static constexpr char const * refElevString() { return "referenceElevation"; } /// String key for the well type static constexpr char const * typeString() { return "type"; } - /// String key for the well input control - static constexpr char const * inputControlString() { return "control"; } /// String key for the well current control static constexpr char const * currentControlString() { return "currentControl"; } - /// String key for the well target BHP - static constexpr char const * targetBHPString() { return "targetBHP"; } - /// String key for the well target rate - static constexpr char const * targetTotalRateString() { return "targetTotalRate"; } - /// String key for the well target phase rate - static constexpr char const * targetPhaseRateString() { return "targetPhaseRate"; } - /// String key for the well target phase name - static constexpr char const * targetPhaseNameString() { return "targetPhaseName"; } - /// String key for the well target phase name - static constexpr char const * targetMassRateString() { return "targetMassRate"; } - /// String key for the well injection stream - static constexpr char const * injectionStreamString() { return "injectionStream"; } - /// String key for the well injection temperature - static constexpr char const * injectionTemperatureString() { return "injectionTemperature"; } + /// String key for the well input control + static constexpr char const * inputControlString() { return "control"; } /// String key for checking the rates at surface conditions static constexpr char const * useSurfaceConditionsString() { return "useSurfaceConditions"; } /// String key for reference reservoir region @@ -388,14 +814,7 @@ class WellControls : public dataRepository::Group static constexpr char const * surfacePressureString() { return "surfacePressure"; } /// String key for the surface temperature static constexpr char const * surfaceTemperatureString() { return "surfaceTemperature"; } - /// string key for total rate table name - static constexpr char const * targetTotalRateTableNameString() { return "targetTotalRateTableName"; } - /// string key for phase rate table name - static constexpr char const * targetPhaseRateTableNameString() { return "targetPhaseRateTableName"; } - /// string key for mass rate table name - static constexpr char const * targetMassRateTableNameString() { return "targetMassRateTableName"; } - /// string key for BHP table name - static constexpr char const * targetBHPTableNameString() { return "targetBHPTableName"; } + /// string key for status table name static constexpr char const * statusTableNameString() { return "statusTableName"; } /// string key for perforation status table name @@ -405,23 +824,151 @@ class WellControls : public dataRepository::Group /// string key for the initial pressure coefficient static constexpr char const * initialPressureCoefficientString() { return "initialPressureCoefficient"; } + /// string key for the minimum BHP presssure for a producer + static constexpr char const * minimumBHPConstraintString() { return "MinimumBHPConstraint"; } + /// string key for the maximum BHP presssure for a injection + static constexpr char const * maximumBHPConstraintString() { return "MaximumBHPConstraint"; } + /// string key for the maximum phase rate for a producer + static constexpr char const * productionPhaseVolumeRateConstraintString() { return "ProductionPhaseVolumeRateConstraint"; } + /// string key for the maximum phase rate for a injection + static constexpr char const * injectionPhaseVolumeRateConstraint() { return "InjectionPhaseVolumeRateConstraint"; } + /// string key for the maximum volume rate for a producer + static constexpr char const * productionVolumeRateConstraint() { return "ProductionVolumeRateConstraint"; } + /// string key for the maximum volume rate for a injector + static constexpr char const * injectionVolumeRateConstraint() { return "InjectionVolumeRateConstraint"; } + /// string key for the maximum mass rate for a producer + static constexpr char const * productionMassRateConstraint() { return "ProductionMassRateConstraint"; } + /// string key for the maximum mass rate for a injector + static constexpr char const * injectionMassRateConstraint() { return "InjectionMassRateConstraint"; } + /// string key for the liquid rate for a producer + static constexpr char const * productionLiquidRateConstraint() { return "ProductionLiquidRateConstraint"; } + /// string key for the minimum BHP presssure for a producer + static constexpr char const * wellNewtonSolverString() { return "WellNewtonSolver"; } + + /// string key for the esitmate well solution flag + static constexpr char const * estimateWellSolutionString() { return "estimateWellSolution"; } + /// string key for the enable iso thermal estimator flag + static constexpr char const * enableIsoThermalEstimatorString() { return "enableIsoThermalEstimator"; } + + // control data (not registered on the mesh) + + static constexpr char const * massDensityString() { return "massDensity";} + + static constexpr char const * currentBHPString() { return "currentBHP"; } + + static constexpr char const * currentPhaseVolRateString() { return "currentPhaseVolumetricRate"; } + static constexpr char const * currentVolRateString() { return "currentVolRate"; } + + static constexpr char const * currentTotalVolRateString() { return "currentTotalVolumetricRate"; } + + static constexpr char const * currentMassRateString() { return "currentMassRate"; } + } /// ViewKey struct for the WellControls class viewKeysWellControls; + void setPerforationStatus( real64 const & time_n, WellElementSubRegion & subRegion ); + void setGravCoef( WellElementSubRegion & subRegion, R1Tensor const & gravVector ); + /** + * @brief Set next time step based on a table function + * @param[in] table the table function + * @param[in] currentTime the current time + * @param[inout] nextDt the time step + */ static void setNextDtFromTable( TableFunction const * table, real64 const currentTime, real64 & nextDt ); -protected: + /** + * @brief Create a constraint + * @tparam ConstraintType the type of constraint to create + * @param[in] constraintName name to assign to the constraint + */ + template< typename ConstraintType > void createConstraint ( string const & constraintName ); - virtual void postInputInitialization() override; + /** + * @brief Getters for constraints + */ + MinimumBHPConstraint * getMinBHPConstraint() { return m_minBHPConstraint; }; + MinimumBHPConstraint * getMinBHPConstraint() const { return m_minBHPConstraint; }; + MaximumBHPConstraint * getMaxBHPConstraint() { return m_maxBHPConstraint; }; + MaximumBHPConstraint * getMaxBHPConstraint() const { return m_maxBHPConstraint; }; + + /** + * @brief Getters for constraint lists + */ + std::vector< WellConstraintBase * > getProdRateConstraints() { return m_productionRateConstraintList; }; + std::vector< WellConstraintBase * > getProdRateConstraints() const { return m_productionRateConstraintList; }; + std::vector< WellConstraintBase * > getInjRateConstraints() { return m_injectionRateConstraintList; } + std::vector< WellConstraintBase * > getInjRateConstraints() const { return m_injectionRateConstraintList; } + + /** + * @brief Set thermal effects enable + * @param[in] true/false + */ + void enableThermalEffects ( bool enable ) { m_thermalEffectsEnabled = enable; }; + + /** + * @brief Are thermal effects enabled + * @return true if thermal effects are enabled, false otherwise + */ + bool thermalEffectsEnabled() const { return m_thermalEffectsEnabled; } + + /** + * @brief Is isoThermalEstimator enabled + * @return true if isoThermalEstimator is enabled, false otherwise + */ + bool isoThermalEstimatorEnabled() const { return m_enableIsoThermalEstimator; } + + void setupWellDofs( DomainPartition & domain, WellElementRegion & wellElementRegion, string const & meshBodyName, MeshLevel const & meshLevel ); + WellNewtonSolver & getWellNewtonSolver() { return m_wellNewtonSolver; } + void selectWellConstraint( real64 const & time_n, + real64 const & dt, + integer const cycleNumber, + integer const coupledIterationNumber, + DomainPartition & domain, + MeshLevel & mesh, + ElementRegionManager & elemManager, + WellElementSubRegion & subRegion, + DofManager const & dofManager ); + +protected: + virtual void postRestartInitialization( )override; private: + /// List of names of regions the solver will be applied to + string_array m_targetRegionNames; + +protected: /// Well type (as Type enum) Type m_type; + /// Name of the flow solver managing this well + std::string m_flowSolverName; + + /// the max number of fluid phases + integer m_numPhases; + + /// the number of fluid components + integer m_numComponents; + + /// the number of Degrees of Freedom per well element + integer m_numDofPerWellElement; + + /// the number of Degrees of Freedom per reservoir element + integer m_numDofPerResElement; + + /// flag indicating whether thermal formulation is used + integer m_isThermal; + /// flag to freeze the initial state during initialization in coupled problems + bool m_keepVariablesConstantDuringInitStep; + /// rates output + integer m_writeCSV; + string const m_ratesOutputDir; + + // flag to enable time step selection base on rates/bhp tables coordinates + integer m_timeStepFromTables; /// Reference elevation real64 m_refElevation; @@ -434,33 +981,15 @@ class WellControls : public dataRepository::Group /// Well controls as a Control enum Control m_currentControl; - /// Target bottom hole pressure value - real64 m_targetBHP; - - /// Target rate value - real64 m_targetTotalRate; - - /// Target phase rate value - real64 m_targetPhaseRate; - - /// Name of the targeted phase - string m_targetPhaseName; - - /// Target MassRate - real64 m_targetMassRate; - - /// Vector with global component fractions at the injector - array1d< real64 > m_injectionStream; - - /// Temperature at the injector - real64 m_injectionTemperature; - /// Flag to decide whether rates are controlled at rates or surface conditions integer m_useSurfaceConditions; // Fuild model to compute properties for constraint equation user specified conditions std::unique_ptr< constitutive::ConstitutiveBase > m_fluidSeparatorPtr; + /// name of the fluid constitutive model used as a reference for component/phase description on subregion + string m_referenceFluidModelName; + /// Reservoir region associated with reservoir volume constraint string m_referenceReservoirRegion; @@ -470,21 +999,6 @@ class WellControls : public dataRepository::Group /// Surface temperature real64 m_surfaceTemp; - /// Total rate table name - string m_targetTotalRateTableName; - - /// Phase rate table name - string m_targetPhaseRateTableName; - - /// Mass rate table name - string m_targetMassRateTableName; - - /// BHP table name - string m_targetBHPTableName; - - /// Well status table name - string m_statusTableName; - /// Perforation status table name string m_perfStatusTableName; @@ -494,41 +1008,58 @@ class WellControls : public dataRepository::Group /// Tuning coefficient for the initial well pressure real64 m_initialPressureCoefficient; - /// Rate sign. +1 for injector, -1 for producer - real64 m_rateSign; + // Current constrint + WellConstraintBase * m_currentConstraint; + + // Minimum and maximum BHP and WHP constraints + MinimumBHPConstraint * m_minBHPConstraint; + MaximumBHPConstraint * m_maxBHPConstraint; - /// Total rate table - TableFunction const * m_targetTotalRateTable; + // Lists of rate constraints + std::vector< WellConstraintBase * > m_productionRateConstraintList; + std::vector< WellConstraintBase * > m_injectionRateConstraintList; - /// Phase rate table - TableFunction const * m_targetPhaseRateTable; + /// Well status + WellControls::Status m_wellStatus; - /// Mass rate table - TableFunction const * m_targetMassRateTable; + /// Well open flag + bool m_wellOpen; - /// BHP table - TableFunction const * m_targetBHPTable; + /// Well status table name + string m_statusTableName; /// Status table TableFunction const * m_statusTable; - /// Well status - WellControls::Status m_wellStatus; - - /// Region average pressure used in volume rate constraint calculations real64 m_regionAveragePressure; /// Region average temperature used in volume rate constraint calculations real64 m_regionAverageTemperature; + integer m_estimateSolution; + integer m_enableIsoThermalEstimator; + bool m_thermalEffectsEnabled; + + WellNewtonSolver m_wellNewtonSolver; + + + /// @brief Well DofManager + /// @details This DofManager is used to store the DOF numbers for the estimator + /// @note This DofManager is used in the assembly of the estimators linear system + DofManager m_estimatorDoFManager; + bool m_dofManagerInitialized; }; -ENUM_STRINGS( WellControls::Type, + +// Use local aliases to avoid accidental macro expansion of the tokens 'Type' or 'Control' +using WellControls_Type = WellControls::Type; +ENUM_STRINGS( WellControls_Type, "producer", "injector" ); -ENUM_STRINGS( WellControls::Control, +using WellControls_Control = WellControls::Control; +ENUM_STRINGS( WellControls_Control, "BHP", "phaseVolRate", "totalVolRate", @@ -536,6 +1067,62 @@ ENUM_STRINGS( WellControls::Control, "uninitialized" ); +template< typename CONSTITUTIVE > +void WellControls::setConstitutiveName( ElementSubRegionBase & subRegion, string const & wrapperName, string const & constitutiveType ) const +{ + subRegion.registerWrapper< string >( wrapperName ). + setPlotLevel( dataRepository::PlotLevel::NOPLOT ). + setRestartFlags( dataRepository::RestartFlags::NO_WRITE ). + setSizedFromParent( 0 ); + + string & constitutiveName = subRegion.getReference< string >( wrapperName ); + constitutiveName = getConstitutiveName< CONSTITUTIVE >( subRegion ); + GEOS_ERROR_IF( constitutiveName.empty(), GEOS_FMT( "{}: {} constitutive model not found on subregion {}", + getDataContext(), constitutiveType, subRegion.getName() ) ); +} +template< typename CONSTITUTIVE_BASE_TYPE > +string WellControls::getConstitutiveName( ElementSubRegionBase const & subRegion ) +{ + string validName; + dataRepository::Group const & constitutiveModels = subRegion.getConstitutiveModels(); + + constitutiveModels.forSubGroups< CONSTITUTIVE_BASE_TYPE >( [&]( dataRepository::Group const & model ) + { + GEOS_ERROR_IF( !validName.empty(), "A valid constitutive model was already found." ); + validName = model.getName(); + } ); + + return validName; +} + +/** + * @brief Get the Constitutive Model object + * @tparam BASETYPE the base type of the constitutive model. + * @tparam LOOKUP_TYPE the type of the key used to look up the constitutive model. + * @param dataGroup the data group containing the constitutive models. + * @param key the key used to look up the constitutive model. + * @return the constitutive model of type @p BASETYPE registered on the @p dataGroup with the key @p key. + */ +template< typename BASETYPE = constitutive::ConstitutiveBase, typename LOOKUP_TYPE > +static BASETYPE const & getConstitutiveModel( dataRepository::Group const & dataGroup, LOOKUP_TYPE const & key ) +{ + dataRepository::Group const & constitutiveModels = dataGroup.getGroup( ElementSubRegionBase::groupKeyStruct::constitutiveModelsString() ); + return constitutiveModels.getGroup< BASETYPE >( key ); +} +/** + * @brief Get the Constitutive Model object + * @tparam BASETYPE the base type of the constitutive model. + * @tparam LOOKUP_TYPE the type of the key used to look up the constitutive model. + * @param dataGroup the data group containing the constitutive models. + * @param key the key used to look up the constitutive model. + * @return the constitutive model of type @p BASETYPE registered on the @p dataGroup with the key @p key. + */ +template< typename BASETYPE = constitutive::ConstitutiveBase, typename LOOKUP_TYPE > +static BASETYPE & getConstitutiveModel( dataRepository::Group & dataGroup, LOOKUP_TYPE const & key ) +{ + dataRepository::Group & constitutiveModels = dataGroup.getGroup( ElementSubRegionBase::groupKeyStruct::constitutiveModelsString() ); + return constitutiveModels.getGroup< BASETYPE >( key ); +} } //namespace geos #endif //GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_WELLCONTROLS_HPP diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/WellFields.hpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellFields.hpp index 5f77922f4ee..3bb07399534 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/wells/WellFields.hpp +++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellFields.hpp @@ -34,6 +34,21 @@ namespace fields namespace well { +DECLARE_FIELD( connectionRate, + "wellElementConnectionRate", + array1d< real64 >, + 0, + LEVEL_0, + WRITE_AND_READ, + "Connection rate" ); + +DECLARE_FIELD( connectionRate_n, + "wellElementConnectionRate_n", + array1d< real64 >, + 0, + NOPLOT, + WRITE_AND_READ, + "Connection rate at the previous converged time step" ); DECLARE_FIELD( energyPerforationFlux, "energyPerforationFlux", array1d< real64 >, @@ -50,6 +65,61 @@ DECLARE_FIELD( dEnergyPerforationFlux, NO_WRITE, "Derivative of energy perforation flux with respect to pressure temperature and global component density (compositional only)" ); +DECLARE_FIELD( pressure, + "pressure", + array1d< real64 >, + 0, + LEVEL_0, + WRITE_AND_READ, + "Pressure" ); + +DECLARE_FIELD( pressure_n, + "pressure_n", + array1d< real64 >, + 0, + NOPLOT, + WRITE_AND_READ, + "Pressure at the previous converged time step" ); + +DECLARE_FIELD( temperature, + "temperature", + array1d< real64 >, + 0, + LEVEL_0, + WRITE_AND_READ, + "Temperature" ); + +DECLARE_FIELD( temperature_n, + "temperature_n", + array1d< real64 >, + 0, + NOPLOT, + WRITE_AND_READ, + "Temperature at the previous converged time step" ); + +DECLARE_FIELD( gravityCoefficient, + "gravityCoefficient", + array1d< real64 >, + 0, + NOPLOT, + WRITE_AND_READ, + "Gravity coefficient (dot product of gravity acceleration by gravity vector)" ); + +DECLARE_FIELD( pressureScalingFactor, + "pressureScalingFactor", + array1d< real64 >, + 1, + NOPLOT, + NO_WRITE, + "Scaling factors for pressure" ); + +DECLARE_FIELD( temperatureScalingFactor, + "temperatureScalingFactor", + array1d< real64 >, + 1, + NOPLOT, + NO_WRITE, + "Scaling factors for temperature" ); } diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/WellInjectionConstraint.cpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellInjectionConstraint.cpp new file mode 100644 index 00000000000..57145a752dc --- /dev/null +++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellInjectionConstraint.cpp @@ -0,0 +1,108 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * SPDX-License-Identifier: LGPL-2.1-only + * + * Copyright (c) 2016-2024 Lawrence Livermore National Security LLC + * Copyright (c) 2018-2024 TotalEnergies + * Copyright (c) 2018-2024 The Board of Trustees of the Leland Stanford Junior University + * Copyright (c) 2023-2024 Chevron + * Copyright (c) 2019- GEOS/GEOSX Contributors + * All rights reserved + * + * See top level LICENSE, COPYRIGHT, CONTRIBUTORS, NOTICE, and ACKNOWLEDGEMENTS files for details. + * ------------------------------------------------------------------------------------------------------------ + */ + +/* + * @file WellInjectionConstraint.cpp + */ + +#include "LogLevelsInfo.hpp" +#include "WellInjectionConstraint.hpp" +#include "WellConstants.hpp" +#include "dataRepository/InputFlags.hpp" +#include "functions/FunctionManager.hpp" + +#include "WellLiquidRateConstraint.hpp" +#include "WellMassRateConstraint.hpp" +#include "WellPhaseVolumeRateConstraint.hpp" +#include "WellVolumeRateConstraint.hpp" + +namespace geos +{ +using namespace dataRepository; + +template< typename ConstraintRateType > +InjectionConstraint< ConstraintRateType >::InjectionConstraint( string const & name, Group * const parent ) + : ConstraintRateType( name, parent ) +{ + // set rate sign for injectors (base class member) + this->m_rateSign = 1.0; + classtype::registerWrapper( injectionStreamKey::injectionStreamString(), &m_injectionStream ). + setDefaultValue( -1 ). + setSizedFromParent( 0 ). + setInputFlag( InputFlags::OPTIONAL ). + setDescription( "Global component densities of the injection stream [moles/m^3 or kg/m^3]" ); + + InjectionConstraint< ConstraintRateType >::registerWrapper( injectionStreamKey::injectionTemperatureString(), &m_injectionTemperature ). + setDefaultValue( -1 ). + setInputFlag( InputFlags::OPTIONAL ). + setDescription( "Temperature of the injection stream [K]" ); +} +template< typename ConstraintRateType > +InjectionConstraint< ConstraintRateType >::~InjectionConstraint() +{} +template< typename ConstraintRateType > +void InjectionConstraint< ConstraintRateType >::postInputInitialization() +{ + // Validate value and table options + ConstraintRateType::postInputInitialization(); + +// Validate the injection stream and temperature + validateInjectionStream( ); + +} +template< typename ConstraintRateType > +void InjectionConstraint< ConstraintRateType >::validateInjectionStream( ) +{ + GEOS_THROW_IF( (m_injectionStream.empty() && m_injectionTemperature >= 0) || + (!m_injectionStream.empty() && m_injectionTemperature < 0), + this->getName() << " " << this->getDataContext() << ": Both " + << injectionStreamKey::injectionStreamString() << " and " << injectionStreamKey::injectionTemperatureString() + << " must be specified for multiphase simulations", + InputError ); + + if( !m_injectionStream.empty()) + { + real64 sum = 0.0; + for( localIndex ic = 0; ic < m_injectionStream.size(); ++ic ) + { + GEOS_ERROR_IF( m_injectionStream[ic] < 0.0 || m_injectionStream[ic] > 1.0, + classtype::getWrapperDataContext( injectionStreamKey::injectionStreamString() ) << ": Invalid injection stream" ); + sum += m_injectionStream[ic]; + } + GEOS_THROW_IF( LvArray::math::abs( 1.0 - sum ) > std::numeric_limits< real64 >::epsilon(), + classtype::getWrapperDataContext( injectionStreamKey::injectionStreamString() ) << ": Invalid injection stream", + InputError ); + } +} + +// Register concrete wrapper constraint types and instantiate templates. +template class InjectionConstraint< LiquidRateConstraint >; +using InjectionLiquidRateConstraint = InjectionConstraint< LiquidRateConstraint >; +REGISTER_CATALOG_ENTRY( WellConstraintBase, InjectionLiquidRateConstraint, string const &, Group * const ) + +template class InjectionConstraint< MassRateConstraint >; +using InjectionMassRateConstraint = InjectionConstraint< MassRateConstraint >; +REGISTER_CATALOG_ENTRY( WellConstraintBase, InjectionMassRateConstraint, string const &, Group * const ) + +template class InjectionConstraint< PhaseVolumeRateConstraint >; +using InjectionPhaseVolumeRateConstraint = InjectionConstraint< PhaseVolumeRateConstraint >; +REGISTER_CATALOG_ENTRY( WellConstraintBase, InjectionPhaseVolumeRateConstraint, string const &, Group * const ) + +template class InjectionConstraint< VolumeRateConstraint >; +using InjectionVolumeRateConstraint = InjectionConstraint< VolumeRateConstraint >; +REGISTER_CATALOG_ENTRY( WellConstraintBase, InjectionVolumeRateConstraint, string const &, Group * const ) + + +} diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/WellInjectionConstraint.hpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellInjectionConstraint.hpp new file mode 100644 index 00000000000..1707f5f20e7 --- /dev/null +++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellInjectionConstraint.hpp @@ -0,0 +1,139 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * SPDX-License-Identifier: LGPL-2.1-only + * + * Copyright (c) 2016-2024 Lawrence Livermore National Security LLC + * Copyright (c) 2018-2024 TotalEnergies + * Copyright (c) 2018-2024 The Board of Trustees of the Leland Stanford Junior University + * Copyright (c) 2023-2024 Chevron + * Copyright (c) 2019- GEOS/GEOSX Contributors + * All rights reserved + * + * See top level LICENSE, COPYRIGHT, CONTRIBUTORS, NOTICE, and ACKNOWLEDGEMENTS files for details. + * ------------------------------------------------------------------------------------------------------------ + */ + +/* + * @file WellInjectionConstraint.hpp + */ + + +#ifndef GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_WELLINJECTIONCONSTRAINT_HPP +#define GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_WELLINJECTIONCONSTRAINT_HPP + +#include "common/format/EnumStrings.hpp" +#include "dataRepository/Group.hpp" +#include "functions/TableFunction.hpp" + +namespace geos +{ + +using namespace dataRepository; +/** + * @class InjectionConstraint + * @brief This class describes constraint used to control a injection well. + */ + +template< typename ConstraintType > +class InjectionConstraint : public ConstraintType +{ +public: + typedef InjectionConstraint< ConstraintType > classtype; + /** + * @name Constructor / Destructor + */ + ///@{ + + /** + * @brief Constructor for WellControls Objects. + * @param[in] name the name of this instantiation of WellControls in the repository + * @param[in] parent the parent group of this instantiation of WellControls + */ + explicit InjectionConstraint( string const & name, dataRepository::Group * const parent ); + + /** + * @brief Default destructor. + */ + ~InjectionConstraint() override; + + /** + * @brief Deleted default constructor. + */ + InjectionConstraint() = delete; + + /** + * @brief Deleted copy constructor. + */ + InjectionConstraint( InjectionConstraint const & ) = delete; + + /** + * @brief Deleted move constructor. + */ + InjectionConstraint( InjectionConstraint && ) = delete; + + /** + * @brief Deleted assignment operator. + * @return a reference to a constraint object + */ + InjectionConstraint & operator=( InjectionConstraint const & ) = delete; + + /** + * @brief Deleted move operator. + * @return a reference to a constraint object + */ + InjectionConstraint & operator=( InjectionConstraint && ) = delete; + + ///@} + + /** + * @brief name of the node manager in the object catalog + * @return string that contains the catalog name to generate a new Constraint object through the object catalog. + */ + static string catalogName() + { + return "Injection"+ConstraintType::catalogName(); + } + + virtual string getCatalogName() const override { return catalogName(); } + + struct injectionStreamKey + { + /// String key for the well injection stream + static constexpr char const * injectionStreamString() { return "injectionStream"; } + /// String key for the well injection temperature + static constexpr char const * injectionTemperatureString() { return "injectionTemperature"; } + }; + + /** + * @brief Const accessor for the composition of the injection stream + * @return a global component fraction vector + */ + arrayView1d< real64 const > getInjectionStream() const { return m_injectionStream; } + + /** + * @brief Const accessor for the temperature of the injection stream + * @return the temperature of the injection stream + */ + real64 getInjectionTemperature() const { return m_injectionTemperature; } + +protected: + + virtual void postInputInitialization() override; + static bool isViolated( const real64 & currentValue, const real64 & constraintValue ) + { return currentValue > constraintValue; } + + void validateInjectionStream(); +private: + + /// Vector with global component fractions at the injector + array1d< real64 > m_injectionStream; + + /// Temperature at the injector + real64 m_injectionTemperature; + +}; + + +} //namespace geos + +#endif //GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_WELLINJECTIONCONSTRAINT_HPP diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/WellLiquidRateConstraint.cpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellLiquidRateConstraint.cpp new file mode 100644 index 00000000000..3c84f70f1a7 --- /dev/null +++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellLiquidRateConstraint.cpp @@ -0,0 +1,76 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * SPDX-License-Identifier: LGPL-2.1-only + * + * Copyright (c) 2016-2024 Lawrence Livermore National Security LLC + * Copyright (c) 2018-2024 TotalEnergies + * Copyright (c) 2018-2024 The Board of Trustees of the Leland Stanford Junior University + * Copyright (c) 2023-2024 Chevron + * Copyright (c) 2019- GEOS/GEOSX Contributors + * All rights reserved + * + * See top level LICENSE, COPYRIGHT, CONTRIBUTORS, NOTICE, and ACKNOWLEDGEMENTS files for details. + * ------------------------------------------------------------------------------------------------------------ + */ + +/* + * @file WellLiquidRateConstraint.cpp + */ + +#include "LogLevelsInfo.hpp" +#include "WellLiquidRateConstraint.hpp" +#include "WellConstants.hpp" +#include "dataRepository/InputFlags.hpp" +#include "functions/FunctionManager.hpp" + +namespace geos +{ + +using namespace dataRepository; + + +LiquidRateConstraint::LiquidRateConstraint( string const & name, Group * const parent ) + : WellConstraintBase( name, parent ) +{ + this->registerWrapper( viewKeyStruct::liquidRateString(), &this->m_constraintValue ). + setDefaultValue( 0.0 ). + setInputFlag( InputFlags::OPTIONAL ). + setRestartFlags( RestartFlags::WRITE_AND_READ ). + setDescription( "Phase rate, (if useSurfaceCondSitions: [surface m^3/s]; else [reservoir m^3/s]) " ); + + this->registerWrapper( viewKeyStruct::phaseNamesString(), &m_phaseNames ). + setRTTypeName( rtTypes::CustomTypes::groupNameRefArray ). + setInputFlag( InputFlags::REQUIRED ). + setDescription( "List of fluid phase names defining the liquid" ); +} + +LiquidRateConstraint::~LiquidRateConstraint() +{} + +void LiquidRateConstraint::postInputInitialization() +{ + // Validate table options + WellConstraintBase::postInputInitialization(); + + // check constraint value + GEOS_THROW_IF( m_constraintValue < 0, + getWrapperDataContext( viewKeyStruct::liquidRateString() ) << ": Target value is negative", + InputError ); + + GEOS_THROW_IF ((m_constraintValue <= 0.0 && m_constraintScheduleTableName.empty()), + getName() << " " << getDataContext() << ": You need to specify a liquid rate constraint. \n" << + "The rate constraint can be specified using " << + "either " << viewKeyStruct::liquidRateString() << + " or " << WellConstraintBase::viewKeyStruct::constraintScheduleTableNameString(), + InputError ); +} + + +bool LiquidRateConstraint::checkViolation( WellConstraintBase const & currentConstraint, real64 const & currentTime ) const +{ + real64 const currentValue = currentConstraint.liquidRate(); + real64 const constraintValue = this->getConstraintValue( currentTime ); + return ( LvArray::math::abs( currentValue ) <= LvArray::math::abs( constraintValue ) ); +} + +} //namespace geos diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/WellLiquidRateConstraint.hpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellLiquidRateConstraint.hpp new file mode 100644 index 00000000000..45ef0b58a98 --- /dev/null +++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellLiquidRateConstraint.hpp @@ -0,0 +1,170 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * SPDX-License-Identifier: LGPL-2.1-only + * + * Copyright (c) 2016-2024 Lawrence Livermore National Security LLC + * Copyright (c) 2018-2024 TotalEnergies + * Copyright (c) 2018-2024 The Board of Trustees of the Leland Stanford Junior University + * Copyright (c) 2023-2024 Chevron + * Copyright (c) 2019- GEOS/GEOSX Contributors + * All rights reserved + * + * See top level LICENSE, COPYRIGHT, CONTRIBUTORS, NOTICE, and ACKNOWLEDGEMENTS files for details. + * ------------------------------------------------------------------------------------------------------------ + */ + +/* + * @file WellLiquidRateConstraint.hpp + */ + + +#ifndef GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_WELLLIQUIDRATECONSTRAINT_HPP +#define GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_WELLLIQUIDRATECONSTRAINT_HPP + +#include "common/format/EnumStrings.hpp" + +#include "functions/TableFunction.hpp" +#include "WellConstraintsBase.hpp" + +namespace geos +{ + + +/** + * @class LiquidRateConstraint + * @brief This class describes a Liquid rate constraint used to control of type WellConstraintType + */ + + +class LiquidRateConstraint : public WellConstraintBase +{ +public: + + + /** + * @name Constructor / Destructor + */ + ///@{ + + /** + * @brief Constructor for WellControls Objects. + * @param[in] name the name of this instantiation of WellControls in the repository + * @param[in] parent the parent group of this instantiation of WellControls + */ + explicit LiquidRateConstraint( string const & name, dataRepository::Group * const parent ); + + + /** + * @brief Default destructor. + */ + ~LiquidRateConstraint() override; + + /** + * @brief Deleted default constructor. + */ + LiquidRateConstraint() = delete; + + /** + * @brief Deleted copy constructor. + */ + LiquidRateConstraint( LiquidRateConstraint const & ) = delete; + + /** + * @brief Deleted move constructor. + */ + LiquidRateConstraint( LiquidRateConstraint && ) = delete; + + /** + * @brief Deleted assignment operator. + * @return a reference to a constraint object + */ + LiquidRateConstraint & operator=( LiquidRateConstraint const & ) = delete; + + /** + * @brief Deleted move operator. + * @return a reference to a constraint object + */ + LiquidRateConstraint & operator=( LiquidRateConstraint && ) = delete; + + /** + * @brief name of the node manager in the object catalog + * @return string that contains the catalog name to generate a new Constraint object through the object catalog. + */ + static string catalogName() + { + return "LiquidRateConstraint"; + } + ///@} + + /** + * @name Getters / Setters + */ + ///@{ + /** + * @brief Get the target phase name + * @return the target phase name + */ + const string_array & getPhaseNames() const { return m_phaseNames; } + + /** + * @brief Set phases associated with liquid constraint + * @param array of phase names + */ + void setPhaseNames( const string_array & phaseNames ) { m_phaseNames=phaseNames; } + + /** + * @brief Get the phase indices + * @return array of phase indices + */ + const array1d< integer > & getPhaseIndices() const { return m_phaseIndices; } + + ///@} + /** + * @brief Struct to serve as a container for variable strings and keys. + * @struct viewKeyStruct + */ + struct viewKeyStruct + { + /// String key for the liquid rate + static constexpr char const * liquidRateString() { return "liquidRate"; } + /// String key for the phases names + static constexpr char const * phaseNamesString() { return "phaseNames"; } + }; + + // Temp interface - tjb + virtual ConstraintTypeId getControl() const override { return ConstraintTypeId::LIQUIDRATE; }; + + virtual bool checkViolation( WellConstraintBase const & currentConstraint, real64 const & currentTime ) const override; + +protected: + + virtual void postInputInitialization() override; + + template< typename T > + void validateLiquidType( T const & fluidModel ) + { + m_phaseIndices.resize( m_phaseNames.size()); + for( size_t ip =0; ip m_phaseIndices; + +}; + + + +} //namespace geos + +#endif //GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_WELLLiquidRateConstraint_HPP diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/WellManager.cpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellManager.cpp new file mode 100644 index 00000000000..88df4c4d110 --- /dev/null +++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellManager.cpp @@ -0,0 +1,904 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * SPDX-License-Identifier: LGPL-2.1-only + * + * Copyright (c) 2016-2024 Lawrence Livermore National Security LLC + * Copyright (c) 2018-2024 TotalEnergies + * Copyright (c) 2018-2024 The Board of Trustees of the Leland Stanford Junior University + * Copyright (c) 2023-2024 Chevron + * Copyright (c) 2019- GEOS/GEOSX Contributors + * All rights reserved + * + * See top level LICENSE, COPYRIGHT, CONTRIBUTORS, NOTICE, and ACKNOWLEDGEMENTS files for details. + * ------------------------------------------------------------------------------------------------------------ + */ + +/** + * @file WellManager.cpp + */ + +#include "WellManager.hpp" + +#include "mesh/DomainPartition.hpp" +#include "mesh/PerforationFields.hpp" +#include "mesh/WellElementRegion.hpp" +#include "mesh/WellElementSubRegion.hpp" +#include "physicsSolvers/LogLevelsInfo.hpp" +#include "physicsSolvers/fluidFlow/wells/LogLevelsInfo.hpp" +#include "physicsSolvers/fluidFlow/wells/CompositionalMultiphaseWell.hpp" +#include "physicsSolvers/fluidFlow/wells/CompositionalMultiphaseWellFields.hpp" +#include "physicsSolvers/fluidFlow/wells/SinglePhaseWell.hpp" +#include "physicsSolvers/fluidFlow/wells/WellControls.hpp" + +#include "physicsSolvers/fluidFlow/wells/WellFields.hpp" +#include "fileIO/Outputs/OutputBase.hpp" +#include "functions/FunctionManager.hpp" +namespace geos +{ + +using namespace dataRepository; +using namespace fields; + +WellManager::WellManager( string const & name, + Group * const parent ) + : PhysicsSolverBase( name, parent ), + m_useMass( false ), + m_useTotalMassEquation( 1 ), + m_isThermal( 0 ), + m_isCompositional( true ), + m_minScalingFactor( 0.01 ), + m_allowCompDensChopping( 1 ) +{ + this->getWrapper< string >( viewKeyStruct::discretizationString() ). + setInputFlag( InputFlags::FALSE ); + + registerWrapper( viewKeyStruct::isThermalString(), &m_isThermal ). + setApplyDefaultValue( 0 ). + setInputFlag( InputFlags::OPTIONAL ). + setDescription( "Flag indicating whether the problem is thermal or not." ); + + + this->registerWrapper( viewKeyStruct::useMassFlagString(), &m_useMass ). + setApplyDefaultValue( 0 ). + setInputFlag( InputFlags::OPTIONAL ). + setDescription( "Use mass formulation instead of molar" ); + + this->registerWrapper( viewKeyStruct::useTotalMassEquationString(), &m_useTotalMassEquation ). + setApplyDefaultValue( 1 ). + setInputFlag( InputFlags::OPTIONAL ). + setDescription( "Use total mass equation" ); + + this->registerWrapper( viewKeyStruct::allowLocalCompDensChoppingString(), &m_allowCompDensChopping ). + setSizedFromParent( 0 ). + setInputFlag( InputFlags::OPTIONAL ). + setApplyDefaultValue( 1 ). + setDescription( "Flag indicating whether local (cell-wise) chopping of negative compositions is allowed" ); + + this->registerWrapper( viewKeyStruct::timeStepFromTablesFlagString(), &m_timeStepFromTables ). + setApplyDefaultValue( 0 ). + setInputFlag( dataRepository::InputFlags::OPTIONAL ). + setDescription ( "Choose time step to honor rates/bhp tables time intervals" ); + +} +Group * WellManager::createChild( string const & childKey, string const & childName ) +{ + static std::set< string > const childTypes = { + keys::compositionalMultiphaseWell, + keys::singlePhaseWell, + PhysicsSolverBase::groupKeyStruct::linearSolverParametersString(), + PhysicsSolverBase::groupKeyStruct::nonlinearSolverParametersString(), + }; + GEOS_ERROR_IF( childTypes.count( childKey ) == 0, + CatalogInterface::unknownTypeError( childKey, getDataContext(), childTypes ), + getDataContext() ); + if( childKey == keys::compositionalMultiphaseWell ) + { + setCompositional( true ); + return ®isterGroup< CompositionalMultiphaseWell >( childName ); + } + else if( childKey == keys::singlePhaseWell ) + { + setCompositional( false ); + return ®isterGroup< SinglePhaseWell >( childName ); + } + else + { + PhysicsSolverBase::createChild( childKey, childName ); + return nullptr; + } +} + +void WellManager::expandObjectCatalogs() +{ + createChild( keys::compositionalMultiphaseWell, keys::compositionalMultiphaseWell ); + createChild( keys::singlePhaseWell, keys::singlePhaseWell ); +} + +void WellManager::registerDataOnMesh( Group & meshBodies ) +{ + + //std::string const & flowSolverName = getParent().getName();//getGroup< CompositionalMultiphaseBase >().getName(); + // CompositionalMultiphaseBase const & flowSolver = getParent().getGroup< CompositionalMultiphaseBase >( getFlowSolverName() ); + + forDiscretizationOnMeshTargets( meshBodies, [&] ( string const &, + MeshLevel & mesh, + string_array const & regionNames ) + { + + ElementRegionManager & elemManager = mesh.getElemManager(); + + elemManager.forElementSubRegions< WellElementSubRegion >( regionNames, + [&]( localIndex const, + WellElementSubRegion & subRegion ) + { + + WellControls & well = getWellControls( subRegion ); + //well.setFlowSolverName( flowSolver.getName() ); + well.registerWellDataOnMesh( subRegion ); + well.setThermal( isThermal() ); + m_numFluidPhases = well.numFluidPhases(); + m_numFluidComponents = well.numFluidComponents(); + + } ); + } ); + // 1. Set key dimensions of the problem + // Empty check needed to avoid errors when running in schema generation mode. + + // 1 pressure + NC compositions + 1 connectionRate + temp if thermal + m_numDofPerWellElement = isThermal() ? m_numFluidComponents + 3 : m_numFluidComponents + 2; + // 1 pressure + NC compositions + temp if thermal + m_numDofPerResElement = isThermal() ? m_numFluidComponents + 2 : m_numFluidComponents + 1; + +} + +WellControls & WellManager::getWell( WellElementSubRegion const & subRegion ) +{ + return this->getGroup< WellControls >( subRegion.getWellControlsName()); +} +WellControls & WellManager::getWell( std::string const & wellControlsName ) +{ + return this->getGroup< WellControls >( wellControlsName ); +} + +WellControls const & WellManager::getWell( std::string const & wellControlsName ) const +{ + return this->getGroup< WellControls >( wellControlsName ); +} +void WellManager::implicitStepSetup( real64 const & time_n, + real64 const & dt, + DomainPartition & domain ) +{ + + forDiscretizationOnMeshTargets ( domain.getMeshBodies(), [&] ( string const & meshBodyName, + MeshLevel & mesh, + string_array const & regionNames ) + { + + ElementRegionManager & elemManager = mesh.getElemManager(); + + elemManager.forElementRegions< WellElementRegion >( regionNames, + [&]( localIndex const, + WellElementRegion & region ) + { + WellControls & well = getWell( region.getWellControlsName() ); + if( well.estimateSolution() ) + { + well.setupWellDofs( domain, region, meshBodyName, mesh ); + } + + } ) + ; + } ); + + forDiscretizationOnMeshTargets ( domain.getMeshBodies(), [&] ( string const & , + MeshLevel & mesh, + string_array const & regionNames ) + { + + ElementRegionManager & elemManager = mesh.getElemManager(); + + elemManager.forElementSubRegions< WellElementSubRegion >( regionNames, + [&]( localIndex const, + WellElementSubRegion & subRegion ) + { + WellControls & well = getWell( subRegion ); + well.implicitStepSetup( time_n, dt, elemManager, subRegion ); + } ) + ; + } ); +} +real64 +WellManager::setNextDt( real64 const & currentTime, const real64 & currentDt, geos::DomainPartition & domain ) +{ + + real64 nextDt = PhysicsSolverBase::setNextDt( currentTime, currentDt, domain ); + + if( m_timeStepFromTables ) + { + real64 nextDt_orig = nextDt; + real64 nextDtLocal = nextDt; + forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &, + MeshLevel & mesh, + string_array const & regionNames ) + { + mesh.getElemManager().forElementSubRegions< WellElementSubRegion >( regionNames, [&]( localIndex const, + WellElementSubRegion & subRegion ) + { + + WellControls & wellControls = getWellControls( subRegion ); + real64 nextDtWell = wellControls.setNextDt( currentTime, nextDt, subRegion ); + if( nextDtWell < nextDtLocal ) + { + nextDtLocal = nextDtWell; + } + } ); + } ); + // get the minimum across all ranks + nextDt = MpiWrapper::min< real64 >( nextDtLocal ); + if( getLogLevel() > 0 && nextDt < nextDt_orig ) + GEOS_LOG_RANK_0( GEOS_FMT( "{}: next time step based on tables coordinates = {}", getName(), nextDt )); + } + + return nextDt; +} +localIndex WellManager::numDofPerWellElement() const +{ + return m_numDofPerWellElement; +} + +localIndex WellManager::numDofPerResElement() const +{ + return m_numDofPerResElement; +} +integer WellManager::isThermal() const +{ + return m_isThermal; +} + +string WellManager::wellElementDofName() const +{ + return viewKeyStruct::dofFieldString(); +} + +string WellManager::resElementDofName() const +{ + if( isCompositional() ) + return CompositionalMultiphaseBase::viewKeyStruct::elemDofFieldString(); + else + return SinglePhaseBase::viewKeyStruct::elemDofFieldString(); +} + +localIndex WellManager::numFluidComponents() const +{ + return m_numFluidComponents; +} + +localIndex WellManager::numFluidPhases() const +{ + return m_numFluidPhases; +} +WellControls & WellManager::getWellControls( WellElementSubRegion const & subRegion ) +{ + return this->getGroup< WellControls >( subRegion.getWellControlsName()); +} + +WellControls const & WellManager::getWellControls( WellElementSubRegion const & subRegion ) const +{ + return this->getGroup< WellControls >( subRegion.getWellControlsName()); +} + +CompositionalMultiphaseWell & WellManager::getCompositionalMultiphaseWell( WellElementSubRegion const & subRegion ) +{ + return this->getGroup< CompositionalMultiphaseWell >( subRegion.getWellControlsName()); +} + +CompositionalMultiphaseWell const & WellManager::getCompositionalMultiphaseWell( WellElementSubRegion const & subRegion ) const +{ + return this->getGroup< CompositionalMultiphaseWell >( subRegion.getWellControlsName()); +} +void WellManager::initializePostSubGroups() +{ +#if 0 + GEOS_MARK_FUNCTION; + // Validate constitutive models + if( isCompositional() ) + { + DomainPartition & domain = this->getGroupByPath< DomainPartition >( "/Problem/domain" ); + constitutive::ConstitutiveManager const & cm = domain.getConstitutiveManager(); + CompositionalMultiphaseBase const & flowSolver = getParent().getGroup< CompositionalMultiphaseBase >( getFlowSolverName() ); + string const referenceFluidName = flowSolver.referenceFluidModelName(); + constitutive::MultiFluidBase const & referenceFluid = cm.getConstitutiveRelation< constitutive::MultiFluidBase >( m_referenceFluidModelName ); + + forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &, + MeshLevel const & mesh, + string_array const & regionNames ) + { + + mesh.getElemManager().forElementSubRegions< WellElementSubRegion >( regionNames, [&]( localIndex const, + WellElementSubRegion const & subRegion ) + { + string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString() ); + constitutive::MultiFluidBase const & fluid = getConstitutiveModel< constitutive::MultiFluidBase >( subRegion, fluidName ); + WellControls const & wellControls = getWellControls( subRegion ); + wellControls.validateFluidModel( fluid, referenceFluid ); + } ); + + } ); + } + else + { + // Single phase validation can be added here in the future + } +#endif +} + +void WellManager::setupDofs( DomainPartition const & domain, + DofManager & dofManager ) const +{ + map< std::pair< string, string >, string_array > meshTargets; + forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const & meshBodyName, + MeshLevel const & meshLevel, + string_array const & regionNames ) + { + string_array regions; + ElementRegionManager const & elementRegionManager = meshLevel.getElemManager(); + elementRegionManager.forElementRegions< WellElementRegion >( regionNames, + [&]( localIndex const, + WellElementRegion const & region ) + { + regions.emplace_back( region.getName() ); + } ); + auto const key = std::make_pair( meshBodyName, meshLevel.getName()); + meshTargets[key] = std::move( regions ); + } ); + + dofManager.addField( wellElementDofName(), + FieldLocation::Elem, + numDofPerWellElement(), + meshTargets ); + + dofManager.addCoupling( wellElementDofName(), + wellElementDofName(), + DofManager::Connector::Node ); +} + +void WellManager::assembleSystem( real64 const time, + real64 const dt, + DomainPartition & domain, + DofManager const & dofManager, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) +{ + + + // selects constraints one of 2 ways + // wellEstimator flag set to 0 => orginal logic rates are computed during update state and constraints are selected every newton + // iteration + // wellEstimator flag > 0 => well esitmator solved for each constraint and then selects the constraint + // => estimator solve only performed first "wellEstimator" iterations + NonlinearSolverParameters const & nonlinearParams = getNonlinearSolverParameters(); + IterationsStatistics const & iterationsStatistics = getIterationStats(); + //selectWellConstraint( time, dt, solverStatistics.m_numNewtonIterations, domain ); + + forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const & meshBodyName, + MeshLevel & meshLevel, + string_array const & regionNames ) + { + GEOS_UNUSED_VAR( meshBodyName ); + ElementRegionManager & elementRegionManager = meshLevel.getElemManager(); + elementRegionManager.forElementRegions< WellElementRegion >( regionNames, + [&]( localIndex const, + WellElementRegion & region ) + { + WellElementSubRegion & subRegion = region.getGroup( ElementRegionBase::viewKeyStruct::elementSubRegions() ) + .getGroup< WellElementSubRegion >( region.getSubRegionName() ); + WellControls & wellControls = getWellControls( subRegion ); + wellControls.selectWellConstraint( time, + dt, + iterationsStatistics.getNumTimeSteps(), + nonlinearParams.m_numNewtonIterations, + domain, + meshLevel, + elementRegionManager, + subRegion, + dofManager ); + + // assemble the accumulation term in the mass balance equations + wellControls.assembleWellAccumulationTerms( time, dt, subRegion, dofManager, localMatrix, localRhs ); + if( wellControls.isWellOpen() ) + { + // assemble the pressure relations between well elements + wellControls.assembleWellPressureRelations( time, dt, subRegion, dofManager, localMatrix, localRhs ); + // assemble well constraint terms + wellControls.assembleWellConstraintTerms( time, dt, subRegion, dofManager, localMatrix.toViewConstSizes(), localRhs ); + // compute the perforation rates (later assembled by the coupled solver) + wellControls.computeWellPerforationRates( time, dt, elementRegionManager, subRegion ); + // assemble the flux terms in the mass balance equations + wellControls.assembleWellFluxTerms( time, dt, subRegion, dofManager, localMatrix, localRhs ); + } + } ); + } ); + +} + + +void WellManager::resetStateToBeginningOfStep( DomainPartition & domain ) +{ + forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &, + MeshLevel & mesh, + string_array const & regionNames ) + { + + ElementRegionManager & elemManager = mesh.getElemManager(); + + elemManager.forElementSubRegions< WellElementSubRegion >( regionNames, + [&]( localIndex const, + WellElementSubRegion & subRegion ) + { + WellControls & wellControls = getWellControls( subRegion ); + wellControls.resetStateToBeginningOfStep( elemManager, subRegion ); + + + } ); + } ); +} + +void WellManager::implicitStepComplete( real64 const & time, + real64 const & dt, + DomainPartition & domain ) +{ + forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &, + MeshLevel & mesh, + string_array const & regionNames ) + { + + ElementRegionManager & elemManager = mesh.getElemManager(); + + elemManager.forElementSubRegions< WellElementSubRegion >( regionNames, + [&]( localIndex const, + WellElementSubRegion & subRegion ) + { + WellControls & wellControls = getWellControls( subRegion ); + wellControls.implicitStepComplete( time, dt, subRegion ); + } ); + } ); +} + +void WellManager::postRestartInitialization() +{ +#if 0 + DomainPartition & domain = this->getGroupByPath< DomainPartition >( "/Problem/domain" ); + forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &, + MeshLevel & mesh, + string_array const & regionNames ) + { + // loop over the wells + mesh.getElemManager().forElementSubRegions< WellElementSubRegion >( regionNames, [&]( localIndex const, + WellElementSubRegion & subRegion ) + + { + WellControls & wellControls = getWell( subRegion ); + wellControls.postRestartInitialization( ); + + } ); + } ); +#endif +} +void WellManager::initializePostInitialConditionsPreSubGroups() +{ + PhysicsSolverBase::initializePostInitialConditionsPreSubGroups(); + DomainPartition & domain = this->getGroupByPath< DomainPartition >( "/Problem/domain" ); + forDiscretizationOnMeshTargets ( domain.getMeshBodies(), [&] ( string const &, + MeshLevel & mesh, + string_array const & regionNames ) + { + + // loop over the wells + mesh.getElemManager().forElementSubRegions< WellElementSubRegion >( regionNames, [&]( localIndex const, + WellElementSubRegion & subRegion ) + { + // reconstruct local connectivity needed for flux calculations + subRegion.reconstructLocalConnectivity(); + WellControls & wellControls = getWellControls( subRegion ); + wellControls.initializeWellPostInitialConditionsPreSubGroups( subRegion ); + + + } ); + } ); +} +void WellManager::setKeepVariablesConstantDuringInitStep( bool const keepVariablesConstantDuringInitStep ) +{ + DomainPartition & domain = this->getGroupByPath< DomainPartition >( "/Problem/domain" ); + forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &, + MeshLevel & mesh, + string_array const & regionNames ) + { + // loop over the wells + mesh.getElemManager().forElementSubRegions< WellElementSubRegion >( regionNames, [&]( localIndex const, + WellElementSubRegion & subRegion ) + + { + WellControls & wellControls = getWellControls( subRegion ); + wellControls.setKeepVariablesConstantDuringInitStep( keepVariablesConstantDuringInitStep ); + + } ); + } ); +} +void WellManager::updateState( DomainPartition & domain ) +{ + GEOS_MARK_FUNCTION; + //tjb + real64 maxPhaseVolFrac = 0.0; + forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &, + MeshLevel & mesh, + string_array const & regionNames ) + { + ElementRegionManager & elemManager = mesh.getElemManager(); + elemManager.forElementSubRegions< WellElementSubRegion >( regionNames, [&]( localIndex const, + WellElementSubRegion & subRegion ) + { + WellControls & wellControls = getWellControls( subRegion ); + if( wellControls.getWellState()) + { + + real64 const maxRegionPhaseVolFrac = wellControls.updateWellState( elemManager, subRegion ); + + maxPhaseVolFrac = LvArray::math::max( maxRegionPhaseVolFrac, maxPhaseVolFrac ); + } + } ); + } ); + maxPhaseVolFrac = MpiWrapper::max( maxPhaseVolFrac ); + + GEOS_LOG_LEVEL_RANK_0( logInfo::Solution, + GEOS_FMT( " {}: Max well phase volume fraction change = {}", + getName(), fmt::format( "{:.{}f}", maxPhaseVolFrac, 4 ) ) ); + +} + +real64 +WellManager::calculateResidualNorm( real64 const & time_n, + real64 const & dt, + DomainPartition const & domain, + DofManager const & dofManager, + arrayView1d< real64 const > const & localRhs ) +{ + GEOS_MARK_FUNCTION; + + integer numNorm = 1; // mass balance + array1d< real64 > localResidualNorm, wellResidalNorm; + array1d< real64 > localResidualNormalizer; + + if( isThermal() ) + { + numNorm = 2; // mass balance and energy balance + } + localResidualNorm.resize( numNorm ); + + localResidualNormalizer.resize( numNorm ); + + string const wellDofKey = dofManager.getKey( wellElementDofName() ); + + forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &, + MeshLevel const & mesh, + string_array const & regionNames ) + { + + + ElementRegionManager const & elemManager = mesh.getElemManager(); + + elemManager.forElementSubRegions< WellElementSubRegion >( regionNames, + [&]( localIndex const, + WellElementSubRegion const & subRegion ) + { + + WellControls & wellControls = getWellControls( subRegion ); + + // step 1: compute the norm in the subRegion + if( true ) // tjb wellControls.isWellOpen( ) ) + { + wellResidalNorm = wellControls.calculateLocalWellResidualNorm( time_n, + dt, + m_nonlinearSolverParameters, + subRegion, + dofManager, + localRhs ); + for( integer i=0; i localResidualNorm[i] ) + { + localResidualNorm[i] = wellResidalNorm[i]; + } + } + } + else + { + for( integer i=0; i const & localSolution ) +{ + GEOS_MARK_FUNCTION; + + string const wellDofKey = dofManager.getKey( wellElementDofName() ); + + real64 scalingFactor = 1.0; + real64 localScalingFactor = 1.0; + if( isCompositional() ) + { + + + real64 maxDeltaPres = 0.0, maxDeltaCompDens = 0.0, maxDeltaTemp = 0.0; + real64 minPresScalingFactor = 1.0, minCompDensScalingFactor = 1.0, minTempScalingFactor = 1.0; + real64 localMaxDeltaPres = 0.0, localMaxDeltaCompDens = 0.0, localMaxDeltaTemp = 0.0; + real64 localMinPresScalingFactor = 1.0, localMinCompDensScalingFactor = 1.0, localMinTempScalingFactor = 1.0; + + forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &, + MeshLevel & mesh, + string_array const & regionNames ) + { + + ElementRegionManager & elemManager = mesh.getElemManager(); + elemManager.forElementSubRegions< WellElementSubRegion >( regionNames, + [&]( localIndex const, + WellElementSubRegion & subRegion ) + + { + CompositionalMultiphaseWell * wellControls = dynamic_cast< CompositionalMultiphaseWell * >(&getWellControls ( subRegion )); + localScalingFactor = wellControls->scalingForLocalSystemSolution( subRegion, + dofManager, + localMaxDeltaPres, + localMaxDeltaCompDens, + localMaxDeltaTemp, + localMinPresScalingFactor, + localMinCompDensScalingFactor, + localMinTempScalingFactor, + localSolution ); + maxDeltaPres = LvArray::math::max( localMaxDeltaPres, maxDeltaPres ); + maxDeltaCompDens = LvArray::math::max( localMaxDeltaCompDens, maxDeltaCompDens ); + maxDeltaTemp = LvArray::math::max( localMaxDeltaTemp, maxDeltaTemp ); + minPresScalingFactor = LvArray::math::min( localMinPresScalingFactor, minPresScalingFactor ); + minCompDensScalingFactor = LvArray::math::min( localMinCompDensScalingFactor, minCompDensScalingFactor ); + minTempScalingFactor = LvArray::math::min( localMinTempScalingFactor, minTempScalingFactor ); + scalingFactor = LvArray::math::min( localScalingFactor, scalingFactor ); + + } ); + } ); + + scalingFactor = MpiWrapper::min( scalingFactor ); + maxDeltaPres = MpiWrapper::max( maxDeltaPres ); + maxDeltaCompDens = MpiWrapper::max( maxDeltaCompDens ); + minPresScalingFactor = MpiWrapper::min( minPresScalingFactor ); + minCompDensScalingFactor = MpiWrapper::min( minCompDensScalingFactor ); + + string const massUnit = m_useMass ? "kg/m3" : "mol/m3"; + GEOS_LOG_LEVEL_RANK_0( logInfo::Solution, + GEOS_FMT( " {}: Max well pressure change: {} Pa (before scaling)", + getName(), GEOS_FMT( "{:.{}f}", maxDeltaPres, 3 ) ) ); + GEOS_LOG_LEVEL_RANK_0( logInfo::Solution, + GEOS_FMT( " {}: Max well component density change: {} {} (before scaling)", + getName(), GEOS_FMT( "{:.{}f}", maxDeltaCompDens, 3 ), massUnit ) ); + + if( m_isThermal ) + { + maxDeltaTemp = MpiWrapper::max( maxDeltaTemp ); + minTempScalingFactor = MpiWrapper::min( minTempScalingFactor ); + GEOS_LOG_LEVEL_RANK_0( logInfo::Solution, + GEOS_FMT( " {}: Max well temperature change: {} K (before scaling)", + getName(), GEOS_FMT( "{:.{}f}", maxDeltaTemp, 3 ) ) ); + } + + + GEOS_LOG_LEVEL_RANK_0( logInfo::Solution, + GEOS_FMT( " {}: Min well pressure scaling factor: {}", + getName(), minPresScalingFactor ) ); + GEOS_LOG_LEVEL_RANK_0( logInfo::Solution, + GEOS_FMT( " {}: Min well component density scaling factor: {}", + getName(), minCompDensScalingFactor ) ); + if( m_isThermal ) + { + GEOS_LOG_LEVEL_RANK_0( logInfo::Solution, + GEOS_FMT( " {}: Min well temperature scaling factor: {}", + getName(), minTempScalingFactor ) ); + } + + } + else + { + // Single phase well scaling- not implemented yet + scalingFactor=1.0; + } + return LvArray::math::max( scalingFactor, m_minScalingFactor ); + +} + +bool +WellManager::checkSystemSolution( DomainPartition & domain, + DofManager const & dofManager, + arrayView1d< real64 const > const & localSolution, + real64 const scalingFactor ) +{ + GEOS_MARK_FUNCTION; + + string const wellDofKey = dofManager.getKey( wellElementDofName() ); + integer globalCheck = 1; + + forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &, + MeshLevel & mesh, + string_array const & regionNames ) + { + + ElementRegionManager & elemManager = mesh.getElemManager(); + elemManager.forElementSubRegions< WellElementSubRegion >( regionNames, + [&]( localIndex const, + WellElementSubRegion & subRegion ) + + { + + WellControls & wellControls = getWellControls( subRegion ); + integer localCheck = wellControls.checkWellSystemSolution( subRegion, dofManager, localSolution, scalingFactor ); + globalCheck = MpiWrapper::min( localCheck ); + } ); + } ); + return globalCheck; +} + +void +WellManager::applySystemSolution( DofManager const & dofManager, + arrayView1d< real64 const > const & localSolution, + real64 const scalingFactor, + real64 const dt, + DomainPartition & domain ) +{ + + + DofManager::CompMask pressureMask( m_numDofPerWellElement, 0, 1 ); + + DofManager::CompMask connRateMask( m_numDofPerWellElement, numFluidComponents()+1, numFluidComponents()+2 ); + GEOS_UNUSED_VAR( dt ); + // update all the fields using the global damping coefficients + dofManager.addVectorToField( localSolution, + wellElementDofName(), + well::pressure::key(), + scalingFactor, + pressureMask ); + + dofManager.addVectorToField( localSolution, + wellElementDofName(), + well::connectionRate::key(), + scalingFactor, + connRateMask ); + if( isCompositional()) + { + DofManager::CompMask componentMask( m_numDofPerWellElement, 1, numFluidComponents()+1 ); + dofManager.addVectorToField( localSolution, + wellElementDofName(), + well::globalCompDensity::key(), + scalingFactor, + componentMask ); + + } + if( isThermal() ) + { + DofManager::CompMask temperatureMask( m_numDofPerWellElement, numFluidComponents()+2, numFluidComponents()+3 ); + + dofManager.addVectorToField( localSolution, + wellElementDofName(), + well::temperature::key(), + scalingFactor, + temperatureMask ); + + } + // if component density chopping is allowed, some component densities may be negative after the update + // these negative component densities are set to zero in this function + if( isCompositional() && m_allowCompDensChopping ) + { + chopNegativeDensities( domain ); + } + + forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &, + MeshLevel & mesh, + string_array const & regionNames ) + { + stdVector< string > propNames; + propNames.emplace_back( well::pressure::key() ); + + propNames.emplace_back( well::connectionRate::key() ); + if( isCompositional()) + { + propNames.emplace_back( well::globalCompDensity::key() ); + } + if( isThermal() ) + { + propNames.emplace_back( well::temperature::key() ); + } + // synchronize + FieldIdentifiers fieldsToBeSync; + + fieldsToBeSync.addElementFields( propNames, + regionNames ); + + + CommunicationTools::getInstance().synchronizeFields( fieldsToBeSync, + mesh, + domain.getNeighbors(), + true ); + } ); + +} + +void WellManager::chopNegativeDensities( DomainPartition & domain ) +{ + integer const numComp = m_numFluidComponents; + + forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &, + MeshLevel & mesh, + string_array const & regionNames ) + { + + ElementRegionManager & elemManager = mesh.getElemManager(); + + elemManager.forElementSubRegions< WellElementSubRegion >( regionNames, + [&]( localIndex const, + WellElementSubRegion & subRegion ) + { + arrayView1d< integer const > const & wellElemGhostRank = subRegion.ghostRank(); + + arrayView2d< real64, compflow::USD_COMP > const & wellElemCompDens = + subRegion.getField< well::globalCompDensity >(); + + forAll< parallelDevicePolicy<> >( subRegion.size(), [=] GEOS_HOST_DEVICE ( localIndex const iwelem ) + { + if( wellElemGhostRank[iwelem] < 0 ) + { + for( integer ic = 0; ic < numComp; ++ic ) + { + // we allowed for some densities to be slightly negative in CheckSystemSolution + // if the new density is negative, chop back to zero + if( wellElemCompDens[iwelem][ic] < 0 ) + { + wellElemCompDens[iwelem][ic] = 0; + } + } + } + } ); + } ); + + } ); +} + +REGISTER_CATALOG_ENTRY( PhysicsSolverBase, WellManager, string const &, Group * const ) +} // namespace geos diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/WellManager.hpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellManager.hpp new file mode 100644 index 00000000000..db764b24072 --- /dev/null +++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellManager.hpp @@ -0,0 +1,577 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * SPDX-License-Identifier: LGPL-2.1-only + * + * Copyright (c) 2016-2024 Lawrence Livermore National Security LLC + * Copyright (c) 2018-2024 TotalEnergies + * Copyright (c) 2018-2024 The Board of Trustees of the Leland Stanford Junior University + * Copyright (c) 2023-2024 Chevron + * Copyright (c) 2019- GEOS/GEOSX Contributors + * All rights reserved + * + * See top level LICENSE, COPYRIGHT, CONTRIBUTORS, NOTICE, and ACKNOWLEDGEMENTS files for details. + * ------------------------------------------------------------------------------------------------------------ + */ + +/** + * @file WellManager.hpp + */ + +#ifndef GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_WELL_MANAGER_HPP_ +#define GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_WELL_MANAGER_HPP_ + +#include "physicsSolvers/PhysicsSolverBase.hpp" +#include "physicsSolvers/fluidFlow/SinglePhaseBase.hpp" +#include "physicsSolvers/fluidFlow/CompositionalMultiphaseBase.hpp" +#include "physicsSolvers/fluidFlow/wells/CompositionalMultiphaseWell.hpp" +#include "physicsSolvers/fluidFlow/wells/SinglePhaseWell.hpp" +namespace geos +{ + +class DomainPartition; +class WellControls; +class WellElementSubRegion; + +namespace dataRepository +{ +namespace keys +{ +static constexpr auto compositionalMultiphaseWell = "CompositionalMultiphaseWell"; +static constexpr auto singlePhaseWell = "SinglePhaseWell"; +} +} +/** + * @class WellManager + * + * Base class for well solvers. + * Provides some common features + */ +class WellManager : public PhysicsSolverBase +{ +public: + + /// String used to form the solverName used to register single-physics solvers in CoupledSolver + static string coupledSolverAttributePrefix() { return "well"; } + + /** + * @brief main constructor for Group Objects + * @param name the name of this instantiation of Group in the repository + * @param parent the parent group of this instantiation of Group + */ + WellManager( const string & name, + Group * const parent ); + + /// default destructor + virtual ~WellManager() override = default; + + /// deleted default constructor + WellManager() = delete; + + /// deleted copy constructor + WellManager( WellManager const & ) = delete; + + /// default move constructor + WellManager( WellManager && ) = default; + + /// deleted assignment operator + WellManager & operator=( WellManager const & ) = delete; + + /// deleted move operator + WellManager & operator=( WellManager && ) = delete; + + virtual Group * createChild( string const & childKey, string const & childName ) override; + + /// Expand catalog for schema generation + virtual void expandObjectCatalogs() override; + + /** + * @brief setter for the name of the flow solver (needed to use the flow kernels like UpdateFluid) + * @param name the name of the flow solver + */ + void setFlowSolverName( string const & name ) { m_flowSolverName = name; } + + /** + * @brief setter for compositional flag + * @param compositional the compositional flag + */ + void setCompositional( bool const & isCompositional ) { m_isCompositional = isCompositional; } + + /** + * @brief getter for compositional flag + * @return the compositional flag + */ + bool isCompositional() const { return m_isCompositional; } + + + /** + * @brief getter for the name of the flow solver (used in UpdateState) + * @return a string containing the name of the flow solver + */ + string const & getFlowSolverName() const { return m_flowSolverName; } + + + /** + * @brief name of the node manager in the object catalog + * @return string that contains the catalog name to generate a new NodeManager object through the object catalog. + */ + static string catalogName() { return "WellManager"; } + /** + * @copydoc PhysicsSolverBase::getCatalogName() + */ + string getCatalogName() const override { return catalogName(); } + + virtual void registerDataOnMesh( Group & meshBodies ) override; + /** + * @brief Get a well solver for a given well element sub-region + * @param subRegion the well subRegion whose well solver is requested + * @return a reference to the well solver + */ + WellControls & getWell( WellElementSubRegion const & subRegion ); + + /** + * @brief Get a well solver for a given well element sub-region + * @param wellControlsName name of well + * @return a reference to the well solver + */ + WellControls & getWell( std::string const & wellControlsName ); + + /** + * @brief Get a well solver for a given well element sub-region + * @param wellControlsName name of well + * @return a reference to the well solver + */ + WellControls const & getWell( std::string const & wellControlsName ) const; +/** + * @brief get the name of DOF defined on well elements + * @return name of the DOF field used by derived solver type + */ + string wellElementDofName() const; + + struct viewKeyStruct : PhysicsSolverBase::viewKeyStruct + { + static constexpr char const * dofFieldString() { return "wellVars"; } + static constexpr char const * isThermalString() { return "isThermal"; } + static constexpr char const * useMassFlagString() {return "useMass"; } + /// @return string for the nextDt targetRegions wrapper + static constexpr char const * targetRegionsString() { return "targetRegions"; } + static constexpr char const * timeStepFromTablesFlagString() { return "timeStepFromTables"; } + static constexpr char const * useTotalMassEquationString() { return "useTotalMassEquation"; } + static constexpr char const * allowLocalCompDensChoppingString() { return CompositionalMultiphaseBase::viewKeyStruct::allowLocalCompDensChoppingString(); } + + + }; + + /** + * @brief getter for the number of degrees of freedom per well element + * @return the number of dofs + */ + localIndex numDofPerWellElement() const; + + /** + * @brief getter for the number of degrees of freedom per mesh element + * @return the number of dofs + */ + localIndex numDofPerResElement() const; + + /** + * @brief getter for iso/thermal switch + * @return True if thermal + */ + integer isThermal() const; + + + /** + * @brief get the name of DOF defined on well elements + * @return name of the DOF field used by derived solver type + */ + virtual string resElementDofName() const; + + /** + * @brief const getter for the number of fluid components + * @return the number of fluid components + */ + virtual localIndex numFluidComponents() const; + + /** + * @brief const getter for the number of fluid phases + * @return the number of fluid phases + */ + virtual localIndex numFluidPhases() const; + + /** + * @brief const getter for well total mass equation usage + * @return true if total mass equation is used + */ + integer useTotalMassEquation() const { return m_useTotalMassEquation; } + + /** + * @brief getter for the well controls associated to this well subRegion + * @param subRegion the well subRegion whose controls are requested + * @return a reference to the controls + */ + WellControls & getWellControls( WellElementSubRegion const & subRegion ); + + /** + * @brief const getter for the well controls associated to this well subRegion + * @param subRegion the well subRegion whose controls are requested + * @return a reference to the const controls + */ + WellControls const & getWellControls( WellElementSubRegion const & subRegion ) const; + + /** + * @brief getter for the compositional multiphase well associated to this well subRegion + * @param subRegion the well subRegion whose controls are requested + * @return a reference to the well + */ + CompositionalMultiphaseWell & getCompositionalMultiphaseWell( WellElementSubRegion const & subRegion ); + + /** + * @brief const getter for the compositional multiphase well associated to this well subRegion + * @param subRegion the well subRegion whose controls are requested + * @return a reference to the const well + */ + CompositionalMultiphaseWell const & getCompositionalMultiphaseWell( WellElementSubRegion const & subRegion ) const; + + /** + * @brief Selects the active well constraint based on current conditions + * @param[in] currentTime the current time + * @param[in] currentDt the current time step size + * @param[in] coupledIterationNumber the current coupled iteration number + * @param[in] domain the domain object + * @return the prescribed time step size + */ + void selectWellConstraint( real64 const & time_n, + real64 const & dt, + integer const coupledIterationNumber, + DomainPartition & domain ); + /* PhysicsSolverBase interfaces */ + + /** + * @brief function to set the next time step size + * @param[in] currentTime the current time + * @param[in] currentDt the current time step size + * @param[in] domain the domain object + * @return the prescribed time step size + */ + virtual real64 setNextDt( real64 const & currentTime, + real64 const & currentDt, + DomainPartition & domain ) override; + + virtual void setupDofs( DomainPartition const & domain, + DofManager & dofManager ) const override; + + /** + * @brief function to perform setup for implicit timestep + * @param time_n the time at the beginning of the step + * @param dt the desired timestep + * @param domain the domain partition + * + * This function should contain any step level initialization required to perform an implicit + * step. + * + * @note This function must be overridden in the derived physics solver in order to use an implict + * solution method such as LinearImplicitStep() or NonlinearImplicitStep(). + */ + virtual void + implicitStepSetup( real64 const & time_n, + real64 const & dt, + DomainPartition & domain ) override; + + + /** + * @brief function to assemble the linear system matrix and rhs + * @param time the time at the beginning of the step + * @param dt the desired timestep + * @param domain the domain partition + * @param dofManager degree-of-freedom manager associated with the linear system + * @param localMatrix the system matrix + * @param localRhs the system right-hand side vector + * + * This function assembles the residual and the jacobian of the residual wrt the primary + * variables. In a stand alone physics solver, this function will fill a single block in the + * block system. However the capability to query the block system structure for any coupled blocks + * may be implemented to fill in off diagonal blocks of the system to enable coupling between + * solvers. + * + * @note This function must be overridden in the derived physics solver in order to use an implict + * solution method such as LinearImplicitStep() or NonlinearImplicitStep(). + */ + virtual void + assembleSystem( real64 const time, + real64 const dt, + DomainPartition & domain, + DofManager const & dofManager, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) override; + + + virtual void + resetStateToBeginningOfStep( DomainPartition & domain ) override; + + virtual void + implicitStepComplete( real64 const & time, + real64 const & dt, + DomainPartition & domain ) override; + + virtual void applyBoundaryConditions( real64 const GEOS_UNUSED_PARAM( time_n ), + real64 const GEOS_UNUSED_PARAM( dt ), + DomainPartition & GEOS_UNUSED_PARAM( domain ), + DofManager const & GEOS_UNUSED_PARAM( dofManager ), + CRSMatrixView< real64, globalIndex const > const & GEOS_UNUSED_PARAM( localMatrix ), + arrayView1d< real64 > const & GEOS_UNUSED_PARAM( localRhs ) ) override {} + + /** + * @brief calculate the norm of the global system residual + * @param time the time at the beginning of the step + * @param dt the desired timestep + * @param domain the domain partition + * @param dofManager degree-of-freedom manager associated with the linear system + * @param localRhs the system right-hand side vector + * @return norm of the residual + * + * This function returns the norm of global residual vector, which is suitable for comparison with + * a tolerance. + */ + virtual real64 + calculateResidualNorm( real64 const & time, + real64 const & dt, + DomainPartition const & domain, + DofManager const & dofManager, + arrayView1d< real64 const > const & localRhs ) override; + /** + * @brief Recompute all dependent quantities from primary variables (including constitutive models) + * @param domain the domain containing the mesh and fields + */ + virtual void updateState( DomainPartition & domain ) override; + + /** + * @brief Function to determine if the solution vector should be scaled back in order to maintain a known constraint. + * @param[in] domain The domain partition. + * @param[in] dofManager degree-of-freedom manager associated with the linear system + * @param[in] localSolution the solution vector + * @return The factor that should be used to scale the solution vector values when they are being applied. + */ + virtual real64 + scalingForSystemSolution( DomainPartition & domain, + DofManager const & dofManager, + arrayView1d< real64 const > const & localSolution )override; + + /** + * @brief Function to check system solution for physical consistency and constraint violation + * @param domain the domain partition + * @param dofManager degree-of-freedom manager associated with the linear system + * @param localSolution the solution vector + * @param scalingFactor factor to scale the solution prior to application + * @return true if solution can be safely applied without violating physical constraints, false otherwise + * + * @note This function must be overridden in the derived physics solver in order to use an implict + * solution method such as LinearImplicitStep() or NonlinearImplicitStep(). + * + */ + virtual bool + checkSystemSolution( DomainPartition & domain, + DofManager const & dofManager, + arrayView1d< real64 const > const & localSolution, + real64 const scalingFactor ) override; + + /** + * @brief Function to apply the solution vector to the state + * @param dofManager degree-of-freedom manager associated with the linear system + * @param localSolution the solution vector + * @param scalingFactor factor to scale the solution prior to application + * @param dt the timestep + * @param domain the domain partition + * + * This function performs 2 operations: + * 1) extract the solution vector for the "blockSystem" parameter, and applies the + * contents of the solution vector to the primary variable field data, + * 2) perform a synchronization of the primary field variable such that all ghosts are updated, + * + * The "scalingFactor" parameter allows for the scaled application of the solution vector. For + * instance, a line search may apply a negative scaling factor to remove part of the previously + * applied solution. + * + * @note This function must be overridden in the derived physics solver in order to use an implict + * solution method such as LinearImplicitStep() or NonlinearImplicitStep(). + * + */ + virtual void + applySystemSolution( DofManager const & dofManager, + arrayView1d< real64 const > const & localSolution, + real64 const scalingFactor, + real64 const dt, + DomainPartition & domain ) override; + + /** + * @brief Sets all the negative component densities (if any) to zero. + * @param domain the physical domain object + */ + void chopNegativeDensities( DomainPartition & domain ); +#if 0 + /** + * @brief calculate the norm of the global system residual + * @param time the time at the beginning of the step + * @param dt the desired timestep + * @param domain the domain partition + * @param dofManager degree-of-freedom manager associated with the linear system + * @param localRhs the system right-hand side vector + * @return norm of the residual + * + * This function returns the norm of global residual vector, which is suitable for comparison with + * a tolerance. + */ + virtual real64 + calculateResidualNorm( real64 const & time, + real64 const & dt, + DomainPartition const & domain, + DofManager const & dofManager, + arrayView1d< real64 const > const & localRhs ); + /** + * @brief Function to check system solution for physical consistency and constraint violation + * @param domain the domain partition + * @param dofManager degree-of-freedom manager associated with the linear system + * @param localSolution the solution vector + * @param scalingFactor factor to scale the solution prior to application + * @return true if solution can be safely applied without violating physical constraints, false otherwise + * + * @note This function must be overridden in the derived physics solver in order to use an implict + * solution method such as LinearImplicitStep() or NonlinearImplicitStep(). + * + */ + virtual bool + checkSystemSolution( DomainPartition & domain, + DofManager const & dofManager, + arrayView1d< real64 const > const & localSolution, + real64 const scalingFactor ); + + /** + * @brief Function to determine if the solution vector should be scaled back in order to maintain a known constraint. + * @param[in] domain The domain partition. + * @param[in] dofManager degree-of-freedom manager associated with the linear system + * @param[in] localSolution the solution vector + * @return The factor that should be used to scale the solution vector values when they are being applied. + */ + virtual real64 + scalingForSystemSolution( DomainPartition & domain, + DofManager const & dofManager, + arrayView1d< real64 const > const & localSolution ); + + /** + * @brief Function to apply the solution vector to the state + * @param dofManager degree-of-freedom manager associated with the linear system + * @param localSolution the solution vector + * @param scalingFactor factor to scale the solution prior to application + * @param dt the timestep + * @param domain the domain partition + * + * This function performs 2 operations: + * 1) extract the solution vector for the "blockSystem" parameter, and applies the + * contents of the solution vector to the primary variable field data, + * 2) perform a synchronization of the primary field variable such that all ghosts are updated, + * + * The "scalingFactor" parameter allows for the scaled application of the solution vector. For + * instance, a line search may apply a negative scaling factor to remove part of the previously + * applied solution. + * + * @note This function must be overridden in the derived physics solver in order to use an implict + * solution method such as LinearImplicitStep() or NonlinearImplicitStep(). + * + */ + virtual void + applySystemSolution( DofManager const & dofManager, + arrayView1d< real64 const > const & localSolution, + real64 const scalingFactor, + real64 const dt, + DomainPartition & domain ); + + + /** + * @brief Recompute all dependent quantities from primary variables (including constitutive models) + * @param domain the domain containing the mesh and fields + */ + virtual void updateState( DomainPartition & domain ); + + /** + * @brief perform cleanup for implicit timestep + * @param time the time at the beginning of the step + * @param dt the desired timestep + * @param domain the domain partition + * + * This function performs whatever tasks are required to complete an implicit timestep. For + * example, the acceptance of the solution will occur during this step, and deallocation of + * temporaries will be be performed in this function. + * + * @note This function must be overridden in the derived physics solver in order to use an implict + * solution method such as LinearImplicitStep() or NonlinearImplicitStep(). + */ + virtual void + implicitStepComplete( real64 const & time, + real64 const & dt, + DomainPartition & domain ) override; + +#endif + + /** + * @brief Utility function to keep the well variables during a time step (used in + * poromechanics simulations) + * @param[in] keepVariablesConstantDuringInitStep flag to tell the solver to freeze its + * primary variables during a time step + * @detail This function is meant to be called by a specific task before/after the + * initialization step + */ + void setKeepVariablesConstantDuringInitStep( bool const keepVariablesConstantDuringInitStep ); + + +protected: + //virtual void postInputInitialization() override; + + virtual void initializePostSubGroups() override; + + virtual void initializePostInitialConditionsPreSubGroups() override; + + virtual void postRestartInitialization() override final; + + +private: + + /// name of the flow solver + string m_flowSolverName; + + /// flag indicating whether mass or molar formulation should be used + integer m_useMass; + + /// flag indicating whether total mass equation should be used + integer m_useTotalMassEquation; + + /// flag indicating whether thermal formulation is used + integer m_isThermal; + + /// flag indicating whether compositional formulation is used + bool m_isCompositional; + + + /// number of phases + integer m_numFluidPhases; + + /// number of components + integer m_numFluidComponents; + + /// number of degrees of freedom per well element + integer m_numDofPerWellElement; + + /// number of degrees of freedom per reservoir element + integer m_numDofPerResElement; + + /// minimum value of the scaling factor obtained by enforcing maxCompFracChange + real64 m_minScalingFactor; + + /// flag indicating whether local (cell-wise) chopping of negative compositions is allowed + integer m_allowCompDensChopping; + + // flag to enable time step selection base on rates/bhp tables coordinates + integer m_timeStepFromTables; +}; + +} + +#endif //GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_WELL_MANAGER_HPP_ diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/WellMassRateConstraint.cpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellMassRateConstraint.cpp new file mode 100644 index 00000000000..eadffc21b42 --- /dev/null +++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellMassRateConstraint.cpp @@ -0,0 +1,75 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * SPDX-License-Identifier: LGPL-2.1-only + * + * Copyright (c) 2016-2024 Lawrence Livermore National Security LLC + * Copyright (c) 2018-2024 TotalEnergies + * Copyright (c) 2018-2024 The Board of Trustees of the Leland Stanford Junior University + * Copyright (c) 2023-2024 Chevron + * Copyright (c) 2019- GEOS/GEOSX Contributors + * All rights reserved + * + * See top level LICENSE, COPYRIGHT, CONTRIBUTORS, NOTICE, and ACKNOWLEDGEMENTS files for details. + * ------------------------------------------------------------------------------------------------------------ + */ + +/* + * @file WellMassRateConstraints.cpp + */ + +#include "LogLevelsInfo.hpp" +#include "WellMassRateConstraint.hpp" +#include "WellConstants.hpp" +#include "dataRepository/InputFlags.hpp" +#include "functions/FunctionManager.hpp" + +namespace geos +{ + +using namespace dataRepository; + +MassRateConstraint::MassRateConstraint( string const & name, Group * const parent ) + : WellConstraintBase( name, parent ) +{ + this->setInputFlags( InputFlags::OPTIONAL_NONUNIQUE ); + + this->registerWrapper( viewKeyStruct::massRateString(), &this->m_constraintValue ). + setDefaultValue( 0.0 ). + setInputFlag( InputFlags::OPTIONAL ). + setRestartFlags( RestartFlags::WRITE_AND_READ ). + setDescription( "Maximum mass rate (kg/s)" ); +} + +MassRateConstraint::~MassRateConstraint() +{} + + +void MassRateConstraint::postInputInitialization() +{ + // Validate table options + WellConstraintBase::postInputInitialization(); + + // check constraint value + GEOS_THROW_IF( m_constraintValue < 0, + getWrapperDataContext( viewKeyStruct::massRateString() ) << ": Target value is negative", + InputError ); + + GEOS_THROW_IF ((m_constraintValue <= 0.0 && m_constraintScheduleTableName.empty()), + getName() << " " << getDataContext() << ": You need to specify a mass rate constraint. \n" << + "The rate constraint can be specified using " << + "either " << viewKeyStruct::massRateString() << + " or " << WellConstraintBase::viewKeyStruct::constraintScheduleTableNameString(), + InputError ); +} + + +bool MassRateConstraint::checkViolation( WellConstraintBase const & currentConstraint, real64 const & currentTime )const +{ + // isViolated is defined as a static method on the specific WellConstraintType (Injection/Production) + // Evaluate violation according to the sign set for injectors/producers + real64 const currentValue = currentConstraint.massRate(); + real64 const constraintValue = this->getConstraintValue( currentTime ); + return ( LvArray::math::abs( currentValue ) > LvArray::math::abs( constraintValue ) ); + +} +} //namespace geos diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/WellMassRateConstraint.hpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellMassRateConstraint.hpp new file mode 100644 index 00000000000..6cd6703ba36 --- /dev/null +++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellMassRateConstraint.hpp @@ -0,0 +1,120 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * SPDX-License-Identifier: LGPL-2.1-only + * + * Copyright (c) 2016-2024 Lawrence Livermore National Security LLC + * Copyright (c) 2018-2024 TotalEnergies + * Copyright (c) 2018-2024 The Board of Trustees of the Leland Stanford Junior University + * Copyright (c) 2023-2024 Chevron + * Copyright (c) 2019- GEOS/GEOSX Contributors + * All rights reserved + * + * See top level LICENSE, COPYRIGHT, CONTRIBUTORS, NOTICE, and ACKNOWLEDGEMENTS files for details. + * ------------------------------------------------------------------------------------------------------------ + */ + +/* + * @file WellMassRateConstraint.hpp + */ + + +#ifndef GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_WELLMASSRATECONSTRAINT_HPP +#define GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_WELLMASSRATECONSTRAINT_HPP + +#include "common/format/EnumStrings.hpp" +#include "dataRepository/Group.hpp" +#include "functions/TableFunction.hpp" +#include "WellConstraintsBase.hpp" +namespace geos +{ + +/** + * @class MassRateConstraint + * @brief This class describes a mass rate constraint used to control a well. + */ + +class MassRateConstraint : public WellConstraintBase +{ +public: + + /** + * @name Constructor / Destructor + */ + ///@{ + + /** + * @brief Constructor for WellControls Objects. + * @param[in] name the name of this instantiation of WellControls in the repository + * @param[in] parent the parent group of this instantiation of WellControls + */ + explicit MassRateConstraint( string const & name, dataRepository::Group * const parent ); + + + /** + * @brief Default destructor. + */ + ~MassRateConstraint() override; + + /** + * @brief Deleted default constructor. + */ + MassRateConstraint() = delete; + + /** + * @brief Deleted copy constructor. + */ + MassRateConstraint( MassRateConstraint const & ) = delete; + + /** + * @brief Deleted move constructor. + */ + MassRateConstraint( MassRateConstraint && ) = delete; + + /** + * @brief Deleted assignment operator. + * @return a reference to a constraint object + */ + MassRateConstraint & operator=( MassRateConstraint const & ) = delete; + + /** + * @brief Deleted move operator. + * @return a reference to a constraint object + */ + MassRateConstraint & operator=( MassRateConstraint && ) = delete; + + /** + * @brief name of the node manager in the object catalog + * @return string that contains the catalog name to generate a new Constraint object through the object catalog. + */ + static string catalogName() + { + return "MassRateConstraint"; + } + ///@} + + struct viewKeyStruct + { + /// String key for the well target rate + static constexpr char const * massRateString() { return "massRate"; } + }; + + /** + * @name Getters / Setters + */ + + // Temp interface - tjb + virtual ConstraintTypeId getControl() const override { return ConstraintTypeId::MASSRATE; }; + ///@} + + virtual bool checkViolation( WellConstraintBase const & currentConstraint, real64 const & currentTime ) const override; + +protected: + + virtual void postInputInitialization() override; + +}; + + +} //namespace geos + +#endif //GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_WELLMASSRATECONSTRAINT_HPP diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/WellNewtonSolver.cpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellNewtonSolver.cpp new file mode 100644 index 00000000000..bad6991f312 --- /dev/null +++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellNewtonSolver.cpp @@ -0,0 +1,535 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * SPDX-License-Identifier: LGPL-2.1-only + * + * Copyright (c) 2016-2024 Lawrence Livermore National Security LLC + * Copyright (c) 2018-2024 TotalEnergies + * Copyright (c) 2018-2024 The Board of Trustees of the Leland Stanford Junior University + * Copyright (c) 2023-2024 Chevron + * Copyright (c) 2019- GEOS/GEOSX Contributors + * All rights reserved + * + * See top level LICENSE, COPYRIGHT, CONTRIBUTORS, NOTICE, and ACKNOWLEDGEMENTS files for details. + * ------------------------------------------------------------------------------------------------------------ + */ + +#include "WellNewtonSolver.hpp" + +#include "common/MpiWrapper.hpp" +#include "codingUtilities/RTTypes.hpp" +#include "common/format/EnumStrings.hpp" +#include "dataRepository/Group.hpp" +#include "physicsSolvers/LogLevelsInfo.hpp" +#include "common/format/LogPart.hpp" +#include "common/TimingMacros.hpp" +#include "linearAlgebra/solvers/KrylovSolver.hpp" +#include "mesh/DomainPartition.hpp" +#include "math/interpolation/Interpolation.hpp" +#include "common/Timer.hpp" +#include "common/Units.hpp" + + +namespace geos +{ + +using namespace dataRepository; + +WellNewtonSolver::WellNewtonSolver( string const & name, + Group * const parent ) + : + dataRepository::Group( name, parent ), + + m_dofManager( name ), + m_usePhysicsScaling( 1 ), + m_linearSolverParameters( groupKeyStruct::linearSolverParametersString(), this ), + m_nonlinearSolverParameters( groupKeyStruct::nonlinearSolverParametersString(), this ), + m_solverStatistics( groupKeyStruct::solverStatisticsString(), this ), + m_systemSetupTimestamp( 0 ), + m_activeCoupledIterations( 1 ), + m_enableIsoThermalEstimator( 0 ) +{ + setInputFlags( InputFlags::OPTIONAL_NONUNIQUE ); + + this->registerWrapper( viewKeyStruct::activeCoupledIterationsString(), &m_activeCoupledIterations ). + setApplyDefaultValue( 1 ). + setInputFlag( InputFlags::OPTIONAL ). + setDescription( "Number of coupled iterations to activate estimator solve." ); + + registerWrapper( viewKeyStruct::enableIsoThermalEstimatorString(), &m_enableIsoThermalEstimator ). + setDefaultValue( 0 ). + setInputFlag( InputFlags::OPTIONAL ). + setDescription( "Estimator configuration option to disable thermal effects on initial well constraint solve and then converge solution with thermal effects enabled: \n" + " - If the flag is set to 1, thermal effects are enabled during the initial constraint solve. \n" + " - If the flag is set to 0, thermal effects are disabled during the initial constraint solve." ); + + + + registerWrapper( viewKeyStruct::writeLinearSystemString(), &m_writeLinearSystem ). + setApplyDefaultValue( 0 ). + setInputFlag( InputFlags::OPTIONAL ). + setRestartFlags( RestartFlags::WRITE_AND_READ ). + setDescription( "Write matrix, rhs, solution to screen ( = 1) or file ( = 2)." ); + + registerWrapper( viewKeyStruct::allowNonConvergedLinearSolverSolutionString(), &m_allowNonConvergedLinearSolverSolution ). + setApplyDefaultValue( 1 ). + setInputFlag( InputFlags::OPTIONAL ). + setRestartFlags( RestartFlags::WRITE_AND_READ ). + setDescription( "Cut time step if linear solution fail without going until max nonlinear iterations." ); + + registerWrapper( viewKeyStruct::usePhysicsScalingString(), &m_usePhysicsScaling ). + setApplyDefaultValue( 1 ). + setInputFlag( InputFlags::OPTIONAL ). + setDescription( "Enable physics-based scaling of the linear system. Default: true." ); + + + registerWrapper( viewKeyStruct::writeStatisticsCSVString(), &m_writeStatisticsCSV ). + setApplyDefaultValue( StatsOutputType::none ). + setInputFlag( InputFlags::OPTIONAL ). + setRestartFlags( RestartFlags::NO_WRITE ). + setDescription( GEOS_FMT( "When set to `{}`, output iterations information to a csv\n" + "When set to `{}`, output convergence information to a csv\n" + "When set to `{}` output both convergence & iteration information to a csv.", + EnumStrings< StatsOutputType >::toString( StatsOutputType::iteration ), + EnumStrings< StatsOutputType >::toString( StatsOutputType::convergence ), + EnumStrings< StatsOutputType >::toString( StatsOutputType::all ) )); + + addLogLevel< logInfo::Convergence >(); + addLogLevel< logInfo::Fields >(); + addLogLevel< logInfo::LinearSolver >(); + addLogLevel< logInfo::ResidualNorm >(); + addLogLevel< logInfo::Solution >(); + addLogLevel< logInfo::TimeStep >(); + addLogLevel< logInfo::Timers >(); + + registerGroup( groupKeyStruct::linearSolverParametersString(), &m_linearSolverParameters ); + registerGroup( groupKeyStruct::nonlinearSolverParametersString(), &m_nonlinearSolverParameters ); + registerGroup( groupKeyStruct::solverStatisticsString(), &m_solverStatistics ); + + m_localMatrix.setName( this->getName() + "/localMatrix" ); + m_matrix.setDofManager( &m_dofManager ); +} + +void WellNewtonSolver::postInputInitialization() +{ + m_solverStatistics.setOutputFilesName( getName() ); + + m_solverStatistics.makeDir( m_writeStatisticsCSV != StatsOutputType::none ); + + getIterationStats().setTableName( getName() ); + getIterationStats().setLogOutputRequest( true ); + getIterationStats().setCSVOutputRequest( m_writeStatisticsCSV == StatsOutputType::iteration || + m_writeStatisticsCSV == StatsOutputType::all ); + getConvergenceStats().setCSVOutputRequest( m_writeStatisticsCSV == StatsOutputType::convergence || + m_writeStatisticsCSV == StatsOutputType::all ); +} + +WellNewtonSolver::~WellNewtonSolver() = default; + +#if 0 +void WellNewtonSolver::initialize_postMeshGeneration() +{ + //ExecutableGroup::initialize_postMeshGeneration(); + //DomainPartition const & domain = this->getGroupByPath< DomainPartition >( "/Problem/domain" ); + //generateMeshTargetsFromTargetRegions( domain.getMeshBodies()); +} +#endif +void WellNewtonSolver::generateMeshTargetsFromTargetRegions( Group const & meshBodies ) +{ + for( auto const & target : m_targetRegionNames ) + { + + stdVector< string > targetTokens = stringutilities::tokenize( target, "/" ); + + if( targetTokens.size()==1 ) // no MeshBody or MeshLevel specified + { + GEOS_ERROR_IF( meshBodies.numSubGroups() != 1, + getDataContext() << ": No MeshBody information is specified in" << + " WellNewtonSolver::meshTargets, but there are multiple MeshBody objects", + getDataContext() ); + MeshBody const & meshBody = meshBodies.getGroup< MeshBody >( 0 ); + string const meshBodyName = meshBody.getName(); + + string const meshLevelName = ""; //tjbm_discretizationName; + + string const regionName = target; + auto const key = std::make_pair( meshBodyName, meshLevelName ); + m_meshTargets[key].emplace_back( regionName ); + } + else if( targetTokens.size()==2 ) + { + string const meshBodyName = targetTokens[0]; + GEOS_ERROR_IF( !meshBodies.hasGroup( meshBodyName ), + getWrapperDataContext( viewKeyStruct::targetRegionsString() ) << ": MeshBody (" << + meshBodyName << ") is specified in targetRegions, but does not exist.", + getWrapperDataContext( viewKeyStruct::targetRegionsString() ) ); + + string const meshLevelName = "";//tjbm_discretizationName; + + string const regionName = targetTokens[1]; + + + auto const key = std::make_pair( meshBodyName, meshLevelName ); + m_meshTargets[key].emplace_back( regionName ); + } + else + { + GEOS_ERROR( getDataContext() << ": Invalid specification of targetRegions" ); + } + } +} + + + +Group * WellNewtonSolver::createChild( string const & GEOS_UNUSED_PARAM( childKey ), string const & GEOS_UNUSED_PARAM( childName ) ) +{ + // Unused as all children are created within the constructor + return nullptr; +} + +WellNewtonSolver::CatalogInterface::CatalogType & WellNewtonSolver::getCatalog() +{ + static WellNewtonSolver::CatalogInterface::CatalogType catalog; + return catalog; +} + + +bool WellNewtonSolver::registerCallback( void * func, const std::type_info & funcType ) +{ + if( std::type_index( funcType ) == std::type_index( typeid( std::function< void( CRSMatrix< real64, globalIndex >, array1d< real64 > ) > ) ) ) + { + m_assemblyCallback = *reinterpret_cast< std::function< void( CRSMatrix< real64, globalIndex >, array1d< real64 > ) > * >( func ); + return true; + } + + return false; +} + + + +void WellNewtonSolver::logEndOfCycleInformation( integer const cycleNumber, + integer const numOfSubSteps, + stdVector< real64 > const & subStepDts ) const +{ + LogPart logpart( "TIMESTEP", MpiWrapper::commRank() == 0 ); + logpart.addEndDescription( "- Cycle ", cycleNumber ); + logpart.addEndDescription( "- N substeps ", numOfSubSteps ); + + std::stringstream logMessage; + for( integer i = 0; i < numOfSubSteps; ++i ) + { + if( i > 0 ) + { + logMessage << ", "; + } + logMessage << subStepDts[i] << " " << units::getSymbol( units::Unit::Time ); + } + + if( logMessage.rdbuf()->in_avail() == 0 ) + logMessage << "/"; + + logpart.addEndDescription( "- substep dts ", logMessage.str() ); + logpart.end(); + + if( isLogLevelActive< logInfo::SolverExecutionDetails >( getLogLevel())) + getIterationStats().outputStatistics(); +} + +#if 0 +// tjb keep this history +void WellNewtonSolver::implicitStepSetup( real64 const & GEOS_UNUSED_PARAM( time_n ), + real64 const & GEOS_UNUSED_PARAM( dt ), + DomainPartition & GEOS_UNUSED_PARAM( domain ) ) +{ + // clean the solution history + while( m_solutionHistory.size() > 0 ) + { + m_solutionHistory.eraseArray( 0 ); + } +} +#endif +void WellNewtonSolver::setupDofs( DomainPartition const & GEOS_UNUSED_PARAM( domain ), + DofManager & GEOS_UNUSED_PARAM( dofManager ) ) const +{ + GEOS_ERROR( "WellNewtonSolver::setupDofs called!. Should be overridden." ); +} + +void WellNewtonSolver::setSparsityPattern( DomainPartition & GEOS_UNUSED_PARAM( domain ), + DofManager & dofManager, + CRSMatrix< real64, globalIndex > & GEOS_UNUSED_PARAM( localMatrix ), + SparsityPattern< globalIndex > & pattern ) +{ + dofManager.setSparsityPattern( pattern ); +} + +void WellNewtonSolver::setSystemSetupTimestamp( Timestamp timestamp ) +{ + m_systemSetupTimestamp = timestamp; + + std::ostringstream oss; + m_dofManager.printFieldInfo( oss ); + GEOS_LOG_LEVEL( logInfo::Fields, oss.str()); +} + +std::unique_ptr< PreconditionerBase< LAInterface > > +WellNewtonSolver::createPreconditioner( DomainPartition & GEOS_UNUSED_PARAM( domain ) ) const +{ + // By default, do not create a preconditioner, one will be created internally inside LA backend + return {}; + + // TODO: refactor interfaces to always create preconditioner externally and pass to backends + // return LAInterface::createPreconditioner( m_linearSolverParameters.get() ); +} + + +namespace +{ + +/** + * @brief Helper for debug output of linear algebra objects (matrices and vectors) + * @tparam T type of LA object (must have stream insertion and .write() implemented) + * @param obj the object to output + * @param cycleNumber event cycle number + * @param nonlinearIteration nonlinear iteration number + * @param filePrefix short filename prefix (e.g. "mat") + * @param screenName long name for screen output (e.g. "System matrix") + * @param toScreen whether to print on screen + * @param toFile whether to write to file + */ +template< typename T > +void debugOutputLAObject( T const & obj, + real64 const & GEOS_UNUSED_PARAM( time ), + integer const cycleNumber, + integer const nonlinearIteration, + string const & filePrefix, + string const & screenName, + bool const toScreen, + bool const toFile ) +{ + if( toScreen ) + { + GEOS_LOG_RANK_0( GEOS_FMT( "{2:=>{1}}\n{0}:\n{2:=>{1}}", screenName, screenName.size() + 1, "" ) ); + GEOS_LOG( obj ); + } + + if( toFile ) + { + string const filename = GEOS_FMT( "{}_{:06}_{:02}.mtx", filePrefix.c_str(), cycleNumber, nonlinearIteration ); + obj.write( filename, LAIOutputFormat::MATRIX_MARKET ); + GEOS_LOG_RANK_0( GEOS_FMT( "{} written to {}", screenName, filename ) ); + } +} + +} + +void WellNewtonSolver::debugOutputSystem( real64 const & time, + integer const cycleNumber, + integer const nonlinearIteration, + ParallelMatrix const & matrix, + ParallelVector const & rhs ) const +{ + // special case when flag value > 2 + if( m_writeLinearSystem > 2 && cycleNumber < m_writeLinearSystem ) + return; + + debugOutputLAObject( matrix, + time, + cycleNumber, + nonlinearIteration, + getName() + "_mat", + "System matrix", + m_writeLinearSystem == 1, + m_writeLinearSystem >= 2 ); + + debugOutputLAObject( rhs, + time, + cycleNumber, + nonlinearIteration, + getName() + "_rhs", + "System right-hand side", + m_writeLinearSystem == 1, + m_writeLinearSystem >= 2 ); +} + +void WellNewtonSolver::debugOutputSolution( real64 const & time, + integer const cycleNumber, + integer const nonlinearIteration, + ParallelVector const & solution ) const +{ + // special case when flag value > 2 + if( m_writeLinearSystem > 2 && cycleNumber < m_writeLinearSystem ) + return; + + debugOutputLAObject( solution, + time, + cycleNumber, + nonlinearIteration, + getName() + "_sol", + "System solution", + m_writeLinearSystem == 1, + m_writeLinearSystem >= 2 ); +} + +void WellNewtonSolver::updateAndWriteConvergenceStep( real64 const & time_n, real64 const & dt, + integer const cycleNumber, integer const iteration ) +{ + getConvergenceStats().updateSolverStep( time_n, dt, cycleNumber, iteration ); + getConvergenceStats().writeConvergenceStatsToTable(); +} + + +void WellNewtonSolver::solveLinearSystem( DofManager const & dofManager, + ParallelMatrix & matrix, + ParallelVector & rhs, + ParallelVector & solution ) +{ + GEOS_MARK_FUNCTION; + + rhs.scale( -1.0 ); + solution.zero(); + + LinearSolverParameters const & params = m_linearSolverParameters.get(); + const bool isDirectSolver = (params.solverType == LinearSolverParameters::SolverType::direct); + const bool isSetupNeeded = !(isDirectSolver && params.direct.reuseFactorization); + + matrix.setDofManager( &dofManager ); + + GEOS_WARNING_IF( isDirectSolver && dofManager.numGlobalDofs() > 100000, + "Direct solver used for large system ( > 100,000 DOFs ). " + "This may lead to high memory consumption and long computation times. " + "Consider using an iterative solver for better performance." ); + + // Apply physics-based scaling to the linear system if enabled + if( m_usePhysicsScaling ) + { + Timer timer_setup( m_timers.get_inserted( "linear solver scaling" ) ); + + matrix.computeScalingVector( m_scaling ); + matrix.leftRightScale( m_scaling, m_scaling ); + rhs.pointwiseProduct( m_scaling ); + // Assume the solution is zeroed out, thus no need to scale it + } + + if( isDirectSolver || !m_precond ) + { + if( !m_linearSolver ) + { + m_linearSolver = LAInterface::createSolver( params ); + } + + if( isSetupNeeded ) + { + Timer timer_setup( m_timers.get_inserted( "linear solver setup" ) ); + m_linearSolver->setup( matrix ); + } + + { + Timer timer_setup( m_timers.get_inserted( "linear solver solve" ) ); + m_linearSolver->solve( rhs, solution ); + } + + m_linearSolverResult = m_linearSolver->result(); + } + else + { + { + Timer timer_setup( m_timers.get_inserted( "linear solver setup" ) ); + m_precond->setup( matrix ); + } + std::unique_ptr< KrylovSolver< ParallelVector > > solver = KrylovSolver< ParallelVector >::create( params, matrix, *m_precond ); + { + Timer timer_setup( m_timers.get_inserted( "linear solver solve" ) ); + solver->solve( rhs, solution ); + } + m_linearSolverResult = solver->result(); + } + + getIterationStats().accumulateSolverLinearTime( m_linearSolverResult.setupTime, m_linearSolverResult.solveTime ); + + GEOS_LOG_LEVEL_RANK_0( logInfo::LinearSolver, + GEOS_FMT( " Linear solve: ( iter, res ) = ( {:3}, {:4.2e} )", + m_linearSolverResult.numIterations, + m_linearSolverResult.residualReduction )); + + if( params.stopIfError ) + { + GEOS_ERROR_IF( m_linearSolverResult.breakdown(), + getDataContext() << ": Linear solution breakdown -> simulation STOP", + getDataContext() ); + } + else + { + GEOS_WARNING_IF( !m_linearSolverResult.success(), + getDataContext() << ": Linear solution failed", + getDataContext() ); + } + + // Unscale the solution vector if physics-based scaling was applied + if( m_usePhysicsScaling ) + { + Timer timer_setup( m_timers.get_inserted( "linear solver scaling" ) ); + + solution.pointwiseProduct( m_scaling ); + } +} + + + +// Detect oscillations for all dofs in the solution history +bool WellNewtonSolver::detectOscillations() const +{ + // grab the parameters + integer const oscillationCheckDepth = m_nonlinearSolverParameters.m_oscillationCheckDepth; + real64 const oscillationTolerance = m_nonlinearSolverParameters.m_oscillationTolerance; + real64 const oscillationFraction = m_nonlinearSolverParameters.m_oscillationFraction; + + if( m_solutionHistory.size() < oscillationCheckDepth ) + return false; // not enough history to check oscillations + + RAJA::ReduceSum< parallelDeviceReduce, localIndex > oscillationCount( 0 ); + + auto const solutionHistory = m_solutionHistory.toViewConst(); + localIndex const numDofs = m_solutionHistory[0].size(); + localIndex const historySize = m_solutionHistory.size(); + + RAJA::forall< parallelDevicePolicy<> >( RAJA::TypedRangeSegment< localIndex >( 0, numDofs ), + [=] GEOS_HOST_DEVICE ( localIndex const dof ) + { + bool oscillationDetected = true; + for( localIndex i = historySize - 1; i > historySize - oscillationCheckDepth; --i ) + { + real64 dxCur = solutionHistory[i][dof]; + real64 dxPrev = solutionHistory[i-1][dof]; + + if( LvArray::math::abs( dxCur ) < oscillationTolerance || LvArray::math::abs( dxPrev ) < oscillationTolerance ) + { + oscillationDetected = false; + break; // solution changes are too small + } + + real64 maxAbs = LvArray::math::max( LvArray::math::abs( dxCur ), LvArray::math::abs( dxPrev ) ); + if( LvArray::math::abs( dxCur + dxPrev ) / maxAbs > oscillationTolerance ) + { + oscillationDetected = false; + break; // solution changes are not oscillating + } + + if( dxCur * dxPrev > 0 ) + { + oscillationDetected = false; + break; // sign is not oscillating + } + } + + if( oscillationDetected ) + { + oscillationCount += 1; + } + } ); + + real64 const f = static_cast< real64 >( MpiWrapper::sum( oscillationCount.get() ) ) / MpiWrapper::sum( numDofs ); + + return f > oscillationFraction; +} + + +} // namespace geos diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/WellNewtonSolver.hpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellNewtonSolver.hpp new file mode 100644 index 00000000000..bf615a0ee37 --- /dev/null +++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellNewtonSolver.hpp @@ -0,0 +1,865 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * SPDX-License-Identifier: LGPL-2.1-only + * + * Copyright (c) 2016-2024 Lawrence Livermore National Security LLC + * Copyright (c) 2018-2024 TotalEnergies + * Copyright (c) 2018-2024 The Board of Trustees of the Leland Stanford Junior University + * Copyright (c) 2023-2024 Chevron + * Copyright (c) 2019- GEOS/GEOSX Contributors + * All rights reserved + * + * See top level LICENSE, COPYRIGHT, CONTRIBUTORS, NOTICE, and ACKNOWLEDGEMENTS files for details. + * ------------------------------------------------------------------------------------------------------------ + */ + +/** + * @file WellNewtonSolver.hpp + */ + +#ifndef GEOS_PHYSICSSOLVERS_WELLNEWTONSOLVER_HPP_ +#define GEOS_PHYSICSSOLVERS_WELLNEWTONSOLVER_HPP_ + +#include "codingUtilities/traits.hpp" +#include "common/DataTypes.hpp" +#include "common/format/LogPart.hpp" + +#include "dataRepository/RestartFlags.hpp" +#include "linearAlgebra/interfaces/InterfaceTypes.hpp" +#include "linearAlgebra/utilities/LinearSolverResult.hpp" +#include "linearAlgebra/DofManager.hpp" +#include "mesh/MeshBody.hpp" +#include "physicsSolvers/NonlinearSolverParameters.hpp" +#include "physicsSolvers/LinearSolverParameters.hpp" +#include "physicsSolvers/SolverStatistics.hpp" +#include "physicsSolvers/LogLevelsInfo.hpp" +#include "common/Timer.hpp" +#include + +namespace geos +{ + +class DomainPartition; + +/** + * @class WellNewtonSolver + * @brief Base class for all physics solvers + * + * This class provides the base interface for all physics solvers. It provides the basic + * functionality for setting up and solving a linear system, as well as the interface for + * performing a timestep. + */ +class WellNewtonSolver : public dataRepository::Group +{ +public: + + /** + * @brief Type of the stat output + */ + enum class StatsOutputType : integer + { + none, iteration, convergence, all + }; + + /** + * @brief Constructor for WellNewtonSolver + * @param name the name of this instantiation of WellNewtonSolver + * @param parent the parent group of this instantiation of WellNewtonSolver + */ + explicit WellNewtonSolver( string const & name, + Group * const parent ); + + /** + * @brief Move constructor for WellNewtonSolver + */ + WellNewtonSolver( WellNewtonSolver && ) = default; + + /** + * @brief Destructor for WellNewtonSolver + */ + virtual ~WellNewtonSolver() override; + + /** + * @brief Deleted constructor + */ + WellNewtonSolver() = delete; + + /** + * @brief Deleted copy constructor + */ + WellNewtonSolver( WellNewtonSolver const & ) = delete; + + /** + * @brief Deleted copy assignment operator + */ + WellNewtonSolver & operator=( WellNewtonSolver const & ) = delete; + + /** + * @brief Deleted move assignment operator + */ + WellNewtonSolver & operator=( WellNewtonSolver && ) = delete; + + /** + * @brief name of the node manager in the object catalog + * @return string that contains the catalog name to generate a new NodeManager object through the object catalog. + */ + static string catalogName() { return "WellNewtonSolver"; } + + + + /** + * @brief Generate mesh targets from target regions + * @param meshBodies the group of mesh bodies + */ + void generateMeshTargetsFromTargetRegions( Group const & meshBodies ); + + + /** + * + * @brief Getter for system matrix + * @return a reference to linear system matrix of this solver + */ + ParallelMatrix & getSystemMatrix() { return m_matrix; } + + /** + * @brief Getter for system rhs vector + * @return a reference to linear system right-hand side of this solver + */ + ParallelMatrix const & getSystemMatrix() const { return m_matrix; } + + /** + * @brief Getter for system rhs vector + * @return a reference to linear system right-hand side of this solver + */ + ParallelVector & getSystemRhs() { return m_rhs; } + + /** + * @brief Getter for system rhs vector + * @return a reference to linear system right-hand side of this solver + */ + ParallelVector const & getSystemRhs() const { return m_rhs; } + + /** + * @brief Getter for system solution vector + * @return a reference to solution vector of this solver + */ + ParallelVector & getSystemSolution() { return m_solution; } + + /** + * @brief Getter for system solution vector + * @return a reference to solution vector of this solver + */ + ParallelVector const & getSystemSolution() const { return m_solution; } + + /** + * @brief Getter for degree-of-freedom manager + * @return a reference to degree-of-freedom manager of this solver + */ + DofManager & getDofManager() { return m_dofManager; } + + /** + * @brief Getter for degree-of-freedom manager + * @return a reference to degree-of-freedom manager of this solver + */ + DofManager const & getDofManager() const { return m_dofManager; } + + /** + * @brief Getter for local matrix + * @return a reference to linear system matrix of this solver + */ + CRSMatrix< real64, globalIndex > & getLocalMatrix() { return m_localMatrix; } + + /** + * @brief Getter for local matrix + * @return a reference to linear system matrix of this solver + */ + CRSMatrixView< real64 const, globalIndex const > getLocalMatrix() const { return m_localMatrix.toViewConst(); } + + + template< typename T > + void setupSystem( T & well, DomainPartition & domain, + std::string const & meshBodyName, + MeshLevel const & meshLevel, + WellElementRegion & wellElementRegion, + bool const setSparsity =true ); + + template< typename T > + bool + solveNonlinearSystem( T & well, real64 const & time_n, + real64 const & stepDt, + integer const cycleNumber, + DomainPartition & domain, + MeshLevel & mesh, + ElementRegionManager & elemManager, + WellElementSubRegion & subRegion ); + + + /** + * @brief Populate degree-of-freedom manager with fields relevant to this solver + * @param domain the domain containing the mesh and fields + * @param dofManager degree-of-freedom manager associated with the linear system + */ + virtual void + setupDofs( DomainPartition const & domain, + DofManager & dofManager ) const; + + /** + * @brief Set up the linear system (DOF indices and sparsity patterns) + * @param domain the domain containing the mesh and fields + * @param dofManager degree-of-freedom manager associated with the linear system + * @param localMatrix the system matrix + * @param rhs the system right-hand side vector + * @param solution the solution vector + * @param setSparsity flag to indicate if the sparsity pattern should be set + * + * @note While the function is virtual, the base class implementation should be + * sufficient for most single-physics solvers. + */ + + + /** + * @brief Set the sparsity pattern of the linear system matrix + * @param domain the domain containing the mesh and fields + * @param dofManager degree-of-freedom manager associated with the linear system + * @param localMatrix the system matrix + * @param pattern the sparsity pattern to be filled + */ + virtual void + setSparsityPattern( DomainPartition & domain, + DofManager & dofManager, + CRSMatrix< real64, globalIndex > & localMatrix, + SparsityPattern< globalIndex > & pattern ); + + /** + * @brief Create a preconditioner for this solver's linear system. + * @param domain the domain containing the mesh and fields + * @return the newly created preconditioner object + */ + virtual std::unique_ptr< PreconditionerBase< LAInterface > > + createPreconditioner( DomainPartition & domain ) const; + + + /** + * @brief Output the assembled linear system for debug purposes. + * @param time beginning-of-step time + * @param cycleNumber event cycle number + * @param nonlinearIteration current nonlinear iteration number + * @param matrix system matrix + * @param rhs system right-hand side vector + */ + void + debugOutputSystem( real64 const & time, + integer const cycleNumber, + integer const nonlinearIteration, + ParallelMatrix const & matrix, + ParallelVector const & rhs ) const; + + /** + * @brief Output the linear system solution for debug purposes. + * @param time beginning-of-step time + * @param cycleNumber event cycle number + * @param nonlinearIteration current nonlinear iteration number + * @param solution system solution vector + */ + void + debugOutputSolution( real64 const & time, + integer const cycleNumber, + integer const nonlinearIteration, + ParallelVector const & solution ) const; + + /** + * @brief Update the convergence information and write then into a CSV file + * @param time_n the time at the beginning of the step + * @param dt the desired timestep + * @param cycleNumber event cycle number + * @param iteration current iteration + */ + virtual void + updateAndWriteConvergenceStep( real64 const & time_n, + real64 const & dt, + integer const cycleNumber, + integer const iteration ); + + + + /** + * @brief function to apply a linear system solver to the assembled system. + * @param dofManager degree-of-freedom manager associated with the linear system + * @param matrix the system matrix + * @param rhs the system right-hand side vector + * @param solution the solution vector + * + * This function calls the linear solver package to perform a single linear solve on the block + * system. The derived physics solver is required to specify the call, as no default is provided. + * + * @note This function must be overridden in the derived physics solver in order to use an implict + * solution method such as LinearImplicitStep() or NonlinearImplicitStep(). + */ + virtual void + solveLinearSystem( DofManager const & dofManager, + ParallelMatrix & matrix, + ParallelVector & rhs, + ParallelVector & solution ); + + + + /** + * @brief creates a child group of of this WellNewtonSolver instantiation + * @param childKey the key of the child type + * @param childName the name of the child + * @return a pointer to the child group + */ + virtual Group * createChild( string const & childKey, string const & childName ) override; + + /** + * @brief Type alias for catalog interface used by this class. See CatalogInterface. + */ + using CatalogInterface = dataRepository::CatalogInterface< WellNewtonSolver, string const &, Group * const >; + + /** + * @brief Get the singleton catalog for WellNewtonSolver. + * @return reference to the catalog object + */ + static CatalogInterface::CatalogType & getCatalog(); + + /** + * @brief Structure to hold scoped key names + */ + struct viewKeyStruct + { + /// @return string for the cflFactor wrapper + static constexpr char const * cflFactorString() { return "cflFactor"; } + + /// @return string for the initialDt wrapper + static constexpr char const * initialDtString() { return "initialDt"; } + + /// @return string for the minDtIncreaseInterval wrapper + static constexpr char const * minDtIncreaseIntervalString() { return "minDtIncreaseInterval"; } + + /// @return string for the discretization wrapper + static constexpr char const * discretizationString() { return "discretization"; } + + /// @return string for the nextDt targetRegions wrapper + static constexpr char const * targetRegionsString() { return "targetRegions"; } + + /// @return string for the writeLinearSystem wrapper + static constexpr char const * writeLinearSystemString() { return "writeLinearSystem"; } + + /// @return string for the usePhysicsScaling wrapper + static constexpr char const * usePhysicsScalingString() { return "usePhysicsScaling"; } + + /// @return string for the allowNonConvergedLinearSolverSolution wrapper + static constexpr char const * allowNonConvergedLinearSolverSolutionString() { return "allowNonConvergedLinearSolverSolution"; } + + /// @return string for the writeStatistics wrapper + static constexpr char const * writeStatisticsCSVString() { return "writeStatistics"; } + + /// @return string for the numTimestepsSinceLastDtCut wrapper + static constexpr char const * numTimestepsSinceLastDtCutString() { return "numTimestepsSinceLastDtCut"; } + + /// string key for the esitmate well solution flag + static constexpr char const * activeCoupledIterationsString() { return "activeCoupledIterations"; } + static constexpr char const * estimateWellSolutionString() { return "estimateWellSolution"; } + /// string key for the enable iso thermal estimator flag + static constexpr char const * enableIsoThermalEstimatorString() { return "enableIsoThermalEstimator"; } + }; + + /** + * @brief Structure to hold scoped key names + */ + struct groupKeyStruct + { + /// @return string for the linearSolverParameters wrapper + static constexpr char const * linearSolverParametersString() { return "LinearSolverParameters"; } + + /// @return string for the nonlinearSolverParameters wrapper + static constexpr char const * nonlinearSolverParametersString() { return "NonlinearSolverParameters"; } + + /// @return string for the solverStatistics wrapper + static constexpr char const * solverStatisticsString() { return "SolverStatistics"; } + }; + + /** + * @brief getter for the timestamp of the system setup + * @return the timestamp of the last time systemSetup was called + */ + Timestamp getSystemSetupTimestamp() const { return m_systemSetupTimestamp; } + + + /** + * @brief set the timestamp of the system setup + * @param[in] timestamp the new timestamp of system setup + */ + void setSystemSetupTimestamp( Timestamp timestamp ); + + + /** + * @brief accessor for the linear solver parameters. + * @return the linear solver parameter list + */ + LinearSolverParameters & getLinearSolverParameters() + { + return m_linearSolverParameters.get(); + } + + /** + * @brief const accessor for the linear solver parameters. + * @return the linear solver parameter list + */ + LinearSolverParameters const & getLinearSolverParameters() const + { + return m_linearSolverParameters.get(); + } + + /** + * @brief accessor for the nonlinear solver parameters. + * @return the nonlinear solver parameter list + */ + NonlinearSolverParameters & getNonlinearSolverParameters() + { + return m_nonlinearSolverParameters; + } + + /** + * @brief const accessor for the nonlinear solver parameters. + * @return the nonlinear solver parameter list + */ + NonlinearSolverParameters const & getNonlinearSolverParameters() const + { + return m_nonlinearSolverParameters; + } + + /** + * @brief synchronize the nonlinear solver parameters. + */ + virtual void + synchronizeNonlinearSolverParameters() + { /* empty here, overriden in CoupledSolver */ } + + /** + * @brief Get position of a given region within solver's target region list + * @param regionName the region name to find + * @return index within target regions list + */ + localIndex targetRegionIndex( string const & regionName ) const; + + /** + * @brief return the list of target regions + * @return the array of region names + */ + string_array const & getTargetRegionNames() const {return m_targetRegionNames;} + + + + /** + * @brief function to set the value of m_assemblyCallback + * @param func the function to set m_assemblyCallback to + * @param funcType the type of the function + * @return true if the function was successfully set, false otherwise + * + * This is used to provide a callback function for to be called in the assembly step. + */ + virtual bool registerCallback( void * func, const std::type_info & funcType ) final override; + + /** + * @return An IterationsStatistics for the "root" solver. + * Otherwise return an empty IterationsStatistics + */ + IterationsStatistics & getIterationStats() + { + return m_solverStatistics.m_iterationsStats; + } + /** + * @return An IterationsStatistics for the "root" solver. + * Otherwise return an empty IterationsStatistics + * (const version) + */ + IterationsStatistics const & getIterationStats() const + { + return m_solverStatistics.m_iterationsStats; + } + /** + * @return A ConvergenceStatistics for all sub-solvers + */ + ConvergenceStatistics & getConvergenceStats() + { + return m_solverStatistics.m_convergenceStats; + } + /** + * @return A ConvergenceStatistics for all sub-solvers (const version) + */ + ConvergenceStatistics const & getConvergenceStats() const + { + return m_solverStatistics.m_convergenceStats; + } + + /** + * @brief accessor for the solver statistics. + * @return reference to m_solverStatistics + */ + SolverStatistics & getSolverStatistics() { return m_solverStatistics; } + + /** + * @brief const accessor for the solver statistics. + * @return reference to m_solverStatistics + */ + SolverStatistics const & getSolverStatistics() const { return m_solverStatistics; } + + + + /** + * @brief Detect oscillations in the solution + * @return true if oscillations are detected, false otherwise + */ + bool detectOscillations() const; + + + /** + * @brief Set thermal effects enable + * @param[in] true/false + */ + void enableThermalEffects ( bool enable ) { m_thermalEffectsEnabled = enable; }; + + /** + * @brief Are thermal effects enabled + * @return true if thermal effects are enabled, false otherwise + */ + bool thermalEffectsEnabled() const { return m_thermalEffectsEnabled; } + + /** + * @brief Is isoThermalEstimator enabled + * @return true if isoThermalEstimator is enabled, false otherwise + */ + bool isoThermalEstimatorEnabled() const { return m_enableIsoThermalEstimator; } + + bool getNumActiveCoupledIterations() const { return m_activeCoupledIterations; } + +protected: + + virtual void postInputInitialization() override; + + /// behavior in case of linear solver failure + integer m_allowNonConvergedLinearSolverSolution; + + + + /// Data structure to handle degrees of freedom + DofManager m_dofManager; + + /// System matrix + ParallelMatrix m_matrix; + + /// System right-hand side vector + ParallelVector m_rhs; + + /// System solution vector + ParallelVector m_solution; + + /// Diagonal scaling vector D (Ahat = D * A * D, bhat = D * b, x = D * xhat) + ParallelVector m_scaling; + + /// Flag to decide whether to apply physics-based scaling to the linear system + integer m_usePhysicsScaling; + + /// Local system matrix and rhs + CRSMatrix< real64, globalIndex > m_localMatrix; + + /// Custom linear solver for the "native" solver type + std::unique_ptr< LinearSolverBase< LAInterface > > m_linearSolver; + + /// Custom preconditioner for the "native" iterative solver + std::unique_ptr< PreconditionerBase< LAInterface > > m_precond; + + /// flag for debug output of matrix, rhs, and solution + integer m_writeLinearSystem; + + /// Parameter for outputing statistics information + StatsOutputType m_writeStatisticsCSV; + + /// Linear solver parameters + LinearSolverParametersInput m_linearSolverParameters; + + /// Result of the last linear solver + LinearSolverResult m_linearSolverResult; + + /// Nonlinear solver parameters + NonlinearSolverParameters m_nonlinearSolverParameters; + + /// Solver statistics + SolverStatistics m_solverStatistics; + + /// Timestamp of the last call to setup system + Timestamp m_systemSetupTimestamp; + + /// Callback function for assembly step + std::function< void( CRSMatrix< real64, globalIndex >, array1d< real64 > ) > m_assemblyCallback; + + /// Timers for the aggregate profiling of the solver + stdMap< std::string, std::chrono::system_clock::duration > m_timers; + + /// History of the solution vector, used for oscillation detection + ArrayOfArrays< real64 > m_solutionHistory; + +private: + /// List of names of regions the solver will be applied to + string_array m_targetRegionNames; + + /// Map containing the array of target regions (value) for each MeshBody (key). + map< std::pair< string, string >, string_array > m_meshTargets; + + /// Number of coupled iterations to activate estimator solve + integer m_activeCoupledIterations; + + /// Flag to enable thermal effects in wellbore calculations + bool m_thermalEffectsEnabled; + integer m_enableIsoThermalEstimator; + + + /** + * @brief output information about the cycle to the log + * @param cycleNumber the current cycle number + * @param numOfSubSteps the number of substeps taken + * @param subStepDts the time step size for each substep + */ + void logEndOfCycleInformation( integer const cycleNumber, + integer const numOfSubSteps, + stdVector< real64 > const & subStepDts ) const; +}; + +template< typename T > +void WellNewtonSolver::setupSystem( T & well, DomainPartition & domain, + std::string const & meshBodyName, + MeshLevel const & meshLevel, + WellElementRegion & wellElementRegion, + bool const setSparsity ) +{ + GEOS_MARK_FUNCTION; + + map< std::pair< string, string >, string_array > meshTargets; + string_array regions; + + meshTargets.clear(); + regions.clear(); + regions.emplace_back( wellElementRegion.getName() ); + auto const key = std::make_pair( meshBodyName, meshLevel.getName() ); + meshTargets[key] = std::move( regions ); + + m_dofManager.setDomain( domain ); + m_dofManager.addField( well.wellElementDofName(), + FieldLocation::Elem, + well.numDofPerWellElement(), + meshTargets ); + + m_dofManager.addCoupling( well.wellElementDofName(), + well.wellElementDofName(), + DofManager::Connector::Node ); + + m_dofManager.reorderByRank(); + if( setSparsity ) + { + SparsityPattern< globalIndex > pattern; + setSparsityPattern( domain, m_dofManager, m_localMatrix, pattern ); + m_localMatrix.assimilate< parallelDevicePolicy<> >( std::move( pattern ) ); + } + m_localMatrix.setName( this->getName() + "/matrix" ); + + m_rhs.setName( this->getName() + "/rhs" ); + m_rhs.create( m_dofManager.numLocalDofs(), MPI_COMM_GEOS ); + + m_solution.setName( this->getName() + "/solution" ); + m_solution.create( m_dofManager.numLocalDofs(), MPI_COMM_GEOS ); +} + + +template< typename T > +bool WellNewtonSolver::solveNonlinearSystem( T & well, real64 const & time_n, + real64 const & stepDt, + integer const cycleNumber, + DomainPartition & domain, + MeshLevel & mesh, + ElementRegionManager & elemManager, + WellElementSubRegion & subRegion ) +{ + integer const maxNewtonIter = m_nonlinearSolverParameters.m_maxIterNewton; + integer dtAttempt = m_nonlinearSolverParameters.m_numTimeStepAttempts; + integer configurationLoopIter = m_nonlinearSolverParameters.m_numConfigurationAttempts; + integer const minNewtonIter = m_nonlinearSolverParameters.m_minIterNewton; + real64 const newtonTol = m_nonlinearSolverParameters.m_newtonTol; + +// keep residual from previous iteration in case we need to do a line search + + integer newtonIter = 0; + real64 scaleFactor = 1.0; + + bool isNewtonConverged = false; + + for( newtonIter = 0; newtonIter < maxNewtonIter; ++newtonIter ) + { + if( m_nonlinearSolverParameters.getLogLevel() > 4 ) + GEOS_LOG_LEVEL_RANK_0( logInfo::NonlinearSolver, + GEOS_FMT( " Well: {} Est Attempt: NewtonIter: {:2}", subRegion.getName(), stepDt, newtonIter )); + + { + Timer timer( m_timers.get_inserted( "assemble" ) ); + +// We sync the nonlinear convergence history. The coupled solver parameters are the one being +// used. We want to propagate the info to subsolvers. It can be important for solvers that +// have special treatment for specific iterations. + synchronizeNonlinearSolverParameters(); + +// zero out matrix/rhs before assembly + m_localMatrix.zero(); + m_rhs.zero(); + + arrayView1d< real64 > const localRhs = m_rhs.open(); + +// call assemble to fill the matrix and the rhs + well.assembleSystem( time_n, + stepDt, + cycleNumber, + elemManager, + subRegion, + m_dofManager, + m_localMatrix.toViewConstSizes(), + localRhs ); + +// apply boundary conditions to system + well.applyWellBoundaryConditions( time_n, + stepDt, + elemManager, + subRegion, + m_dofManager, + localRhs, + m_localMatrix.toViewConstSizes() ); + + m_rhs.close(); + + if( m_assemblyCallback ) + { +// Make a copy of LA objects and ship off to the callback + array1d< real64 > localRhsCopy( m_rhs.localSize() ); + localRhsCopy.setValues< parallelDevicePolicy<> >( m_rhs.values() ); + m_assemblyCallback( m_localMatrix, std::move( localRhsCopy ) ); + } + } + + // well.outputSingleWellDebug( time_n, stepDt, 0, newtonIter, 0, + // mesh, subRegion, dofManager, m_localMatrix.toViewConstSizes(), m_rhs.values() ); + real64 residualNorm = 0; + { + Timer timer( m_timers.get_inserted( "convergence check" ) ); + +// get residual norm + residualNorm = well.calculateWellResidualNorm( time_n, stepDt, m_nonlinearSolverParameters, subRegion, m_dofManager, m_rhs.values() ); + if( m_nonlinearSolverParameters.getLogLevel() > 4 ) + GEOS_LOG_LEVEL_RANK_0( logInfo::Convergence, + GEOS_FMT( " ( R ) = ( {:4.2e} )", residualNorm ) ); + } + //auto iterInfo = currentIter( time_n, dt ); + //outputSingleWellDebug( time_n, stepDt, 0, newtonIter, 0, + // mesh, subRegion, dofManager, m_localMatrix.toViewConstSizes(), m_rhs.values() ); +// if the residual norm is less than the Newton tolerance we denote that we have +// converged and break from the Newton loop immediately. + std::cout << " Well: " << subRegion.getName() << " Est Attempt: " << dtAttempt + << ", ConfigurationIter: " << configurationLoopIter + << ", NewtonIter: " << newtonIter + << ", Residual Norm: " << residualNorm << std::endl; + if( residualNorm < newtonTol && newtonIter >= minNewtonIter ) + { + isNewtonConverged = true; + break; + } + +// if the residual norm is above the max allowed residual norm, we break from +// the Newton loop to avoid crashes due to Newton divergence + if( residualNorm > m_nonlinearSolverParameters.m_maxAllowedResidualNorm ) + { + string const maxAllowedResidualNormString = NonlinearSolverParameters::viewKeysStruct::maxAllowedResidualNormString(); + if( m_nonlinearSolverParameters.getLogLevel() > 4 ) + GEOS_LOG_LEVEL_RANK_0( logInfo::Convergence, + GEOS_FMT( " The residual norm is above the {} of {}. Newton loop terminated.", + maxAllowedResidualNormString, + m_nonlinearSolverParameters.m_maxAllowedResidualNorm ) ); + isNewtonConverged = false; + break; + } + + { + Timer timer( m_timers.get_inserted( "linear solver total" ) ); + +// TODO: Trilinos currently requires this, re-evaluate after moving to Tpetra-based solvers + if( m_precond ) + { + m_precond->clear(); + } + + { + Timer timer_setup( m_timers.get_inserted( "linear solver create" ) ); + +// Compose parallel LA matrix/rhs out of local LA matrix/rhs +// + m_matrix.create( m_localMatrix.toViewConst(), m_dofManager.numLocalDofs(), MPI_COMM_GEOS ); + } + +// Output the linear system matrix/rhs for debugging purposes + //string tag = "_"+std::to_string( my_ctime ); tjb + //debugOutputSystem( time_n, cycleNumber, newtonIter, m_matrix, m_rhs, tag ); + + debugOutputSystem( time_n, cycleNumber, newtonIter, m_matrix, m_rhs ); +// Solve the linear system + solveLinearSystem( m_dofManager, m_matrix, m_rhs, m_solution ); + +// Increment the solver statistics for reporting purposes + getIterationStats().updateNonlinearIteration( m_linearSolverResult.numIterations ); + +// Output the linear system solution for debugging purposes + debugOutputSolution( time_n, cycleNumber, newtonIter, m_solution ); + //debugOutputSolution( time_n, cycleNumber, newtonIter, m_solution, tag ); + } + + { + Timer timer( m_timers.get_inserted( "apply solution" ) ); + +// Compute the scaling factor for the Newton update + scaleFactor = well.scalingForWellSystemSolution( subRegion, m_dofManager, m_solution.values() ); + if( m_nonlinearSolverParameters.getLogLevel() > 4 ) + GEOS_LOG_LEVEL_RANK_0( logInfo::Solution, + GEOS_FMT( " {}: Global solution scaling factor = {}", getName(), scaleFactor ) ); + + if( !well.checkWellSystemSolution( subRegion, m_dofManager, m_solution.values(), scaleFactor ) ) + { +// TODO try chopping (similar to line search) + if( m_nonlinearSolverParameters.getLogLevel() > 4 ) + GEOS_LOG_RANK_0( GEOS_FMT( " {}: Solution check failed. Newton loop terminated.", getName()) ); + break; + } + +// apply the system solution to the fields/variables + well.applyWellSystemSolution( m_dofManager, m_solution.values(), scaleFactor, stepDt, domain, mesh, subRegion ); + } + + { + Timer timer( m_timers.get_inserted( "update state" ) ); + + // update derived variables (constitutive models) + well.updateWellState( elemManager, subRegion ); + } + + } + return isNewtonConverged; +} + + +/** + * @brief String for the stats output type + */ +ENUM_STRINGS( WellNewtonSolver::StatsOutputType, + "none", + "iteration", + "convergence", + "all" ); + +} // namespace geos + + +#endif /* GEOS_PHYSICSSOLVERS_WELLNEWTONSOLVER_HPP_ */ diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/WellPhaseVolumeRateConstraint.cpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellPhaseVolumeRateConstraint.cpp new file mode 100644 index 00000000000..63f7baa7053 --- /dev/null +++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellPhaseVolumeRateConstraint.cpp @@ -0,0 +1,81 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * SPDX-License-Identifier: LGPL-2.1-only + * + * Copyright (c) 2016-2024 Lawrence Livermore National Security LLC + * Copyright (c) 2018-2024 TotalEnergies + * Copyright (c) 2018-2024 The Board of Trustees of the Leland Stanford Junior University + * Copyright (c) 2023-2024 Chevron + * Copyright (c) 2019- GEOS/GEOSX Contributors + * All rights reserved + * + * See top level LICENSE, COPYRIGHT, CONTRIBUTORS, NOTICE, and ACKNOWLEDGEMENTS files for details. + * ------------------------------------------------------------------------------------------------------------ + */ + +/* + * @file WellPhaseVolumeRateConstraint.cpp + */ + +#include "LogLevelsInfo.hpp" +#include "WellPhaseVolumeRateConstraint.hpp" +#include "WellConstants.hpp" +#include "dataRepository/InputFlags.hpp" +#include "functions/FunctionManager.hpp" + + +namespace geos +{ + +using namespace dataRepository; + + +PhaseVolumeRateConstraint::PhaseVolumeRateConstraint( string const & name, Group * const parent ) + : WellConstraintBase( name, parent ) +{ + this->setInputFlags( InputFlags::OPTIONAL_NONUNIQUE ); + + this->registerWrapper( viewKeyStruct::phaseRateString(), &this->m_constraintValue ). + setDefaultValue( 0.0 ). + setInputFlag( InputFlags::OPTIONAL ). + setRestartFlags( RestartFlags::WRITE_AND_READ ). + setDescription( "Phase rate, (if useSurfaceConditions: [surface m^3/s]; else [reservoir m^3/s]) " ); + + this->registerWrapper( viewKeyStruct::phaseNameString(), &this->m_phaseName ). + setRTTypeName( rtTypes::CustomTypes::groupNameRef ). + setDefaultValue( "" ). + setInputFlag( InputFlags::OPTIONAL ). + setRestartFlags( RestartFlags::WRITE_AND_READ ). + setDescription( "Name of the target phase" ); +} + +PhaseVolumeRateConstraint::~PhaseVolumeRateConstraint() +{} + +void PhaseVolumeRateConstraint::postInputInitialization() +{ + // Validate table options + WellConstraintBase::postInputInitialization(); + + // check constraint value + GEOS_THROW_IF( m_constraintValue < 0, + getWrapperDataContext( viewKeyStruct::phaseRateString() ) << ": Target value is negative", + InputError ); + + + GEOS_THROW_IF ((m_constraintValue <= 0.0 && m_constraintScheduleTableName.empty()), + getName() << " " << getDataContext() << ": You need to specify a phase rate constraint. \n" << + "The rate constraint can be specified using " << + "either " << viewKeyStruct::phaseRateString() << + " or " << WellConstraintBase::viewKeyStruct::constraintScheduleTableNameString(), + InputError ); +} + +bool PhaseVolumeRateConstraint::checkViolation( WellConstraintBase const & currentConstraint, real64 const & currentTime ) const +{ + real64 const currentValue = currentConstraint.phaseVolumeRates()[m_phaseIndex]; + real64 const constraintValue = getConstraintValue( currentTime ); + return ( LvArray::math::abs( currentValue ) > LvArray::math::abs( constraintValue ) ); +} + +} //namespace geos diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/WellPhaseVolumeRateConstraint.hpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellPhaseVolumeRateConstraint.hpp new file mode 100644 index 00000000000..bb7880cfc0c --- /dev/null +++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellPhaseVolumeRateConstraint.hpp @@ -0,0 +1,179 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * SPDX-License-Identifier: LGPL-2.1-only + * + * Copyright (c) 2016-2024 Lawrence Livermore National Security LLC + * Copyright (c) 2018-2024 TotalEnergies + * Copyright (c) 2018-2024 The Board of Trustees of the Leland Stanford Junior University + * Copyright (c) 2023-2024 Chevron + * Copyright (c) 2019- GEOS/GEOSX Contributors + * All rights reserved + * + * See top level LICENSE, COPYRIGHT, CONTRIBUTORS, NOTICE, and ACKNOWLEDGEMENTS files for details. + * ------------------------------------------------------------------------------------------------------------ + */ + +/* + * @file WellPhaseVolumeRateConstraint.hpp + */ + + +#ifndef GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_WELLPHASEVOLUMERATECONSTRAINT_HPP +#define GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_WELLPHASEVOLUMERATECONSTRAINT_HPP + +#include "common/format/EnumStrings.hpp" +#include "dataRepository/Group.hpp" +#include "functions/TableFunction.hpp" +#include "WellConstraintsBase.hpp" +#include "WellConstants.hpp" + +namespace geos +{ + + +template< typename T > +localIndex getPhaseIndexFromFluidModel( T const & fluidModel, std::string const & inputPhase ) +{ + localIndex phaseIndex=-1; + // Find target phase index for phase rate constraint + for( integer ip = 0; ip < fluidModel.numFluidPhases(); ++ip ) + { + if( fluidModel.phaseNames()[ip] == inputPhase ) + { + phaseIndex = ip; + } + } + return phaseIndex; +} + +/** + * @class PhaseVolumeRateConstraint + * @brief This class describes a phase rate constraint used to control a well of WellConstraintType type (Injection or Production). + */ + +class PhaseVolumeRateConstraint : public WellConstraintBase +{ +public: + + + /** + * @name Constructor / Destructor + */ + ///@{ + + /** + * @brief Constructor for WellControls Objects. + * @param[in] name the name of this instantiation of WellControls in the repository + * @param[in] parent the parent group of this instantiation of WellControls + */ + explicit PhaseVolumeRateConstraint( string const & name, dataRepository::Group * const parent ); + + /** + * @brief Default destructor. + */ + ~PhaseVolumeRateConstraint() override; + + /** + * @brief Deleted default constructor. + */ + PhaseVolumeRateConstraint() = delete; + + /** + * @brief Deleted copy constructor. + */ + PhaseVolumeRateConstraint( PhaseVolumeRateConstraint const & ) = delete; + + /** + * @brief Deleted move constructor. + */ + PhaseVolumeRateConstraint( PhaseVolumeRateConstraint && ) = delete; + + /** + * @brief Deleted assignment operator. + * @return a reference to a constraint object + */ + PhaseVolumeRateConstraint & operator=( PhaseVolumeRateConstraint const & ) = delete; + + /** + * @brief Deleted move operator. + * @return a reference to a constraint object + */ + PhaseVolumeRateConstraint & operator=( PhaseVolumeRateConstraint && ) = delete; + + ///@} + + /** + * @brief name of the node manager in the object catalog + * @return string that contains the catalog name to generate a new Constraint object through the object catalog. + */ + static string catalogName() + { + return "PhaseVolumeRateConstraint"; + } + + /** + * @name Getters / Setters + */ + ///@{ + + // Temp interface - tjb + virtual ConstraintTypeId getControl() const override { return ConstraintTypeId::PHASEVOLRATE; }; + + /** + * @brief Get the target phase name + * @return the target phase name + */ + const string & getPhaseName() const { return m_phaseName; } + + /** + * @brief Get the target phase index + * @return the target phase index + */ + const localIndex & getPhaseIndex() const { return m_phaseIndex; } + + ///@} + + struct viewKeyStruct + { + /// String key for the well target phase rate + static constexpr char const * phaseRateString() { return "phaseRate"; } + /// String key for the well target phase name + static constexpr char const * phaseNameString() { return "phaseName"; } + }; + + /** + * @brief Validate phase type is consistent with fluidmodel + */ + template< typename T > void validatePhaseType( T const & fluidModel ); + ///@} + + virtual bool checkViolation( WellConstraintBase const & currentConstraint, real64 const & currentTime ) const override; +protected: + + virtual void postInputInitialization() override; + +private: + + /// Name of the targeted phase + string m_phaseName; + + /// Index of the target phase, used to impose the phase rate constraint + localIndex m_phaseIndex; + +}; + +template< typename T > +void PhaseVolumeRateConstraint::validatePhaseType( T const & fluidModel ) +{ + // Find target phase index for phase rate constraint + m_phaseIndex = getPhaseIndexFromFluidModel( fluidModel, this->template getReference< string >( viewKeyStruct::phaseNameString())); + + GEOS_THROW_IF( m_phaseIndex == -1, + "PhaseVolumeRateConstraint " << this->template getReference< string >( viewKeyStruct::phaseNameString()) << + ": Invalid phase type for simulation fluid model", + InputError ); +} + +} //namespace geos + +#endif //GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_WELLPHASEVOLUMERATECONSTRAINT_HPP diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/WellProductionConstraint.cpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellProductionConstraint.cpp new file mode 100644 index 00000000000..4ec3ec3299d --- /dev/null +++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellProductionConstraint.cpp @@ -0,0 +1,70 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * SPDX-License-Identifier: LGPL-2.1-only + * + * Copyright (c) 2016-2024 Lawrence Livermore National Security LLC + * Copyright (c) 2018-2024 TotalEnergies + * Copyright (c) 2018-2024 The Board of Trustees of the Leland Stanford Junior University + * Copyright (c) 2023-2024 Chevron + * Copyright (c) 2019- GEOS/GEOSX Contributors + * All rights reserved + * + * See top level LICENSE, COPYRIGHT, CONTRIBUTORS, NOTICE, and ACKNOWLEDGEMENTS files for details. + * ------------------------------------------------------------------------------------------------------------ + */ + +/* + * @file WellProductionConstraint.cpp + */ + +#include "LogLevelsInfo.hpp" +#include "WellProductionConstraint.hpp" +#include "WellConstants.hpp" +#include "dataRepository/InputFlags.hpp" +#include "functions/FunctionManager.hpp" + +#include "WellLiquidRateConstraint.hpp" +#include "WellMassRateConstraint.hpp" +#include "WellPhaseVolumeRateConstraint.hpp" +#include "WellVolumeRateConstraint.hpp" + +namespace geos +{ + +template< typename ConstraintRateType > +ProductionConstraint< ConstraintRateType >::ProductionConstraint( string const & name, Group * const parent ) + : ConstraintRateType( name, parent ) +{ + // set rate sign for producers (base class member) + this->m_rateSign = -1.0; +} +template< typename ConstraintRateType > +ProductionConstraint< ConstraintRateType >::~ProductionConstraint() +{} + +template< typename ConstraintRateType > +void ProductionConstraint< ConstraintRateType >::postInputInitialization() +{ + // Validate value and table options + ConstraintRateType::postInputInitialization(); + +} +// Register concrete wrapper constraint types and instantiate templates. + +template class ProductionConstraint< LiquidRateConstraint >; +using ProductionLiquidRateConstraint = ProductionConstraint< LiquidRateConstraint >; +REGISTER_CATALOG_ENTRY( WellConstraintBase, ProductionLiquidRateConstraint, string const &, Group * const ) + +template class ProductionConstraint< MassRateConstraint >; +using ProductionMassRateConstraint = ProductionConstraint< MassRateConstraint >; +REGISTER_CATALOG_ENTRY( WellConstraintBase, ProductionMassRateConstraint, string const &, Group * const ) + +template class ProductionConstraint< PhaseVolumeRateConstraint >; +using ProductionPhaseVolumeRateConstraint = ProductionConstraint< PhaseVolumeRateConstraint >; +REGISTER_CATALOG_ENTRY( WellConstraintBase, ProductionPhaseVolumeRateConstraint, string const &, Group * const ) + +template class ProductionConstraint< VolumeRateConstraint >; +using ProductionVolumeRateConstraint = ProductionConstraint< VolumeRateConstraint >; +REGISTER_CATALOG_ENTRY( WellConstraintBase, ProductionVolumeRateConstraint, string const &, Group * const ) + +} //namespace geos diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/WellProductionConstraint.hpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellProductionConstraint.hpp new file mode 100644 index 00000000000..ce1ca16de14 --- /dev/null +++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellProductionConstraint.hpp @@ -0,0 +1,106 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * SPDX-License-Identifier: LGPL-2.1-only + * + * Copyright (c) 2016-2024 Lawrence Livermore National Security LLC + * Copyright (c) 2018-2024 TotalEnergies + * Copyright (c) 2018-2024 The Board of Trustees of the Leland Stanford Junior University + * Copyright (c) 2023-2024 Chevron + * Copyright (c) 2019- GEOS/GEOSX Contributors + * All rights reserved + * + * See top level LICENSE, COPYRIGHT, CONTRIBUTORS, NOTICE, and ACKNOWLEDGEMENTS files for details. + * ------------------------------------------------------------------------------------------------------------ + */ + +/* + * @file WellProductionConstraints.hpp + */ + + +#ifndef GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_WELLPRODUCTIONCONSTRAINT_HPP +#define GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_WELLPRODUCTIONCONSTRAINT_HPP + +#include "common/format/EnumStrings.hpp" +#include "dataRepository/Group.hpp" +#include "functions/TableFunction.hpp" + +namespace geos +{ +using namespace dataRepository; +/** + * @class ProductionConstraint + * @brief This class describes constraint used to control a production well. + */ + +template< typename ConstraintType > +class ProductionConstraint : public ConstraintType +{ +public: + /** + * @name Constructor / Destructor + */ + ///@{ + + /** + * @brief Constructor for WellControls Objects. + * @param[in] name the name of this instantiation of WellControls in the repository + * @param[in] parent the parent group of this instantiation of WellControls + */ + explicit ProductionConstraint( string const & name, dataRepository::Group * const parent ); + + /** + * @brief Default destructor. + */ + ~ProductionConstraint() override; + + /** + * @brief Deleted default constructor. + */ + ProductionConstraint() = delete; + + /** + * @brief Deleted copy constructor. + */ + ProductionConstraint( ProductionConstraint const & ) = delete; + + /** + * @brief Deleted move constructor. + */ + ProductionConstraint( ProductionConstraint && ) = delete; + + /** + * @brief Deleted assignment operator. + * @return a reference to a constraint object + */ + ProductionConstraint & operator=( ProductionConstraint const & ) = delete; + + /** + * @brief Deleted move operator. + * @return a reference to a constraint object + */ + ProductionConstraint & operator=( ProductionConstraint && ) = delete; + + ///@} + + /** + * @brief name of the node manager in the object catalog + * @return string that contains the catalog name to generate a new Constraint object through the object catalog. + */ + static string catalogName() + { + return "Production"+ConstraintType::catalogName(); + } + virtual string getCatalogName() const override { return catalogName(); } +protected: + + virtual void postInputInitialization() override; + + static bool isViolated( const real64 & currentValue, const real64 & constraintValue ) + { return currentValue < constraintValue; } +}; + + +} //namespace geos + +#endif //GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_WELLPRODUCTIONCONSTRAINT_HPP diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/WellSolverBase.cpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellSolverBase.cpp index a76a66c2c72..e0850cf43c9 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/wells/WellSolverBase.cpp +++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellSolverBase.cpp @@ -36,7 +36,7 @@ using namespace fields; WellSolverBase::WellSolverBase( string const & name, Group * const parent ) - : PhysicsSolverBase( name, parent ), + : WellControls( name, parent ), m_numPhases( 0 ), m_numComponents( 0 ), m_numDofPerWellElement( 0 ), @@ -44,6 +44,7 @@ WellSolverBase::WellSolverBase( string const & name, m_isThermal( 0 ), m_ratesOutputDir( joinPath( OutputBase::getOutputDirectory(), name + "_rates" ) ), m_keepVariablesConstantDuringInitStep( false ) + { registerWrapper( viewKeyStruct::isThermalString(), &m_isThermal ). setApplyDefaultValue( 0 ). @@ -63,40 +64,41 @@ WellSolverBase::WellSolverBase( string const & name, setInputFlag( dataRepository::InputFlags::OPTIONAL ). setDescription( "Choose time step to honor rates/bhp tables time intervals" ); + addLogLevel< logInfo::WellControl >(); } Group * WellSolverBase::createChild( string const & childKey, string const & childName ) { + Group * baseChild = WellControls::createChild( childKey, childName ); + if( baseChild != nullptr ) + { + return baseChild; + } static std::set< string > const childTypes = { - keys::wellControls, + //keys::wellControls, PhysicsSolverBase::groupKeyStruct::linearSolverParametersString(), PhysicsSolverBase::groupKeyStruct::nonlinearSolverParametersString(), }; GEOS_ERROR_IF( childTypes.count( childKey ) == 0, CatalogInterface::unknownTypeError( childKey, getDataContext(), childTypes ), getDataContext() ); - if( childKey == keys::wellControls ) - { - return ®isterGroup< WellControls >( childName ); - } - else - { - PhysicsSolverBase::createChild( childKey, childName ); - return nullptr; - } + + PhysicsSolverBase::createChild( childKey, childName ); + return nullptr; + } void WellSolverBase::expandObjectCatalogs() { - createChild( keys::wellControls, keys::wellControls ); + //createChild( keys::wellControls, keys::wellControls ); } WellSolverBase::~WellSolverBase() = default; void WellSolverBase::postInputInitialization() { - PhysicsSolverBase::postInputInitialization(); + WellControls::postInputInitialization(); // 1. Set key dimensions of the problem m_numDofPerWellElement = m_isThermal ? m_numComponents + 2 : m_numComponents + 1; // 1 pressure connectionRate + temp if thermal @@ -207,95 +209,67 @@ void WellSolverBase::setupDofs( DomainPartition const & domain, DofManager::Connector::Node ); } -void WellSolverBase::setPerforationStatus( real64 const & time_n, DomainPartition & domain ) + + +void WellSolverBase::selectWellConstraint( real64 const & time_n, + real64 const & dt, + const integer coupledIterationNumber, + DomainPartition & domain ) { - FunctionManager & functionManager = FunctionManager::getInstance(); + GEOS_MARK_FUNCTION; + GEOS_UNUSED_VAR( dt ); + GEOS_UNUSED_VAR( coupledIterationNumber ); - // Set well element/perf status - forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &, - MeshLevel & mesh, - string_array const & regionNames ) + forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const & meshBodyName, + MeshLevel & meshLevel, + string_array const & regionNames ) { - - ElementRegionManager & elemManager = mesh.getElemManager(); - elemManager.forElementSubRegions< WellElementSubRegion >( regionNames, - [&]( localIndex const, - WellElementSubRegion & subRegion ) + GEOS_UNUSED_VAR( meshBodyName ); + ElementRegionManager & elementRegionManager = meshLevel.getElemManager(); + elementRegionManager.forElementRegions< WellElementRegion >( regionNames, + [&]( localIndex const, + WellElementRegion & region ) { + WellElementSubRegion & subRegion = region.getGroup( ElementRegionBase::viewKeyStruct::elementSubRegions() ) + .getGroup< WellElementSubRegion >( region.getSubRegionName() ); WellControls & wellControls = getWellControls( subRegion ); - - // Set perforation status - - PerforationData & perforationData = *subRegion.getPerforationData(); - string_array const & perfStatusTableName = perforationData.getPerfStatusTableName(); - arrayView1d< integer > perfStatus = perforationData.getLocalPerfStatus(); - // for now set to open - for( integer i=0; i( perfStatusTableName[i] ); - perfStatus[i]=PerforationData::PerforationStatus::OPEN; - if( tableFunction->evaluate( &time_n ) < LvArray::NumericLimits< real64 >::epsilon ) + if( !wellControls.getWellState() ) { - perfStatus[i]=PerforationData::PerforationStatus::CLOSED; - } - } - - array1d< localIndex > const perfWellElemIndex = perforationData.getField< fields::perforation::wellElementIndex >(); - // global index local elements (size == subregion.size) - arrayView1d< globalIndex const > globalWellElementIndex = subRegion.getGlobalWellElementIndex(); - - arrayView1d< integer const > const elemGhostRank = subRegion.ghostRank(); - array1d< integer > & currentStatus = subRegion.getWellElementStatus(); - // Local elements - array1d< integer > & localElemStatus = subRegion.getWellLocalElementStatus(); - - integer numLocalElements = subRegion.getNumLocalElements(); - array1d< integer > segStatus( numLocalElements ); + wellControls.setWellState( 1 ); - // Local perforations - for( integer j = 0; j < perforationData.size(); j++ ) - { - localIndex const iwelem = perfWellElemIndex[j]; - if( elemGhostRank[iwelem] < 0 ) - { - if( perfStatus[j] ) - { - segStatus[iwelem] +=1; - } + initializeWell( domain, meshLevel, subRegion, time_n ); } } - // Broadcast segment status so all cores have same well status - subRegion.setElementStatus( segStatus ); - integer numOpenElements = 0; - array1d< integer > const & updatedStatus = subRegion.getWellElementStatus(); - for( integer i=0; i0 ? wellControls.setWellStatus( time_n, WellControls::Status::OPEN ) : wellControls.setWellStatus( time_n, WellControls::Status::CLOSED ); - - // Set local well element status array - for( integer i=0; i const & localMatrix, arrayView1d< real64 > const & localRhs ) { - string const wellDofKey = dofManager.getKey( wellElementDofName()); + + + // selects constraints one of 2 ways + // wellEstimator flag set to 0 => orginal logic rates are computed during update state and constraints are selected every newton + // iteration + // wellEstimator flag > 0 => well esitmator solved for each constraint and then selects the constraint + // => estimator solve only performed first "wellEstimator" iterations + NonlinearSolverParameters const & nonlinearParams = getNonlinearSolverParameters(); + selectWellConstraint( time, dt, nonlinearParams.m_numNewtonIterations, domain ); + + // assemble the accumulation term in the mass balance equations assembleAccumulationTerms( time, dt, domain, dofManager, localMatrix, localRhs ); // then assemble the pressure relations between well elements - assemblePressureRelations( time, dt, domain, dofManager, localMatrix, localRhs ); + //assemblePressureRelations( time, dt, domain, dofManager, localMatrix, localRhs ); + + forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &, + MeshLevel & mesh, + string_array const & regionNames ) + { + ElementRegionManager & elementRegionManager = mesh.getElemManager(); + elementRegionManager.forElementRegions< WellElementRegion >( regionNames, + [&]( localIndex const, + WellElementRegion & region ) + { + WellElementSubRegion & subRegion = region.getGroup( ElementRegionBase::viewKeyStruct::elementSubRegions() ) + .getGroup< WellElementSubRegion >( region.getSubRegionName() ); + assembleWellConstraintTerms( time, dt, subRegion, dofManager, localMatrix.toViewConstSizes(), localRhs ); + } ); + } ); + // then compute the perforation rates (later assembled by the coupled solver) computePerforationRates( time, dt, domain ); @@ -388,8 +388,15 @@ void WellSolverBase::precomputeData( DomainPartition & domain ) wellElemGravCoef[iwelem] = LvArray::tensorOps::AiBi< 3 >( wellElemLocation[iwelem], gravVector ); } ); + wellControls.forSubGroups< BHPConstraint >( [&]( auto & constraint ) + { + // set the reference well element where the BHP control is applied + real64 const refElev1 = constraint.getReferenceElevation(); + constraint.setReferenceGravityCoef( refElev1 * gravVector[2] ); + } ); + // set the reference well element where the BHP control is applied - wellControls.setReferenceGravityCoef( refElev * gravVector[2] ); + wellControls.setReferenceGravityCoef( refElev * gravVector[2] ); // tjb remove } ); } ); } diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/WellSolverBase.hpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellSolverBase.hpp index 04fe58112b4..0f999eacb3e 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/wells/WellSolverBase.hpp +++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellSolverBase.hpp @@ -20,13 +20,13 @@ #ifndef GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_WELLSOLVERBASE_HPP_ #define GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_WELLSOLVERBASE_HPP_ -#include "physicsSolvers/PhysicsSolverBase.hpp" - +#include "physicsSolvers/fluidFlow/wells/WellControls.hpp" +#include "dataRepository/Group.hpp" namespace geos { class DomainPartition; -class WellControls; + class WellElementSubRegion; /** @@ -35,7 +35,7 @@ class WellElementSubRegion; * Base class for well solvers. * Provides some common features */ -class WellSolverBase : public PhysicsSolverBase +class WellSolverBase : public WellControls { public: @@ -60,7 +60,7 @@ class WellSolverBase : public PhysicsSolverBase WellSolverBase( WellSolverBase const & ) = delete; /// default move constructor - WellSolverBase( WellSolverBase && ) = default; + WellSolverBase( WellSolverBase && ) = delete; /// deleted assignment operator WellSolverBase & operator=( WellSolverBase const & ) = delete; @@ -128,6 +128,12 @@ class WellSolverBase : public PhysicsSolverBase */ virtual localIndex numFluidPhases() const = 0; + /** + * @brief getter for the well associated to this subRegion + * @param subRegion the well subRegion whose controls are requested + * @return a reference to the well + */ + WellSolverBase & getWell( WellElementSubRegion const & subRegion ); /** * @brief getter for the well controls associated to this well subRegion * @param subRegion the well subRegion whose controls are requested @@ -149,27 +155,63 @@ class WellSolverBase : public PhysicsSolverBase * @param domain the domain */ void setPerforationStatus( real64 const & time_n, DomainPartition & domain ); - + void setPerforationStatus( real64 const & time_n, WellElementSubRegion & subRegion ); /** * @defgroup Solver Interface Functions * * These functions provide the primary interface that is required for derived classes + * The "Well" versions apply to individual well subRegions, whereas the others apply to all wells + */ /**@{*/ virtual void registerDataOnMesh( Group & meshBodies ) override; + virtual real64 + calculateWellResidualNorm( real64 const & GEOS_UNUSED_PARAM( time_n ), + real64 const & GEOS_UNUSED_PARAM( dt ), + WellElementSubRegion const & GEOS_UNUSED_PARAM( subRegion ), + DofManager const & GEOS_UNUSED_PARAM( dofManager ), + arrayView1d< real64 const > const & GEOS_UNUSED_PARAM( localRhs ) ) = 0; + + virtual real64 + scalingForWellSystemSolution( ElementSubRegionBase & GEOS_UNUSED_PARAM( subRegion ), + DofManager const & GEOS_UNUSED_PARAM( dofManager ), + arrayView1d< real64 const > const & GEOS_UNUSED_PARAM( localSolution ) ) = 0; + + virtual bool + checkWellSystemSolution( ElementSubRegionBase & GEOS_UNUSED_PARAM( subRegion ), + DofManager const & GEOS_UNUSED_PARAM( dofManager ), + arrayView1d< real64 const > const & GEOS_UNUSED_PARAM( localSolution ), + real64 const GEOS_UNUSED_PARAM( scalingFactor ) ) = 0; + virtual void + applyWellSystemSolution( DofManager const & GEOS_UNUSED_PARAM( dofManager ), + arrayView1d< real64 const > const & GEOS_UNUSED_PARAM( localSolution ), + real64 const GEOS_UNUSED_PARAM( scalingFactor ), + real64 const GEOS_UNUSED_PARAM( dt ), + DomainPartition & GEOS_UNUSED_PARAM( domain ), + MeshLevel & GEOS_UNUSED_PARAM( mesh ), + WellElementSubRegion & GEOS_UNUSED_PARAM( subRegion ) ) = 0; + + /** + * @brief function to set the next time step size + * @param[in] currentTime the current time + * @param[in] currentDt the current time step size + * @param[in] domain the domain object + * @return the prescribed time step size + */ + virtual real64 setNextDt( real64 const & currentTime, + real64 const & currentDt, + DomainPartition & domain ) override; virtual void setupDofs( DomainPartition const & domain, DofManager & dofManager ) const override; - virtual void implicitStepSetup( real64 const & time_n, - real64 const & dt, - DomainPartition & domain ) override; virtual void implicitStepComplete( real64 const & GEOS_UNUSED_PARAM( time_n ), real64 const & GEOS_UNUSED_PARAM( dt ), DomainPartition & GEOS_UNUSED_PARAM( domain ) ) override {} + virtual void applyBoundaryConditions( real64 const GEOS_UNUSED_PARAM( time_n ), real64 const GEOS_UNUSED_PARAM( dt ), DomainPartition & GEOS_UNUSED_PARAM( domain ), @@ -177,17 +219,23 @@ class WellSolverBase : public PhysicsSolverBase CRSMatrixView< real64, globalIndex const > const & GEOS_UNUSED_PARAM( localMatrix ), arrayView1d< real64 > const & GEOS_UNUSED_PARAM( localRhs ) ) override {} + /** + * @brief Selects the active well constraint based on current conditions + * @param[in] currentTime the current time + * @param[in] currentDt the current time step size + * @param[in] coupledIterationNumber the current coupled iteration number + * @param[in] domain the domain object + * @return the prescribed time step size + */ + void selectWellConstraint( real64 const & time_n, + real64 const & dt, + integer const coupledIterationNumber, + DomainPartition & domain ); /**@}*/ /** - * @brief function to assemble the linear system matrix and rhs - * @param time the time at the beginning of the step - * @param dt the desired timestep - * @param domain the domain partition - * @param dofManager degree-of-freedom manager associated with the linear system - * @param matrix the system matrix - * @param rhs the system right-hand side vector + * @copydoc PhysicsSolverBase::assembleSystem() */ virtual void assembleSystem( real64 const time, real64 const dt, @@ -197,14 +245,15 @@ class WellSolverBase : public PhysicsSolverBase arrayView1d< real64 > const & localRhs ) override; /** - * @brief assembles the flux terms for all connections between well elements + * @brief assembles the flux terms for individual well for all connections between well elements * @param time_n previous time value * @param dt time step - * @param domain the physical domain object + * @param subRegion the well subregion containing all the primary and dependent fields * @param dofManager degree-of-freedom manager associated with the linear system * @param matrix the system matrix * @param rhs the system right-hand side vector */ + virtual void assembleFluxTerms( real64 const & time_n, real64 const & dt, DomainPartition & domain, @@ -226,35 +275,37 @@ class WellSolverBase : public PhysicsSolverBase CRSMatrixView< real64, globalIndex const > const & localMatrix, arrayView1d< real64 > const & localRhs ) = 0; + + /** - * @brief assembles the pressure relations at all connections between well elements except at the well head - * @param time_n time at the beginning of the time step - * @param dt the time step size - * @param domain the physical domain object - * @param dofManager degree-of-freedom manager associated with the linear system - * @param matrix the system matrix - * @param rhs the system right-hand side vector + * @brief Recompute all dependent quantities from primary variables (including constitutive + * models) + * @param elemManager the element region manager + * @param subRegion the well subRegion containing the well elements and their associated fields */ - virtual void assemblePressureRelations( real64 const & time_n, - real64 const & dt, - DomainPartition const & domain, - DofManager const & dofManager, - CRSMatrixView< real64, globalIndex const > const & localMatrix, - arrayView1d< real64 > const & localRhs ) = 0; - + virtual real64 updateWellState( ElementRegionManager const & elemManager, WellElementSubRegion & subRegion ) = 0; /** - * @brief Recompute all dependent quantities from primary variables (including constitutive models) + * @brief Recompute all dependent quantities from primary variables (including constitutive + * models) * @param domain the domain containing the mesh and fields */ virtual void updateState( DomainPartition & domain ) override; /** - * @brief Recompute all dependent quantities from primary variables (including constitutive models) - * @param elemManager the elemManager containing the well - * @param subRegion the well subRegion containing the well elements and their associated fields + * @brief Initialize all the primary and secondary variables in all the wells + * @param domain the domain containing the well manager to access individual wells + */ + virtual void initializeWells( DomainPartition & domain, real64 const & time_n ) = 0; + + /** + * @brief Recompute all dependent quantities from primary variables (including constitutive + * models) + * @param elemManager the element region manager + * fields */ virtual real64 updateSubRegionState( ElementRegionManager const & elemManager, WellElementSubRegion & subRegion ) = 0; + /** * @brief Recompute the perforation rates for all the wells * @param domain the domain containing the mesh and fields @@ -263,21 +314,14 @@ class WellSolverBase : public PhysicsSolverBase real64 const & dt, DomainPartition & domain ) = 0; - /** - * @brief function to set the next time step size - * @param[in] currentTime the current time - * @param[in] currentDt the current time step size - * @param[in] domain the domain object - * @return the prescribed time step size - */ - virtual real64 setNextDt( real64 const & currentTime, - real64 const & currentDt, - DomainPartition & domain ) override; /** - * @brief Utility function to keep the well variables during a time step (used in poromechanics simulations) - * @param[in] keepVariablesConstantDuringInitStep flag to tell the solver to freeze its primary variables during a time step - * @detail This function is meant to be called by a specific task before/after the initialization step + * @brief Utility function to keep the well variables during a time step (used in + * poromechanics simulations) + * @param[in] keepVariablesConstantDuringInitStep flag to tell the solver to freeze its + * primary variables during a time step + * @detail This function is meant to be called by a specific task before/after the + * initialization step */ void setKeepVariablesConstantDuringInitStep( bool const keepVariablesConstantDuringInitStep ) { m_keepVariablesConstantDuringInitStep = keepVariablesConstantDuringInitStep; } @@ -287,6 +331,8 @@ class WellSolverBase : public PhysicsSolverBase static constexpr char const * isThermalString() { return "isThermal"; } static constexpr char const * writeCSVFlagString() { return "writeCSV"; } static constexpr char const * timeStepFromTablesFlagString() { return "timeStepFromTables"; } + /// @return string for the targetRegions wrapper + static constexpr char const * targetRegionsString() { return "targetRegions"; } static constexpr char const * fluidNamesString() { return "fluidNames"; } }; @@ -307,11 +353,6 @@ class WellSolverBase : public PhysicsSolverBase virtual void initializePostSubGroups() override; - /** - * @brief Initialize all the primary and secondary variables in all the wells - * @param domain the domain containing the well manager to access individual wells - */ - virtual void initializeWells( DomainPartition & domain, real64 const & time_n ) = 0; /** * @brief Make sure that the well constraints are compatible @@ -327,6 +368,8 @@ class WellSolverBase : public PhysicsSolverBase real64 const & dt, DomainPartition & domain ) = 0; + + /// name of the flow solver string m_flowSolverName; @@ -357,6 +400,10 @@ class WellSolverBase : public PhysicsSolverBase /// name of the fluid constitutive model used as a reference for component/phase description string m_referenceFluidModelName; + + /// flag to use the estimator + integer m_estimateSolution; + }; } diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/WellSolverBaseFields.hpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellSolverBaseFields.hpp index b9dfebc799b..1c4b639f4f7 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/wells/WellSolverBaseFields.hpp +++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellSolverBaseFields.hpp @@ -31,65 +31,7 @@ namespace fields { namespace well -{ - -DECLARE_FIELD( pressure, - "pressure", - array1d< real64 >, - 0, - LEVEL_0, - WRITE_AND_READ, - "Pressure" ); - -DECLARE_FIELD( pressure_n, - "pressure_n", - array1d< real64 >, - 0, - NOPLOT, - WRITE_AND_READ, - "Pressure at the previous converged time step" ); - -DECLARE_FIELD( temperature, - "temperature", - array1d< real64 >, - 0, - LEVEL_0, - WRITE_AND_READ, - "Temperature" ); - -DECLARE_FIELD( temperature_n, - "temperature_n", - array1d< real64 >, - 0, - NOPLOT, - WRITE_AND_READ, - "Temperature at the previous converged time step" ); - -DECLARE_FIELD( gravityCoefficient, - "gravityCoefficient", - array1d< real64 >, - 0, - NOPLOT, - WRITE_AND_READ, - "Gravity coefficient (dot product of gravity acceleration by gravity vector)" ); - -DECLARE_FIELD( pressureScalingFactor, - "pressureScalingFactor", - array1d< real64 >, - 1, - NOPLOT, - NO_WRITE, - "Scaling factors for pressure" ); - -DECLARE_FIELD( temperatureScalingFactor, - "temperatureScalingFactor", - array1d< real64 >, - 1, - NOPLOT, - NO_WRITE, - "Scaling factors for temperature" ); - -} +{} } diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/WellVolumeRateConstraint.cpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellVolumeRateConstraint.cpp new file mode 100644 index 00000000000..e675af7459e --- /dev/null +++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellVolumeRateConstraint.cpp @@ -0,0 +1,73 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * SPDX-License-Identifier: LGPL-2.1-only + * + * Copyright (c) 2016-2024 Lawrence Livermore National Security LLC + * Copyright (c) 2018-2024 TotalEnergies + * Copyright (c) 2018-2024 The Board of Trustees of the Leland Stanford Junior University + * Copyright (c) 2023-2024 Chevron + * Copyright (c) 2019- GEOS/GEOSX Contributors + * All rights reserved + * + * See top level LICENSE, COPYRIGHT, CONTRIBUTORS, NOTICE, and ACKNOWLEDGEMENTS files for details. + * ------------------------------------------------------------------------------------------------------------ + */ + +/* + * @file WellVolumeRateConstraint.cpp + */ + +#include "LogLevelsInfo.hpp" +#include "WellVolumeRateConstraint.hpp" +#include "WellConstants.hpp" +#include "dataRepository/InputFlags.hpp" +#include "functions/FunctionManager.hpp" + +namespace geos +{ + +using namespace dataRepository; + +VolumeRateConstraint::VolumeRateConstraint( string const & name, Group * const parent ) + : WellConstraintBase( name, parent ) +{ + this->setInputFlags( InputFlags::OPTIONAL_NONUNIQUE ); + + this->registerWrapper( viewKeyStruct::volumeRateString(), &this->m_constraintValue ). + setDefaultValue( 0.0 ). + setInputFlag( InputFlags::OPTIONAL ). + setRestartFlags( RestartFlags::WRITE_AND_READ ). + setDescription( "Volumetric rate (if useSurfaceConditions: [surface m^3/s]; else [reservoir m^3/s])" ); + +} + +VolumeRateConstraint::~VolumeRateConstraint() +{} + + +void VolumeRateConstraint::postInputInitialization() +{ + // Validate table options + WellConstraintBase::postInputInitialization(); + + // check constraint value + GEOS_THROW_IF( m_constraintValue < 0, + getWrapperDataContext( viewKeyStruct::volumeRateString() ) << ": Target value is negative", + InputError ); + + GEOS_THROW_IF ((m_constraintValue <= 0.0 && m_constraintScheduleTableName.empty()), + getName() << " " << getDataContext() << ": You need to specify a volume rate constraint. \n" << + "The rate constraint can be specified using " << + "either " << viewKeyStruct::volumeRateString() << + " or " << WellConstraintBase::viewKeyStruct::constraintScheduleTableNameString(), + InputError ); +} + +bool VolumeRateConstraint::checkViolation( WellConstraintBase const & currentConstraint, real64 const & currentTime ) const +{ + real64 const currentValue = currentConstraint.totalVolumeRate(); + real64 const constraintValue = this->getConstraintValue( currentTime ); + return ( LvArray::math::abs( currentValue ) > LvArray::math::abs( constraintValue ) ); +} + +} //namespace geos diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/WellVolumeRateConstraint.hpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellVolumeRateConstraint.hpp new file mode 100644 index 00000000000..1ed14597ca3 --- /dev/null +++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellVolumeRateConstraint.hpp @@ -0,0 +1,124 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * SPDX-License-Identifier: LGPL-2.1-only + * + * Copyright (c) 2016-2024 Lawrence Livermore National Security LLC + * Copyright (c) 2018-2024 TotalEnergies + * Copyright (c) 2018-2024 The Board of Trustees of the Leland Stanford Junior University + * Copyright (c) 2023-2024 Chevron + * Copyright (c) 2019- GEOS/GEOSX Contributors + * All rights reserved + * + * See top level LICENSE, COPYRIGHT, CONTRIBUTORS, NOTICE, and ACKNOWLEDGEMENTS files for details. + * ------------------------------------------------------------------------------------------------------------ + */ + +/* + * @file WellVolumeRateConstraints.hpp + */ + + +#ifndef GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_WELLTOTALVOLRATECONSTRAINTS_HPP +#define GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_WELLTOTALVOLRATECONSTRAINTS_HPP + +#include "common/format/EnumStrings.hpp" +#include "dataRepository/Group.hpp" +#include "functions/TableFunction.hpp" +#include "WellConstraintsBase.hpp" +namespace geos +{ + + + +/** + * @class VolumeRateConstraint + * @brief This class describes a volume rate constraint used to control a well. + */ + +class VolumeRateConstraint : public WellConstraintBase +{ +public: + + /** + * @name Constructor / Destructor + */ + ///@{ + + /** + * @brief Constructor for WellControls Objects. + * @param[in] name the name of this instantiation of WellControls in the repository + * @param[in] parent the parent group of this instantiation of WellControls + */ + explicit VolumeRateConstraint( string const & name, dataRepository::Group * const parent ); + + + /** + * @brief Default destructor. + */ + ~VolumeRateConstraint() override; + + /** + * @brief Deleted default constructor. + */ + VolumeRateConstraint() = delete; + + /** + * @brief Deleted copy constructor. + */ + VolumeRateConstraint( VolumeRateConstraint const & ) = delete; + + /** + * @brief Deleted move constructor. + */ + VolumeRateConstraint( VolumeRateConstraint && ) = delete; + + /** + * @brief Deleted assignment operator. + * @return a reference to a constraint object + */ + VolumeRateConstraint & operator=( VolumeRateConstraint const & ) = delete; + + /** + * @brief Deleted move operator. + * @return a reference to a constraint object + */ + VolumeRateConstraint & operator=( VolumeRateConstraint && ) = delete; + + /** + * @brief name of the node manager in the object catalog + * @return string that contains the catalog name to generate a new Constraint object through the object catalog. + */ + static string catalogName() + { + return "VolumeRateConstraint"; + } + ///@} + /** + * @brief Struct to serve as a container for variable strings and keys. + * @struct viewKeyStruct + */ + struct viewKeyStruct + { + /// String key for the volume rate + static constexpr char const * volumeRateString() { return "volumeRate"; } + }; + /** + * @name Getters / Setters + */ + ///@{ + + // Temp interface - tjb + virtual ConstraintTypeId getControl() const override { return ConstraintTypeId::TOTALVOLRATE; }; + ///@} + + virtual bool checkViolation( WellConstraintBase const & currentConstraint, real64 const & currentTime ) const override; +protected: + + virtual void postInputInitialization() override; + +}; + + +} //namespace geos + +#endif //GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_WELLCONSTRAINT_HPP diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/CompositionalMultiphaseWellConstraintKernels.hpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/CompositionalMultiphaseWellConstraintKernels.hpp new file mode 100644 index 00000000000..229813ee52a --- /dev/null +++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/CompositionalMultiphaseWellConstraintKernels.hpp @@ -0,0 +1,555 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * SPDX-License-Identifier: LGPL-2.1-only + * + * Copyright (c) 2016-2024 Lawrence Livermore National Security LLC + * Copyright (c) 2018-2024 TotalEnergies + * Copyright (c) 2018-2024 The Board of Trustees of the Leland Stanford Junior University + * Copyright (c) 2023-2024 Chevron + * Copyright (c) 2019- GEOS/GEOSX Contributors + * All rights reserved + * + * See top level LICENSE, COPYRIGHT, CONTRIBUTORS, NOTICE, and ACKNOWLEDGEMENTS files for details. + * ------------------------------------------------------------------------------------------------------------ + */ + +/** + * @file CompositionalMultiphaseWellConstraintKernels.hpp + */ + +#ifndef GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_WELLCONSTRAINTKERNELS_HPP +#define GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_WELLCONSTRAINTKERNELS_HPP + +#include "codingUtilities/Utilities.hpp" +#include "constitutive/fluid/multifluid/MultiFluidBase.hpp" +#include "constitutive/fluid/multifluid/MultiFluidFields.hpp" + + +#include "physicsSolvers/fluidFlow/wells/WellControls.hpp" +#include "physicsSolvers/fluidFlow/wells/WellBHPConstraints.hpp" +#include "physicsSolvers/fluidFlow/wells/WellVolumeRateConstraint.hpp" +#include "physicsSolvers/fluidFlow/wells/WellPhaseVolumeRateConstraint.hpp" +#include "physicsSolvers/fluidFlow/wells/WellMassRateConstraint.hpp" +#include "physicsSolvers/fluidFlow/wells/WellLiquidRateConstraint.hpp" +namespace geos +{ + +namespace wellConstraintKernels +{ + +/******************************** ControlEquationHelper ********************************/ +//template< integer NC, integer IS_THERMAL, typname S, typename T > +//struct ConstraintHelper< NC, IS_THERMAL > {}; + +template< integer NC, integer IS_THERMAL, typename CONSTRAINT = BHPConstraint > +struct ConstraintHelper +{ + static void assembleConstraintEquation( real64 const & time_n, + WellControls & wellControls, + BHPConstraint & constraint, + WellElementSubRegion const & subRegion, + string const & wellDofKey, + localIndex const & rankOffset, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) + { + // subRegion data + localIndex const iwelemRef = subRegion.getTopWellElementIndex(); + arrayView1d< globalIndex const > const & wellElemDofNumber = subRegion.getReference< array1d< globalIndex > >( wellDofKey ); + arrayView1d< real64 const > const & pres = subRegion.getField< fields::well::pressure >(); + arrayView1d< real64 const > const & totalMassDens = subRegion.getField< fields::well::totalMassDensity >(); + arrayView2d< real64 const > const & dTotalMassDens = subRegion.getField< fields::well::dTotalMassDensity >(); + arrayView1d< real64 const > const wellElemGravCoef = subRegion.getField< fields::well::gravityCoefficient >(); + + // setup row/column indices for constraint equation + using COFFSET_WJ = compositionalMultiphaseWellKernels::ColOffset_WellJac< NC, IS_THERMAL >; + using WJ_ROFFSET = compositionalMultiphaseWellKernels::RowOffset_WellJac< NC, IS_THERMAL >; + using Deriv = constitutive::multifluid::DerivativeOffset; + + localIndex const eqnRowIndex = wellElemDofNumber[iwelemRef] + WJ_ROFFSET::CONTROL - rankOffset; + globalIndex dofColIndices[COFFSET_WJ::nDer]{}; + for( integer ic = 0; ic < COFFSET_WJ::nDer; ++ic ) + { + dofColIndices[ ic ] = wellElemDofNumber[iwelemRef] + ic; + } + + // constraint data + real64 const & targetBHP = constraint.getConstraintValue( time_n ); + real64 const & refGravCoef = constraint.getReferenceGravityCoef(); + + // current constraint value + real64 const & currentBHP = + wellControls.getReference< real64 >( CompositionalMultiphaseWell::viewKeyStruct::currentBHPString() ); + + // residual + real64 controlEqn = currentBHP - targetBHP; + + // setup Jacobian terms + real64 dControlEqn[NC+2+IS_THERMAL]{}; + + // bring everything back to host, capture the scalars by reference + forAll< serialPolicy >( 1, [pres, + totalMassDens, + dTotalMassDens, + wellElemGravCoef, + &dControlEqn, + &iwelemRef, + &refGravCoef] ( localIndex const ) + { + real64 const diffGravCoef = refGravCoef - wellElemGravCoef[iwelemRef]; + dControlEqn[COFFSET_WJ::dP] = 1 + dTotalMassDens[iwelemRef][Deriv::dP] * diffGravCoef; + for( integer ic = 0; ic < NC; ++ic ) + { + dControlEqn[COFFSET_WJ::dC+ic] = dTotalMassDens[iwelemRef][Deriv::dC+ic] * diffGravCoef; + } + if constexpr ( IS_THERMAL ) + { + dControlEqn[COFFSET_WJ::dT] = dTotalMassDens[iwelemRef][Deriv::dT] * diffGravCoef; + } + } ); + + // add solver matrices + localRhs[eqnRowIndex] += controlEqn; + localMatrix.addToRowBinarySearchUnsorted< serialAtomic >( eqnRowIndex, + dofColIndices, + dControlEqn, + COFFSET_WJ::nDer ); + } + + + template< template< typename U > class T, typename U=PhaseVolumeRateConstraint > + static void assembleConstraintEquation( real64 const & time_n, + WellControls & wellControls, + T< PhaseVolumeRateConstraint > & constraint, + WellElementSubRegion const & subRegion, + string const & wellDofKey, + localIndex const & rankOffset, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) + { + // subRegion data + + localIndex const iwelemRef = subRegion.getTopWellElementIndex(); + arrayView1d< globalIndex const > const & wellElemDofNumber = subRegion.getReference< array1d< globalIndex > >( wellDofKey ); + arrayView1d< real64 const > const & connRate = subRegion.getField< fields::well::connectionRate >(); + arrayView2d< real64 const, compflow::USD_COMP > const & compFrac = subRegion.getField< fields::well::globalCompFraction >(); + arrayView3d< real64 const, compflow::USD_COMP_DC > const & dCompFrac_dCompDens = subRegion.getField< fields::well::dGlobalCompFraction_dGlobalCompDensity >(); + + // setup row/column indices for constraint equation + using COFFSET_WJ = compositionalMultiphaseWellKernels::ColOffset_WellJac< NC, IS_THERMAL >; + using WJ_ROFFSET = compositionalMultiphaseWellKernels::RowOffset_WellJac< NC, IS_THERMAL >; + using Deriv = constitutive::multifluid::DerivativeOffset; + + localIndex const eqnRowIndex = wellElemDofNumber[iwelemRef] + WJ_ROFFSET::CONTROL - rankOffset; + globalIndex dofColIndices[COFFSET_WJ::nDer]{}; + for( integer ic = 0; ic < COFFSET_WJ::nDer; ++ic ) + { + dofColIndices[ ic ] = wellElemDofNumber[iwelemRef] + ic; + } + + // fluid data + constitutive::MultiFluidBase & fluidSeparator = wellControls.getMultiFluidSeparator(); + + arrayView3d< real64 const, constitutive::multifluid::USD_PHASE > const & phaseFrac = fluidSeparator.phaseFraction(); + arrayView4d< real64 const, constitutive::multifluid::USD_PHASE_DC > const & dPhaseFrac = fluidSeparator.dPhaseFraction(); + arrayView3d< real64 const, constitutive::multifluid::USD_PHASE > const & phaseDens = fluidSeparator.phaseDensity(); + arrayView4d< real64 const, constitutive::multifluid::USD_PHASE_DC > const & dPhaseDens = fluidSeparator.dPhaseDensity(); + + // constraint data + integer ip = getPhaseIndexFromFluidModel( fluidSeparator, constraint.getPhaseName()); + real64 const & targetPhaseRate = constraint.getConstraintValue( time_n ); + + // current constraint value + arrayView1d< real64 > const & currentPhaseVolRate = + wellControls.getReference< array1d< real64 > >( CompositionalMultiphaseWell::viewKeyStruct::currentPhaseVolRateString() ); + integer const useSurfaceConditions = wellControls.useSurfaceConditions(); + + // residual + real64 controlEqn = currentPhaseVolRate[ip] - targetPhaseRate; + + // setup Jacobian terms + real64 dControlEqn[NC+2+IS_THERMAL]{}; + + // bring everything back to host, capture the scalars by reference + forAll< serialPolicy >( 1, [&ip, + connRate, + phaseDens, + dPhaseDens, + phaseFrac, + dPhaseFrac, + compFrac, + dCompFrac_dCompDens, + &dControlEqn, + &useSurfaceConditions, + &iwelemRef] ( localIndex const ) + { + // skip the rest of this function if phase ip is absent + bool const phaseExists = (phaseFrac[iwelemRef][0][ip] > 0); + if( phaseExists ) + { + stackArray1d< real64, NC > work( NC ); + real64 const currentTotalRate = connRate[iwelemRef]; + + real64 const phaseDensInv = 1.0 / phaseDens[iwelemRef][0][ip]; + real64 const phaseFracTimesPhaseDensInv = phaseFrac[iwelemRef][0][ip] * phaseDensInv; + real64 const dPhaseFracTimesPhaseDensInv_dPres = dPhaseFrac[iwelemRef][0][ip][Deriv::dP] * phaseDensInv + - dPhaseDens[iwelemRef][0][ip][Deriv::dP] * phaseFracTimesPhaseDensInv * phaseDensInv; + + // divide the total mass/molar rate by the (phase density * phase fraction) to get the phase volumetric rate + dControlEqn[COFFSET_WJ::dP] = ( useSurfaceConditions == 0 ) * currentTotalRate * dPhaseFracTimesPhaseDensInv_dPres; + dControlEqn[COFFSET_WJ::dQ] = phaseFracTimesPhaseDensInv; + if constexpr (IS_THERMAL ) + { + real64 const dPhaseFracTimesPhaseDensInv_dTemp = dPhaseFrac[iwelemRef][0][ip][Deriv::dT] * phaseDensInv + - dPhaseDens[iwelemRef][0][ip][Deriv::dT] * phaseFracTimesPhaseDensInv * phaseDensInv; + dControlEqn[COFFSET_WJ::dT] = ( useSurfaceConditions == 0 ) * currentTotalRate * dPhaseFracTimesPhaseDensInv_dTemp; + } + + for( integer ic = 0; ic < NC; ++ic ) + { + dControlEqn[COFFSET_WJ::dC+ic] = -phaseFracTimesPhaseDensInv * dPhaseDens[iwelemRef][0][ip][Deriv::dC+ic] * phaseDensInv; + dControlEqn[COFFSET_WJ::dC+ic] += dPhaseFrac[iwelemRef][0][ip][Deriv::dC+ic] * phaseDensInv; + dControlEqn[COFFSET_WJ::dC+ic] *= currentTotalRate; + } + applyChainRuleInPlace( NC, dCompFrac_dCompDens[iwelemRef], &dControlEqn[COFFSET_WJ::dC], work.data() ); + + } + } ); + + // add solver matrices + localRhs[eqnRowIndex] += controlEqn; + localMatrix.addToRowBinarySearchUnsorted< serialAtomic >( eqnRowIndex, + dofColIndices, + dControlEqn, + COFFSET_WJ::nDer ); + } + + template< template< typename U > class T, typename U=LiquidRateConstraint > + static void assembleConstraintEquation( real64 const & time_n, + WellControls & wellControls, + T< LiquidRateConstraint > & constraint, + WellElementSubRegion const & subRegion, + string const & wellDofKey, + localIndex const & rankOffset, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) + { + // subRegion data + + localIndex const iwelemRef = subRegion.getTopWellElementIndex(); + arrayView1d< globalIndex const > const & wellElemDofNumber = subRegion.getReference< array1d< globalIndex > >( wellDofKey ); + arrayView1d< real64 const > const & connRate = subRegion.getField< fields::well::connectionRate >(); + arrayView2d< real64 const, compflow::USD_COMP > const & compFrac = subRegion.getField< fields::well::globalCompFraction >(); + arrayView3d< real64 const, compflow::USD_COMP_DC > const & dCompFrac_dCompDens = subRegion.getField< fields::well::dGlobalCompFraction_dGlobalCompDensity >(); + + // setup row/column indices for constraint equation + using COFFSET_WJ = compositionalMultiphaseWellKernels::ColOffset_WellJac< NC, IS_THERMAL >; + using WJ_ROFFSET = compositionalMultiphaseWellKernels::RowOffset_WellJac< NC, IS_THERMAL >; + using Deriv = constitutive::multifluid::DerivativeOffset; + + localIndex const eqnRowIndex = wellElemDofNumber[iwelemRef] + WJ_ROFFSET::CONTROL - rankOffset; + globalIndex dofColIndices[COFFSET_WJ::nDer]{}; + for( integer ic = 0; ic < COFFSET_WJ::nDer; ++ic ) + { + dofColIndices[ ic ] = wellElemDofNumber[iwelemRef] + ic; + } + + // fluid data + constitutive::MultiFluidBase & fluidSeparator = wellControls.getMultiFluidSeparator(); + + arrayView3d< real64 const, constitutive::multifluid::USD_PHASE > const & phaseFrac = fluidSeparator.phaseFraction(); + arrayView4d< real64 const, constitutive::multifluid::USD_PHASE_DC > const & dPhaseFrac = fluidSeparator.dPhaseFraction(); + arrayView3d< real64 const, constitutive::multifluid::USD_PHASE > const & phaseDens = fluidSeparator.phaseDensity(); + arrayView4d< real64 const, constitutive::multifluid::USD_PHASE_DC > const & dPhaseDens = fluidSeparator.dPhaseDensity(); + + // constraint data + real64 const & targetPhaseRate = constraint.getConstraintValue( time_n ); + const array1d< int > & phaseIndices = constraint.getPhaseIndices(); + + + // current constraint value + arrayView1d< real64 > const & currentPhaseVolRate = + wellControls.getReference< array1d< real64 > >( CompositionalMultiphaseWell::viewKeyStruct::currentPhaseVolRateString() ); + integer const useSurfaceConditions = wellControls.useSurfaceConditions(); + + // residual + real64 controlEqn = -targetPhaseRate; + for( integer ip= 0; ip < phaseIndices.size(); ++ip ) + { + controlEqn += currentPhaseVolRate[phaseIndices[ip]]; + } + + // setup Jacobian terms + real64 dControlEqn[NC+2+IS_THERMAL]{}; + + // bring everything back to host, capture the scalars by reference + forAll< serialPolicy >( 1, [phaseIndices, + connRate, + phaseDens, + dPhaseDens, + phaseFrac, + dPhaseFrac, + compFrac, + dCompFrac_dCompDens, + &dControlEqn, + &useSurfaceConditions, + &iwelemRef] ( localIndex const ) + { + + stackArray1d< real64, NC > work( NC ); + for( integer i= 0; i < phaseIndices.size(); ++i ) + { + integer ip = phaseIndices[i]; + bool const phaseExists = (phaseFrac[iwelemRef][0][ip] > 0); + // skip the rest of this function if phase ip is absent + if( phaseExists ) + { + + real64 const currentTotalRate = connRate[iwelemRef]; + + real64 const phaseDensInv = 1.0 / phaseDens[iwelemRef][0][ip]; + real64 const phaseFracTimesPhaseDensInv = phaseFrac[iwelemRef][0][ip] * phaseDensInv; + real64 const dPhaseFracTimesPhaseDensInv_dPres = dPhaseFrac[iwelemRef][0][ip][Deriv::dP] * phaseDensInv + - dPhaseDens[iwelemRef][0][ip][Deriv::dP] * phaseFracTimesPhaseDensInv * phaseDensInv; + + // divide the total mass/molar rate by the (phase density * phase fraction) to get the phase volumetric rate + dControlEqn[COFFSET_WJ::dP] += ( useSurfaceConditions == 0 ) * currentTotalRate * dPhaseFracTimesPhaseDensInv_dPres; + dControlEqn[COFFSET_WJ::dQ] += phaseFracTimesPhaseDensInv; + if constexpr (IS_THERMAL ) + { + real64 const dPhaseFracTimesPhaseDensInv_dTemp = dPhaseFrac[iwelemRef][0][ip][Deriv::dT] * phaseDensInv + - dPhaseDens[iwelemRef][0][ip][Deriv::dT] * phaseFracTimesPhaseDensInv * phaseDensInv; + dControlEqn[COFFSET_WJ::dT] += ( useSurfaceConditions == 0 ) * currentTotalRate * dPhaseFracTimesPhaseDensInv_dTemp; + } + + for( integer ic = 0; ic < NC; ++ic ) + { + real64 temp = -phaseFracTimesPhaseDensInv * dPhaseDens[iwelemRef][0][ip][Deriv::dC+ic] * phaseDensInv; + temp += dPhaseFrac[iwelemRef][0][ip][Deriv::dC+ic] * phaseDensInv; + temp *= currentTotalRate; + dControlEqn[COFFSET_WJ::dC+ic] += temp; + } + } + } + applyChainRuleInPlace( NC, dCompFrac_dCompDens[iwelemRef], &dControlEqn[COFFSET_WJ::dC], work.data() ); + } ); + + // add solver matrices + localRhs[eqnRowIndex] += controlEqn; + localMatrix.addToRowBinarySearchUnsorted< serialAtomic >( eqnRowIndex, + dofColIndices, + dControlEqn, + COFFSET_WJ::nDer ); + } + template< template< typename U > class T, typename U=VolumeRateConstraint > + static void assembleConstraintEquation( real64 const & time_n, + WellControls & wellControls, + T< VolumeRateConstraint > & constraint, + WellElementSubRegion const & subRegion, + string const & wellDofKey, + localIndex const & rankOffset, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) + { + // subRegion data + + localIndex const iwelemRef = subRegion.getTopWellElementIndex(); + arrayView1d< globalIndex const > const & wellElemDofNumber = subRegion.getReference< array1d< globalIndex > >( wellDofKey ); + arrayView1d< real64 const > const & connRate = subRegion.getField< fields::well::connectionRate >(); + arrayView2d< real64 const, compflow::USD_COMP > const & compFrac = subRegion.getField< fields::well::globalCompFraction >(); + arrayView3d< real64 const, compflow::USD_COMP_DC > const & dCompFrac_dCompDens = subRegion.getField< fields::well::dGlobalCompFraction_dGlobalCompDensity >(); + + // setup row/column indices for constraint equation + using COFFSET_WJ = compositionalMultiphaseWellKernels::ColOffset_WellJac< NC, IS_THERMAL >; + using WJ_ROFFSET = compositionalMultiphaseWellKernels::RowOffset_WellJac< NC, IS_THERMAL >; + using Deriv = constitutive::multifluid::DerivativeOffset; + + localIndex const eqnRowIndex = wellElemDofNumber[iwelemRef] + WJ_ROFFSET::CONTROL - rankOffset; + globalIndex dofColIndices[COFFSET_WJ::nDer]{}; + for( integer ic = 0; ic < COFFSET_WJ::nDer; ++ic ) + { + dofColIndices[ ic ] = wellElemDofNumber[iwelemRef] + ic; + } + + // fluid data + constitutive::MultiFluidBase & fluidSeparator = wellControls.getMultiFluidSeparator(); + + arrayView2d< real64 const, constitutive::multifluid::USD_FLUID > const & totalDens = fluidSeparator.totalDensity(); + arrayView3d< real64 const, constitutive::multifluid::USD_FLUID_DC > const & dTotalDens = fluidSeparator.dTotalDensity(); + + // constraint data + real64 const & targetTotalVolRate = constraint.getConstraintValue( time_n ); + + // current constraint value + real64 const & currentTotalVolRate = + wellControls.getReference< real64 >( CompositionalMultiphaseWell::viewKeyStruct::currentTotalVolRateString() ); + integer const useSurfaceConditions = wellControls.useSurfaceConditions(); + + // residual + real64 controlEqn = currentTotalVolRate - targetTotalVolRate; + + // setup Jacobian terms + real64 dControlEqn[NC+2+IS_THERMAL]{}; + + // bring everything back to host, capture the scalars by reference + forAll< serialPolicy >( 1, [connRate, + totalDens, + dTotalDens, + compFrac, + dCompFrac_dCompDens, + &dControlEqn, + &useSurfaceConditions, + &iwelemRef] ( localIndex const ) + { + stackArray1d< real64, NC > work( NC ); + + real64 const currentTotalRate = connRate[iwelemRef]; + + // compute the inverse of the total density and derivatives + + real64 const totalDensInv = 1.0 / totalDens[iwelemRef][0]; + + stackArray1d< real64, NC > dTotalDensInv_dCompDens( NC ); + for( integer ic = 0; ic < NC; ++ic ) + { + dTotalDensInv_dCompDens[ic] = -dTotalDens[iwelemRef][0][Deriv::dC+ic] * totalDensInv * totalDensInv; + } + applyChainRuleInPlace( NC, dCompFrac_dCompDens[iwelemRef], dTotalDensInv_dCompDens, work.data() ); + + // Step 2.2: divide the total mass/molar rate by the total density to get the total volumetric rate + + // Compute derivatives dP dT + real64 const dTotalDensInv_dPres = -dTotalDens[iwelemRef][0][Deriv::dP] * totalDensInv * totalDensInv; + dControlEqn[COFFSET_WJ::dP] = ( useSurfaceConditions == 0 ) * currentTotalRate * dTotalDensInv_dPres; + if constexpr ( IS_THERMAL ) + { + dControlEqn[COFFSET_WJ::dT] = ( useSurfaceConditions == 0 ) * currentTotalRate * -dTotalDens[iwelemRef][0][Deriv::dT] * totalDensInv * totalDensInv; + } + + dControlEqn[COFFSET_WJ::dQ] = totalDensInv; + for( integer ic = 0; ic < NC; ++ic ) + { + dControlEqn[COFFSET_WJ::dC+ic] = currentTotalRate * dTotalDensInv_dCompDens[ic]; + } + + } ); + + // add solver matrices + localRhs[eqnRowIndex] += controlEqn; + localMatrix.addToRowBinarySearchUnsorted< serialAtomic >( eqnRowIndex, + dofColIndices, + dControlEqn, + COFFSET_WJ::nDer ); + } + template< template< typename U > class T, typename U=MassRateConstraint > + static void assembleConstraintEquation( real64 const & time_n, + WellControls & wellControls, + T< MassRateConstraint > & constraint, + WellElementSubRegion const & subRegion, + string const & wellDofKey, + localIndex const & rankOffset, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) + { + // subRegion data + + localIndex const iwelemRef = subRegion.getTopWellElementIndex(); + arrayView1d< globalIndex const > const & wellElemDofNumber = subRegion.getReference< array1d< globalIndex > >( wellDofKey ); + arrayView1d< real64 const > const & connRate = subRegion.getField< fields::well::connectionRate >(); + arrayView2d< real64 const, compflow::USD_COMP > const & compFrac = subRegion.getField< fields::well::globalCompFraction >(); + arrayView3d< real64 const, compflow::USD_COMP_DC > const & dCompFrac_dCompDens = subRegion.getField< fields::well::dGlobalCompFraction_dGlobalCompDensity >(); + + // setup row/column indices for constraint equation + using COFFSET_WJ = compositionalMultiphaseWellKernels::ColOffset_WellJac< NC, IS_THERMAL >; + using WJ_ROFFSET = compositionalMultiphaseWellKernels::RowOffset_WellJac< NC, IS_THERMAL >; + using Deriv = constitutive::multifluid::DerivativeOffset; + + localIndex const eqnRowIndex = wellElemDofNumber[iwelemRef] + WJ_ROFFSET::CONTROL - rankOffset; + globalIndex dofColIndices[COFFSET_WJ::nDer]{}; + for( integer ic = 0; ic < COFFSET_WJ::nDer; ++ic ) + { + dofColIndices[ ic ] = wellElemDofNumber[iwelemRef] + ic; + } + + // fluid data + constitutive::MultiFluidBase & fluidSeparator = wellControls.getMultiFluidSeparator(); + arrayView2d< real64 const, constitutive::multifluid::USD_FLUID > const & totalDens = fluidSeparator.totalDensity(); + arrayView3d< real64 const, constitutive::multifluid::USD_FLUID_DC > const & dTotalDens = fluidSeparator.dTotalDensity(); + + // constraint data + real64 const & targetMassRate = constraint.getConstraintValue( time_n ); + + // current constraint value + real64 const & massDensity = + wellControls.getReference< real64 >( CompositionalMultiphaseWell::viewKeyStruct::massDensityString() ); + + // fix to use stored massrate + real64 const & currentTotalVolRate = + wellControls.getReference< real64 >( CompositionalMultiphaseWell::viewKeyStruct::currentTotalVolRateString() ); + integer const useSurfaceConditions = wellControls.useSurfaceConditions(); + + // residual + real64 controlEqn = massDensity*currentTotalVolRate - targetMassRate; + + // setup Jacobian terms + real64 dControlEqn[NC+2+IS_THERMAL]{}; + + // bring everything back to host, capture the scalars by reference + forAll< serialPolicy >( 1, [connRate, + massDensity, + totalDens, + dTotalDens, + compFrac, + dCompFrac_dCompDens, + &dControlEqn, + &useSurfaceConditions, + &iwelemRef] ( localIndex const ) + { + stackArray1d< real64, NC > work( NC ); + + real64 const currentTotalRate = connRate[iwelemRef]; + + // compute the inverse of the total density and derivatives + + real64 const totalDensInv = 1.0 / totalDens[iwelemRef][0]; + + stackArray1d< real64, NC > dTotalDensInv_dCompDens( NC ); + for( integer ic = 0; ic < NC; ++ic ) + { + dTotalDensInv_dCompDens[ic] = -dTotalDens[iwelemRef][0][Deriv::dC+ic] * totalDensInv * totalDensInv; + } + applyChainRuleInPlace( NC, dCompFrac_dCompDens[iwelemRef], dTotalDensInv_dCompDens, work.data() ); + + // Step 2.2: divide the total mass/molar rate by the total density to get the total volumetric rate + + // Compute derivatives dP dT + real64 const dTotalDensInv_dPres = -dTotalDens[iwelemRef][0][Deriv::dP] * totalDensInv * totalDensInv; + dControlEqn[COFFSET_WJ::dP] = ( useSurfaceConditions == 0 )*massDensity * currentTotalRate * dTotalDensInv_dPres; + if constexpr ( IS_THERMAL ) + { + dControlEqn[COFFSET_WJ::dT] = ( useSurfaceConditions == 0 ) * massDensity* currentTotalRate * -dTotalDens[iwelemRef][0][Deriv::dT] * totalDensInv * totalDensInv; + } + + dControlEqn[COFFSET_WJ::dQ] = massDensity*totalDensInv; + for( integer ic = 0; ic < NC; ++ic ) + { + dControlEqn[COFFSET_WJ::dC+ic] = massDensity* currentTotalRate * dTotalDensInv_dCompDens[ic]; + } + + } ); + + // add solver matrices + localRhs[eqnRowIndex] += controlEqn; + localMatrix.addToRowBinarySearchUnsorted< serialAtomic >( eqnRowIndex, + dofColIndices, + dControlEqn, + COFFSET_WJ::nDer ); + } +}; + + +} // end namespace wellConstraintKernels + +} // end namespace geos + +#endif //GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_WELLCONSTRAINTKERNELS_HPP diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/CompositionalMultiphaseWellKernels.cpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/CompositionalMultiphaseWellKernels.cpp index ee04ffa8c73..a0c71194971 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/CompositionalMultiphaseWellKernels.cpp +++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/CompositionalMultiphaseWellKernels.cpp @@ -31,214 +31,6 @@ namespace compositionalMultiphaseWellKernels using namespace constitutive; -/******************************** ControlEquationHelper ********************************/ - -GEOS_HOST_DEVICE -inline -void -ControlEquationHelper:: - switchControl( bool const isProducer, - WellControls::Control const & inputControl, - WellControls::Control const & currentControl, - integer const phasePhaseIndex, - real64 const & targetBHP, - real64 const & targetPhaseRate, - real64 const & targetTotalRate, - real64 const & targetMassRate, - real64 const & currentBHP, - arrayView1d< real64 const > const & currentPhaseVolRate, - real64 const & currentTotalVolRate, - real64 const & currentMassRate, - WellControls::Control & newControl ) -{ - // if isViable is true at the end of the following checks, no need to switch - bool controlIsViable = false; - - // The limiting flow rates are treated as upper limits, while the pressure limits - // are treated as lower limits in production wells and upper limits in injectors. - // The well changes its mode of control whenever the existing control mode would - // violate one of these limits. - - // Currently, the available constraints are: - // - Producer: BHP, PHASEVOLRATE - // - Injector: BHP, TOTALVOLRATE, MASSRATE - - // TODO: support GRAT, WRAT, LIQUID for producers and check if any of the active constraint is violated - - // BHP control - if( currentControl == WellControls::Control::BHP ) - { - // the control is viable if the reference oil rate is below the max rate for producers - if( isProducer ) - { - controlIsViable = ( LvArray::math::abs( currentPhaseVolRate[phasePhaseIndex] ) <= LvArray::math::abs( targetPhaseRate ) ); - } - // the control is viable if the reference total rate is below the max rate for injectors - else if( inputControl == WellControls::Control::MASSRATE ) - { - controlIsViable = ( LvArray::math::abs( currentMassRate ) <= LvArray::math::abs( targetMassRate ) ); - } - else - { - controlIsViable = ( LvArray::math::abs( currentTotalVolRate ) <= LvArray::math::abs( targetTotalRate ) ); - } - } - else // rate control - { - // the control is viable if the reference pressure is below/above the max/min pressure - if( isProducer ) - { - // targetBHP specifies a min pressure here - controlIsViable = ( currentBHP >= targetBHP ); - } - else - { - // targetBHP specifies a max pressure here - controlIsViable = ( currentBHP <= targetBHP ); - } - } - - if( controlIsViable ) - { - newControl = currentControl; - } - else - { - if( isProducer ) - { - newControl = ( currentControl == WellControls::Control::BHP ) - ? WellControls::Control::PHASEVOLRATE - : WellControls::Control::BHP; - } - else - { - if( isZero( targetMassRate ) ) - { - newControl = ( currentControl == WellControls::Control::BHP ) - ? WellControls::Control::TOTALVOLRATE - : WellControls::Control::BHP; - } - else - { - newControl = ( currentControl == WellControls::Control::BHP ) - ? WellControls::Control::MASSRATE - : WellControls::Control::BHP; - } - } - } -} - -template< integer NC, integer IS_THERMAL > -GEOS_HOST_DEVICE -inline -void -ControlEquationHelper:: - compute( globalIndex const rankOffset, - WellControls::Control const currentControl, - integer const targetPhaseIndex, - real64 const & targetBHP, - real64 const & targetPhaseRate, - real64 const & targetTotalRate, - real64 const & targetMassRate, - real64 const & currentBHP, - arrayView1d< real64 const > const & dCurrentBHP, - arrayView1d< real64 const > const & currentPhaseVolRate, - arrayView2d< real64 const > const & dCurrentPhaseVolRate, - - real64 const & currentTotalVolRate, - arrayView1d< real64 const > const & dCurrentTotalVolRate, - real64 const & massDensity, - globalIndex const dofNumber, - CRSMatrixView< real64, globalIndex const > const & localMatrix, - arrayView1d< real64 > const & localRhs ) -{ - - using COFFSET_WJ = compositionalMultiphaseWellKernels::ColOffset_WellJac< NC, IS_THERMAL >; - using Deriv = multifluid::DerivativeOffset; - - localIndex const eqnRowIndex = dofNumber + ROFFSET::CONTROL - rankOffset; - globalIndex dofColIndices[COFFSET_WJ::nDer]{}; - for( integer ic = 0; ic < COFFSET_WJ::nDer; ++ic ) - { - dofColIndices[ ic ] = dofNumber + ic; - } - - real64 controlEqn = 0; - real64 dControlEqn[NC+2+IS_THERMAL]{}; - - // Note: We assume in the computation of currentBHP that the reference elevation - // is in the top well element. This is enforced by a check in the solver. - // If we wanted to allow the reference elevation to be outside the top - // well element, it would make more sense to check the BHP constraint in - // the well element that contains the reference elevation. - - // BHP control - if( currentControl == WellControls::Control::BHP ) - { - // control equation is a difference between current BHP and target BHP - controlEqn = currentBHP - targetBHP; - dControlEqn[COFFSET_WJ::dP] = dCurrentBHP[Deriv::dP]; - for( integer ic = 0; ic < NC; ++ic ) - { - dControlEqn[COFFSET_WJ::dC+ic] = dCurrentBHP[Deriv::dC+ic]; - } - if constexpr ( IS_THERMAL ) - - dControlEqn[COFFSET_WJ::dT] = dCurrentBHP[Deriv::dT]; - - } - // Oil volumetric rate control - else if( currentControl == WellControls::Control::PHASEVOLRATE ) - { - controlEqn = currentPhaseVolRate[targetPhaseIndex] - targetPhaseRate; - dControlEqn[COFFSET_WJ::dP] = dCurrentPhaseVolRate[targetPhaseIndex][COFFSET_WJ::dP]; - dControlEqn[COFFSET_WJ::dQ] = dCurrentPhaseVolRate[targetPhaseIndex][COFFSET_WJ::dQ]; - for( integer ic = 0; ic < NC; ++ic ) - { - dControlEqn[COFFSET_WJ::dC+ic] = dCurrentPhaseVolRate[targetPhaseIndex][COFFSET_WJ::dC+ic]; - } - if constexpr ( IS_THERMAL ) - dControlEqn[COFFSET_WJ::dT] = dCurrentPhaseVolRate[targetPhaseIndex][COFFSET_WJ::dT]; - } - // Total volumetric rate control - else if( currentControl == WellControls::Control::TOTALVOLRATE ) - { - controlEqn = currentTotalVolRate - targetTotalRate; - dControlEqn[COFFSET_WJ::dP] = dCurrentTotalVolRate[COFFSET_WJ::dP]; - dControlEqn[COFFSET_WJ::dQ] = dCurrentTotalVolRate[COFFSET_WJ::dQ]; - for( integer ic = 0; ic < NC; ++ic ) - { - dControlEqn[COFFSET_WJ::dC+ic] = dCurrentTotalVolRate[COFFSET_WJ::dC+ic]; - } - if constexpr ( IS_THERMAL ) - dControlEqn[COFFSET_WJ::dT] = dCurrentTotalVolRate[COFFSET_WJ::dT]; - } - // Total mass rate control - else if( currentControl == WellControls::Control::MASSRATE ) - { - controlEqn = massDensity*currentTotalVolRate - targetMassRate; - dControlEqn[COFFSET_WJ::dP] = massDensity*dCurrentTotalVolRate[COFFSET_WJ::dP]; - dControlEqn[COFFSET_WJ::dQ] = massDensity*dCurrentTotalVolRate[COFFSET_WJ::dQ]; - for( integer ic = 0; ic < NC; ++ic ) - { - dControlEqn[COFFSET_WJ::dC+ic] = massDensity*dCurrentTotalVolRate[COFFSET_WJ::dC+ic]; - } - if constexpr ( IS_THERMAL ) - dControlEqn[COFFSET_WJ::dT] = massDensity*dCurrentTotalVolRate[COFFSET_WJ::dT]; - } - else - { - GEOS_ERROR( "This constraint is not supported in CompositionalMultiphaseWell" ); - } - localRhs[eqnRowIndex] += controlEqn; - - localMatrix.addToRowBinarySearchUnsorted< serialAtomic >( eqnRowIndex, - dofColIndices, - dControlEqn, - COFFSET_WJ::nDer ); - - -} /******************************** PressureRelationKernel ********************************/ @@ -300,11 +92,6 @@ void PressureRelationKernel:: launch( localIndex const size, globalIndex const rankOffset, - bool const isLocallyOwned, - localIndex const iwelemControl, - integer const targetPhaseIndex, - WellControls const & wellControls, - real64 const & time, arrayView1d< integer const > const elemStatus, arrayView1d< globalIndex const > const & wellElemDofNumber, arrayView1d< real64 const > const & wellElemGravCoef, @@ -317,36 +104,6 @@ PressureRelationKernel:: arrayView1d< real64 > const & localRhs ) { using COFFSET_WJ = compositionalMultiphaseWellKernels::ColOffset_WellJac< NC, IS_THERMAL >; - // static well control data - bool const isProducer = wellControls.isProducer(); - WellControls::Control const currentControl = wellControls.getControl(); - WellControls::Control const inputControl = wellControls.getInputControl(); - real64 const targetBHP = wellControls.getTargetBHP( time ); - real64 const targetTotalRate = wellControls.getTargetTotalRate( time ); - real64 const targetPhaseRate = wellControls.getTargetPhaseRate( time ); - real64 const targetMassRate = wellControls.getTargetMassRate( time ); - - // dynamic well control data - real64 const & currentBHP = - wellControls.getReference< real64 >( CompositionalMultiphaseWell::viewKeyStruct::currentBHPString() ); - arrayView1d< real64 const > const & dCurrentBHP = - wellControls.getReference< array1d< real64 > >( CompositionalMultiphaseWell::viewKeyStruct::dCurrentBHPString() ); - - arrayView1d< real64 const > const & currentPhaseVolRate = - wellControls.getReference< array1d< real64 > >( CompositionalMultiphaseWell::viewKeyStruct::currentPhaseVolRateString() ); - arrayView2d< real64 const > const & dCurrentPhaseVolRate = - wellControls.getReference< array2d< real64 > >( CompositionalMultiphaseWell::viewKeyStruct::dCurrentPhaseVolRateString() ); - - real64 const & currentTotalVolRate = - wellControls.getReference< real64 >( CompositionalMultiphaseWell::viewKeyStruct::currentTotalVolRateString() ); - arrayView1d< real64 const > const & dCurrentTotalVolRate = - wellControls.getReference< array1d< real64 > >( CompositionalMultiphaseWell::viewKeyStruct::dCurrentTotalVolRateString() ); - - real64 const & currentMassRate = - wellControls.getReference< real64 >( CompositionalMultiphaseWell::viewKeyStruct::currentMassRateString() ); - - real64 const & massDensity = - wellControls.getReference< real64 >( CompositionalMultiphaseWell::viewKeyStruct::massDensityString() ); RAJA::ReduceMax< parallelDeviceReduce, localIndex > switchControl( 0 ); @@ -360,48 +117,7 @@ PressureRelationKernel:: localIndex const iwelemNext = nextWellElemIndex[iwelem]; - if( iwelemNext < 0 && isLocallyOwned ) // if iwelemNext < 0, form control equation - { - - WellControls::Control newControl = currentControl; - ControlEquationHelper::switchControl( isProducer, - inputControl, - currentControl, - targetPhaseIndex, - targetBHP, - targetPhaseRate, - targetTotalRate, - targetMassRate, - currentBHP, - currentPhaseVolRate, - currentTotalVolRate, - currentMassRate, - newControl ); - if( currentControl != newControl ) - { - switchControl.max( 1 ); - } - ControlEquationHelper::compute< NC, IS_THERMAL >( rankOffset, - newControl, - targetPhaseIndex, - targetBHP, - targetPhaseRate, - targetTotalRate, - targetMassRate, - currentBHP, - dCurrentBHP, - currentPhaseVolRate, - dCurrentPhaseVolRate, - currentTotalVolRate, - dCurrentTotalVolRate, - massDensity, - wellElemDofNumber[iwelemControl], - localMatrix, - localRhs ); - // TODO: for consistency, we should assemble here, not in compute... - - } - else if( iwelemNext >= 0 ) // if iwelemNext >= 0, form momentum equation + if( iwelemNext >= 0 ) // if iwelemNext >= 0, form momentum equation { real64 localPresRel = 0; @@ -455,11 +171,6 @@ PressureRelationKernel:: void PressureRelationKernel:: \ launch< NC, IS_THERMAL >( localIndex const size, \ globalIndex const rankOffset, \ - bool const isLocallyOwned, \ - localIndex const iwelemControl, \ - integer const targetPhaseIndex, \ - WellControls const & wellControls, \ - real64 const & time, \ arrayView1d< integer const > const elemStatus, \ arrayView1d< globalIndex const > const & wellElemDofNumber, \ arrayView1d< real64 const > const & wellElemGravCoef, \ @@ -716,62 +427,97 @@ CompDensInitializationKernel:: void RateInitializationKernel:: launch( localIndex const subRegionSize, - integer const targetPhaseIndex, WellControls const & wellControls, real64 const & time, arrayView3d< real64 const, multifluid::USD_PHASE > const & phaseDens, arrayView2d< real64 const, multifluid::USD_FLUID > const & totalDens, arrayView1d< real64 > const & connRate ) { - WellControls::Control const control = wellControls.getControl(); - bool const isProducer = wellControls.isProducer(); - real64 const targetTotalRate = wellControls.getTargetTotalRate( time ); - real64 const targetPhaseRate = wellControls.getTargetPhaseRate( time ); - real64 const targetMassRate = wellControls.getTargetMassRate( time ); - - // Estimate the connection rates - forAll< parallelDevicePolicy<> >( subRegionSize, [=] GEOS_HOST_DEVICE ( localIndex const iwelem ) + if( wellControls.isProducer() ) { - if( control == WellControls::Control::BHP ) + // Use use defined control type to set initial connection rates + WellConstraintBase const * constraint = wellControls.getCurrentConstraint(); + real64 const constraintVal = constraint->getConstraintValue( time ); + ConstraintTypeId const controlType = constraint->getControl(); + if( controlType == ConstraintTypeId::PHASEVOLRATE ) { - // if BHP constraint set rate below the absolute max rate - // with the appropriate sign (negative for prod, positive for inj) - if( isProducer ) + integer const targetPhaseIndex = wellControls.getConstraintPhaseIndex(); + + forAll< parallelDevicePolicy<> >( subRegionSize, [=] GEOS_HOST_DEVICE ( localIndex const iwelem ) { - connRate[iwelem] = LvArray::math::max( 0.1 * targetPhaseRate * phaseDens[iwelem][0][targetPhaseIndex], -1e3 ); - } - else + connRate[iwelem] = constraintVal * phaseDens[iwelem][0][targetPhaseIndex]; + } ); + } + else if( controlType == ConstraintTypeId::TOTALVOLRATE ) + { + forAll< parallelDevicePolicy<> >( subRegionSize, [=] GEOS_HOST_DEVICE ( localIndex const iwelem ) + { + connRate[iwelem] = LvArray::math::max( 0.1 * constraintVal * totalDens[iwelem][0], -1e3 ); + } ); + } + else if( controlType == ConstraintTypeId::MASSRATE ) + { + forAll< parallelDevicePolicy<> >( subRegionSize, [=] GEOS_HOST_DEVICE ( localIndex const iwelem ) + { + connRate[iwelem] = constraintVal; + } ); + } + else if( controlType == ConstraintTypeId::BHP ) + { + // this assumes phase control present + integer const targetPhaseIndex = wellControls.getConstraintPhaseIndex(); + std::vector< WellConstraintBase * > const constraints = wellControls.getProdRateConstraints(); + // Use first rate constraint to set initial connection rates + real64 const rateVal = constraints[0]->getConstraintValue( time ); + forAll< parallelDevicePolicy<> >( subRegionSize, [&] GEOS_HOST_DEVICE ( localIndex const iwelem ) { - if( isZero( targetMassRate ) ) - { - connRate[iwelem] = LvArray::math::min( 0.1 * targetTotalRate * totalDens[iwelem][0], 1e3 ); - } - else - { - connRate[iwelem] = targetMassRate; - } - } + connRate[iwelem] = LvArray::math::max( 0.1 * rateVal * phaseDens[iwelem][0][targetPhaseIndex], -1e3 ); + } ); } - else if( control == WellControls::Control::MASSRATE ) + } + else + { + // Use use defined control type to set initial connection rates + WellConstraintBase const * constraint = wellControls.getCurrentConstraint(); + real64 const constraintVal = constraint->getConstraintValue( time ); + ConstraintTypeId const controlType = constraint->getControl(); + if( controlType == ConstraintTypeId::PHASEVOLRATE ) { - connRate[iwelem] = targetMassRate; + integer const targetPhaseIndex = wellControls.getConstraintPhaseIndex(); + + forAll< parallelDevicePolicy<> >( subRegionSize, [=] GEOS_HOST_DEVICE ( localIndex const iwelem ) + { + connRate[iwelem] = LvArray::math::max( 0.1 * constraintVal * phaseDens[iwelem][0][targetPhaseIndex], 1e3 ); + } ); } - else + else if( controlType == ConstraintTypeId::TOTALVOLRATE ) { - if( isProducer ) + forAll< parallelDevicePolicy<> >( subRegionSize, [=] GEOS_HOST_DEVICE ( localIndex const iwelem ) { - connRate[iwelem] = targetPhaseRate * phaseDens[iwelem][0][targetPhaseIndex]; - } - else + connRate[iwelem] = constraintVal * totalDens[iwelem][0]; + } ); + } + else if( controlType == ConstraintTypeId::MASSRATE ) + { + forAll< parallelDevicePolicy<> >( subRegionSize, [=] GEOS_HOST_DEVICE ( localIndex const iwelem ) { - connRate[iwelem] = targetTotalRate * totalDens[iwelem][0]; - } + connRate[iwelem] = constraintVal; + } ); } - } ); + else if( controlType == ConstraintTypeId::BHP ) + { + std::vector< WellConstraintBase * > const constraints = wellControls.getInjRateConstraints(); + // Use first rate constraint to set initial connection rates + real64 const rateVal = constraints[0]->getConstraintValue( time ); + forAll< parallelDevicePolicy<> >( subRegionSize, [=] GEOS_HOST_DEVICE ( localIndex const iwelem ) + { + connRate[iwelem] = LvArray::math::min( 0.1 * rateVal * totalDens[iwelem][0], 1e3 ); + } ); + } + } } - } // end namespace compositionalMultiphaseWellKernels } // end namespace geos diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/CompositionalMultiphaseWellKernels.hpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/CompositionalMultiphaseWellKernels.hpp index a8123abbb12..c81c2582537 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/CompositionalMultiphaseWellKernels.hpp +++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/CompositionalMultiphaseWellKernels.hpp @@ -39,7 +39,8 @@ #include "physicsSolvers/fluidFlow/kernels/compositional/SolutionCheckKernel.hpp" #include "physicsSolvers/fluidFlow/wells/CompositionalMultiphaseWellFields.hpp" #include "physicsSolvers/fluidFlow/wells/WellControls.hpp" -#include "physicsSolvers/fluidFlow/wells/WellSolverBaseFields.hpp" +#include "physicsSolvers/fluidFlow/wells/WellFields.hpp" +#include "physicsSolvers/fluidFlow/wells/WellPhaseVolumeRateConstraint.hpp" namespace geos { @@ -134,19 +135,19 @@ struct ControlEquationHelper inline static void - switchControl( bool const isProducer, - WellControls::Control const & inputControl, - WellControls::Control const & currentControl, - integer const phasePhaseIndex, - real64 const & targetBHP, - real64 const & targetPhaseRate, - real64 const & targetTotalRate, - real64 const & targetMassRate, - real64 const & currentBHP, - arrayView1d< real64 const > const & currentPhaseVolRate, - real64 const & currentTotalVolRate, - real64 const & currentMassRate, - WellControls::Control & newControl ); + selectLimitingConstraint( bool const isProducer, + WellControls::Control const & inputControl, + WellControls::Control const & currentControl, + integer const phasePhaseIndex, + real64 const & targetBHP, + real64 const & targetPhaseRate, + real64 const & targetTotalRate, + real64 const & targetMassRate, + real64 const & currentBHP, + arrayView1d< real64 const > const & currentPhaseVolRate, + real64 const & currentTotalVolRate, + real64 const & currentMassRate, + WellControls::Control & newControl ); template< integer NC, integer IS_THERMAL > GEOS_HOST_DEVICE @@ -160,6 +161,7 @@ struct ControlEquationHelper real64 const & targetTotalRate, real64 const & targetMassRate, real64 const & currentBHP, + real64 const & targetValue, arrayView1d< real64 const > const & dCurrentBHP, arrayView1d< real64 const > const & currentPhaseVolRate, arrayView2d< real64 const > const & dCurrentPhaseVolRate, @@ -200,11 +202,6 @@ struct PressureRelationKernel static void launch( localIndex const size, globalIndex const rankOffset, - bool const isLocallyOwned, - localIndex const iwelemControl, - integer const targetPhaseIndex, - WellControls const & wellControls, - real64 const & time, arrayView1d< integer const > const elemStatus, arrayView1d< globalIndex const > const & wellElemDofNumber, arrayView1d< real64 const > const & wellElemGravCoef, @@ -323,7 +320,6 @@ struct RateInitializationKernel static void launch( localIndex const subRegionSize, - integer const targetPhaseIndex, WellControls const & wellControls, real64 const & currentTime, arrayView3d< real64 const, constitutive::multifluid::USD_PHASE > const & phaseDens, @@ -505,7 +501,6 @@ class ResidualNormKernel : public physicsSolverBaseKernels::ResidualNormKernelBa arrayView1d< localIndex const > const & ghostRank, integer const numComp, integer const numDof, - integer const targetPhaseIndex, WellElementSubRegion const & subRegion, constitutive::MultiFluidBase const & fluid, WellControls const & wellControls, @@ -519,20 +514,42 @@ class ResidualNormKernel : public physicsSolverBaseKernels::ResidualNormKernelBa minNormalizer ), m_numComp( numComp ), m_numDof( numDof ), - m_targetPhaseIndex( targetPhaseIndex ), m_dt( dt ), m_isLocallyOwned( subRegion.isLocallyOwned() ), m_iwelemControl( subRegion.getTopWellElementIndex() ), m_isProducer( wellControls.isProducer() ), m_currentControl( wellControls.getControl() ), - m_targetBHP( wellControls.getTargetBHP( time ) ), - m_targetTotalRate( wellControls.getTargetTotalRate( time ) ), - m_targetPhaseRate( wellControls.getTargetPhaseRate( time ) ), - m_targetMassRate( wellControls.getTargetMassRate( time ) ), m_volume( subRegion.getElementVolume() ), m_phaseDens_n( fluid.phaseDensity_n() ), m_totalDens_n( fluid.totalDensity_n() ) - {} + { + if( m_isProducer ) + { + if( wellControls.getMinBHPConstraint()->isConstraintActive()) + m_targetBHP = wellControls.getMinBHPConstraint()->getConstraintValue( time ); + // Note this assumes that there is only one rate constraint + // This is a normalizer for the balance equations. The normalizaer should be the current rate not the constraint value!! + // This is one of the reasons for restricting constraint type for a production well + // another pr will remove fix this (so the cause for difference results is isolated to one change) + m_targetPhaseIndex = wellControls.getConstraintPhaseIndex( ); + m_constraintValue = wellControls.getProdRateConstraints()[0]->getConstraintValue( time ); + + } + else + { + m_targetBHP = wellControls.getMaxBHPConstraint()->getConstraintValue( time ); + + // tjbNote this assumes that there is only one rate constraint + // This is a normalizer for the balance equations. The normalizaer should be the current rate not the constraint value!! + // This is one of the reasons for restricting constraint type for a production well + // another pr will remove fix this (so the cause for difference results is isolated to one change) + m_targetPhaseIndex = -1; + m_constraintValue = wellControls.getInjRateConstraints()[0]->getConstraintValue( time ); + + } + + + } GEOS_HOST_DEVICE virtual void computeLinf( localIndex const iwelem, @@ -561,17 +578,17 @@ class ResidualNormKernel : public physicsSolverBaseKernels::ResidualNormKernelBa else if( m_currentControl == WellControls::Control::TOTALVOLRATE ) { // the residual entry is in volume / time units - normalizer = LvArray::math::max( LvArray::math::abs( m_targetTotalRate ), m_minNormalizer ); + normalizer = LvArray::math::max( LvArray::math::abs( m_constraintValue ), m_minNormalizer ); } else if( m_currentControl == WellControls::Control::PHASEVOLRATE ) { // the residual entry is in volume / time units - normalizer = LvArray::math::max( LvArray::math::abs( m_targetPhaseRate ), m_minNormalizer ); + normalizer = LvArray::math::max( LvArray::math::abs( m_constraintValue ), m_minNormalizer ); } else if( m_currentControl == WellControls::Control::MASSRATE ) { // the residual entry is in volume / time units - normalizer = LvArray::math::max( LvArray::math::abs( m_targetMassRate ), m_minNormalizer ); + normalizer = LvArray::math::max( LvArray::math::abs( m_constraintValue ), m_minNormalizer ); } } // for the pressure difference equation, always normalize by the BHP @@ -586,18 +603,18 @@ class ResidualNormKernel : public physicsSolverBaseKernels::ResidualNormKernelBa if( m_isProducer ) // only PHASEVOLRATE is supported for now { // the residual is in mass units - normalizer = m_dt * LvArray::math::abs( m_targetPhaseRate ) * m_phaseDens_n[iwelem][0][m_targetPhaseIndex]; + normalizer = m_dt * LvArray::math::abs( m_constraintValue ) * m_phaseDens_n[iwelem][0][m_targetPhaseIndex]; } else // Type::INJECTOR, only TOTALVOLRATE is supported for now { if( m_currentControl == WellControls::Control::MASSRATE ) { - normalizer = m_dt * LvArray::math::abs( m_targetMassRate ); + normalizer = m_dt * LvArray::math::abs( m_constraintValue ); } else { // the residual is in mass units - normalizer = m_dt * LvArray::math::abs( m_targetTotalRate ) * m_totalDens_n[iwelem][0]; + normalizer = m_dt * LvArray::math::abs( m_constraintValue ) * m_totalDens_n[iwelem][0]; } } @@ -611,17 +628,17 @@ class ResidualNormKernel : public physicsSolverBaseKernels::ResidualNormKernelBa if( m_isProducer ) // only PHASEVOLRATE is supported for now { // the residual is in volume units - normalizer = m_dt * LvArray::math::abs( m_targetPhaseRate ); + normalizer = m_dt * LvArray::math::abs( m_constraintValue ); } else // Type::INJECTOR, only TOTALVOLRATE is supported for now { if( m_currentControl == WellControls::Control::MASSRATE ) { - normalizer = m_dt * LvArray::math::abs( m_targetMassRate/ m_totalDens_n[iwelem][0] ); + normalizer = m_dt * LvArray::math::abs( m_constraintValue/ m_totalDens_n[iwelem][0] ); } else { - normalizer = m_dt * LvArray::math::abs( m_targetTotalRate ); + normalizer = m_dt * LvArray::math::abs( m_constraintValue ); } } @@ -658,7 +675,7 @@ class ResidualNormKernel : public physicsSolverBaseKernels::ResidualNormKernelBa integer const m_numDof; /// Index of the target phase - integer const m_targetPhaseIndex; + integer m_targetPhaseIndex; /// Time step size real64 const m_dt; @@ -674,10 +691,9 @@ class ResidualNormKernel : public physicsSolverBaseKernels::ResidualNormKernelBa /// Controls WellControls::Control const m_currentControl; - real64 const m_targetBHP; - real64 const m_targetTotalRate; - real64 const m_targetPhaseRate; - real64 const m_targetMassRate; + real64 m_constraintValue; + real64 m_targetBHP; + /// View on the volume arrayView1d< real64 const > const m_volume; @@ -700,7 +716,6 @@ class ResidualNormKernelFactory * @tparam POLICY the policy used in the RAJA kernel * @param[in] numComp number of fluid components * @param[in] numDof number of dofs per well element - * @param[in] targetPhaseIndex the index of the target phase (for phase volume control) * @param[in] rankOffset the offset of my MPI rank * @param[in] dofKey the string key to retrieve the degress of freedom numbers * @param[in] localResidual the residual vector on my MPI rank @@ -715,7 +730,6 @@ class ResidualNormKernelFactory static void createAndLaunch( integer const numComp, integer const numDof, - integer const targetPhaseIndex, globalIndex const rankOffset, string const & dofKey, arrayView1d< real64 const > const & localResidual, @@ -731,7 +745,7 @@ class ResidualNormKernelFactory arrayView1d< integer const > const ghostRank = subRegion.ghostRank(); ResidualNormKernel kernel( rankOffset, localResidual, dofNumber, ghostRank, - numComp, numDof, targetPhaseIndex, subRegion, fluid, wellControls, time, dt, minNormalizer ); + numComp, numDof, subRegion, fluid, wellControls, time, dt, minNormalizer ); ResidualNormKernel::launchLinf< POLICY >( subRegion.size(), kernel, residualNorm ); } @@ -830,6 +844,7 @@ class ElementBasedAssemblyKernel * @param[inout] localRhs the local right-hand side vector */ ElementBasedAssemblyKernel( localIndex const numPhases, + integer const thermalEffectsEnabled, integer const isProducer, globalIndex const rankOffset, string const dofKey, @@ -839,6 +854,7 @@ class ElementBasedAssemblyKernel arrayView1d< real64 > const & localRhs, BitFlags< isothermalCompositionalMultiphaseBaseKernels::KernelFlags > const kernelFlags ) : m_numPhases( numPhases ), + m_thermalEffectsEnabled( thermalEffectsEnabled ), m_isProducer( isProducer ), m_rankOffset( rankOffset ), m_iwelemControl( subRegion.getTopWellElementIndex() ), @@ -1120,7 +1136,20 @@ class ElementBasedAssemblyKernel if constexpr ( IS_THERMAL) { - if( ei == m_iwelemControl && !m_isProducer ) + if( !m_thermalEffectsEnabled ) + { + for( integer i=0; i < numComp+1+IS_THERMAL; i++ ) + { + stack.localJacobian[numRows-1][i] = 0.0; + } + // constant Temperature + for( integer i=0; i < numComp+1+IS_THERMAL; i++ ) + stack.localJacobian[i][numRows-1] = 0.0; + stack.localJacobian[numRows-1][numRows-1] = 1.0; + + stack.localResidual[numRows-1]=0.0; + } + else if( ei == m_iwelemControl && !m_isProducer ) { // For top segment energy balance eqn replaced with T(n+1) - T = 0 // No other energy balance derivatives @@ -1197,6 +1226,8 @@ class ElementBasedAssemblyKernel /// Number of fluid phases integer const m_numPhases; + /// Flag indicating whether thermal effects are enabled + bool const m_thermalEffectsEnabled; /// Well type integer const m_isProducer; @@ -1276,6 +1307,7 @@ class ElementBasedAssemblyKernelFactory static void createAndLaunch( localIndex const numComps, localIndex const numPhases, + integer const & thermalEffectsEnabled, integer const isProducer, globalIndex const rankOffset, BitFlags< isothermalCompositionalMultiphaseBaseKernels::KernelFlags > kernelFlags, @@ -1292,7 +1324,7 @@ class ElementBasedAssemblyKernelFactory integer constexpr istherm = IS_THERMAL(); ElementBasedAssemblyKernel< NUM_COMP, istherm > - kernel( numPhases, isProducer, rankOffset, dofKey, subRegion, fluid, localMatrix, localRhs, kernelFlags ); + kernel( numPhases, thermalEffectsEnabled, isProducer, rankOffset, dofKey, subRegion, fluid, localMatrix, localRhs, kernelFlags ); ElementBasedAssemblyKernel< NUM_COMP, istherm >::template launch< POLICY, ElementBasedAssemblyKernel< NUM_COMP, istherm > >( subRegion.size(), kernel ); } ); @@ -1359,7 +1391,7 @@ class FaceBasedAssemblyKernel m_wellElemDofNumber ( subRegion.getReference< array1d< globalIndex > >( wellDofKey ) ), m_nextWellElemIndex ( subRegion.getReference< array1d< localIndex > >( WellElementSubRegion::viewKeyStruct::nextWellElementIndexString()) ), m_elemStatus( subRegion.getLocalWellElementStatus() ), - m_connRate ( subRegion.getField< fields::well::mixtureConnectionRate >() ), + m_connRate ( subRegion.getField< fields::well::connectionRate >() ), m_wellElemCompFrac ( subRegion.getField< fields::well::globalCompFraction >() ), m_dWellElemCompFrac_dCompDens ( subRegion.getField< fields::well::dGlobalCompFraction_dGlobalCompDensity >() ), m_localMatrix( localMatrix ), diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/PerforationFluxKernels.hpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/PerforationFluxKernels.hpp index 18a48ee1e63..1d37e5acb04 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/PerforationFluxKernels.hpp +++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/PerforationFluxKernels.hpp @@ -332,7 +332,6 @@ class PerforationFluxKernel // increment component fluxes for( integer ic = 0; ic < NC; ++ic ) { - // Note this needs to be uncommented out m_compPerfRate[iperf][ic] += flux * m_resPhaseCompFrac[er][esr][ei][0][ip][ic]; dCompFrac[CP_Deriv::dP] = m_dResPhaseCompFrac[er][esr][ei][0][ip][ic][Deriv::dP]; if constexpr (IS_THERMAL) @@ -353,10 +352,6 @@ class PerforationFluxKernel m_dCompPerfRate[iperf][TAG::WELL][ic][jc] += dFlux[TAG::WELL][jc] * m_resPhaseCompFrac[er][esr][ei][0][ip][ic]; } } - if constexpr ( IS_THERMAL ) - { - fluxKernelOp( iwelem, er, esr, ei, ip, potDiff, flux, dFlux ); - } } // end resevoir is upstream phase loop @@ -480,11 +475,12 @@ class PerforationFluxKernel m_dCompPerfRate[iperf][TAG::WELL][ic][CP_Deriv::dC+jc] += m_dWellElemCompFrac_dCompDens[iwelem][ic][jc] * flux; } } - if constexpr ( IS_THERMAL ) - { - fluxKernelOp( iwelem, er, esr, ei, -1, potDiff, flux, dFlux ); - } + } // end upstream + if constexpr ( IS_THERMAL ) + { + fluxKernelOp( iwelem, er, esr, ei, potDiff, dPotDiff, flux, dFlux ); + } } /** * @brief Performs the kernel launch @@ -569,7 +565,7 @@ class PerforationFluxKernelFactory string const flowSolverName, PerforationData * const perforationData, ElementSubRegionBase const & subRegion, - ElementRegionManager & elemManager, + ElementRegionManager const & elemManager, bool const isInjector, bool const isCrossflowEnabled ) { @@ -607,9 +603,21 @@ class PerforationFluxKernel : public isothermalPerforationFluxKernels::Perforati public: using Base = isothermalPerforationFluxKernels::PerforationFluxKernel< NC, NP, IS_THERMAL >; - using Base::m_resPhaseCompFrac; - using Base::m_dResCompFrac_dCompDens; using Base::m_dWellElemCompFrac_dCompDens; + using Base::m_resPres; + using Base::m_resPhaseVolFrac; + using Base::m_dResPhaseVolFrac; + using Base::m_dResCompFrac_dCompDens; + using Base::m_resPhaseDens; + using Base::m_dResPhaseDens; + using Base::m_resPhaseVisc; + using Base::m_dResPhaseVisc; + using Base::m_resPhaseCompFrac; + using Base::m_dResPhaseCompFrac; + using Base::m_resPhaseRelPerm; + using Base::m_dResPhaseRelPerm_dPhaseVolFrac; + using Base::m_isInjector; + using Base::m_isCrossflowEnabled; /// Compile time value for the number of components static constexpr integer numComp = NC; @@ -631,6 +639,8 @@ class PerforationFluxKernel : public isothermalPerforationFluxKernels::Perforati using ThermalMultiFluidAccessors = StencilMaterialAccessors< MultiFluidBase, + fields::multifluid::phaseFraction, + fields::multifluid::dPhaseFraction, fields::multifluid::phaseEnthalpy, fields::multifluid::dPhaseEnthalpy >; @@ -645,7 +655,7 @@ class PerforationFluxKernel : public isothermalPerforationFluxKernels::Perforati using ElementViewConst = ElementRegionManager::ElementViewConst< VIEWTYPE >; PerforationFluxKernel ( PerforationData * const perforationData, - ElementSubRegionBase const & subRegion, + ElementSubRegionBase & subRegion, MultiFluidBase const & wellFluid, CompFlowAccessors const & compFlowAccessors, MultiFluidAccessors const & multiFluidAccessors, @@ -668,6 +678,8 @@ class PerforationFluxKernel : public isothermalPerforationFluxKernels::Perforati m_energyPerfFlux( perforationData->getField< fields::well::energyPerforationFlux >() ), m_dEnergyPerfFlux( perforationData->getField< fields::well::dEnergyPerforationFlux >() ), m_temp( thermalCompFlowAccessors.get( fields::flow::temperature {} ) ), + m_resPhaseFraction( thermalMultiFluidAccessors.get( fields::multifluid::phaseFraction {} ) ), + m_dResPhaseFraction( thermalMultiFluidAccessors.get( fields::multifluid::dPhaseFraction {} )), m_resPhaseEnthalpy( thermalMultiFluidAccessors.get( fields::multifluid::phaseEnthalpy {} ) ), m_dResPhaseEnthalpy( thermalMultiFluidAccessors.get( fields::multifluid::dPhaseEnthalpy {} ) ) {} @@ -690,38 +702,121 @@ class PerforationFluxKernel : public isothermalPerforationFluxKernels::Perforati } } - Base::computeFlux ( iperf, [&]( localIndex const iwelem, localIndex const er, localIndex const esr, localIndex const ei, localIndex const ip, - real64 const potDiff, real64 const flux, real64 const (&dFlux)[2][CP_Deriv::nDer] ) + Base::computeFlux ( iperf, [&]( localIndex const iwelem, localIndex const er, localIndex const esr, localIndex const ei + , real64 const potDiff, real64 const dPotDiff[2][CP_Deriv::nDer], + real64 const flux, real64 const (&dFlux)[2][CP_Deriv::nDer] ) { + real64 dMob[CP_Deriv::nDer]{}; + GEOS_UNUSED_VAR( iwelem ); if( potDiff >= 0 ) // ** reservoir cell is upstream ** { + real64 eflux = 0.0; + // loop over phases, compute and upwind phase flux + // and sum contributions to each component's perforation rate + for( integer ip = 0; ip < NP; ++ip ) + { + // skip the rest of the calculation if the phase is absent + // or if crossflow is disabled for injectors + real64 const resPhaseVolFrac = m_resPhaseVolFrac[er][esr][ei][ip]; + bool const phaseExists = (resPhaseVolFrac > 0 ); + if( !phaseExists || (m_isInjector && !m_isCrossflowEnabled) ) + { + continue; + } + + // here, we have to recompute the reservoir phase mobility (not including density) + + // density + real64 const resDens = m_resPhaseDens[er][esr][ei][0][ip]; + real64 dDens[CP_Deriv::nDer]{}; + dDens[CP_Deriv::dP] = m_dResPhaseDens[er][esr][ei][0][ip][Deriv::dP]; + dDens[CP_Deriv::dT] = m_dResPhaseDens[er][esr][ei][0][ip][Deriv::dT]; + applyChainRule( NC, m_dResCompFrac_dCompDens[er][esr][ei], + m_dResPhaseDens[er][esr][ei][0][ip], + &dDens[CP_Deriv::dC], + Deriv::dC ); + // viscosity + real64 const resVisc = m_resPhaseVisc[er][esr][ei][0][ip]; + real64 dVisc[CP_Deriv::nDer]{}; + dVisc[CP_Deriv::dP] = m_dResPhaseVisc[er][esr][ei][0][ip][Deriv::dP]; + dVisc[CP_Deriv::dT] = m_dResPhaseVisc[er][esr][ei][0][ip][Deriv::dT]; + applyChainRule( NC, m_dResCompFrac_dCompDens[er][esr][ei], + m_dResPhaseVisc[er][esr][ei][0][ip], + &dVisc[CP_Deriv::dC], + Deriv::dC ); + // enthalpy + real64 const resEnthalpy = m_resPhaseEnthalpy[er][esr][ei][0][ip]; + real64 dResEnthalpy[CP_Deriv::nDer]{}; + dResEnthalpy[CP_Deriv::dP] = m_dResPhaseEnthalpy[er][esr][ei][0][ip][Deriv::dP]; + dResEnthalpy[CP_Deriv::dT] = m_dResPhaseEnthalpy[er][esr][ei][0][ip][Deriv::dT]; + + applyChainRule( NC, m_dResCompFrac_dCompDens[er][esr][ei], + m_dResPhaseEnthalpy[er][esr][ei][0][ip], + &dResEnthalpy[CP_Deriv::dC], + Deriv::dC ); - real64 const res_enthalpy = m_resPhaseEnthalpy[er][esr][ei][0][ip]; + // relative permeability + real64 const resRelPerm = m_resPhaseRelPerm[er][esr][ei][0][ip]; + real64 dRelPerm[CP_Deriv::nDer]{}; + for( integer jc = 0; jc < CP_Deriv::nDer; ++jc ) + { + dRelPerm[jc]=0; + } + for( integer jp = 0; jp < NP; ++jp ) + { + real64 const dResRelPerm_dS = m_dResPhaseRelPerm_dPhaseVolFrac[er][esr][ei][0][ip][jp]; + dRelPerm[CP_Deriv::dP] += dResRelPerm_dS * m_dResPhaseVolFrac[er][esr][ei][jp][Deriv::dP]; + dRelPerm[CP_Deriv::dT] += dResRelPerm_dS * m_dResPhaseVolFrac[er][esr][ei][jp][Deriv::dT]; + for( integer jc = 0; jc < NC; ++jc ) + { + dRelPerm[CP_Deriv::dC+jc] += dResRelPerm_dS * m_dResPhaseVolFrac[er][esr][ei][jp][Deriv::dC+jc]; + } + } - m_energyPerfFlux[iperf] += flux * res_enthalpy; + // compute the reservoir phase mobility, including phase density and enthalpy + real64 const resPhaseMob = resDens * resRelPerm / resVisc; + real64 const resPhaseMobE = resEnthalpy * resPhaseMob; - // energy equation derivatives WRT res P & T - m_dEnergyPerfFlux[iperf][TAG::RES][CP_Deriv::dP] += dFlux[TAG::RES][CP_Deriv::dP] * res_enthalpy + - flux * m_dResPhaseEnthalpy[er][esr][ei][0][ip][Deriv::dP]; - m_dEnergyPerfFlux[iperf][TAG::RES][CP_Deriv::dT] += dFlux[TAG::RES][CP_Deriv::dT] * res_enthalpy + - flux * m_dResPhaseEnthalpy[er][esr][ei][0][ip][Deriv::dT]; - // energy equation derivatives WRT well P - m_dEnergyPerfFlux[iperf][TAG::WELL][CP_Deriv::dP] += dFlux[TAG::WELL][CP_Deriv::dP] * res_enthalpy; - m_dEnergyPerfFlux[iperf][TAG::WELL][CP_Deriv::dT] += dFlux[TAG::WELL][CP_Deriv::dT] * res_enthalpy; + // Handles all dependencies + for( integer jc = 0; jc < CP_Deriv::nDer; ++jc ) + { + dMob[jc] = resPhaseMob*dResEnthalpy[jc] + + resPhaseMobE*(dRelPerm[jc]/(resRelPerm+0.000000001) + dDens[jc] / (resDens +0.000000001) - dVisc[jc]/(resVisc+0.000000001)); + } + // compute the phase flux and derivatives using upstream cell mobility + eflux = resPhaseMobE * potDiff; + real64 dEFlux[2][CP_Deriv::nDer]{}; + // Handles all dependencies + for( integer jc = 0; jc < CP_Deriv::nDer; ++jc ) + { + dEFlux[TAG::RES][jc] = dMob[jc] * potDiff + resPhaseMobE * dPotDiff[TAG::RES][jc]; + m_dEnergyPerfFlux[iperf][TAG::WELL][jc] = resPhaseMobE * dPotDiff[TAG::WELL][jc]; + } + m_energyPerfFlux[iperf] += resPhaseVolFrac * eflux; + // energy equation derivatives WRT res P & T + m_dEnergyPerfFlux[iperf][TAG::RES][CP_Deriv::dP] += dEFlux[TAG::RES][CP_Deriv::dP] * resPhaseVolFrac + + eflux * m_dResPhaseFraction[er][esr][ei][0][ip][Deriv::dP]; + m_dEnergyPerfFlux[iperf][TAG::RES][CP_Deriv::dT] += dEFlux[TAG::RES][CP_Deriv::dT] * resPhaseVolFrac + + eflux *m_dResPhaseFraction[er][esr][ei][0][ip][Deriv::dT]; + // energy equation derivatives WRT well P - //tjb + m_dEnergyPerfFlux[iperf][TAG::WELL][CP_Deriv::dP] += dEFlux[TAG::WELL][CP_Deriv::dP]; + m_dEnergyPerfFlux[iperf][TAG::WELL][CP_Deriv::dT] += dEFlux[TAG::WELL][CP_Deriv::dT]; + real64 dProp_dC[numComp]{}; + applyChainRule( NC, + m_dResCompFrac_dCompDens[er][esr][ei], + m_dResPhaseFraction[er][esr][ei][0][ip], + dProp_dC, + Deriv::dC ); + for( integer jc = 0; jc < NC; ++jc ) + { + real64 const resPhaseCompFrac = m_resPhaseCompFrac[er][esr][ei][0][ip][jc]; + m_dEnergyPerfFlux[iperf][TAG::RES][CP_Deriv::dC+jc] += eflux * dProp_dC[jc] + + resPhaseCompFrac * dFlux[TAG::RES][CP_Deriv::dC+jc]; + } - // energy equation derivatives WRT reservoir dens - real64 dProp_dC[numComp]{}; - applyChainRule( NC, - m_dResCompFrac_dCompDens[er][esr][ei], - m_dResPhaseEnthalpy[er][esr][ei][0][ip], - dProp_dC, - Deriv::dC ); + } // end resevoir is upstream phase loop - for( integer jc = 0; jc < NC; ++jc ) - { - m_dEnergyPerfFlux[iperf][TAG::RES][CP_Deriv::dC+jc] += flux * dProp_dC[jc]; - } } else // ** reservoir cell is downstream { @@ -814,6 +909,8 @@ class PerforationFluxKernel : public isothermalPerforationFluxKernels::Perforati ElementViewConst< arrayView1d< real64 const > > const m_temp; /// Views on phase enthalpies + ElementViewConst< arrayView3d< real64 const, multifluid::USD_PHASE > > const m_resPhaseFraction; + ElementViewConst< arrayView4d< real64 const, multifluid::USD_PHASE_DC > > const m_dResPhaseFraction; ElementViewConst< arrayView3d< real64 const, multifluid::USD_PHASE > > const m_resPhaseEnthalpy; ElementViewConst< arrayView4d< real64 const, multifluid::USD_PHASE_DC > > const m_dResPhaseEnthalpy; @@ -846,9 +943,9 @@ class PerforationFluxKernelFactory integer const numPhases, string const flowSolverName, PerforationData * const perforationData, - ElementSubRegionBase const & subRegion, + ElementSubRegionBase & subRegion, MultiFluidBase const & fluid, - ElementRegionManager & elemManager, + ElementRegionManager const & elemManager, bool const isInjector, bool const isCrossflowEnabled ) { diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/SinglePhaseWellConstraintKernels.hpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/SinglePhaseWellConstraintKernels.hpp new file mode 100644 index 00000000000..d779d44d791 --- /dev/null +++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/SinglePhaseWellConstraintKernels.hpp @@ -0,0 +1,195 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * SPDX-License-Identifier: LGPL-2.1-only + * + * Copyright (c) 2016-2024 Lawrence Livermore National Security LLC + * Copyright (c) 2018-2024 TotalEnergies + * Copyright (c) 2018-2024 The Board of Trustees of the Leland Stanford Junior University + * Copyright (c) 2023-2024 Chevron + * Copyright (c) 2019- GEOS/GEOSX Contributors + * All rights reserved + * + * See top level LICENSE, COPYRIGHT, CONTRIBUTORS, NOTICE, and ACKNOWLEDGEMENTS files for details. + * ------------------------------------------------------------------------------------------------------------ + */ + +/** + * @file SinglePhaseWellConstraintKernels.hpp + */ + +#ifndef GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_SINGLEPHASEWELLCONSTRAINTKERNELS_HPP +#define GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_SINGLEPHASEWELLCONSTRAINTKERNELS_HPP + +#include "codingUtilities/Utilities.hpp" +#include "constitutive/fluid/singlefluid/SingleFluidBase.hpp" +#include "constitutive/fluid/singlefluid/SingleFluidFields.hpp" + + +#include "physicsSolvers/fluidFlow/wells/WellControls.hpp" +#include "physicsSolvers/fluidFlow/wells/WellBHPConstraints.hpp" +#include "physicsSolvers/fluidFlow/wells/WellVolumeRateConstraint.hpp" + + +namespace geos +{ + +namespace singlePhaseWellConstraintKernels +{ + +/******************************** ControlEquationHelper ********************************/ + + +template< integer IS_THERMAL > +struct ConstraintHelper +{ + static void assembleConstraintEquation( real64 const & time_n, + WellControls & wellControls, + BHPConstraint & constraint, + WellElementSubRegion const & subRegion, + string const & wellDofKey, + localIndex const & rankOffset, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) + { + // subRegion data + localIndex const iwelemRef = subRegion.getTopWellElementIndex(); + arrayView1d< globalIndex const > const & wellElemDofNumber = subRegion.getReference< array1d< globalIndex > >( wellDofKey ); + arrayView1d< real64 const > const & pres = subRegion.getField< fields::well::pressure >(); + + constitutive::SingleFluidBase & fluidSeparator = wellControls.getSingleFluidSeparator(); + arrayView2d< real64 const, constitutive::singlefluid::USD_FLUID > const & density = fluidSeparator.density(); + arrayView3d< real64 const, constitutive::singlefluid::USD_FLUID_DER > const & dDensity = fluidSeparator.dDensity(); + + arrayView1d< real64 const > const wellElemGravCoef = subRegion.getField< fields::well::gravityCoefficient >(); + + // setup row/column indices for constraint equation + using ROFFSET_WJ = singlePhaseWellKernels::RowOffset_WellJac< IS_THERMAL >; + using COFFSET_WJ = singlePhaseWellKernels::ColOffset_WellJac< IS_THERMAL >; + using Deriv = constitutive::singlefluid::DerivativeOffsetC< IS_THERMAL >; + + localIndex const eqnRowIndex = wellElemDofNumber[iwelemRef] + ROFFSET_WJ::CONTROL - rankOffset; + globalIndex dofColIndices[COFFSET_WJ::nDer]{}; + for( integer i = 0; i < COFFSET_WJ::nDer; ++i ) + { + dofColIndices[ i ] = wellElemDofNumber[iwelemRef] + i; + } + // constraint data + real64 const & targetBHP = constraint.getConstraintValue( time_n ); + real64 const & refGravCoef = constraint.getReferenceGravityCoef(); + + // current constraint value + real64 const & currentBHP = + wellControls.getReference< real64 >( SinglePhaseWell::viewKeyStruct::currentBHPString() ); + + // residual + real64 controlEqn = currentBHP - targetBHP; + + // setup Jacobian terms + real64 dControlEqn[2+IS_THERMAL]{}; + + // bring everything back to host, capture the scalars by reference + forAll< serialPolicy >( 1, [pres, + density, + dDensity, + wellElemGravCoef, + &dControlEqn, + &iwelemRef, + &refGravCoef] ( localIndex const ) + { + real64 const diffGravCoef = refGravCoef - wellElemGravCoef[iwelemRef]; + dControlEqn[COFFSET_WJ::dP] = 1.0 + dDensity[iwelemRef][0][Deriv::dP] *diffGravCoef; + if constexpr ( IS_THERMAL ) + { + dControlEqn[COFFSET_WJ::dT] = dDensity[iwelemRef][0][Deriv::dT] * diffGravCoef; + } + } ); + + // add solver matrices + localRhs[eqnRowIndex] += controlEqn; + localMatrix.addToRowBinarySearchUnsorted< serialAtomic >( eqnRowIndex, + dofColIndices, + dControlEqn, + COFFSET_WJ::nDer ); + } + template< template< typename U > class T, typename U=VolumeRateConstraint > + static void assembleConstraintEquation( real64 const & time_n, + WellControls & wellControls, + T< VolumeRateConstraint > & constraint, + WellElementSubRegion const & subRegion, + string const & wellDofKey, + localIndex const & rankOffset, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) + { + // subRegion data + + localIndex const iwelemRef = subRegion.getTopWellElementIndex(); + arrayView1d< globalIndex const > const & wellElemDofNumber = subRegion.getReference< array1d< globalIndex > >( wellDofKey ); + + + // setup row/column indices for constraint equation + using ROFFSET_WJ = singlePhaseWellKernels::RowOffset_WellJac< IS_THERMAL >; + using COFFSET_WJ = singlePhaseWellKernels::ColOffset_WellJac< IS_THERMAL >; + using Deriv = constitutive::singlefluid::DerivativeOffsetC< IS_THERMAL >; + + localIndex const eqnRowIndex = wellElemDofNumber[iwelemRef] + ROFFSET_WJ::CONTROL - rankOffset; + globalIndex dofColIndices[COFFSET_WJ::nDer]{}; + for( integer i = 0; i < COFFSET_WJ::nDer; ++i ) + { + dofColIndices[ i ] = wellElemDofNumber[iwelemRef] + i; + } + + // fluid data + constitutive::SingleFluidBase & fluidSeparator = wellControls.getSingleFluidSeparator(); + arrayView2d< real64 const, constitutive::singlefluid::USD_FLUID > const & density = fluidSeparator.density(); + arrayView3d< real64 const, constitutive::singlefluid::USD_FLUID_DER > const & dDensity = fluidSeparator.dDensity(); + + // constraint data + real64 const & targetVolRate = constraint.getConstraintValue( time_n ); + + // current constraint value + real64 & currentVolRate = + wellControls.getReference< real64 >( WellControls::viewKeyStruct::currentVolRateString() ); + + integer const useSurfaceConditions = wellControls.useSurfaceConditions(); + + // residual + real64 controlEqn = currentVolRate - targetVolRate; + + // setup Jacobian terms + real64 dControlEqn[2+IS_THERMAL]{}; + + // bring everything back to host, capture the scalars by reference + forAll< serialPolicy >( 1, [currentVolRate, + density, + dDensity, + &dControlEqn, + &useSurfaceConditions, + &iwelemRef] ( localIndex const ) + { + // compute the inverse of the total density and derivatives + real64 const densInv = 1.0 / density[iwelemRef][0]; + + dControlEqn[COFFSET_WJ::dP] = -( useSurfaceConditions == 0 ) * dDensity[iwelemRef][0][Deriv::dP] * currentVolRate * densInv; + dControlEqn[COFFSET_WJ::dQ] = densInv; + if constexpr ( IS_THERMAL ) + { + dControlEqn[COFFSET_WJ::dT] = -( useSurfaceConditions == 0 ) * dDensity[iwelemRef][0][Deriv::dT] * currentVolRate * densInv; + } + + } ); + + // add solver matrices + localRhs[eqnRowIndex] += controlEqn; + localMatrix.addToRowBinarySearchUnsorted< serialAtomic >( eqnRowIndex, + dofColIndices, + dControlEqn, + COFFSET_WJ::nDer ); + } +}; + +} // end namespace wellConstraintKernels + +} // end namespace geos + +#endif //GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_WELLCONSTRAINTKERNELS_HPP diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/SinglePhaseWellKernels.cpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/SinglePhaseWellKernels.cpp index ddd707cf9b1..35bb78a54c4 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/SinglePhaseWellKernels.cpp +++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/SinglePhaseWellKernels.cpp @@ -23,6 +23,7 @@ #include "physicsSolvers/fluidFlow/wells/SinglePhaseWell.hpp" #include "physicsSolvers/fluidFlow/wells/SinglePhaseWellFields.hpp" #include "constitutive/fluid/singlefluid/SingleFluidLayouts.hpp" + namespace geos { @@ -262,14 +263,10 @@ FluxKernel:: #define INST_PressureRelationKernel( IS_THERMAL ) \ template \ - localIndex \ + void \ PressureRelationKernel:: \ launch< IS_THERMAL >( localIndex const size, \ globalIndex const rankOffset, \ - bool const isLocallyOwned, \ - localIndex const iwelemControl, \ - WellControls const & wellControls, \ - real64 const & timeAtEndOfStep, \ arrayView1d< globalIndex const > const & wellElemDofNumber, \ arrayView1d< real64 const > const & wellElemGravCoef, \ arrayView1d< localIndex const > const & nextWellElemIndex, \ @@ -283,14 +280,10 @@ INST_PressureRelationKernel( 0 ); INST_PressureRelationKernel( 1 ); template< integer IS_THERMAL > -localIndex +void PressureRelationKernel:: launch( localIndex const size, globalIndex const rankOffset, - bool const isLocallyOwned, - localIndex const iwelemControl, - WellControls const & wellControls, - real64 const & time, arrayView1d< globalIndex const > const & wellElemDofNumber, arrayView1d< real64 const > const & wellElemGravCoef, arrayView1d< localIndex const > const & nextWellElemIndex, @@ -303,23 +296,6 @@ PressureRelationKernel:: using Deriv = constitutive::singlefluid::DerivativeOffset; using COFFSET_WJ = singlePhaseWellKernels::ColOffset_WellJac< IS_THERMAL >; // static well control data - bool const isProducer = wellControls.isProducer(); - WellControls::Control const currentControl = wellControls.getControl(); - real64 const targetBHP = wellControls.getTargetBHP( time ); - real64 const targetRate = wellControls.getTargetTotalRate( time ); - - // dynamic well control data - real64 const & currentBHP = - wellControls.getReference< real64 >( SinglePhaseWell::viewKeyStruct::currentBHPString() ); - arrayView1d< real64 const > const & dCurrentBHP = - wellControls.getReference< array1d< real64 > >( SinglePhaseWell::viewKeyStruct::dCurrentBHPString() ); - - real64 const & currentVolRate = - wellControls.getReference< real64 >( SinglePhaseWell::viewKeyStruct::currentVolRateString() ); - arrayView1d< real64 const > const & dCurrentVolRate = - wellControls.getReference< array1d< real64 > >( SinglePhaseWell::viewKeyStruct::dCurrentVolRateString() ); - - RAJA::ReduceMax< parallelDeviceReduce, localIndex > switchControl( 0 ); // loop over the well elements to compute the pressure relations between well elements forAll< parallelDevicePolicy<> >( size, [=] GEOS_HOST_DEVICE ( localIndex const iwelem ) @@ -327,34 +303,7 @@ PressureRelationKernel:: localIndex const iwelemNext = nextWellElemIndex[iwelem]; - if( iwelemNext < 0 && isLocallyOwned ) // if iwelemNext < 0, form control equation - { - WellControls::Control newControl = currentControl; - ControlEquationHelper::switchControl( isProducer, - currentControl, - targetBHP, - targetRate, - currentBHP, - currentVolRate, - newControl ); - if( currentControl != newControl ) - { - switchControl.max( 1 ); - } - - ControlEquationHelper::compute< IS_THERMAL >( rankOffset, - newControl, - targetBHP, - targetRate, - currentBHP, - dCurrentBHP, - currentVolRate, - dCurrentVolRate, - wellElemDofNumber[iwelemControl], - localMatrix, - localRhs ); - } - else if( iwelemNext >= 0 ) // if iwelemNext >= 0, form momentum equation + if( iwelemNext >= 0 ) // if iwelemNext >= 0, form momentum equation { // local working variables and arrays @@ -406,7 +355,6 @@ PressureRelationKernel:: } } } ); - return switchControl.get(); } /******************************** AccumulationKernel ********************************/ @@ -599,10 +547,22 @@ RateInitializationKernel:: arrayView2d< real64 const, constitutive::singlefluid::USD_FLUID > const & wellElemDens, arrayView1d< real64 > const & connRate ) { - real64 const targetRate = wellControls.getTargetTotalRate( currentTime ); + WellControls::Control const control = wellControls.getControl(); bool const isProducer = wellControls.isProducer(); - + real64 constraintVal; + if( isProducer ) + { + std::vector< WellConstraintBase * > const constraints = wellControls.getProdRateConstraints(); + // Use first rate constraint to set initial connection rates + constraintVal = constraints[0]->getConstraintValue( currentTime ); + } + else + { + std::vector< WellConstraintBase * > const constraints = wellControls.getInjRateConstraints(); + // Use first rate constraint to set initial connection rates + constraintVal = constraints[0]->getConstraintValue( currentTime );; + } // Estimate the connection rates forAll< parallelDevicePolicy<> >( subRegionSize, [=] GEOS_HOST_DEVICE ( localIndex const iwelem ) { @@ -612,16 +572,16 @@ RateInitializationKernel:: // with the appropriate sign (negative for prod, positive for inj) if( isProducer ) { - connRate[iwelem] = LvArray::math::max( 0.1 * targetRate * wellElemDens[iwelem][0], -1e3 ); + connRate[iwelem] = LvArray::math::max( 0.1 * constraintVal * wellElemDens[iwelem][0], -1e3 ); } else { - connRate[iwelem] = LvArray::math::min( 0.1 * targetRate * wellElemDens[iwelem][0], 1e3 ); + connRate[iwelem] = LvArray::math::min( 0.1 * constraintVal * wellElemDens[iwelem][0], 1e3 ); } } else { - connRate[iwelem] = targetRate * wellElemDens[iwelem][0]; + connRate[iwelem] = constraintVal * wellElemDens[iwelem][0]; } } ); } diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/SinglePhaseWellKernels.hpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/SinglePhaseWellKernels.hpp index 5221162c73c..8365d25c946 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/SinglePhaseWellKernels.hpp +++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/SinglePhaseWellKernels.hpp @@ -20,18 +20,22 @@ #ifndef GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_SINGLEPHASEWELLKERNELS_HPP #define GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_SINGLEPHASEWELLKERNELS_HPP +#include "common/DataTypes.hpp" +#include "common/GEOS_RAJA_Interface.hpp" +#include "physicsSolvers/PhysicsSolverBaseKernels.hpp" + #include "constitutive/fluid/singlefluid/SingleFluidFields.hpp" #include "constitutive/fluid/singlefluid/SingleFluidBase.hpp" #include "constitutive/fluid/singlefluid/SingleFluidLayouts.hpp" #include "constitutive/fluid/singlefluid/SingleFluidLayouts.hpp" -#include "common/DataTypes.hpp" -#include "common/GEOS_RAJA_Interface.hpp" + #include "mesh/ElementRegionManager.hpp" #include "physicsSolvers/fluidFlow/FlowSolverBaseFields.hpp" #include "physicsSolvers/fluidFlow/StencilAccessors.hpp" #include "physicsSolvers/fluidFlow/wells/WellControls.hpp" +#include "physicsSolvers/fluidFlow/wells/WellFields.hpp" #include "physicsSolvers/fluidFlow/wells/SinglePhaseWellFields.hpp" -#include "physicsSolvers/PhysicsSolverBaseKernels.hpp" + #include "physicsSolvers/KernelLaunchSelectors.hpp" namespace geos @@ -185,13 +189,9 @@ struct PressureRelationKernel using TAG = singlePhaseWellKernels::ElemTag; template< integer IS_THERMAL > - static localIndex + static void launch( localIndex const size, globalIndex const rankOffset, - bool const isLocallyOwned, - localIndex const iwelemControl, - WellControls const & wellControls, - real64 const & time, arrayView1d< globalIndex const > const & wellElemDofNumber, arrayView1d< real64 const > const & wellElemGravCoef, arrayView1d< localIndex const > const & nextWellElemIndex, @@ -811,12 +811,19 @@ class ResidualNormKernel : public physicsSolverBaseKernels::ResidualNormKernelBa m_isLocallyOwned( subRegion.isLocallyOwned() ), m_iwelemControl( subRegion.getTopWellElementIndex() ), m_currentControl( wellControls.getControl() ), - m_targetBHP( wellControls.getTargetBHP( time ) ), - m_targetRate( wellControls.getTargetTotalRate( time ) ), - m_targetMassRate( wellControls.getTargetMassRate( time ) ), + m_constraintValue ( wellControls.getCurrentConstraint()->getConstraintValue( time )), m_volume( subRegion.getElementVolume() ), m_density_n( fluid.density_n() ) - {} + { + if( wellControls.isProducer() ) + { + m_targetBHP = wellControls.getMinBHPConstraint()->getConstraintValue( time ); + } + else + { + m_targetBHP = wellControls.getMaxBHPConstraint()->getConstraintValue( time ); + } + } GEOS_HOST_DEVICE virtual void computeLinf( localIndex const iwelem, @@ -838,12 +845,12 @@ class ResidualNormKernel : public physicsSolverBaseKernels::ResidualNormKernelBa else if( m_currentControl == WellControls::Control::TOTALVOLRATE ) { // this residual entry is in volume / time units - normalizer = LvArray::math::max( LvArray::math::abs( m_targetRate ), m_minNormalizer ); + normalizer = LvArray::math::max( LvArray::math::abs( m_constraintValue ), m_minNormalizer ); } else if( m_currentControl == WellControls::Control::MASSRATE ) { // the residual entry is in volume / time units - normalizer = LvArray::math::max( LvArray::math::abs( m_targetMassRate ), m_minNormalizer ); + normalizer = LvArray::math::max( LvArray::math::abs( m_constraintValue ), m_minNormalizer ); } } // for the pressure difference equation, always normalize by the BHP @@ -856,7 +863,7 @@ class ResidualNormKernel : public physicsSolverBaseKernels::ResidualNormKernelBa else // SinglePhaseWell::RowOffset::MASSBAL { // this residual entry is in mass units - normalizer = m_dt * LvArray::math::abs( m_targetRate ) * m_density_n[iwelem][0]; + normalizer = m_dt * LvArray::math::abs( m_constraintValue ) * m_density_n[iwelem][0]; // to make sure that everything still works well if the rate is zero, we add this check normalizer = LvArray::math::max( normalizer, m_volume[iwelem] * m_density_n[iwelem][0] ); @@ -894,9 +901,9 @@ class ResidualNormKernel : public physicsSolverBaseKernels::ResidualNormKernelBa /// Controls WellControls::Control const m_currentControl; - real64 const m_targetBHP; - real64 const m_targetRate; - real64 const m_targetMassRate; + real64 const m_constraintValue; + real64 m_targetBHP; + /// View on the volume arrayView1d< real64 const > const m_volume; diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/ThermalCompositionalMultiphaseWellKernels.hpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/ThermalCompositionalMultiphaseWellKernels.hpp index 54b7819e8bf..a8b5b91032f 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/ThermalCompositionalMultiphaseWellKernels.hpp +++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/ThermalCompositionalMultiphaseWellKernels.hpp @@ -22,6 +22,8 @@ #include "physicsSolvers/fluidFlow/wells/kernels/CompositionalMultiphaseWellKernels.hpp" #include "physicsSolvers/PhysicsSolverBaseKernels.hpp" +#include "physicsSolvers/fluidFlow/wells/WellConstraintsBase.hpp" +#include "physicsSolvers/fluidFlow/wells/WellPhaseVolumeRateConstraint.hpp" namespace geos { @@ -80,7 +82,7 @@ class TotalMassDensityKernel : public compositionalMultiphaseWellKernels::TotalM arraySlice2d< real64 const, multifluid::USD_PHASE_DC - 2 > dPhaseMassDens = m_dPhaseMassDens[ei][0]; real64 & dTotalMassDens_dT = m_dTotalMassDens[ei][Deriv::dT]; - dTotalMassDens_dT=0.0; + // Call the base compute the compute the total mass density and derivatives return Base::compute( ei, [&]( localIndex const ip ) { @@ -161,7 +163,7 @@ class ResidualNormKernel : public physicsSolverBaseKernels::ResidualNormKernelBa arrayView1d< real64 const > const & localResidual, arrayView1d< globalIndex const > const & dofNumber, arrayView1d< localIndex const > const & ghostRank, - integer const targetPhaseIndex, + WellElementSubRegion const & subRegion, MultiFluidBase const & fluid, WellControls const & wellControls, @@ -174,22 +176,39 @@ class ResidualNormKernel : public physicsSolverBaseKernels::ResidualNormKernelBa ghostRank, minNormalizer ), m_numPhases( fluid.numFluidPhases()), - m_targetPhaseIndex( targetPhaseIndex ), m_dt( dt ), m_isLocallyOwned( subRegion.isLocallyOwned() ), m_iwelemControl( subRegion.getTopWellElementIndex() ), m_isProducer( wellControls.isProducer() ), m_currentControl( wellControls.getControl() ), - m_targetBHP( wellControls.getTargetBHP( time ) ), - m_targetTotalRate( wellControls.getTargetTotalRate( time ) ), - m_targetPhaseRate( wellControls.getTargetPhaseRate( time ) ), - m_targetMassRate( wellControls.getTargetMassRate( time ) ), m_volume( subRegion.getElementVolume() ), m_phaseDens_n( fluid.phaseDensity_n() ), m_totalDens_n( fluid.totalDensity_n() ), m_phaseVolFraction_n( subRegion.getField< fields::well::phaseVolumeFraction_n >()), m_phaseInternalEnergy_n( fluid.phaseInternalEnergy_n() ) - {} + { + if( m_isProducer ) + { + // tjb This needs to be fixed should use current constraint rate for normalization + if( wellControls.getMinBHPConstraint()->isConstraintActive() ) + { + m_targetBHP = wellControls.getMinBHPConstraint()->getConstraintValue( time ); + } + if( m_currentControl == WellControls::Control::PHASEVOLRATE ) + { + m_targetPhaseIndex = wellControls.getConstraintPhaseIndex(); + m_constraintValue = wellControls.getProdRateConstraints()[0]->getConstraintValue( time ); + } + } + else + { + m_targetBHP = wellControls.getMaxBHPConstraint()->getConstraintValue( time ); + m_targetPhaseIndex = -1; + m_constraintValue = wellControls.getInjRateConstraints()[0]->getConstraintValue( time ); + + } + + } GEOS_HOST_DEVICE @@ -232,17 +251,17 @@ class ResidualNormKernel : public physicsSolverBaseKernels::ResidualNormKernelBa else if( m_currentControl == WellControls::Control::TOTALVOLRATE ) { // the residual entry is in volume / time units - normalizer = LvArray::math::max( LvArray::math::abs( m_targetTotalRate ), m_minNormalizer ); + normalizer = LvArray::math::max( LvArray::math::abs( m_constraintValue ), m_minNormalizer ); } else if( m_currentControl == WellControls::Control::PHASEVOLRATE ) { // the residual entry is in volume / time units - normalizer = LvArray::math::max( LvArray::math::abs( m_targetPhaseRate ), m_minNormalizer ); + normalizer = LvArray::math::max( LvArray::math::abs( m_constraintValue ), m_minNormalizer ); } else if( m_currentControl == WellControls::Control::MASSRATE ) { // the residual entry is in volume / time units - normalizer = LvArray::math::max( LvArray::math::abs( m_targetMassRate ), m_minNormalizer ); + normalizer = LvArray::math::max( LvArray::math::abs( m_constraintValue ), m_minNormalizer ); } } // for the pressure difference equation, always normalize by the BHP @@ -257,18 +276,18 @@ class ResidualNormKernel : public physicsSolverBaseKernels::ResidualNormKernelBa if( m_isProducer ) // only PHASEVOLRATE is supported for now { // the residual is in mass units - normalizer = m_dt * LvArray::math::abs( m_targetPhaseRate ) * m_phaseDens_n[iwelem][0][m_targetPhaseIndex]; + normalizer = m_dt * LvArray::math::abs( m_constraintValue ) * m_phaseDens_n[iwelem][0][m_targetPhaseIndex]; } else // Type::INJECTOR, only TOTALVOLRATE is supported for now { if( m_currentControl == WellControls::Control::MASSRATE ) { - normalizer = m_dt * LvArray::math::abs( m_targetMassRate ); + normalizer = m_dt * LvArray::math::abs( m_constraintValue ); } else { // the residual is in mass units - normalizer = m_dt * LvArray::math::abs( m_targetTotalRate ) * m_totalDens_n[iwelem][0]; + normalizer = m_dt * LvArray::math::abs( m_constraintValue ) * m_totalDens_n[iwelem][0]; } } @@ -282,17 +301,17 @@ class ResidualNormKernel : public physicsSolverBaseKernels::ResidualNormKernelBa if( m_isProducer ) // only PHASEVOLRATE is supported for now { // the residual is in volume units - normalizer = m_dt * LvArray::math::abs( m_targetPhaseRate ); + normalizer = m_dt * LvArray::math::abs( m_constraintValue ); } else // Type::INJECTOR, only TOTALVOLRATE is supported for now { if( m_currentControl == WellControls::Control::MASSRATE ) { - normalizer = m_dt * LvArray::math::abs( m_targetMassRate/ m_totalDens_n[iwelem][0] ); + normalizer = m_dt * LvArray::math::abs( m_constraintValue/ m_totalDens_n[iwelem][0] ); } else { - normalizer = m_dt * LvArray::math::abs( m_targetTotalRate ); + normalizer = m_dt * LvArray::math::abs( m_constraintValue ); } } @@ -339,7 +358,7 @@ class ResidualNormKernel : public physicsSolverBaseKernels::ResidualNormKernelBa integer const m_numPhases; /// Index of the target phase - integer const m_targetPhaseIndex; + integer m_targetPhaseIndex; /// Time step size real64 const m_dt; @@ -355,10 +374,9 @@ class ResidualNormKernel : public physicsSolverBaseKernels::ResidualNormKernelBa /// Controls WellControls::Control const m_currentControl; - real64 const m_targetBHP; - real64 const m_targetTotalRate; - real64 const m_targetPhaseRate; - real64 const m_targetMassRate; + real64 m_constraintValue; + real64 m_targetBHP; + /// View on the volume arrayView1d< real64 const > const m_volume; @@ -383,7 +401,6 @@ class ResidualNormKernelFactory * @tparam POLICY the policy used in the RAJA kernel * @param[in] numComp number of fluid components * @param[in] numDof number of dofs per well element - * @param[in] targetPhaseIndex the index of the target phase (for phase volume control) * @param[in] rankOffset the offset of my MPI rank * @param[in] dofKey the string key to retrieve the degress of freedom numbers * @param[in] localResidual the residual vector on my MPI rank @@ -397,7 +414,6 @@ class ResidualNormKernelFactory template< typename POLICY > static void createAndLaunch( integer const numComp, - integer const targetPhaseIndex, globalIndex const rankOffset, string const & dofKey, arrayView1d< real64 const > const & localResidual, @@ -418,7 +434,7 @@ class ResidualNormKernelFactory arrayView1d< integer const > const ghostRank = subRegion.ghostRank(); kernelType kernel( rankOffset, localResidual, dofNumber, ghostRank, - targetPhaseIndex, subRegion, fluid, wellControls, time, dt, minNormalizer ); + subRegion, fluid, wellControls, time, dt, minNormalizer ); kernelType::template launchLinf< POLICY >( subRegion.size(), kernel, residualNorm ); } ); } @@ -476,6 +492,7 @@ class ElementBasedAssemblyKernel : public compositionalMultiphaseWellKernels::El * @param[inout] localRhs the local right-hand side vector */ ElementBasedAssemblyKernel( localIndex const numPhases, + bool const thermalEffectsEnabled, integer const isProducer, globalIndex const rankOffset, string const dofKey, @@ -484,7 +501,7 @@ class ElementBasedAssemblyKernel : public compositionalMultiphaseWellKernels::El CRSMatrixView< real64, globalIndex const > const & localMatrix, arrayView1d< real64 > const & localRhs, BitFlags< isothermalCompositionalMultiphaseBaseKernels::KernelFlags > const kernelFlags ) - : Base( numPhases, isProducer, rankOffset, dofKey, subRegion, fluid, localMatrix, localRhs, kernelFlags ), + : Base( numPhases, thermalEffectsEnabled, isProducer, rankOffset, dofKey, subRegion, fluid, localMatrix, localRhs, kernelFlags ), m_phaseInternalEnergy_n( fluid.phaseInternalEnergy_n()), m_phaseInternalEnergy( fluid.phaseInternalEnergy()), m_dPhaseInternalEnergy( fluid.dPhaseInternalEnergy()) @@ -636,6 +653,7 @@ class ElementBasedAssemblyKernelFactory static void createAndLaunch( localIndex const numComps, localIndex const numPhases, + integer const thermalEffectsEnabled, integer const isProducer, globalIndex const rankOffset, BitFlags< isothermalCompositionalMultiphaseBaseKernels::KernelFlags > kernelFlags, @@ -645,13 +663,12 @@ class ElementBasedAssemblyKernelFactory CRSMatrixView< real64, globalIndex const > const & localMatrix, arrayView1d< real64 > const & localRhs ) { - isothermalCompositionalMultiphaseBaseKernels:: - internal::kernelLaunchSelectorCompSwitch( numComps, [&]( auto NC ) + isothermalCompositionalMultiphaseBaseKernels::internal::kernelLaunchSelectorCompSwitch( numComps, [&]( auto NC ) { localIndex constexpr NUM_COMP = NC(); ElementBasedAssemblyKernel< NUM_COMP > - kernel( numPhases, isProducer, rankOffset, dofKey, subRegion, fluid, localMatrix, localRhs, kernelFlags ); + kernel( numPhases, thermalEffectsEnabled, isProducer, rankOffset, dofKey, subRegion, fluid, localMatrix, localRhs, kernelFlags ); ElementBasedAssemblyKernel< NUM_COMP >::template launch< POLICY, ElementBasedAssemblyKernel< NUM_COMP > >( subRegion.size(), kernel ); } ); @@ -731,6 +748,7 @@ class FaceBasedAssemblyKernel : public compositionalMultiphaseWellKernels::FaceB , localRhs , kernelFlags ), m_numPhases ( fluid.numFluidPhases()), + m_thermalEffectsEnabled( wellControls.thermalEffectsEnabled() ), m_globalWellElementIndex( subRegion.getGlobalWellElementIndex() ), m_phaseFraction( fluid.phaseFraction()), m_dPhaseFraction( fluid.dPhaseFraction()), @@ -777,7 +795,8 @@ class FaceBasedAssemblyKernel : public compositionalMultiphaseWellKernels::FaceB void complete( localIndex const iwelem, StackVariables & stack ) const { Base::complete ( iwelem, stack ); - + // tjb iso return; + if( !m_thermalEffectsEnabled ) return; using namespace compositionalMultiphaseUtilities; if( stack.numConnectedElems ==1 ) { @@ -939,7 +958,7 @@ class FaceBasedAssemblyKernel : public compositionalMultiphaseWellKernels::FaceB stack.localEnergyFlux[0] = -m_dt * eflux * currentConnRate; stack.localEnergyFluxJacobian_dQ[0][0] = -m_dt * eflux_dq; } - else if( ( iwelemNext < 0 && m_isProducer ) || currentConnRate < 0 ) // exit connection, producer + else if( ( iwelemNext < 0 && m_isProducer ) ) // exit connection, producer { real64 eflux=0; real64 eflux_dq=0; @@ -966,9 +985,9 @@ class FaceBasedAssemblyKernel : public compositionalMultiphaseWellKernels::FaceB for( integer dof=0; dof < CP_Deriv::nDer; dof++ ) { - stack.localEnergyFluxJacobian[0][dof] *= -m_dt*currentConnRate; + stack.localEnergyFluxJacobian[0][dof] *= -m_dt*currentConnRate; } - stack.localEnergyFlux[0] = -m_dt * eflux * currentConnRate; + stack.localEnergyFlux[0] = -m_dt * eflux * currentConnRate; stack.localEnergyFluxJacobian_dQ[0][0] = -m_dt*eflux_dq; } else @@ -1047,6 +1066,8 @@ class FaceBasedAssemblyKernel : public compositionalMultiphaseWellKernels::FaceB protected: /// Number of phases integer const m_numPhases; + /// Flag specifying whether thermal effects are enabled + bool const m_thermalEffectsEnabled; /// Global index of local element arrayView1d< globalIndex const > m_globalWellElementIndex; diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/ThermalSinglePhaseWellKernels.hpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/ThermalSinglePhaseWellKernels.hpp index f736826561c..b19ca90845d 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/ThermalSinglePhaseWellKernels.hpp +++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/ThermalSinglePhaseWellKernels.hpp @@ -522,14 +522,21 @@ class ResidualNormKernel : public physicsSolverBaseKernels::ResidualNormKernelBa m_iwelemControl( subRegion.getTopWellElementIndex() ), m_isProducer( wellControls.isProducer() ), m_currentControl( wellControls.getControl() ), - m_targetBHP( wellControls.getTargetBHP( time ) ), - m_targetRate( wellControls.getTargetTotalRate( time ) ), - m_targetMassRate( wellControls.getTargetMassRate( time ) ), + m_constraintValue ( wellControls.getCurrentConstraint()->getConstraintValue( time )), m_volume( subRegion.getElementVolume() ), m_density_n( fluid.density_n() ), m_internalEnergy_n( fluid.internalEnergy_n() ) - {} + { + if( wellControls.isProducer() ) + { + m_targetBHP = wellControls.getMinBHPConstraint()->getConstraintValue( time ); + } + else + { + m_targetBHP = wellControls.getMaxBHPConstraint()->getConstraintValue( time ); + } + } GEOS_HOST_DEVICE @@ -568,12 +575,12 @@ class ResidualNormKernel : public physicsSolverBaseKernels::ResidualNormKernelBa else if( m_currentControl == WellControls::Control::TOTALVOLRATE ) { // the residual entry is in volume / time units - normalizer = LvArray::math::max( LvArray::math::abs( m_targetRate ), m_minNormalizer ); + normalizer = LvArray::math::max( LvArray::math::abs( m_constraintValue ), m_minNormalizer ); } else if( m_currentControl == WellControls::Control::MASSRATE ) { // the residual entry is in volume / time units - normalizer = LvArray::math::max( LvArray::math::abs( m_targetMassRate ), m_minNormalizer ); + normalizer = LvArray::math::max( LvArray::math::abs( m_constraintValue ), m_minNormalizer ); } } // for the pressure difference equation, always normalize by the BHP @@ -587,7 +594,7 @@ class ResidualNormKernel : public physicsSolverBaseKernels::ResidualNormKernelBa { // this residual entry is in mass units - normalizer = m_dt * LvArray::math::abs( m_targetRate ) * m_density_n[iwelem][0]; + normalizer = m_dt * LvArray::math::abs( m_constraintValue ) * m_density_n[iwelem][0]; // to make sure that everything still works well if the rate is zero, we add this check normalizer = LvArray::math::max( normalizer, m_volume[iwelem] * m_density_n[iwelem][0] ); @@ -640,9 +647,8 @@ class ResidualNormKernel : public physicsSolverBaseKernels::ResidualNormKernelBa /// Controls WellControls::Control const m_currentControl; - real64 const m_targetBHP; - real64 const m_targetRate; - real64 const m_targetMassRate; + real64 const m_constraintValue; + real64 m_targetBHP; /// View on the volume arrayView1d< real64 const > const m_volume; diff --git a/src/coreComponents/physicsSolvers/multiphysics/CompositionalMultiphaseReservoirAndWells.cpp b/src/coreComponents/physicsSolvers/multiphysics/CompositionalMultiphaseReservoirAndWells.cpp index 425bccfa72c..ab99ec2ca8a 100644 --- a/src/coreComponents/physicsSolvers/multiphysics/CompositionalMultiphaseReservoirAndWells.cpp +++ b/src/coreComponents/physicsSolvers/multiphysics/CompositionalMultiphaseReservoirAndWells.cpp @@ -147,9 +147,8 @@ initializePreSubGroups() CompositionalMultiphaseBase const * const flowSolver = this->flowSolver(); Base::wellSolver()->setFlowSolverName( flowSolver->getName() ); - bool const useMassFlow = flowSolver->getReference< integer >( CompositionalMultiphaseBase::viewKeyStruct::useMassFlagString() ); - bool const useMassWell = Base::wellSolver()->template getReference< integer >( CompositionalMultiphaseWell::viewKeyStruct::useMassFlagString() ); + bool const useMassWell = Base::wellSolver()->template getReference< integer >( WellManager::viewKeyStruct::useMassFlagString() ); GEOS_THROW_IF( useMassFlow != useMassWell, GEOS_FMT( "{}: the input flag {} must be the same in the flow and well solvers, respectively '{}' and '{}'", this->getDataContext(), CompositionalMultiphaseBase::viewKeyStruct::useMassFlagString(), @@ -157,12 +156,27 @@ initializePreSubGroups() InputError, this->getDataContext(), Base::reservoirSolver()->getDataContext(), Base::wellSolver()->getDataContext() ); bool const isThermalFlow = flowSolver->getReference< integer >( CompositionalMultiphaseBase::viewKeyStruct::isThermalString() ); - bool const isThermalWell = Base::wellSolver()->template getReference< integer >( CompositionalMultiphaseWell::viewKeyStruct::isThermalString() ); + bool const isThermalWell = Base::wellSolver()->template getReference< integer >( WellManager::viewKeyStruct::isThermalString() ); GEOS_THROW_IF( isThermalFlow != isThermalWell, GEOS_FMT( "{}: the input flag {} must be the same in the flow and well solvers, respectively '{}' and '{}'", this->getDataContext(), CompositionalMultiphaseBase::viewKeyStruct::isThermalString(), Base::reservoirSolver()->getDataContext(), Base::wellSolver()->getDataContext() ), InputError, this->getDataContext(), Base::reservoirSolver()->getDataContext(), Base::wellSolver()->getDataContext() ); + DomainPartition & domain = this->template getGroupByPath< DomainPartition >( "/Problem/domain" ); + + this->template forDiscretizationOnMeshTargets<>( domain.getMeshBodies(), [&] ( string const &, + MeshLevel & mesh, + string_array const & regionNames ) + { + ElementRegionManager & elemManager = mesh.getElemManager(); + elemManager.forElementSubRegions< WellElementSubRegion >( regionNames, [&]( localIndex const, + WellElementSubRegion const & subRegion ) + { + WellControls & wellControls = Base::wellSolver()->getWellControls( subRegion ); + wellControls.setFlowSolverName( flowSolver->getName() ); + + } ); + } ); } template< typename RESERVOIR_SOLVER > @@ -344,6 +358,7 @@ assembleCouplingTerms( real64 const time_n, coupledReservoirAndWellKernels:: ThermalCompositionalMultiPhaseFluxKernelFactory:: createAndLaunch< parallelDevicePolicy<> >( numComps, + wellControls.thermalEffectsEnabled( ), wellControls.isProducer(), dt, rankOffset, diff --git a/src/coreComponents/physicsSolvers/multiphysics/CompositionalMultiphaseReservoirAndWells.hpp b/src/coreComponents/physicsSolvers/multiphysics/CompositionalMultiphaseReservoirAndWells.hpp index b2b1f7a4800..b99975f2ba4 100644 --- a/src/coreComponents/physicsSolvers/multiphysics/CompositionalMultiphaseReservoirAndWells.hpp +++ b/src/coreComponents/physicsSolvers/multiphysics/CompositionalMultiphaseReservoirAndWells.hpp @@ -23,7 +23,7 @@ #include "physicsSolvers/multiphysics/CoupledReservoirAndWellsBase.hpp" #include "physicsSolvers/fluidFlow/CompositionalMultiphaseBase.hpp" -#include "physicsSolvers/fluidFlow/wells/CompositionalMultiphaseWell.hpp" +#include "physicsSolvers/fluidFlow/wells/WellManager.hpp" namespace geos { @@ -31,12 +31,12 @@ namespace geos /// @tparam RESERVOIR_SOLVER compositional flow or compositional poromechanics solver template< typename RESERVOIR_SOLVER = CompositionalMultiphaseBase > class CompositionalMultiphaseReservoirAndWells : public CoupledReservoirAndWellsBase< RESERVOIR_SOLVER, - CompositionalMultiphaseWell > + WellManager > { public: using Base = CoupledReservoirAndWellsBase< RESERVOIR_SOLVER, - CompositionalMultiphaseWell >; + WellManager >; using Base::getLogLevel; using Base::m_solvers; using Base::m_linearSolverParameters; diff --git a/src/coreComponents/physicsSolvers/multiphysics/CoupledReservoirAndWellKernels.hpp b/src/coreComponents/physicsSolvers/multiphysics/CoupledReservoirAndWellKernels.hpp index 5d106df3156..05592b5725a 100644 --- a/src/coreComponents/physicsSolvers/multiphysics/CoupledReservoirAndWellKernels.hpp +++ b/src/coreComponents/physicsSolvers/multiphysics/CoupledReservoirAndWellKernels.hpp @@ -329,7 +329,263 @@ class IsothermalCompositionalMultiPhaseFluxKernelFactory } }; +/** + * @class FaceBasedAssemblyKernel + * @tparam NUM_COMP number of fluid components + * @brief Define the interface for the assembly kernel in charge of flux terms + */ +template< integer NC, integer IS_THERMAL > +class IsothermalCompositionalMultiPhaseWellFluxKernel +{ +public: + + /// Compile time value for the number of components + static constexpr integer numComp = NC; + static constexpr integer resNumDOF = NC+1+IS_THERMAL; + + // Well jacobian column and row indicies + using WJ_COFFSET = compositionalMultiphaseWellKernels::ColOffset_WellJac< NC, IS_THERMAL >; + using WJ_ROFFSET = compositionalMultiphaseWellKernels::RowOffset_WellJac< NC, IS_THERMAL >; + + using ROFFSET = compositionalMultiphaseWellKernels::RowOffset; + using COFFSET = compositionalMultiphaseWellKernels::ColOffset; + + using CP_Deriv = multifluid::DerivativeOffsetC< NC, IS_THERMAL >; + + using TAG = compositionalMultiphaseWellKernels::SubRegionTag; + + + + /// Compute time value for the number of degrees of freedom + static constexpr integer numDof = WJ_COFFSET::nDer; + + /// Compile time value for the number of equations except volume and momentum + static constexpr integer numEqn = WJ_ROFFSET::nEqn - 2; + + /** + * @brief Constructor for the kernel interface + * @param[in] rankOffset the offset of my MPI rank + * @param[in] stencilWrapper reference to the stencil wrapper + * @param[in] dofNumberAccessor + * @param[in] compFlowAccessors + * @param[in] multiFluidAccessors + * @param[in] capPressureAccessors + * @param[in] permeabilityAccessors + * @param[in] dt time step size + * @param[inout] localMatrix the local CRS matrix + * @param[inout] localRhs the local right-hand side vector + * @param[in] kernelFlags flags packed together + */ + IsothermalCompositionalMultiPhaseWellFluxKernel( real64 const dt, + globalIndex const rankOffset, + string const wellDofKey, + WellElementSubRegion const & subRegion, + PerforationData const * const perforationData, + MultiFluidBase const & fluid, + arrayView1d< real64 > const & localRhs, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + BitFlags< isothermalCompositionalMultiphaseBaseKernels::KernelFlags > kernelFlags ) + : + m_dt( dt ), + m_numPhases ( fluid.numFluidPhases()), + m_rankOffset( rankOffset ), + m_compPerfRate( perforationData->getField< fields::well::compPerforationRate >() ), + m_dCompPerfRate( perforationData->getField< fields::well::dCompPerforationRate >() ), + m_perfWellElemIndex( perforationData->getField< fields::perforation::wellElementIndex >() ), + m_wellElemDofNumber( subRegion.getReference< array1d< globalIndex > >( wellDofKey ) ), + m_localRhs( localRhs ), + m_localMatrix( localMatrix ), + m_useTotalMassEquation ( kernelFlags.isSet( isothermalCompositionalMultiphaseBaseKernels::KernelFlags::TotalMassEquation ) ) + { } + + + /** + * @brief Compute the local flux contributions to the residual and Jacobian + * @tparam FUNC the type of the function that can be used to customize the computation of the phase fluxes + * @param[in] ie the element index + * @param[inout] stack the stack variables + * @param[in] compFluxKernelOp the function used to customize the computation of the component fluxes + */ + + template< typename FUNC = NoOpFunc > + GEOS_HOST_DEVICE + inline + void computeFlux( localIndex const iperf, + FUNC && compFluxKernelOp = NoOpFunc{} ) const + { + + using namespace compositionalMultiphaseUtilities; + // local working variables and arrays + stackArray1d< localIndex, numComp > eqnRowIndices( numComp ); + stackArray1d< globalIndex, resNumDOF > dofColIndices( resNumDOF ); + + stackArray1d< real64, numComp > localPerf( numComp ); + stackArray2d< real64, numComp *resNumDOF > localPerfJacobian( numComp, resNumDOF ); + + // get the reservoir (sub)region and element indices + //localIndex const er = m_resElementRegion[iperf]; + //localIndex const esr = m_resElementSubRegion[iperf]; + //localIndex const ei = m_resElementIndex[iperf]; + + // get the well element index for this perforation + localIndex const iwelem = m_perfWellElemIndex[iperf]; + //globalIndex const resOffset = m_resElemDofNumber[er][esr][ei]; + globalIndex const wellElemOffset = m_wellElemDofNumber[iwelem]; + + for( integer ic = 0; ic < numComp; ++ic ) + { + eqnRowIndices[ ic] = LvArray::integerConversion< localIndex >( wellElemOffset - m_rankOffset ) + WJ_ROFFSET::MASSBAL + ic; + } + for( integer jdof = 0; jdof < NC+1; ++jdof ) + { + dofColIndices[ jdof] = wellElemOffset + WJ_COFFSET::dP + jdof; + } + // For temp its different + if constexpr ( IS_THERMAL ) + { + dofColIndices[ NC+1 ] = wellElemOffset + WJ_COFFSET::dT; + } + // populate local flux vector and derivatives + + for( integer ic = 0; ic < numComp; ++ic ) + { + localPerf[ic] = -m_dt * m_compPerfRate[iperf][ic]; + } + for( integer ic = 0; ic < numComp; ++ic ) + { + localIndex localDofIndexPres = 0; + + localPerfJacobian[ic][localDofIndexPres] = -m_dt * m_dCompPerfRate[iperf][TAG::WELL ][ic][CP_Deriv::dP]; + for( integer jc = 0; jc < numComp; ++jc ) + { + localIndex const localDofIndexComp = localDofIndexPres + jc + 1; + localPerfJacobian[ic][localDofIndexComp] = -m_dt * m_dCompPerfRate[iperf][TAG::WELL ][ic][CP_Deriv::dC+jc]; + } + if constexpr ( IS_THERMAL ) + { + localIndex localDofIndexTemp = localDofIndexPres + NC + 1; + localPerfJacobian[ic][localDofIndexTemp] = -m_dt * m_dCompPerfRate[iperf][TAG::WELL ][ic][CP_Deriv::dT]; + } + } + + if( m_useTotalMassEquation ) + { + stackArray1d< real64, resNumDOF > work( resNumDOF ); + shiftBlockRowsAheadByOneAndReplaceFirstRowWithColumnSum( numComp, numComp, resNumDOF, 1, localPerfJacobian, work ); + + // Apply equation/variable change transformation(s) + shiftBlockElementsAheadByOneAndReplaceFirstElementWithSum( numComp, numComp, 1, localPerf ); + } + + for( localIndex i = 0; i < localPerf.size(); ++i ) + { + if( eqnRowIndices[i] >= 0 && eqnRowIndices[i] < m_localMatrix.numRows() ) + { + m_localMatrix.addToRowBinarySearchUnsorted< parallelDeviceAtomic >( eqnRowIndices[i], + dofColIndices.data(), + localPerfJacobian[i].dataIfContiguous(), + resNumDOF ); + RAJA::atomicAdd( parallelDeviceAtomic{}, &m_localRhs[eqnRowIndices[i]], localPerf[i] ); + } + } + + compFluxKernelOp( wellElemOffset, iwelem, dofColIndices ); + + } + + +/** + * @brief Performs the kernel launch + * @tparam POLICY the policy used in the RAJA kernels + * @tparam KERNEL_TYPE the kernel type + * @param[in] numElements the number of elements + * @param[inout] kernelComponent the kernel component providing access to setup/compute/complete functions and stack + * variables + */ + template< typename POLICY, typename KERNEL_TYPE > + static void + launch( localIndex const numElements, + KERNEL_TYPE const & kernelComponent ) + { + GEOS_MARK_FUNCTION; + forAll< POLICY >( numElements, [=] GEOS_HOST_DEVICE ( localIndex const ie ) + { + kernelComponent.computeFlux( ie ); + + } ); + } + +protected: + +/// Time step size + real64 const m_dt; + +/// Number of phases + integer const m_numPhases; + + globalIndex const m_rankOffset; +// Perfoation variables + arrayView2d< real64 const > const m_compPerfRate; + arrayView4d< real64 const > const m_dCompPerfRate; + arrayView1d< localIndex const > const m_perfWellElemIndex; + +// Element region, subregion, index + arrayView1d< globalIndex const > const m_wellElemDofNumber; + +// RHS and Jacobian + arrayView1d< real64 > const m_localRhs; + CRSMatrixView< real64, globalIndex const > m_localMatrix; + + integer const m_useTotalMassEquation; +}; + +/** + * @class FaceBasedAssemblyKernelFactory + */ +class IsothermalCompositionalMultiPhaseWellFluxKernelFactory +{ +public: + + /** + * @brief Create a new kernel and launch + * @tparam POLICY the policy used in the RAJA kernel + * @param[in] numComps the number of fluid components + * @param[in] dt time step size + * @param[in] rankOffset the offset of my MPI rank + * @param[in] useTotalMassEquation flag specifying whether to replace one component bal eqn with total mass eqn + * @param[in] dofKey string to get the element degrees of freedom numbers + * @param[in] wellControls object holding well control/constraint information + * @param[in] subregion well subregion + * @param[inout] localMatrix the local CRS matrix + * @param[inout] localRhs the local right-hand side vector + */ + template< typename POLICY > + static void + createAndLaunch( integer const numComps, + real64 const dt, + globalIndex const rankOffset, + string const wellDofKey, + WellElementSubRegion const & subRegion, + PerforationData const * const perforationData, + MultiFluidBase const & fluid, + arrayView1d< real64 > const & localRhs, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + BitFlags< isothermalCompositionalMultiphaseBaseKernels::KernelFlags > kernelFlags ) + { + isothermalCompositionalMultiphaseBaseKernels::internal::kernelLaunchSelectorCompSwitch( numComps, [&]( auto NC ) + { + integer constexpr NUM_COMP = NC(); + + using kernelType = IsothermalCompositionalMultiPhaseWellFluxKernel< NUM_COMP, 0 >; + kernelType kernel( dt, rankOffset, wellDofKey, subRegion, perforationData, + fluid, localRhs, localMatrix, kernelFlags ); + kernelType::template launch< POLICY >( perforationData->size(), kernel ); + } ); + + } +}; +/********************************************************/ /** * @class FaceBasedAssemblyKernel * @tparam NUM_COMP number of fluid components @@ -383,6 +639,7 @@ class ThermalCompositionalMultiPhaseFluxKernel : public IsothermalCompositionalM * @param[in] kernelFlags flags packed together */ ThermalCompositionalMultiPhaseFluxKernel( real64 const dt, + bool const thermalEffectsEnabled, integer const isProducer, globalIndex const rankOffset, string const wellDofKey, @@ -407,6 +664,7 @@ class ThermalCompositionalMultiPhaseFluxKernel : public IsothermalCompositionalM detectCrossflow, numCrossFlowPerforations, kernelFlags ), + m_thermalEffectsEnabled( thermalEffectsEnabled ), m_isProducer( isProducer ), m_globalWellElementIndex( subRegion.getGlobalWellElementIndex() ), m_energyPerfFlux( perforationData->getField< fields::well::energyPerforationFlux >()), @@ -432,6 +690,8 @@ class ThermalCompositionalMultiPhaseFluxKernel : public IsothermalCompositionalM stackArray1d< globalIndex, 2*resNumDOF > & dofColIndices, localIndex const iwelem ) { + if( !m_thermalEffectsEnabled ) + return; // No energy equation if top element and Injector // Top element defined by global index == 0 // Assumption is global index == 0 is top segment with fixed temp BC @@ -441,7 +701,7 @@ class ThermalCompositionalMultiPhaseFluxKernel : public IsothermalCompositionalM return; } // local working variables and arrays - stackArray1d< localIndex, 2* numComp > eqnRowIndices( 2 ); + stackArray1d< localIndex, 2* numComp > eqnRowIndices( 2* numComp ); stackArray1d< real64, 2 * numComp > localPerf( 2 ); stackArray2d< real64, 2 * resNumDOF * 2 * numComp > localPerfJacobian( 2, 2 * resNumDOF ); @@ -510,6 +770,8 @@ class ThermalCompositionalMultiPhaseFluxKernel : public IsothermalCompositionalM } protected: + /// Flag specifying whether thermal effects are enabled + bool const m_thermalEffectsEnabled; /// Well type integer const m_isProducer; @@ -545,6 +807,7 @@ class ThermalCompositionalMultiPhaseFluxKernelFactory template< typename POLICY > static void createAndLaunch( integer const numComps, + integer const thermalEffectsEnabled, integer const isProducer, real64 const dt, globalIndex const rankOffset, @@ -565,7 +828,7 @@ class ThermalCompositionalMultiPhaseFluxKernelFactory integer constexpr NUM_COMP = NC(); using kernelType = ThermalCompositionalMultiPhaseFluxKernel< NUM_COMP, 1 >; - kernelType kernel( dt, isProducer, rankOffset, wellDofKey, subRegion, resDofNumber, perforationData, + kernelType kernel( dt, thermalEffectsEnabled, isProducer, rankOffset, wellDofKey, subRegion, resDofNumber, perforationData, fluid, localRhs, localMatrix, detectCrossflow, numCrossFlowPerforations, kernelFlags ); kernelType::template launch< POLICY >( perforationData->size(), kernel ); } ); @@ -573,6 +836,234 @@ class ThermalCompositionalMultiPhaseFluxKernelFactory } }; +/********************************************************/ +/** + * @class FaceBasedAssemblyKernel + * @tparam NUM_COMP number of fluid components + * @brief Define the interface for the assembly kernel in charge of flux terms + */ +template< integer NC, integer IS_THERMAL > +class ThermalCompositionalMultiPhaseWellFluxKernel : public IsothermalCompositionalMultiPhaseWellFluxKernel< NC, IS_THERMAL > +{ +public: + using Base = IsothermalCompositionalMultiPhaseWellFluxKernel< NC, IS_THERMAL >; + /// Compile time value for the number of components + static constexpr integer numComp = NC; + static constexpr integer resNumDOF = NC+1+IS_THERMAL; + + // Well jacobian column and row indicies + using WJ_COFFSET = compositionalMultiphaseWellKernels::ColOffset_WellJac< NC, IS_THERMAL >; + using WJ_ROFFSET = compositionalMultiphaseWellKernels::RowOffset_WellJac< NC, IS_THERMAL >; + + using ROFFSET = compositionalMultiphaseWellKernels::RowOffset; + using COFFSET = compositionalMultiphaseWellKernels::ColOffset; + + using CP_Deriv = multifluid::DerivativeOffsetC< NC, IS_THERMAL >; + + using TAG = compositionalMultiphaseWellKernels::SubRegionTag; + + using Base::m_dt; + using Base::m_localRhs; + using Base::m_localMatrix; + using Base::m_rankOffset; + + + + /// Compute time value for the number of degrees of freedom + static constexpr integer numDof = WJ_COFFSET::nDer; + + /// Compile time value for the number of equations except volume and momentum + static constexpr integer numEqn = WJ_ROFFSET::nEqn - 2; + + /** + * @brief Constructor for the kernel interface + * @param[in] rankOffset the offset of my MPI rank + * @param[in] stencilWrapper reference to the stencil wrapper + * @param[in] dofNumberAccessor + * @param[in] compFlowAccessors + * @param[in] multiFluidAccessors + * @param[in] capPressureAccessors + * @param[in] permeabilityAccessors + * @param[in] dt time step size + * @param[inout] localMatrix the local CRS matrix + * @param[inout] localRhs the local right-hand side vector + * @param[in] kernelFlags flags packed together + */ + ThermalCompositionalMultiPhaseWellFluxKernel( real64 const dt, + bool const thermalEffectsEnabled, + integer const isProducer, + globalIndex const rankOffset, + string const wellDofKey, + WellElementSubRegion const & subRegion, + PerforationData const * const perforationData, + MultiFluidBase const & fluid, + arrayView1d< real64 > const & localRhs, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + BitFlags< isothermalCompositionalMultiphaseBaseKernels::KernelFlags > kernelFlags ) + : Base( dt, + rankOffset, + wellDofKey, + subRegion, + perforationData, + fluid, + localRhs, + localMatrix, + kernelFlags ), + m_thermalEffectsEnabled( thermalEffectsEnabled ), + m_isProducer( isProducer ), + m_globalWellElementIndex( subRegion.getGlobalWellElementIndex() ), + m_energyPerfFlux( perforationData->getField< fields::well::energyPerforationFlux >()), + m_dEnergyPerfFlux( perforationData->getField< fields::well::dEnergyPerforationFlux >()) + + { } + + + /** + * @brief Compute the local flux contributions to the residual and Jacobian + * @tparam FUNC the type of the function that can be used to customize the computation of the phase fluxes + * @param[in] ie the element index + * @param[inout] stack the stack variables + * @param[in] compFluxKernelOp the function used to customize the computation of the component fluxes + */ + + GEOS_HOST_DEVICE + inline + void computeFlux( localIndex const iperf ) const + { + Base::computeFlux( iperf, [&] ( globalIndex const & wellElemOffset, + localIndex const iwelem, + stackArray1d< globalIndex, resNumDOF > & dofColIndices ) + { + GEOS_UNUSED_VAR( dofColIndices ); + if( !m_thermalEffectsEnabled ) // tjb iso + return; + // No energy equation if top element and Injector + // Top element defined by global index == 0 + // Assumption is global index == 0 is top segment with fixed temp BC + if( !m_isProducer ) + { + if( m_globalWellElementIndex[iwelem] == 0 ) + return; + } + // local working variables and arrays + localIndex eqnRowIndices = LvArray::integerConversion< localIndex >( wellElemOffset - m_rankOffset ) + WJ_ROFFSET::ENERGYBAL; + + stackArray2d< real64, resNumDOF > localPerfJacobian( 1, resNumDOF ); + // populate local flux vector and derivatives + + real64 localPerf = -m_dt * m_energyPerfFlux[iperf]; + + // std::cout << "Local perf: " << iperf << " " << localPerf << std::endl; + localIndex localDofIndexPres = 0; + localPerfJacobian [0][localDofIndexPres] = -m_dt * m_dEnergyPerfFlux[iperf][TAG::WELL][CP_Deriv::dP]; + + // populate local flux vector and derivatives + for( integer ic = 0; ic < numComp; ++ic ) + { + localIndex const localDofIndexComp = localDofIndexPres + ic + 1; + localPerfJacobian [0][localDofIndexComp] = -m_dt * m_dEnergyPerfFlux[iperf][TAG::WELL ][CP_Deriv::dC+ic]; + } + localPerfJacobian [0][localDofIndexPres+NC+1] = -m_dt * m_dEnergyPerfFlux[iperf][TAG::WELL ][CP_Deriv::dT]; + + if( eqnRowIndices >= 0 && eqnRowIndices < m_localMatrix.numRows() ) + { + // tjb iso + m_localMatrix.template addToRowBinarySearchUnsorted< parallelDeviceAtomic >( eqnRowIndices, + dofColIndices.data(), + localPerfJacobian[0].dataIfContiguous(), + resNumDOF ); + RAJA::atomicAdd( parallelDeviceAtomic{}, &m_localRhs[eqnRowIndices], localPerf ); + } + } ); + + + } + + + /** + * @brief Performs the kernel launch + * @tparam POLICY the policy used in the RAJA kernels + * @tparam KERNEL_TYPE the kernel type + * @param[in] numElements the number of elements + * @param[inout] kernelComponent the kernel component providing access to setup/compute/complete functions and stack + * variables + */ + template< typename POLICY, typename KERNEL_TYPE > + static void + launch( localIndex const numElements, + KERNEL_TYPE const & kernelComponent ) + { + GEOS_MARK_FUNCTION; + forAll< POLICY >( numElements, [=] GEOS_HOST_DEVICE ( localIndex const ie ) + { + kernelComponent.computeFlux( ie ); + + } ); + } + +protected: + /// Thermal effects enabled + bool const m_thermalEffectsEnabled; + /// Well type + integer const m_isProducer; + + /// Global index of local element + arrayView1d< globalIndex const > m_globalWellElementIndex; + + /// Views on energy flux + arrayView1d< real64 const > const m_energyPerfFlux; + arrayView3d< real64 const > const m_dEnergyPerfFlux; +}; + +/** + * @class ThermalCompositionalMultiPhaseFluxWellKernelFactory + */ +class ThermalCompositionalMultiPhaseWellFluxKernelFactory +{ +public: + + /** + * @brief Create a new kernel and launch + * @tparam POLICY the policy used in the RAJA kernel + * @param[in] numComps the number of fluid components + * @param[in] dt time step size + * @param[in] rankOffset the offset of my MPI rank + * @param[in] useTotalMassEquation flag specifying whether to replace one component bal eqn with total mass eqn + * @param[in] dofKey string to get the element degrees of freedom numbers + * @param[in] wellControls object holding well control/constraint information + * @param[in] subregion well subregion + * @param[inout] localMatrix the local CRS matrix + * @param[inout] localRhs the local right-hand side vector + */ + template< typename POLICY > + static void + createAndLaunch( integer const numComps, + WellControls const & wellControls, + integer const isProducer, + real64 const dt, + globalIndex const rankOffset, + string const wellDofKey, + WellElementSubRegion const & subRegion, + PerforationData const * const perforationData, + MultiFluidBase const & fluid, + BitFlags< isothermalCompositionalMultiphaseBaseKernels::KernelFlags > kernelFlags, + arrayView1d< real64 > const & localRhs, + CRSMatrixView< real64, globalIndex const > const & localMatrix + ) + { + isothermalCompositionalMultiphaseBaseKernels::internal::kernelLaunchSelectorCompSwitch( numComps, [&]( auto NC ) + { + integer constexpr NUM_COMP = NC(); + + using kernelType = ThermalCompositionalMultiPhaseWellFluxKernel< NUM_COMP, 1 >; + kernelType kernel( dt, wellControls.thermalEffectsEnabled(), isProducer, rankOffset, wellDofKey, subRegion, perforationData, + fluid, localRhs, localMatrix, kernelFlags ); + kernelType::template launch< POLICY >( perforationData->size(), kernel ); + } ); + + } +}; + } // end namespace coupledReservoirAndWellKernels } // end namespace geos diff --git a/src/coreComponents/physicsSolvers/multiphysics/CoupledReservoirAndWellsBase.cpp b/src/coreComponents/physicsSolvers/multiphysics/CoupledReservoirAndWellsBase.cpp index fbb8e7fb935..d2fb522266a 100644 --- a/src/coreComponents/physicsSolvers/multiphysics/CoupledReservoirAndWellsBase.cpp +++ b/src/coreComponents/physicsSolvers/multiphysics/CoupledReservoirAndWellsBase.cpp @@ -116,7 +116,7 @@ addCouplingNumNonzeros( PhysicsSolverBase const * const solver, } bool validateWellPerforations( PhysicsSolverBase const * const reservoirSolver, - WellSolverBase const * const wellSolver, + WellManager const * const wellSolver, DomainPartition const & domain ) { std::pair< string, string > badPerforation; diff --git a/src/coreComponents/physicsSolvers/multiphysics/CoupledReservoirAndWellsBase.hpp b/src/coreComponents/physicsSolvers/multiphysics/CoupledReservoirAndWellsBase.hpp index 32a67843aec..c7a1f3ea030 100644 --- a/src/coreComponents/physicsSolvers/multiphysics/CoupledReservoirAndWellsBase.hpp +++ b/src/coreComponents/physicsSolvers/multiphysics/CoupledReservoirAndWellsBase.hpp @@ -29,7 +29,7 @@ #include "mesh/PerforationFields.hpp" #include "mesh/DomainPartition.hpp" #include "physicsSolvers/fluidFlow/wells/WellControls.hpp" -#include "physicsSolvers/fluidFlow/wells/WellSolverBase.hpp" +#include "physicsSolvers/fluidFlow/wells/WellManager.hpp" namespace geos { @@ -65,7 +65,7 @@ addCouplingNumNonzeros( PhysicsSolverBase const * const solver, * @param domain the physical domain object */ bool validateWellPerforations( PhysicsSolverBase const * const reservoirSolver, - WellSolverBase const * const wellSolver, + WellManager const * const wellSolver, DomainPartition const & domain ); } diff --git a/src/coreComponents/physicsSolvers/multiphysics/SinglePhaseReservoirAndWells.cpp b/src/coreComponents/physicsSolvers/multiphysics/SinglePhaseReservoirAndWells.cpp index 46cf3257088..5b3d2f05188 100644 --- a/src/coreComponents/physicsSolvers/multiphysics/SinglePhaseReservoirAndWells.cpp +++ b/src/coreComponents/physicsSolvers/multiphysics/SinglePhaseReservoirAndWells.cpp @@ -126,6 +126,29 @@ initializePreSubGroups() Base::initializePreSubGroups(); SinglePhaseBase const * const flowSolver = this->flowSolver(); Base::wellSolver()->setFlowSolverName( flowSolver->getName() ); + + bool const isThermalFlow = flowSolver->getReference< integer >( SinglePhaseBase::viewKeyStruct::isThermalString() ); + bool const isThermalWell = Base::wellSolver()->template getReference< integer >( WellManager::viewKeyStruct::isThermalString() ); + GEOS_THROW_IF( isThermalFlow != isThermalWell, + GEOS_FMT( "{}: the input flag {} must be the same in the flow and well solvers, respectively '{}' and '{}'", + this->getDataContext(), SinglePhaseBase::viewKeyStruct::isThermalString(), + Base::reservoirSolver()->getDataContext(), Base::wellSolver()->getDataContext() ), + InputError, this->getDataContext(), Base::reservoirSolver()->getDataContext(), Base::wellSolver()->getDataContext() ); + DomainPartition & domain = this->template getGroupByPath< DomainPartition >( "/Problem/domain" ); + + this->template forDiscretizationOnMeshTargets<>( domain.getMeshBodies(), [&] ( string const &, + MeshLevel & mesh, + string_array const & regionNames ) + { + ElementRegionManager & elemManager = mesh.getElemManager(); + elemManager.forElementSubRegions< WellElementSubRegion >( regionNames, [&]( localIndex const, + WellElementSubRegion const & subRegion ) + { + WellControls & wellControls = Base::wellSolver()->getWellControls( subRegion ); + wellControls.setFlowSolverName( flowSolver->getName() ); + + } ); + } ); } template< typename RESERVOIR_SOLVER > diff --git a/src/coreComponents/physicsSolvers/multiphysics/SinglePhaseReservoirAndWells.hpp b/src/coreComponents/physicsSolvers/multiphysics/SinglePhaseReservoirAndWells.hpp index 0adeee58026..98da0ab2737 100644 --- a/src/coreComponents/physicsSolvers/multiphysics/SinglePhaseReservoirAndWells.hpp +++ b/src/coreComponents/physicsSolvers/multiphysics/SinglePhaseReservoirAndWells.hpp @@ -23,7 +23,7 @@ #include "physicsSolvers/multiphysics/CoupledReservoirAndWellsBase.hpp" #include "physicsSolvers/fluidFlow/SinglePhaseBase.hpp" -#include "physicsSolvers/fluidFlow/wells/SinglePhaseWell.hpp" +#include "physicsSolvers/fluidFlow/wells/WellManager.hpp" namespace geos { @@ -31,12 +31,12 @@ namespace geos /// @tparam RESERVOIR_SOLVER single-phase flow or single-phase poromechanics solver template< typename RESERVOIR_SOLVER = SinglePhaseBase > class SinglePhaseReservoirAndWells : public CoupledReservoirAndWellsBase< RESERVOIR_SOLVER, - SinglePhaseWell > + WellManager > { public: using Base = CoupledReservoirAndWellsBase< RESERVOIR_SOLVER, - SinglePhaseWell >; + WellManager >; using Base::m_solvers; using Base::m_linearSolverParameters; diff --git a/src/coreComponents/schema/schema.xsd b/src/coreComponents/schema/schema.xsd index 12808396834..ef1a6aa78f1 100644 --- a/src/coreComponents/schema/schema.xsd +++ b/src/coreComponents/schema/schema.xsd @@ -222,18 +222,10 @@ - - - - - - - - @@ -363,10 +355,6 @@ - - - - @@ -511,10 +499,6 @@ - - - - @@ -543,6 +527,10 @@ + + + + @@ -1604,26 +1592,10 @@ stress - traction is applied to the faces as specified by the inner product of i - - - - - - - - - - - - - - - - @@ -1632,18 +1604,6 @@ stress - traction is applied to the faces as specified by the inner product of i - - - - - - - - - - - - @@ -2760,12 +2720,6 @@ Information output from lower logLevels is added with the desired log level - - - - - - @@ -2802,12 +2756,6 @@ Information output from lower logLevels is added with the desired log level - - - - - - @@ -2815,6 +2763,16 @@ Information output from lower logLevels is added with the desired log level + + + + + + + + + + @@ -3654,144 +3612,6 @@ When set to `all` output both convergence & iteration information to a csv.--> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -4187,7 +4007,7 @@ Information output from lower logLevels is added with the desired log level - + @@ -4216,7 +4036,7 @@ When set to `all` output both convergence & iteration information to a csv.--> - + @@ -5697,52 +5517,6 @@ Local- Add jump stabilization on interior of macro elements--> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -6228,6 +6002,411 @@ When set to `all` output both convergence & iteration information to a csv.--> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/coreComponents/schema/schema.xsd.other b/src/coreComponents/schema/schema.xsd.other index e77cc9d197a..e6101f33339 100644 --- a/src/coreComponents/schema/schema.xsd.other +++ b/src/coreComponents/schema/schema.xsd.other @@ -351,15 +351,11 @@ A field can represent a physical variable. (pressure, temperature, global compos - - - - @@ -526,7 +522,7 @@ A field can represent a physical variable. (pressure, temperature, global compos - + @@ -570,7 +566,6 @@ A field can represent a physical variable. (pressure, temperature, global compos - @@ -607,7 +602,6 @@ A field can represent a physical variable. (pressure, temperature, global compos - @@ -615,6 +609,7 @@ A field can represent a physical variable. (pressure, temperature, global compos + @@ -867,27 +862,6 @@ A field can represent a physical variable. (pressure, temperature, global compos - - - - - - - - - - - - - - - - - - - - - @@ -1342,18 +1316,6 @@ A field can represent a physical variable. (pressure, temperature, global compos - - - - - - - - - - - - @@ -1497,6 +1459,63 @@ A field can represent a physical variable. (pressure, temperature, global compos + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -1590,7 +1609,7 @@ A field can represent a physical variable. (pressure, temperature, global compos - + @@ -3504,7 +3523,7 @@ A field can represent a physical variable. (pressure, temperature, global compos - + @@ -3532,7 +3551,7 @@ A field can represent a physical variable. (pressure, temperature, global compos - + @@ -3551,11 +3570,11 @@ A field can represent a physical variable. (pressure, temperature, global compos - + - + @@ -3565,7 +3584,7 @@ A field can represent a physical variable. (pressure, temperature, global compos - + @@ -3575,11 +3594,11 @@ A field can represent a physical variable. (pressure, temperature, global compos - + - + @@ -3589,7 +3608,7 @@ A field can represent a physical variable. (pressure, temperature, global compos - + @@ -3599,7 +3618,7 @@ A field can represent a physical variable. (pressure, temperature, global compos - + @@ -3609,7 +3628,7 @@ A field can represent a physical variable. (pressure, temperature, global compos - + @@ -3639,7 +3658,7 @@ A field can represent a physical variable. (pressure, temperature, global compos - + @@ -3667,7 +3686,7 @@ A field can represent a physical variable. (pressure, temperature, global compos - + @@ -3681,7 +3700,7 @@ A field can represent a physical variable. (pressure, temperature, global compos - + @@ -3693,7 +3712,7 @@ A field can represent a physical variable. (pressure, temperature, global compos - + @@ -3701,11 +3720,11 @@ A field can represent a physical variable. (pressure, temperature, global compos - + - + @@ -3728,7 +3747,7 @@ A field can represent a physical variable. (pressure, temperature, global compos - + @@ -3754,7 +3773,7 @@ A field can represent a physical variable. (pressure, temperature, global compos - + @@ -3775,7 +3794,7 @@ A field can represent a physical variable. (pressure, temperature, global compos - + @@ -3805,7 +3824,7 @@ A field can represent a physical variable. (pressure, temperature, global compos - + @@ -3819,7 +3838,7 @@ A field can represent a physical variable. (pressure, temperature, global compos - + @@ -3850,7 +3869,7 @@ A field can represent a physical variable. (pressure, temperature, global compos - + @@ -3895,7 +3914,7 @@ A field can represent a physical variable. (pressure, temperature, global compos - +