diff --git a/.github/workflows/ci_tests.yml b/.github/workflows/ci_tests.yml index 88decc83bda..429f5836282 100644 --- a/.github/workflows/ci_tests.yml +++ b/.github/workflows/ci_tests.yml @@ -204,14 +204,15 @@ jobs: GEOS_ENABLE_BOUNDS_CHECK: OFF HOST_CONFIG: /spack-generated.cmake - - name: Ubuntu (22.04, clang 15.0.7, open-mpi 4.1.2) - CMAKE_BUILD_TYPE: Release - DOCKER_REPOSITORY: geosx/ubuntu22.04-clang15 - ENABLE_HYPRE: ON - ENABLE_TRILINOS: OFF - GEOS_ENABLE_BOUNDS_CHECK: ON - BUILD_SHARED_LIBS: ON - HOST_CONFIG: /spack-generated.cmake + #- name: Ubuntu (22.04, clang 15.0.7, open-mpi 4.1.2) + # CMAKE_BUILD_TYPE: Release + # DOCKER_REPOSITORY: geosx/ubuntu22.04-clang15 + # ENABLE_HYPRE: ON + ## ENABLE_TRILINOS: OFF + # GEOS_ENABLE_BOUNDS_CHECK: ON + # BUILD_SHARED_LIBS: ON + # HOST_CONFIG: /spack-generated.cmake + # USE_SCCACHE: false #- name: Sherlock CPU (centos 7.9.2009, gcc 10.1.0, open-mpi 4.1.2, openblas 0.3.10) # CMAKE_BUILD_TYPE: Release diff --git a/inputFiles/compositionalMultiphaseFlow/soreideWhitson/1D_100cells/1D_benchmark.xml b/inputFiles/compositionalMultiphaseFlow/soreideWhitson/1D_100cells/1D_benchmark.xml index 89b9cc7dc86..1218d8156a2 100644 --- a/inputFiles/compositionalMultiphaseFlow/soreideWhitson/1D_100cells/1D_benchmark.xml +++ b/inputFiles/compositionalMultiphaseFlow/soreideWhitson/1D_100cells/1D_benchmark.xml @@ -1,294 +1,390 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inputFiles/compositionalMultiphaseFlow/soreideWhitson/1D_100cells/1D_smoke.xml b/inputFiles/compositionalMultiphaseFlow/soreideWhitson/1D_100cells/1D_smoke.xml index 19a577bde98..3e4fe211b1b 100644 --- a/inputFiles/compositionalMultiphaseFlow/soreideWhitson/1D_100cells/1D_smoke.xml +++ b/inputFiles/compositionalMultiphaseFlow/soreideWhitson/1D_100cells/1D_smoke.xml @@ -1,294 +1,384 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inputFiles/compositionalMultiphaseWell/benchmarks/Class09Pb3/class09_pb3_drainageOnly_direct_base.xml b/inputFiles/compositionalMultiphaseWell/benchmarks/Class09Pb3/class09_pb3_drainageOnly_direct_base.xml index f2c31503e8a..4d4ff61234c 100644 --- a/inputFiles/compositionalMultiphaseWell/benchmarks/Class09Pb3/class09_pb3_drainageOnly_direct_base.xml +++ b/inputFiles/compositionalMultiphaseWell/benchmarks/Class09Pb3/class09_pb3_drainageOnly_direct_base.xml @@ -1,8 +1,6 @@ - - + useSurfaceConditions="1" + surfacePressure="101325" + surfaceTemperature="288.71" + control="totalVolRate"> + + + - + - + + name="fluidTPFA"/> @@ -80,13 +83,16 @@ solidModelName="nullSolid" porosityModelName="rockPorosity" permeabilityModelName="rockPerm"/> + + + @@ -94,9 +100,8 @@ - + wettingNonWettingRelPermTableNames="{ waterRelativePermeabilityTable, gasRelativePermeabilityTable }"/> + - - - + + + + - @@ -152,11 +157,13 @@ name="initCO2CompFracTable" coordinates="{ -3238.2, -2506.13 }" values="{ 0.000001, 0.000001 }"/> + - @@ -165,37 +172,36 @@ name="waterRelativePermeabilityTable" coordinateFiles="{ tables/phaseVolumeFraction_water.txt }" voxelFile="tables/relPerm_water.txt"/> + + voxelFile="tables/relPerm_gas.txt"/> - + voxelFile="tables/capPres_water.txt"/> - + interpolation="linear"/> - + interpolation="linear"/> - - diff --git a/inputFiles/compositionalMultiphaseWell/benchmarks/Class09Pb3/class09_pb3_drainageOnly_iterative_base.xml b/inputFiles/compositionalMultiphaseWell/benchmarks/Class09Pb3/class09_pb3_drainageOnly_iterative_base.xml index 8e76b08905b..e585c6e4238 100644 --- a/inputFiles/compositionalMultiphaseWell/benchmarks/Class09Pb3/class09_pb3_drainageOnly_iterative_base.xml +++ b/inputFiles/compositionalMultiphaseWell/benchmarks/Class09Pb3/class09_pb3_drainageOnly_iterative_base.xml @@ -1,10 +1,7 @@ - - - - - - - + control="totalVolRate"> + + + + control="massRate"> + + + + control="massRate"> + + + - - - - + + name="fluidTPFA"/> - - - + + + - - - + wettingNonWettingRelPermTableNames="{ waterRelativePermeabilityTable, gasRelativePermeabilityTable }"/> - - - - - - - - + + + - @@ -204,11 +199,13 @@ name="initCO2CompFracTable" coordinates="{ -3238.2, -2506.13 }" values="{ 0.000001, 0.000001 }"/> + - @@ -217,43 +214,43 @@ name="waterRelativePermeabilityTable" coordinateFiles="{ tables/phaseVolumeFraction_water.txt }" voxelFile="tables/relPerm_water.txt"/> + + voxelFile="tables/relPerm_gas.txt"/> - + voxelFile="tables/capPres_water.txt"/> - + interpolation="linear"/> - + interpolation="linear"/> - diff --git a/inputFiles/compositionalMultiphaseWell/benchmarks/Class09Pb3/class09_pb3_hystRelperm_direct_base.xml b/inputFiles/compositionalMultiphaseWell/benchmarks/Class09Pb3/class09_pb3_hystRelperm_direct_base.xml index c731abcf696..83ca125931e 100644 --- a/inputFiles/compositionalMultiphaseWell/benchmarks/Class09Pb3/class09_pb3_hystRelperm_direct_base.xml +++ b/inputFiles/compositionalMultiphaseWell/benchmarks/Class09Pb3/class09_pb3_hystRelperm_direct_base.xml @@ -1,8 +1,6 @@ - - + useSurfaceConditions="1" + surfacePressure="101325" + surfaceTemperature="288.71" + control="totalVolRate"> + + + - + - + + name="fluidTPFA"/> @@ -80,26 +83,27 @@ solidModelName="nullSolid" porosityModelName="rockPorosity" permeabilityModelName="rockPerm"/> + + + - - + - - - - - + + + + - @@ -157,59 +159,61 @@ name="initCO2CompFracTable" coordinates="{ -3238.2, -2506.13 }" values="{ 0.000001, 0.000001 }"/> + + - + voxelFile="tables/capPres_water.txt"/> - + interpolation="linear"/> - + interpolation="linear"/> - - + + + - - diff --git a/inputFiles/compositionalMultiphaseWell/benchmarks/Class09Pb3/class09_pb3_hystRelperm_iterative_base.xml b/inputFiles/compositionalMultiphaseWell/benchmarks/Class09Pb3/class09_pb3_hystRelperm_iterative_base.xml index ee1af0ef88d..6067d36b757 100644 --- a/inputFiles/compositionalMultiphaseWell/benchmarks/Class09Pb3/class09_pb3_hystRelperm_iterative_base.xml +++ b/inputFiles/compositionalMultiphaseWell/benchmarks/Class09Pb3/class09_pb3_hystRelperm_iterative_base.xml @@ -1,8 +1,6 @@ - - + control="totalVolRate"> + + + - + - + + name="fluidTPFA"/> @@ -84,28 +87,27 @@ solidModelName="nullSolid" porosityModelName="rockPorosity" permeabilityModelName="rockPerm"/> + + + - - - - + - - + + + + - @@ -161,10 +163,12 @@ name="initCO2CompFracTable" coordinates="{ -3238.2, -2506.13 }" values="{ 0.000001, 0.000001 }"/> + + + + voxelFile="tables/relPerm_gas.txt"/> + voxelFile="tables/capPres_water.txt"/> - + interpolation="linear"/> - + interpolation="linear"/> - + + + - - diff --git a/inputFiles/compositionalMultiphaseWell/benchmarks/Egg/deadOilEgg_base_direct.xml b/inputFiles/compositionalMultiphaseWell/benchmarks/Egg/deadOilEgg_base_direct.xml index 048e88999d7..742d818218b 100644 --- a/inputFiles/compositionalMultiphaseWell/benchmarks/Egg/deadOilEgg_base_direct.xml +++ b/inputFiles/compositionalMultiphaseWell/benchmarks/Egg/deadOilEgg_base_direct.xml @@ -1,7 +1,6 @@ - + control="BHP"> + + + - - - + control="BHP"> + + + + control="BHP"> + + + + control="BHP"> + + + + control="totalVolRate"> + + + + control="totalVolRate"> + + + + control="totalVolRate"> + + + + control="totalVolRate"> + + + + control="totalVolRate"> + + + + control="totalVolRate"> + + + + control="totalVolRate"> + + + + control="totalVolRate"> + + + - - - - - - - - @@ -279,7 +327,6 @@ materialList="{ fluid }"/> - - - - - @@ -373,11 +416,8 @@ - - - - - diff --git a/inputFiles/compositionalMultiphaseWell/benchmarks/Egg/deadOilEgg_base_iterative.xml b/inputFiles/compositionalMultiphaseWell/benchmarks/Egg/deadOilEgg_base_iterative.xml index 615688bef78..fa47c6541e9 100644 --- a/inputFiles/compositionalMultiphaseWell/benchmarks/Egg/deadOilEgg_base_iterative.xml +++ b/inputFiles/compositionalMultiphaseWell/benchmarks/Egg/deadOilEgg_base_iterative.xml @@ -1,7 +1,6 @@ - + control="BHP"> + + + - - - + control="BHP"> + + + + control="BHP"> + + + + control="BHP"> + + + + control="totalVolRate"> + + + + control="totalVolRate"> + + + + control="totalVolRate"> + + + + control="totalVolRate"> + + + + control="totalVolRate"> + + + + control="totalVolRate"> + + + + control="totalVolRate"> + + + + control="totalVolRate"> + + + - - - - - - - - @@ -281,7 +329,6 @@ materialList="{ fluid }"/> - - - - - @@ -375,11 +418,8 @@ - - - - - diff --git a/inputFiles/compositionalMultiphaseWell/black_oil_wells_saturated_3d.xml b/inputFiles/compositionalMultiphaseWell/black_oil_wells_saturated_3d.xml index 7ebac080cbc..14479ff4132 100644 --- a/inputFiles/compositionalMultiphaseWell/black_oil_wells_saturated_3d.xml +++ b/inputFiles/compositionalMultiphaseWell/black_oil_wells_saturated_3d.xml @@ -2,7 +2,6 @@ - - + + control="BHP"> + + + + control="BHP"> + + + - - - - + - + - + - @@ -148,12 +153,14 @@ name="region" cellBlocks="{ * }" materialList="{ fluid, rock, relperm, cappres }"/> + + + materialList="{ fluid }"/> @@ -163,7 +170,7 @@ surfaceDensities="{ 800.907131537, 0.856234902739, 1020.3440 }" componentMolarWeight="{ 120e-3, 25e-3, 18e-3 }" tableFiles="{ pvto_bo.txt, pvtg_norv_bo.txt, pvtw_bo.txt }"/> - + - + - - + + + scale="0.2"/> + - + coordinates="{ 0.0, 0.05, 0.10, 0.15, 0.20, 0.25, 0.30, 0.35, 0.40, 0.45, 0.5, 0.55, 0.60, 0.65, 0.70, 0.75, 0.80, 0.85, 0.90, 0.95, 1.00 }" + values="{ 0, 0.0025, 0.0100, 0.0225, 0.0400, 0.0625, 0.0900, 0.1225, 0.1600, 0.2025, 0.2500, 0.3025, 0.3600, 0.4225, 0.4900, 0.5625, 0.6400, 0.7225, 0.8100, 0.9025, 1.0000 }"/> + + coordinates="{ 0.0, 0.05, 0.10, 0.15, 0.20, 0.25, 0.30, 0.35, 0.40, 0.45, 0.5, 0.55, 0.60, 0.65, 0.70, 0.75, 0.80, 0.85, 0.90, 0.95, 1.00 }" + values="{ 0, 0.0025, 0.0100, 0.0225, 0.0400, 0.0625, 0.0900, 0.1225, 0.1600, 0.2025, 0.2500, 0.3025, 0.3600, 0.4225, 0.4900, 0.5625, 0.6400, 0.7225, 0.8100, 0.9025, 1.0000 }"/> + + coordinates="{ 0.0, 0.05, 0.10, 0.15, 0.20, 0.25, 0.30, 0.35, 0.40, 0.45, 0.5, 0.55, 0.60, 0.65, 0.70, 0.75, 0.80, 0.85, 0.90, 0.95, 1.00 }" + values="{ 0, 0.0025, 0.0100, 0.0225, 0.0400, 0.0625, 0.0900, 0.1225, 0.1600, 0.2025, 0.2500, 0.3025, 0.3600, 0.4225, 0.4900, 0.5625, 0.6400, 0.7225, 0.8100, 0.9025, 1.0000 }"/> + + coordinates="{ 0.0, 0.05, 0.10, 0.15, 0.20, 0.25, 0.30, 0.35, 0.40, 0.45, 0.5, 0.55, 0.60, 0.65, 0.70, 0.75, 0.80, 0.85, 0.90, 0.95, 1.00 }" + values="{ 0, 0.0025, 0.0100, 0.0225, 0.0400, 0.0625, 0.0900, 0.1225, 0.1600, 0.2025, 0.2500, 0.3025, 0.3600, 0.4225, 0.4900, 0.5625, 0.6400, 0.7225, 0.8100, 0.9025, 1.0000 }"/> + coordinates="{ 0.0, 0.05, 0.10, 0.15, 0.20, 0.25, 0.30, 0.35, 0.40, 0.45, 0.5, 0.55, 0.60, 0.65, 0.70, 0.75, 0.80, 0.85, 0.90, 0.95, 1.00 }" + values="{ 10000, 9025, 8100, 7225, 6400, 5625, 4900, 4225, 3600, 3025, 2500, 2025, 1600, 1225, 900, 625, 400, 225, 100, 25, 0 }"/> + - + coordinates="{ 0.0, 0.05, 0.10, 0.15, 0.20, 0.25, 0.30, 0.35, 0.40, 0.45, 0.5, 0.55, 0.60, 0.65, 0.70, 0.75, 0.80, 0.85, 0.90, 0.95, 1.00 }" + values="{ 0, 50, 200, 450, 800, 1250, 1800, 2450, 3200, 4050, 5000, 6050, 7200, 8450, 9800, 11250, 12800, 14450, 16200, 18050, 20000 }"/> - + - + - + - + - diff --git a/inputFiles/compositionalMultiphaseWell/black_oil_wells_saturated_3d_stone2.xml b/inputFiles/compositionalMultiphaseWell/black_oil_wells_saturated_3d_stone2.xml index ce669961545..5df4a8cf991 100644 --- a/inputFiles/compositionalMultiphaseWell/black_oil_wells_saturated_3d_stone2.xml +++ b/inputFiles/compositionalMultiphaseWell/black_oil_wells_saturated_3d_stone2.xml @@ -2,7 +2,6 @@ - - + + control="BHP"> + + + + control="BHP"> + + + - - - - - - - + + + + + + - - + - + - + - @@ -147,12 +153,14 @@ name="region" cellBlocks="{ * }" materialList="{ fluid, rock, relperm, cappres }"/> + + + materialList="{ fluid }"/> @@ -162,7 +170,7 @@ surfaceDensities="{ 800.907131537, 0.856234902739, 1020.3440 }" componentMolarWeight="{ 120e-3, 25e-3, 18e-3 }" tableFiles="{ pvto_bo.txt, pvtg_norv_bo.txt, pvtw_bo.txt }"/> - + - + - - + + + scale="0.2"/> + - + coordinates="{ 0.0, 0.05, 0.10, 0.15, 0.20, 0.25, 0.30, 0.35, 0.40, 0.45, 0.5, 0.55, 0.60, 0.65, 0.70, 0.75, 0.80, 0.85, 0.90, 0.95, 1.00 }" + values="{ 0, 0.0025, 0.0100, 0.0225, 0.0400, 0.0625, 0.0900, 0.1225, 0.1600, 0.2025, 0.2500, 0.3025, 0.3600, 0.4225, 0.4900, 0.5625, 0.6400, 0.7225, 0.8100, 0.9025, 1.0000 }"/> + + coordinates="{ 0.0, 0.05, 0.10, 0.15, 0.20, 0.25, 0.30, 0.35, 0.40, 0.45, 0.5, 0.55, 0.60, 0.65, 0.70, 0.75, 0.80, 0.85, 0.90, 0.95, 1.00 }" + values="{ 0, 0.0025, 0.0100, 0.0225, 0.0400, 0.0625, 0.0900, 0.1225, 0.1600, 0.2025, 0.2500, 0.3025, 0.3600, 0.4225, 0.4900, 0.5625, 0.6400, 0.7225, 0.8100, 0.9025, 1.0000 }"/> + + coordinates="{ 0.0, 0.05, 0.10, 0.15, 0.20, 0.25, 0.30, 0.35, 0.40, 0.45, 0.5, 0.55, 0.60, 0.65, 0.70, 0.75, 0.80, 0.85, 0.90, 0.95, 1.00 }" + values="{ 0, 0.0025, 0.0100, 0.0225, 0.0400, 0.0625, 0.0900, 0.1225, 0.1600, 0.2025, 0.2500, 0.3025, 0.3600, 0.4225, 0.4900, 0.5625, 0.6400, 0.7225, 0.8100, 0.9025, 1.0000 }"/> + + coordinates="{ 0.0, 0.05, 0.10, 0.15, 0.20, 0.25, 0.30, 0.35, 0.40, 0.45, 0.5, 0.55, 0.60, 0.65, 0.70, 0.75, 0.80, 0.85, 0.90, 0.95, 1.00 }" + values="{ 0, 0.0025, 0.0100, 0.0225, 0.0400, 0.0625, 0.0900, 0.1225, 0.1600, 0.2025, 0.2500, 0.3025, 0.3600, 0.4225, 0.4900, 0.5625, 0.6400, 0.7225, 0.8100, 0.9025, 1.0000 }"/> + coordinates="{ 0.0, 0.05, 0.10, 0.15, 0.20, 0.25, 0.30, 0.35, 0.40, 0.45, 0.5, 0.55, 0.60, 0.65, 0.70, 0.75, 0.80, 0.85, 0.90, 0.95, 1.00 }" + values="{ 10000, 9025, 8100, 7225, 6400, 5625, 4900, 4225, 3600, 3025, 2500, 2025, 1600, 1225, 900, 625, 400, 225, 100, 25, 0 }"/> + - + coordinates="{ 0.0, 0.05, 0.10, 0.15, 0.20, 0.25, 0.30, 0.35, 0.40, 0.45, 0.5, 0.55, 0.60, 0.65, 0.70, 0.75, 0.80, 0.85, 0.90, 0.95, 1.00 }" + values="{ 0, 50, 200, 450, 800, 1250, 1800, 2450, 3200, 4050, 5000, 6050, 7200, 8450, 9800, 11250, 12800, 14450, 16200, 18050, 20000 }"/> - + - + - + - + - diff --git a/inputFiles/compositionalMultiphaseWell/black_oil_wells_unsaturated_3d.xml b/inputFiles/compositionalMultiphaseWell/black_oil_wells_unsaturated_3d.xml index 323b9a144c6..b2e5a4c7d91 100644 --- a/inputFiles/compositionalMultiphaseWell/black_oil_wells_unsaturated_3d.xml +++ b/inputFiles/compositionalMultiphaseWell/black_oil_wells_unsaturated_3d.xml @@ -2,7 +2,6 @@ - - + + + control="BHP"> + + + + control="BHP"> + + + - - - - - + - - + - + - @@ -148,12 +154,14 @@ name="region" cellBlocks="{ * }" materialList="{ fluid, rock, relperm }"/> + + + materialList="{ fluid }"/> @@ -163,7 +171,7 @@ surfaceDensities="{ 800.907131537, 0.856234902739, 1020.3440 }" componentMolarWeight="{ 120e-3, 25e-3, 18e-3 }" tableFiles="{ pvto_bo.txt, pvtg_norv_bo.txt, pvtw_bo.txt }"/> - + - - + + + scale="0.1"/> + - + - + - + name="vtkOutput"/> + - - diff --git a/inputFiles/compositionalMultiphaseWell/black_oil_wells_unsaturated_3d_stone2.xml b/inputFiles/compositionalMultiphaseWell/black_oil_wells_unsaturated_3d_stone2.xml index ea41c8adf9c..464449a53f4 100644 --- a/inputFiles/compositionalMultiphaseWell/black_oil_wells_unsaturated_3d_stone2.xml +++ b/inputFiles/compositionalMultiphaseWell/black_oil_wells_unsaturated_3d_stone2.xml @@ -1,260 +1,267 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inputFiles/compositionalMultiphaseWell/compositional_multiphase_wells_1d.xml b/inputFiles/compositionalMultiphaseWell/compositional_multiphase_wells_1d.xml index bea2c48815d..2ba87aa5909 100644 --- a/inputFiles/compositionalMultiphaseWell/compositional_multiphase_wells_1d.xml +++ b/inputFiles/compositionalMultiphaseWell/compositional_multiphase_wells_1d.xml @@ -30,20 +30,30 @@ + control="BHP"> + + + + control="totalVolRate"> + + + @@ -58,7 +68,6 @@ ny="{ 1 }" nz="{ 1 }" cellBlockNames="{ cb1 }"> - - + name="fluidTPFA"/> @@ -175,7 +182,6 @@ - - - + control="BHP"> + + + + control="phaseVolRate"> + + + + control="totalVolRate"> + + + - - @@ -69,7 +81,6 @@ ny="{ 20 }" nz="{ 1 }" cellBlockNames="{ cb1 }"> - - - + name="fluidTPFA"/> @@ -223,7 +231,6 @@ - - + control="BHP"> + + + + control="phaseVolRate"> + + + + control="totalVolRate"> + + + @@ -78,7 +93,6 @@ ny="{ 20 }" nz="{ 1 }" cellBlockNames="{ cb1 }"> - - - + name="fluidTPFA"/> @@ -223,7 +234,6 @@ - - - - + + control="BHP"> + + + + control="BHP"> + + + + control="totalVolRate"> + + + @@ -77,7 +92,6 @@ ny="{ 20 }" nz="{ 1 }" cellBlockNames="{ cb1 }"> - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inputFiles/compositionalMultiphaseWell/dome_soreide_whitson_base.xml b/inputFiles/compositionalMultiphaseWell/dome_soreide_whitson_base.xml index 2e00878f00e..e5f85ebfbdd 100644 --- a/inputFiles/compositionalMultiphaseWell/dome_soreide_whitson_base.xml +++ b/inputFiles/compositionalMultiphaseWell/dome_soreide_whitson_base.xml @@ -1,287 +1,339 @@ + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inputFiles/compositionalMultiphaseWell/isothm_mass_inj_table.xml b/inputFiles/compositionalMultiphaseWell/isothm_mass_inj_table.xml index 14076fab917..cf1631e6f94 100644 --- a/inputFiles/compositionalMultiphaseWell/isothm_mass_inj_table.xml +++ b/inputFiles/compositionalMultiphaseWell/isothm_mass_inj_table.xml @@ -10,7 +10,7 @@ initialDt="1e2" targetRegions="{ region, injwell }"> - + targetRegions="{ region }"/> - + control="massRate"> + + + + @@ -83,10 +85,8 @@ - - @@ -94,8 +94,6 @@ name="sink" xMin="{ 89.99, 89.99, -0.01 }" xMax="{ 101.01, 101.01, 1.01 }"/> - - - - @@ -143,25 +142,28 @@ name="region" cellBlocks="{ cb }" materialList="{ fluid, rock, relperm }"/> + - + + + @@ -181,11 +183,9 @@ phaseMinVolumeFraction="{ 0.0, 0.0 }" phaseRelPermExponent="{ 1.5, 1.5 }" phaseRelPermMaxValue="{ 0.9, 0.9 }"/> - - + + + - diff --git a/inputFiles/compositionalMultiphaseWell/isothm_vol_inj_table.xml b/inputFiles/compositionalMultiphaseWell/isothm_vol_inj_table.xml index d39421e36fc..7c198945edd 100644 --- a/inputFiles/compositionalMultiphaseWell/isothm_vol_inj_table.xml +++ b/inputFiles/compositionalMultiphaseWell/isothm_vol_inj_table.xml @@ -10,7 +10,7 @@ initialDt="1e2" targetRegions="{ region, injwell }"> - + targetRegions="{ region }"/> - + control="totalVolRate"> + + + + @@ -82,10 +85,8 @@ - - @@ -93,8 +94,6 @@ name="sink" xMin="{ 89.99, 89.99, -0.01 }" xMax="{ 101.01, 101.01, 1.01 }"/> - - - - - @@ -148,25 +149,28 @@ name="region" cellBlocks="{ cb }" materialList="{ fluid, rock, relperm }"/> + - + + + @@ -186,11 +190,9 @@ phaseMinVolumeFraction="{ 0.0, 0.0 }" phaseRelPermExponent="{ 1.5, 1.5 }" phaseRelPermMaxValue="{ 0.9, 0.9 }"/> - - + + + - diff --git a/inputFiles/compositionalMultiphaseWell/simpleCo2InjTutorial_base.xml b/inputFiles/compositionalMultiphaseWell/simpleCo2InjTutorial_base.xml index 705b4934fb9..5d4f887ad1e 100644 --- a/inputFiles/compositionalMultiphaseWell/simpleCo2InjTutorial_base.xml +++ b/inputFiles/compositionalMultiphaseWell/simpleCo2InjTutorial_base.xml @@ -1,8 +1,6 @@ - - + useSurfaceConditions="1" + surfacePressure="101325" + surfaceTemperature="288.71" + control="totalVolRate"> + + + - - - + - - - + - - - - - - - - @@ -185,42 +177,37 @@ - + sources="{ /Tasks/wellPressureCollection }" + filename="wellPressureHistory"/> - - - + fieldName="pressure"/> - + - - - - - + + + + + - diff --git a/inputFiles/compositionalMultiphaseWell/staged_perf_base.xml b/inputFiles/compositionalMultiphaseWell/staged_perf_base.xml index 302d728e9b4..9e87e03c62f 100644 --- a/inputFiles/compositionalMultiphaseWell/staged_perf_base.xml +++ b/inputFiles/compositionalMultiphaseWell/staged_perf_base.xml @@ -10,11 +10,11 @@ targetRegions="{ reservoir, wellRegion1 }"> + directParallel="0"/> + control="BHP"> + + + - - + + name="fluidTPFA"/> @@ -91,7 +93,6 @@ - @@ -158,34 +159,31 @@ scale="1.0"/> - + name="equil" + objectPath="ElementRegions" + datumElevation="0" + datumPressure="2.214e7" + initialPhaseName="water" + componentNames="{ co2, water }" + componentFractionVsElevationTableNames="{ initCO2CompFracTable, initWaterCompFracTable }" + temperatureVsElevationTableName="initTempTable"/> + name="initCO2CompFracTable" + coordinates="{ -3000.0, 0.0 }" + values="{ 0.0, 0.0 }"/> + name="initWaterCompFracTable" + coordinates="{ -3000.0, 0.0 }" + values="{ 1.0, 1.0 }"/> - + name="initTempTable" + coordinates="{ -3000.0, 0.0 }" + values="{ 368, 288 }"/> diff --git a/inputFiles/compositionalMultiphaseWell/staircase_co2_wells_3d.xml b/inputFiles/compositionalMultiphaseWell/staircase_co2_wells_3d.xml index 388b15ca605..ad4a7cc4368 100644 --- a/inputFiles/compositionalMultiphaseWell/staircase_co2_wells_3d.xml +++ b/inputFiles/compositionalMultiphaseWell/staircase_co2_wells_3d.xml @@ -33,28 +33,36 @@ useMass="1"> + control="phaseVolRate"> + + + + control="totalVolRate"> + + + - - - + cellBlockNames="{ cb-0_0_0, cb-1_0_0, cb-0_1_0, cb-1_1_0, cb-0_0_1, cb-1_0_1, cb-0_1_1, cb-1_1_1, cb-0_0_2, cb-1_0_2, cb-0_1_2, cb-1_1_2, cb-0_0_3, cb-1_0_3, cb-0_1_3, cb-1_1_3 }"> - + wettingNonWettingRelPermTableNames="{ waterRelativePermeabilityTable, gasRelativePermeabilityTable }"/> - @@ -222,35 +223,37 @@ scale="0.96"/> - + + voxelFile="relPerm_gas.txt"/> + + voxelFile="capPres_water.txt"/> + + - diff --git a/inputFiles/compositionalMultiphaseWell/staircase_co2_wells_hybrid_3d.xml b/inputFiles/compositionalMultiphaseWell/staircase_co2_wells_hybrid_3d.xml index b7e30e91dcb..f5d6b2a7340 100644 --- a/inputFiles/compositionalMultiphaseWell/staircase_co2_wells_hybrid_3d.xml +++ b/inputFiles/compositionalMultiphaseWell/staircase_co2_wells_hybrid_3d.xml @@ -38,25 +38,34 @@ + control="BHP"> + + + + control="totalVolRate"> + + + - - - - + diff --git a/inputFiles/poromechanics/PoroElastic_staircase_co2_3d_fim.xml b/inputFiles/poromechanics/PoroElastic_staircase_co2_3d_fim.xml index 0ca07929f8e..5e1a987f1cc 100644 --- a/inputFiles/poromechanics/PoroElastic_staircase_co2_3d_fim.xml +++ b/inputFiles/poromechanics/PoroElastic_staircase_co2_3d_fim.xml @@ -1,13 +1,12 @@ - - - + + - - + + targetRegions="{ channel, barrier }"/> @@ -55,34 +54,42 @@ useMass="1"> + control="BHP"> + + + + control="totalVolRate"> + + + - - + + - + + - + - - - diff --git a/inputFiles/poromechanics/PoroElastic_staircase_co2_3d_sequential.xml b/inputFiles/poromechanics/PoroElastic_staircase_co2_3d_sequential.xml index e978595a49f..6a336f2fda3 100755 --- a/inputFiles/poromechanics/PoroElastic_staircase_co2_3d_sequential.xml +++ b/inputFiles/poromechanics/PoroElastic_staircase_co2_3d_sequential.xml @@ -1,13 +1,12 @@ - - - + + - - + + targetRegions="{ channel, barrier }"/> @@ -62,34 +61,42 @@ useMass="1"> + control="BHP"> + + + + control="totalVolRate"> + + + - - + + - + + - + - - diff --git a/inputFiles/poromechanics/PoroElastic_staircase_singlephase_3d_fim.xml b/inputFiles/poromechanics/PoroElastic_staircase_singlephase_3d_fim.xml index 95f2090ada1..29c208416b7 100644 --- a/inputFiles/poromechanics/PoroElastic_staircase_singlephase_3d_fim.xml +++ b/inputFiles/poromechanics/PoroElastic_staircase_singlephase_3d_fim.xml @@ -1,13 +1,12 @@ - - - + + - - + + targetRegions="{ channel, barrier }"/> + control="BHP"> + + + + surfacePressure="101325" + control="totalVolRate"> + + + - - + minTime="-1e11" + maxTime="1e7"> - + + - + + - - + + - diff --git a/inputFiles/poromechanics/PoroElastic_staircase_singlephase_3d_sequential.xml b/inputFiles/poromechanics/PoroElastic_staircase_singlephase_3d_sequential.xml index 0c952c6934f..5f5b8cbc6ba 100755 --- a/inputFiles/poromechanics/PoroElastic_staircase_singlephase_3d_sequential.xml +++ b/inputFiles/poromechanics/PoroElastic_staircase_singlephase_3d_sequential.xml @@ -1,13 +1,12 @@ - - - + + - - + + targetRegions="{ channel, barrier }"/> + control="BHP"> + + + + surfacePressure="101325" + control="totalVolRate"> + + + - - + minTime="-1e11" + maxTime="1e7"> - + + - + - @@ -118,24 +125,25 @@ name="linearElasticityStatistics" solidSolverName="linearElasticity" logLevel="1"/> + - - + + - diff --git a/inputFiles/poromechanicsFractures/multiphasePoromechanics_FaultModel_well_fim_smoke.xml b/inputFiles/poromechanicsFractures/multiphasePoromechanics_FaultModel_well_fim_smoke.xml index 0ce10628d75..3660699ecaf 100644 --- a/inputFiles/poromechanicsFractures/multiphasePoromechanics_FaultModel_well_fim_smoke.xml +++ b/inputFiles/poromechanicsFractures/multiphasePoromechanics_FaultModel_well_fim_smoke.xml @@ -1,14 +1,16 @@ + + - + - + maxAllowedResidualNorm="1e+15"/> + directParallel="0"/> + targetRegions="{ Region, Fault }" + discretization="FE1"/> + logLevel="3"/> - + targetRegions="{ Region, Fault }" + temperature="368.15"/> + logLevel="2"> + + + @@ -88,47 +95,44 @@ numElementsPerSegment="1"> + distanceFromHead="250"/> - + + - - - + target="/Outputs/vtkOutput"/> + target="/Solvers/reservoirSolver"/> + target="/Outputs/vtkOutput"/> + target="/Outputs/restartOutput"/> diff --git a/inputFiles/poromechanicsFractures/multiphasePoromechanics_FaultModel_well_seq_smoke.xml b/inputFiles/poromechanicsFractures/multiphasePoromechanics_FaultModel_well_seq_smoke.xml index d0959438756..5557c612313 100644 --- a/inputFiles/poromechanicsFractures/multiphasePoromechanics_FaultModel_well_seq_smoke.xml +++ b/inputFiles/poromechanicsFractures/multiphasePoromechanics_FaultModel_well_seq_smoke.xml @@ -1,14 +1,16 @@ + + - + - + maxAllowedResidualNorm="1e+15"/> + directParallel="0"/> + targetRegions="{ Region, Fault }" + discretization="FE1"/> + newtonMaxIter="20"/> + preconditionerType="mgr"/> - + targetRegions="{ Region, Fault }" + temperature="368.15"/> + logLevel="2"> + + + @@ -97,47 +104,44 @@ numElementsPerSegment="1"> + distanceFromHead="250"/> - + + - - - + target="/Outputs/vtkOutput"/> + target="/Solvers/reservoirSolver"/> + target="/Outputs/vtkOutput"/> + target="/Outputs/restartOutput"/> diff --git a/inputFiles/poromechanicsFractures/singlePhasePoromechanics_FaultModel_well_fim_new_smoke.xml b/inputFiles/poromechanicsFractures/singlePhasePoromechanics_FaultModel_well_fim_new_smoke.xml index 07a6e1521ff..369356badf7 100644 --- a/inputFiles/poromechanicsFractures/singlePhasePoromechanics_FaultModel_well_fim_new_smoke.xml +++ b/inputFiles/poromechanicsFractures/singlePhasePoromechanics_FaultModel_well_fim_new_smoke.xml @@ -1,14 +1,16 @@ + + - + - + solverType="direct"/> - + logLevel="1"/> + targetRegions="{ Region, Fault }" + discretization="FE1"/> - + targetRegions="{ Region, Fault }"/> + control="BHP"> + + + + control="BHP"> + + + - + + - - - + - + - + solverType="direct"/> + targetRegions="{ Region, Fault }" + discretization="FE1"/> - + targetRegions="{ Region, Fault }"/> + control="BHP"> + + + + control="BHP"> + + + - + + - - - + @@ -5,10 +7,9 @@ - - + logLevel="1"/> + targetRegions="{ Region, Fault }" + discretization="FE1"/> - + targetRegions="{ Region, Fault }"/> + targetRegions="{ wellRegion1, wellRegion2 }" + writeCSV="1"> + logLevel="2" + type="producer"> + + + + logLevel="2" + type="injector"> + + + - + + - - - + @@ -5,10 +7,9 @@ - + targetRegions="{ Region, Fault }" + discretization="FE1"/> - + targetRegions="{ Region, Fault }"/> + targetRegions="{ wellRegion1, wellRegion2 }" + writeCSV="1"> + logLevel="2" + type="producer"> + + + + logLevel="2" + type="injector"> + + + - + + - - - + targetRegions="{ region , wellRegion2 }"> - + targetRegions="{ region }"/> + control="BHP"> + + + @@ -78,13 +82,13 @@ name="region" cellBlocks="{ * }" materialList="{ fluid, rock, thermalCond }"/> + - - - - diff --git a/inputFiles/singlePhaseWell/compressible_single_phase_wells_1d.xml b/inputFiles/singlePhaseWell/compressible_single_phase_wells_1d.xml index 4b5f64d0287..bd55d92c512 100644 --- a/inputFiles/singlePhaseWell/compressible_single_phase_wells_1d.xml +++ b/inputFiles/singlePhaseWell/compressible_single_phase_wells_1d.xml @@ -27,17 +27,27 @@ + control="BHP"> + + + + control="totalVolRate"> + + + @@ -52,7 +62,6 @@ ny="{ 1 }" nz="{ 1 }" cellBlockNames="{ cb1 }"> - - + control="BHP"> + + + + control="totalVolRate"> + + + @@ -52,7 +62,6 @@ ny="{ 1 }" nz="{ 1 }" cellBlockNames="{ cb1 }"> - + distanceFromHead="1.45" + skinFactor="1"/> - + targetRegions="{ wellRegion1, wellRegion2 }"> + control="BHP"> + + + + control="totalVolRate"> + + + @@ -53,7 +63,6 @@ ny="{ 1 }" nz="{ 1 }" cellBlockNames="{ cb1 }"> - - + targetRegions="{ wellRegion1, wellRegion2, wellRegion3 }"> + control="BHP"> + + + + control="BHP"> + + + + initialPressureCoefficient="0.01" + control="totalVolRate"> + + + @@ -60,7 +75,6 @@ ny="{ 20 }" nz="{ 1 }" cellBlockNames="{ cb1 }"> - - - + name="singlePhaseTPFA"/> diff --git a/inputFiles/singlePhaseWell/incompressible_single_phase_wells_hybrid_2d.xml b/inputFiles/singlePhaseWell/incompressible_single_phase_wells_hybrid_2d.xml index 743a1f7be70..911c36553c7 100644 --- a/inputFiles/singlePhaseWell/incompressible_single_phase_wells_hybrid_2d.xml +++ b/inputFiles/singlePhaseWell/incompressible_single_phase_wells_hybrid_2d.xml @@ -28,24 +28,39 @@ + control="BHP"> + + + + control="BHP"> + + + + control="totalVolRate"> + + + @@ -60,7 +75,6 @@ ny="{ 20 }" nz="{ 1 }" cellBlockNames="{ cb1 }"> - - - + targetRegions="{ wellRegion1, wellRegion2, wellRegion3 }"> + control="BHP"> + + + + control="BHP"> + + + + initialPressureCoefficient="0.01" + control="totalVolRate"> + + + @@ -62,7 +76,6 @@ ny="{ 20 }" nz="{ 1 }" cellBlockNames="{ cb1 }"> - - - + name="singlePhaseTPFA"/> diff --git a/inputFiles/singlePhaseWell/staircase_single_phase_wells_3d.xml b/inputFiles/singlePhaseWell/staircase_single_phase_wells_3d.xml index 6789ab90542..c8cb5ca9a9f 100644 --- a/inputFiles/singlePhaseWell/staircase_single_phase_wells_3d.xml +++ b/inputFiles/singlePhaseWell/staircase_single_phase_wells_3d.xml @@ -16,7 +16,7 @@ @@ -27,17 +27,27 @@ + control="totalVolRate"> + + + + control="totalVolRate"> + + + @@ -51,11 +61,7 @@ nx="{ 5, 5 }" ny="{ 5, 5 }" nz="{ 3, 3, 3, 3 }" - cellBlockNames="{ cb-0_0_0, cb-1_0_0, cb-0_1_0, cb-1_1_0, - cb-0_0_1, cb-1_0_1, cb-0_1_1, cb-1_1_1, - cb-0_0_2, cb-1_0_2, cb-0_1_2, cb-1_1_2, - cb-0_0_3, cb-1_0_3, cb-0_1_3, cb-1_1_3 }"> - + cellBlockNames="{ cb-0_0_0, cb-1_0_0, cb-0_1_0, cb-1_1_0, cb-0_0_1, cb-1_0_1, cb-0_1_1, cb-1_1_1, cb-0_0_2, cb-1_0_2, cb-0_1_2, cb-1_1_2, cb-0_0_3, cb-1_0_3, cb-0_1_3, cb-1_1_3 }"> - @@ -172,9 +177,9 @@ diff --git a/inputFiles/singlePhaseWell/staircase_single_phase_wells_hybrid_3d.xml b/inputFiles/singlePhaseWell/staircase_single_phase_wells_hybrid_3d.xml index 2298a32e483..c9c874c8a07 100644 --- a/inputFiles/singlePhaseWell/staircase_single_phase_wells_hybrid_3d.xml +++ b/inputFiles/singlePhaseWell/staircase_single_phase_wells_hybrid_3d.xml @@ -28,19 +28,29 @@ + control="BHP"> + + + + control="totalVolRate"> + + + @@ -54,11 +64,7 @@ nx="{ 5, 5 }" ny="{ 5, 5 }" nz="{ 3, 3, 3, 3 }" - cellBlockNames="{ cb-0_0_0, cb-1_0_0, cb-0_1_0, cb-1_1_0, - cb-0_0_1, cb-1_0_1, cb-0_1_1, cb-1_1_1, - cb-0_0_2, cb-1_0_2, cb-0_1_2, cb-1_1_2, - cb-0_0_3, cb-1_0_3, cb-0_1_3, cb-1_1_3 }"> - + cellBlockNames="{ cb-0_0_0, cb-1_0_0, cb-0_1_0, cb-1_1_0, cb-0_0_1, cb-1_0_1, cb-0_1_1, cb-1_1_1, cb-0_0_2, cb-1_0_2, cb-0_1_2, cb-1_1_2, cb-0_0_3, cb-1_0_3, cb-0_1_3, cb-1_1_3 }"> - - + + referenceElevation="-0.01"/> + + diff --git a/src/coreComponents/integrationTests/wellsTests/testIsothermalReservoirCompositionalMultiphaseMSWells.cpp b/src/coreComponents/integrationTests/wellsTests/testIsothermalReservoirCompositionalMultiphaseMSWells.cpp index da7d3606fb2..83cf41e02f6 100644 --- a/src/coreComponents/integrationTests/wellsTests/testIsothermalReservoirCompositionalMultiphaseMSWells.cpp +++ b/src/coreComponents/integrationTests/wellsTests/testIsothermalReservoirCompositionalMultiphaseMSWells.cpp @@ -104,15 +104,20 @@ char const * xmlInput = logLevel="2" type="injector" control="totalVolRate" - referenceElevation="-0.01" - targetBHP="1.45e7" enableCrossflow="0" useSurfaceConditions="1" surfacePressure="1.45e7" - surfaceTemperature="300.15" - targetTotalRate="0.001" - injectionTemperature="300.15" - injectionStream="{ 1.0, 0.0 }"/> + surfaceTemperature="300.15"> + + + @@ -580,6 +585,7 @@ class CompositionalMultiphaseReservoirSolverTest : public ::testing::Test solver->getSystemSolution() ); solver->implicitStepSetup( time, dt, domain ); + solver->wellSolver()->initializeWells( domain, time ); } static real64 constexpr time = 0.0; diff --git a/src/coreComponents/integrationTests/wellsTests/testIsothermalReservoirCompositionalMultiphaseSSWells.cpp b/src/coreComponents/integrationTests/wellsTests/testIsothermalReservoirCompositionalMultiphaseSSWells.cpp index 277c7a2a38c..bf01638a399 100644 --- a/src/coreComponents/integrationTests/wellsTests/testIsothermalReservoirCompositionalMultiphaseSSWells.cpp +++ b/src/coreComponents/integrationTests/wellsTests/testIsothermalReservoirCompositionalMultiphaseSSWells.cpp @@ -104,15 +104,20 @@ char const * xmlInput = logLevel="2" type="injector" control="totalVolRate" - referenceElevation="-0.01" - targetBHP="1.45e7" enableCrossflow="0" useSurfaceConditions="1" surfacePressure="1.45e7" - surfaceTemperature="300.15" - targetTotalRate="0.001" - injectionTemperature="300.15" - injectionStream="{ 1.0, 0.0 }"/> + surfaceTemperature="300.15"> + + + @@ -531,7 +536,6 @@ void testNumericalJacobian( CompositionalMultiphaseReservoirAndWells< Compositio real64 const dP = perturbParameter * ( wellElemPressure[iwelem] + perturbParameter ); wellElemPressure.move( hostMemorySpace, true ); wellElemPressure[iwelem] += dP; - // after perturbing, update the pressure-dependent quantities in the well wellSolver.updateState( domain ); @@ -652,6 +656,7 @@ class CompositionalMultiphaseReservoirSolverTest : public ::testing::Test solver->getSystemSolution() ); solver->implicitStepSetup( time, dt, domain ); + solver->wellSolver()->initializeWells( domain, time ); } static real64 constexpr time = 0.0; diff --git a/src/coreComponents/integrationTests/wellsTests/testOpenClosePerf.cpp b/src/coreComponents/integrationTests/wellsTests/testOpenClosePerf.cpp index c45c9a21e68..5502909a7c5 100644 --- a/src/coreComponents/integrationTests/wellsTests/testOpenClosePerf.cpp +++ b/src/coreComponents/integrationTests/wellsTests/testOpenClosePerf.cpp @@ -61,13 +61,19 @@ char const * PreXmlInput = logLevel="1" targetRegions="{wellRegion1}" useMass="0"> - + + + + diff --git a/src/coreComponents/integrationTests/wellsTests/testReservoirCompositionalMultiphaseMSWells.cpp b/src/coreComponents/integrationTests/wellsTests/testReservoirCompositionalMultiphaseMSWells.cpp index f1ca32e9e81..3ed0c30607e 100644 --- a/src/coreComponents/integrationTests/wellsTests/testReservoirCompositionalMultiphaseMSWells.cpp +++ b/src/coreComponents/integrationTests/wellsTests/testReservoirCompositionalMultiphaseMSWells.cpp @@ -61,21 +61,33 @@ char const * xmlInput = logLevel="1" targetRegions="{wellRegion1,wellRegion2}" useMass="0"> - - + + + + + + + + @@ -483,6 +495,7 @@ class CompositionalMultiphaseReservoirSolverTest : public ::testing::Test solver->getSystemSolution() ); solver->implicitStepSetup( time, dt, domain ); + solver->wellSolver()->initializeWells( domain, time ); } static real64 constexpr time = 0.0; diff --git a/src/coreComponents/integrationTests/wellsTests/testReservoirSinglePhaseMSWells.cpp b/src/coreComponents/integrationTests/wellsTests/testReservoirSinglePhaseMSWells.cpp index d75ad50a957..8fc88fd94ac 100644 --- a/src/coreComponents/integrationTests/wellsTests/testReservoirSinglePhaseMSWells.cpp +++ b/src/coreComponents/integrationTests/wellsTests/testReservoirSinglePhaseMSWells.cpp @@ -61,18 +61,31 @@ char const * PreXmlInput = - - + + + + + + + + + @@ -357,6 +370,8 @@ class SinglePhaseReservoirSolverTest : public ::testing::Test solver->getSystemSolution() ); solver->implicitStepSetup( TIME, DT, domain ); + + solver->wellSolver()->initializeWells( domain, TIME ); } void TestAssembleCouplingTerms() diff --git a/src/coreComponents/integrationTests/wellsTests/testReservoirThermalSinglePhaseMSWells.cpp b/src/coreComponents/integrationTests/wellsTests/testReservoirThermalSinglePhaseMSWells.cpp index d10f1da56f6..5baf73b0430 100644 --- a/src/coreComponents/integrationTests/wellsTests/testReservoirThermalSinglePhaseMSWells.cpp +++ b/src/coreComponents/integrationTests/wellsTests/testReservoirThermalSinglePhaseMSWells.cpp @@ -64,22 +64,35 @@ char const * PreXmlInput = logLevel="1" isThermal="1" targetRegions="{wellRegion1,wellRegion2}"> - - + + + + + + + + + @@ -515,6 +528,7 @@ class SinglePhaseReservoirSolverTest : public ::testing::Test solver->getSystemSolution() ); solver->implicitStepSetup( TIME, DT, domain ); + solver->wellSolver()->initializeWells( domain, TIME ); } void TestAssembleCouplingTerms() diff --git a/src/coreComponents/integrationTests/wellsTests/testReservoirThermalSinglePhaseMSWells_RateInj.cpp b/src/coreComponents/integrationTests/wellsTests/testReservoirThermalSinglePhaseMSWells_RateInj.cpp index 255b0201692..07ccbe5626a 100644 --- a/src/coreComponents/integrationTests/wellsTests/testReservoirThermalSinglePhaseMSWells_RateInj.cpp +++ b/src/coreComponents/integrationTests/wellsTests/testReservoirThermalSinglePhaseMSWells_RateInj.cpp @@ -79,19 +79,24 @@ char const * XmlInput = isThermal="1" writeCSV="1" targetRegions="{ wellRegion2 }"> - + + referenceElevation="-0.01"/> + + @@ -597,6 +602,8 @@ class SinglePhaseReservoirSolverTest : public ::testing::Test solver->getSystemSolution() ); solver->implicitStepSetup( TIME, DT, domain ); + + solver->wellSolver()->initializeWells( domain, TIME ); } void TestAssembleCouplingTerms() diff --git a/src/coreComponents/integrationTests/wellsTests/testThermalReservoirCompositionalMultiphaseMSWells.cpp b/src/coreComponents/integrationTests/wellsTests/testThermalReservoirCompositionalMultiphaseMSWells.cpp index 98aa57a5c84..bd711598535 100644 --- a/src/coreComponents/integrationTests/wellsTests/testThermalReservoirCompositionalMultiphaseMSWells.cpp +++ b/src/coreComponents/integrationTests/wellsTests/testThermalReservoirCompositionalMultiphaseMSWells.cpp @@ -108,15 +108,20 @@ char const * xmlInput = logLevel="2" type="injector" control="totalVolRate" - referenceElevation="-0.01" - targetBHP="1.45e7" enableCrossflow="0" useSurfaceConditions="1" surfacePressure="1.45e7" - surfaceTemperature="300.15" - targetTotalRate="0.001" - injectionTemperature="300.15" - injectionStream="{ 0.99, 0.01 }"/> + surfaceTemperature="300.15"> + + + @@ -699,6 +704,7 @@ class CompositionalMultiphaseReservoirSolverTest : public ::testing::Test solver->getSystemSolution() ); solver->implicitStepSetup( time, dt, domain ); + solver->wellSolver()->initializeWells( domain, time ); } static real64 constexpr time = 0.0; diff --git a/src/coreComponents/integrationTests/wellsTests/testThermalReservoirCompositionalMultiphaseSSWells.cpp b/src/coreComponents/integrationTests/wellsTests/testThermalReservoirCompositionalMultiphaseSSWells.cpp index 76fa20d4cea..055682b1863 100644 --- a/src/coreComponents/integrationTests/wellsTests/testThermalReservoirCompositionalMultiphaseSSWells.cpp +++ b/src/coreComponents/integrationTests/wellsTests/testThermalReservoirCompositionalMultiphaseSSWells.cpp @@ -109,15 +109,20 @@ char const * xmlInput = logLevel="2" type="injector" control="totalVolRate" - referenceElevation="-0.01" - targetBHP="1.45e7" enableCrossflow="0" useSurfaceConditions="1" surfacePressure="1.45e7" - surfaceTemperature="300.15" - targetTotalRate="0.001" - injectionTemperature="300.15" - injectionStream="{ 0.99, 0.01 }"/> + surfaceTemperature="300.15"> + + + @@ -688,6 +693,7 @@ class CompositionalMultiphaseReservoirSolverTest : public ::testing::Test solver->getSystemSolution() ); solver->implicitStepSetup( time, dt, domain ); + solver->wellSolver()->initializeWells( domain, time ); } static real64 constexpr time = 0.0; diff --git a/src/coreComponents/linearAlgebra/utilities/ComponentMask.hpp b/src/coreComponents/linearAlgebra/utilities/ComponentMask.hpp index a27d1a37a64..42eab7c568e 100644 --- a/src/coreComponents/linearAlgebra/utilities/ComponentMask.hpp +++ b/src/coreComponents/linearAlgebra/utilities/ComponentMask.hpp @@ -92,10 +92,10 @@ class ComponentMask private: /// Number of bits in mask storage - static constexpr int NUM_BITS = internal::roundToNextPowerOfTwo( MAX_COMP ); + static constexpr int NUM_BITS = geos::internal::roundToNextPowerOfTwo( MAX_COMP ); /// Type used to represent the bit mask - using mask_t = typename internal::ComponentMaskType< NUM_BITS >::type; + using mask_t = typename geos::internal::ComponentMaskType< NUM_BITS >::type; public: diff --git a/src/coreComponents/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/fluidFlow/CMakeLists.txt b/src/coreComponents/physicsSolvers/fluidFlow/CMakeLists.txt index 28ffa57e587..c7b51561083 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/CMakeLists.txt +++ b/src/coreComponents/physicsSolvers/fluidFlow/CMakeLists.txt @@ -116,11 +116,21 @@ set( fluidFlowSolvers_headers wells/SinglePhaseWellFields.hpp wells/WellConstants.hpp wells/WellControls.hpp + wells/WellConstraintsBase.hpp + wells/WellInjectionConstraint.hpp + wells/WellProductionConstraint.hpp + wells/WellBHPConstraints.hpp + wells/WellVolumeRateConstraint.hpp + wells/WellMassRateConstraint.hpp + wells/WellPhaseVolumeRateConstraint.hpp + wells/WellLiquidRateConstraint.hpp wells/WellSolverBase.hpp wells/WellSolverBaseFields.hpp wells/LogLevelsInfo.hpp wells/kernels/SinglePhaseWellKernels.hpp wells/kernels/CompositionalMultiphaseWellKernels.hpp + wells/kernels/CompositionalMultiphaseWellConstraintKernels.hpp + wells/kernels/SinglePhaseWellConstraintKernels.hpp proppantTransport/ProppantTransport.hpp proppantTransport/ProppantTransportFields.hpp proppantTransport/ProppantTransportKernels.hpp ) @@ -150,6 +160,14 @@ set( fluidFlowSolvers_sources wells/SinglePhaseWell.cpp wells/kernels/SinglePhaseWellKernels.cpp wells/WellControls.cpp + wells/WellConstraintsBase.cpp + wells/WellInjectionConstraint.cpp + wells/WellProductionConstraint.cpp + wells/WellBHPConstraints.cpp + wells/WellVolumeRateConstraint.cpp + wells/WellMassRateConstraint.cpp + wells/WellPhaseVolumeRateConstraint.cpp + wells/WellLiquidRateConstraint.cpp wells/WellSolverBase.cpp proppantTransport/ProppantTransport.cpp proppantTransport/ProppantTransportKernels.cpp ) diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/CompositionalMultiphaseWell.cpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/CompositionalMultiphaseWell.cpp index 3b8184a1a38..61574270b4a 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" + + + #if defined( __INTEL_COMPILER ) #pragma GCC optimize "O0" #endif @@ -237,24 +247,13 @@ void CompositionalMultiphaseWell::registerDataOnMesh( Group & meshBodies ) WellControls & wellControls = getWellControls( subRegion ); wellControls.registerWrapper< real64 >( viewKeyStruct::currentBHPString() ); - wellControls.registerWrapper< array1d< real64 > >( viewKeyStruct::dCurrentBHPString() ). - setSizedFromParent( 0 ). - reference().resizeDimension< 0 >( m_numComponents + 2 ); // dP, dT, dC - wellControls.registerWrapper< array1d< real64 > >( viewKeyStruct::currentPhaseVolRateString() ). setSizedFromParent( 0 ). reference().resizeDimension< 0 >( m_numPhases ); - wellControls.registerWrapper< array2d< real64 > >( viewKeyStruct::dCurrentPhaseVolRateString() ). - setSizedFromParent( 0 ). - reference().resizeDimension< 0, 1 >( m_numPhases, m_numComponents + 3 ); // dP, dT, dC, dQ - wellControls.registerWrapper< real64 >( viewKeyStruct::massDensityString() ); wellControls.registerWrapper< real64 >( viewKeyStruct::currentTotalVolRateString() ); - wellControls.registerWrapper< array1d< real64 > >( viewKeyStruct::dCurrentTotalVolRateString() ). - setSizedFromParent( 0 ). - reference().resizeDimension< 0 >( m_numComponents + 3 ); // dP, dT, dC dQ wellControls.registerWrapper< real64 >( viewKeyStruct::massDensityString() ); @@ -390,49 +389,14 @@ void CompositionalMultiphaseWell::validateConstitutiveModels( DomainPartition co } ); } -void CompositionalMultiphaseWell::validateInjectionStreams( WellElementSubRegion const & subRegion ) const -{ - WellControls const & wellControls = getWellControls( subRegion ); - - // check well injection stream for injectors - if( wellControls.isInjector()) - { - arrayView1d< real64 const > const & injectionStream = wellControls.getInjectionStream(); - - integer const streamSize = injectionStream.size(); - GEOS_THROW_IF( ( streamSize == 0 ), - "WellControls " << wellControls.getName() << - " : Injection stream not specified for well " << subRegion.getName(), - InputError ); - GEOS_THROW_IF( ( streamSize != m_numComponents ), - "WellControls " << wellControls.getName() << - " : Injection stream for well " << subRegion.getName() << " should have " << - m_numComponents << " components.", - InputError ); - real64 compFracSum = 0; - for( integer ic = 0; ic < m_numComponents; ++ic ) - { - real64 const compFrac = injectionStream[ic]; - GEOS_THROW_IF( ( compFrac < 0.0 ) || ( compFrac > 1.0 ), - "WellControls " << wellControls.getDataContext() << - ": Invalid injection stream for well " << subRegion.getName(), - InputError, 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, ElementRegionManager const & elemManager ) { + GEOS_UNUSED_VAR( time_n ); WellControls & wellControls = getWellControls( subRegion ); if( !wellControls.useSurfaceConditions() ) { @@ -472,63 +436,14 @@ void CompositionalMultiphaseWell::validateWellConstraints( real64 const & time_n string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString()); MultiFluidBase const & fluid = subRegion.getConstitutiveModel< MultiFluidBase >( fluidName ); - WellControls::Control const currentControl = wellControls.getControl(); - real64 const & targetTotalRate = wellControls.getTargetTotalRate( time_n ); - real64 const & targetPhaseRate = wellControls.getTargetPhaseRate( time_n ); - real64 const & targetMassRate = wellControls.getTargetMassRate( time_n ); - - GEOS_THROW_IF( wellControls.isInjector() && currentControl == WellControls::Control::PHASEVOLRATE, - "WellControls " << wellControls.getDataContext() << - ": Phase rate control is not available for injectors", - InputError, 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 ) - { - 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() ); + // tjb + wellControls.forSubGroups< InjectionConstraint< PhaseVolumeRateConstraint >, ProductionConstraint< PhaseVolumeRateConstraint > >( [&]( auto & constraint ) + { + constraint.validatePhaseType( fluid ); + } ); + + + } void CompositionalMultiphaseWell::initializePostSubGroups() @@ -539,16 +454,6 @@ void CompositionalMultiphaseWell::initializePostSubGroups() validateConstitutiveModels( domain ); - forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &, - MeshLevel & mesh, - string_array const & regionNames ) - { - mesh.getElemManager().forElementSubRegions< WellElementSubRegion >( regionNames, [&]( localIndex const, - WellElementSubRegion & subRegion ) - { - validateInjectionStreams( subRegion ); - } ); - } ); } void CompositionalMultiphaseWell::initializePostInitialConditionsPreSubGroups() @@ -567,6 +472,7 @@ void CompositionalMultiphaseWell::postRestartInitialization() // loop over the wells mesh.getElemManager().forElementSubRegions< WellElementSubRegion >( regionNames, [&]( localIndex const, WellElementSubRegion & subRegion ) + { // setup fluid separator WellControls & wellControls = getWellControls( subRegion ); @@ -622,63 +528,35 @@ void CompositionalMultiphaseWell::updateBHPForConstraint( WellElementSubRegion & { return; } - using Deriv = constitutive::multifluid::DerivativeOffset; - integer const numComp = m_numComponents; localIndex const iwelemRef = subRegion.getTopWellElementIndex(); - string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString() ); - MultiFluidBase const & fluid = subRegion.getConstitutiveModel< MultiFluidBase >( fluidName ); - integer const isThermal = fluid.isThermal(); // subRegion data - arrayView1d< real64 const > const & pres = subRegion.getField< well::pressure >(); - arrayView1d< real64 > const & totalMassDens = subRegion.getField< well::totalMassDensity >(); - arrayView2d< real64, compflow::USD_FLUID_DC > const & dTotalMassDens = subRegion.getField< well::dTotalMassDensity >(); - arrayView1d< real64 const > const wellElemGravCoef = subRegion.getField< well::gravityCoefficient >(); // control data - WellControls & wellControls = getWellControls( subRegion ); string const wellControlsName = wellControls.getName(); real64 const & refGravCoef = wellControls.getReferenceGravityCoef(); real64 & currentBHP = wellControls.getReference< real64 >( CompositionalMultiphaseWell::viewKeyStruct::currentBHPString() ); - arrayView1d< real64 > const & dCurrentBHP = - wellControls.getReference< array1d< real64 > >( CompositionalMultiphaseWell::viewKeyStruct::dCurrentBHPString() ); - geos::internal::kernelLaunchSelectorCompThermSwitch( numComp, isThermal, [&] ( auto NC, auto ISTHERMAL ) + // bring everything back to host, capture the scalars by reference + forAll< serialPolicy >( 1, [ pres, + totalMassDens, + wellElemGravCoef, + ¤tBHP, + &iwelemRef, + &refGravCoef] ( localIndex const ) { - integer constexpr IS_THERMAL = ISTHERMAL(); - GEOS_UNUSED_VAR( NC ); - // bring everything back to host, capture the scalars by reference - forAll< serialPolicy >( 1, [&numComp, - pres, - totalMassDens, - dTotalMassDens, - wellElemGravCoef, - ¤tBHP, - &dCurrentBHP, - &iwelemRef, - &refGravCoef] ( localIndex const ) - { - real64 const diffGravCoef = refGravCoef - wellElemGravCoef[iwelemRef]; - currentBHP = pres[iwelemRef] + totalMassDens[iwelemRef] * diffGravCoef; - dCurrentBHP[Deriv::dP] = 1 + dTotalMassDens[iwelemRef][Deriv::dP] * diffGravCoef; - for( integer ic = 0; ic < numComp; ++ic ) - { - dCurrentBHP[Deriv::dC+ic] = dTotalMassDens[iwelemRef][Deriv::dC+ic] * diffGravCoef; - } - if constexpr ( IS_THERMAL ) - { - dCurrentBHP[Deriv::dT] = dTotalMassDens[iwelemRef][Deriv::dT] * diffGravCoef; - } - } ); + real64 const diffGravCoef = refGravCoef - wellElemGravCoef[iwelemRef]; + currentBHP = pres[iwelemRef] + totalMassDens[iwelemRef] * diffGravCoef; } ); + GEOS_LOG_LEVEL_BY_RANK( logInfo::BoundaryConditions, GEOS_FMT( "{}: BHP (at the specified reference elevation) = {} Pa", wellControlsName, currentBHP ) ); @@ -695,8 +573,7 @@ void CompositionalMultiphaseWell::updateVolRatesForConstraint( WellElementSubReg return; } - integer constexpr maxNumComp = constitutive::MultiFluidBase::MAX_NUM_COMPONENTS; - integer const numComp = m_numComponents; + integer const numPhase = m_numPhases; localIndex const iwelemRef = subRegion.getTopWellElementIndex(); @@ -704,60 +581,110 @@ void CompositionalMultiphaseWell::updateVolRatesForConstraint( WellElementSubReg // subRegion data - arrayView1d< real64 const > const & pres = subRegion.getField< well::pressure >(); - arrayView1d< real64 const > const & temp = subRegion.getField< well::temperature >(); arrayView1d< real64 const > const & connRate = subRegion.getField< well::mixtureConnectionRate >(); - arrayView2d< real64 const, compflow::USD_COMP > const & compFrac = subRegion.getField< well::globalCompFraction >(); - arrayView3d< real64 const, compflow::USD_COMP_DC > const & dCompFrac_dCompDens = subRegion.getField< well::dGlobalCompFraction_dGlobalCompDensity >(); - // fluid data constitutive::MultiFluidBase & fluidSeparator = wellControls.getMultiFluidSeparator(); - integer isThermal = fluidSeparator.isThermal(); - arrayView3d< real64 const, constitutive::multifluid::USD_PHASE > const & phaseFrac = fluidSeparator.phaseFraction(); - arrayView4d< real64 const, constitutive::multifluid::USD_PHASE_DC > const & dPhaseFrac = fluidSeparator.dPhaseFraction(); + arrayView3d< real64 const, constitutive::multifluid::USD_PHASE > const & phaseFrac = fluidSeparator.phaseFraction(); arrayView2d< real64 const, constitutive::multifluid::USD_FLUID > const & totalDens = fluidSeparator.totalDensity(); - arrayView3d< real64 const, constitutive::multifluid::USD_FLUID_DC > const & dTotalDens = fluidSeparator.dTotalDensity(); - arrayView3d< real64 const, constitutive::multifluid::USD_PHASE > const & phaseDens = fluidSeparator.phaseDensity(); - arrayView4d< real64 const, constitutive::multifluid::USD_PHASE_DC > const & dPhaseDens = fluidSeparator.dPhaseDensity(); // control data string const wellControlsName = wellControls.getName(); - bool const logSurfaceCondition = isLogLevelActive< logInfo::BoundaryConditions >( wellControls.getLogLevel()); - string const massUnit = m_useMass ? "kg" : "mol"; - integer const useSurfaceConditions = wellControls.useSurfaceConditions(); - real64 flashPressure; - real64 flashTemperature; - if( useSurfaceConditions ) - { - // use surface conditions - flashPressure = wellControls.getSurfacePressure(); - flashTemperature = wellControls.getSurfaceTemperature(); - } - else + arrayView1d< real64 > const & currentPhaseVolRate = + wellControls.getReference< array1d< real64 > >( CompositionalMultiphaseWell::viewKeyStruct::currentPhaseVolRateString() ); + + real64 & currentTotalVolRate = + wellControls.getReference< real64 >( CompositionalMultiphaseWell::viewKeyStruct::currentTotalVolRateString() ); + + real64 & currentMassRate = + wellControls.getReference< real64 >( CompositionalMultiphaseWell::viewKeyStruct::currentMassRateString() ); + + real64 & massDensity = + wellControls.getReference< real64 >( CompositionalMultiphaseWell::viewKeyStruct::massDensityString() ); + + // 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 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 + // Step 1: update the total volume rate + + real64 const currentTotalRate = connRate[iwelemRef]; + // Assumes useMass is true + currentMassRate = currentTotalRate; + // Step 1.1: compute the inverse of the total density and derivatives + massDensity = totalDens[iwelemRef][0]; + real64 const totalDensInv = 1.0 / totalDens[iwelemRef][0]; + + // 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(); + + WellControls & wellControls = getWellControls( subRegion ); + + // subRegion data + arrayView1d< real64 const > const & connRate = subRegion.getField< fields::well::mixtureConnectionRate >(); + + // fluid data + constitutive::MultiFluidBase & fluidSeparator = wellControls.getMultiFluidSeparator(); + arrayView3d< real64 const, constitutive::multifluid::USD_PHASE > const & phaseFrac = fluidSeparator.phaseFraction(); + arrayView2d< real64 const, constitutive::multifluid::USD_FLUID > const & totalDens = fluidSeparator.totalDensity(); + arrayView3d< real64 const, constitutive::multifluid::USD_PHASE > const & phaseDens = fluidSeparator.phaseDensity(); + + // control data + string const wellControlsName = wellControls.getName(); + bool const logSurfaceCondition = isLogLevelActive< logInfo::BoundaryConditions >( wellControls.getLogLevel()); + string const massUnit = m_useMass ? "kg" : "mol"; + + integer const useSurfaceConditions = wellControls.useSurfaceConditions(); + arrayView1d< real64 > const & currentPhaseVolRate = wellControls.getReference< array1d< real64 > >( CompositionalMultiphaseWell::viewKeyStruct::currentPhaseVolRateString() ); - arrayView2d< real64 > const & dCurrentPhaseVolRate = - wellControls.getReference< array2d< real64 > >( CompositionalMultiphaseWell::viewKeyStruct::dCurrentPhaseVolRateString() ); real64 & currentTotalVolRate = wellControls.getReference< real64 >( CompositionalMultiphaseWell::viewKeyStruct::currentTotalVolRateString() ); @@ -765,165 +692,84 @@ void CompositionalMultiphaseWell::updateVolRatesForConstraint( WellElementSubReg real64 & currentMassRate = wellControls.getReference< real64 >( CompositionalMultiphaseWell::viewKeyStruct::currentMassRateString() ); - arrayView1d< real64 > const & dCurrentTotalVolRate = - wellControls.getReference< array1d< real64 > >( CompositionalMultiphaseWell::viewKeyStruct::dCurrentTotalVolRateString() ); - real64 & massDensity = wellControls.getReference< real64 >( CompositionalMultiphaseWell::viewKeyStruct::massDensityString() ); + constitutive::constitutiveUpdatePassThru( fluidSeparator, [&] ( auto & castedFluidSeparator ) { // 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, + &useSurfaceConditions, + ¤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 && useSurfaceConditions ) + { + GEOS_LOG_RANK( GEOS_FMT( "{}: total fluid density at surface conditions = {} {}/sm3, total rate = {} {}/s, total surface volumetric rate = {} sm3/s", + wellControlsName, totalDens[iwelemRef][0], massUnit, connRate[iwelemRef], massUnit, currentTotalVolRate ) ); + } - // skip the rest of this function if phase ip is absent - bool const phaseExists = (phaseFrac[iwelemRef][0][ip] > 0); - if( !phaseExists ) - { - continue; - } + // 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 && useSurfaceConditions ) + { + GEOS_LOG_RANK( GEOS_FMT( "{}: density of phase {} at surface conditions = {} {}/sm3, phase surface volumetric rate = {} sm3/s", + wellControlsName, ip, phaseDens[iwelemRef][0][ip], massUnit, currentPhaseVolRate[ip] ) ); } - } ); + } } ); } ); } - void CompositionalMultiphaseWell::updateFluidModel( WellElementSubRegion & subRegion ) { GEOS_MARK_FUNCTION; @@ -950,70 +796,181 @@ void CompositionalMultiphaseWell::updateFluidModel( WellElementSubRegion & subRe } -real64 CompositionalMultiphaseWell::updatePhaseVolumeFraction( WellElementSubRegion & subRegion ) const +void CompositionalMultiphaseWell::updateSeparator( 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, - m_numPhases, - subRegion, - fluid ); + localIndex const iwelemRef = subRegion.getTopWellElementIndex(); - return maxDeltaPhaseVolFrac; -} + WellControls & wellControls = getWellControls( subRegion ); -void CompositionalMultiphaseWell::updateTotalMassDensity( WellElementSubRegion & subRegion ) const -{ - GEOS_MARK_FUNCTION; + // 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 >(); - string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString() ); - MultiFluidBase & fluid = subRegion.getConstitutiveModel< MultiFluidBase >( fluidName ); - fluid.isThermal() ? - thermalCompositionalMultiphaseWellKernels:: - TotalMassDensityKernelFactory:: - createAndLaunch< parallelDevicePolicy<> >( m_numComponents, - m_numPhases, - subRegion, - fluid ) - : - compositionalMultiphaseWellKernels:: - TotalMassDensityKernelFactory:: - createAndLaunch< parallelDevicePolicy<> >( m_numComponents, - m_numPhases, - subRegion, - fluid ); -} + // fluid data + constitutive::MultiFluidBase & fluidSeparator = wellControls.getMultiFluidSeparator(); -void CompositionalMultiphaseWell::updateState( DomainPartition & domain ) -{ - GEOS_MARK_FUNCTION; + // control data - real64 maxPhaseVolFrac = 0.0; - forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &, - MeshLevel & mesh, - string_array const & regionNames ) - { - mesh.getElemManager().forElementSubRegions< WellElementSubRegion >( regionNames, [&]( localIndex const, - WellElementSubRegion & subRegion ) - { + string const wellControlsName = wellControls.getName(); + bool const logSurfaceCondition = isLogLevelActive< logInfo::BoundaryConditions >( wellControls.getLogLevel()); + string const massUnit = m_useMass ? "kg" : "mol"; + + integer const useSurfaceConditions = wellControls.useSurfaceConditions(); + real64 flashPressure; + real64 flashTemperature; + if( useSurfaceConditions ) + { + // use surface conditions + flashPressure = wellControls.getSurfacePressure(); + flashTemperature = wellControls.getSurfaceTemperature(); + } + else + { + // If flashPressure is not set by region the value is defaulted to -1 and indicates to use top segment conditions + flashPressure = wellControls.getRegionAveragePressure(); + if( flashPressure < 0.0 ) + { + // region name not set, use segment conditions + flashPressure = pres[iwelemRef]; + flashTemperature = temp[iwelemRef]; + } + else + { + // use reservoir region averages + flashTemperature = wellControls.getRegionAverageTemperature(); + } + } + + constitutive::constitutiveUpdatePassThru( fluidSeparator, [&] ( auto & castedFluidSeparator ) + { + // typename TYPEOFREF( castedFluid ) ::KernelWrapper fluidWrapper = castedFluid.createKernelWrapper(); + typename TYPEOFREF( castedFluidSeparator ) ::KernelWrapper fluidSeparatorWrapper = castedFluidSeparator.createKernelWrapper(); + // bring everything back to host, capture the scalars by reference + forAll< serialPolicy >( 1, [fluidSeparatorWrapper, + wellControlsName, + useSurfaceConditions, + flashPressure, + flashTemperature, + logSurfaceCondition, + iwelemRef, + pres, + temp, + compFrac] ( localIndex const ) + { + // - Surface conditions: using the surface pressure provided by the user + // - Reservoir conditions: using the pressure in the top element + if( useSurfaceConditions ) + { + // we need to compute the surface density + //fluidWrapper.update( iwelemRef, 0, surfacePres, surfaceTemp, compFrac[iwelemRef] ); + fluidSeparatorWrapper.update( iwelemRef, 0, flashPressure, flashTemperature, compFrac[iwelemRef] ); + if( logSurfaceCondition ) + { + GEOS_LOG_RANK( GEOS_FMT( "{}: surface density computed with P_surface = {} Pa and T_surface = {} K", + wellControlsName, flashPressure, flashTemperature ) ); + } +#ifdef GEOS_USE_HIP + GEOS_UNUSED_VAR( wellControlsName ); +#endif + } + else + { + fluidSeparatorWrapper.update( iwelemRef, 0, flashPressure, flashTemperature, compFrac[iwelemRef] ); + } + } ); + } ); +} + + +real64 CompositionalMultiphaseWell::updatePhaseVolumeFraction( WellElementSubRegion & subRegion ) const +{ + GEOS_MARK_FUNCTION; + + 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 ); + + return maxDeltaPhaseVolFrac; +} + +void CompositionalMultiphaseWell::updateTotalMassDensity( WellElementSubRegion & subRegion ) const +{ + GEOS_MARK_FUNCTION; + + string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString() ); + MultiFluidBase & fluid = subRegion.getConstitutiveModel< MultiFluidBase >( fluidName ); + fluid.isThermal() ? + thermalCompositionalMultiphaseWellKernels:: + TotalMassDensityKernelFactory:: + createAndLaunch< parallelDevicePolicy<> >( m_numComponents, + m_numPhases, + subRegion, + fluid ) + : + compositionalMultiphaseWellKernels:: + TotalMassDensityKernelFactory:: + createAndLaunch< parallelDevicePolicy<> >( m_numComponents, + m_numPhases, + subRegion, + fluid ); + +} + +real64 CompositionalMultiphaseWell::updateWellState( WellElementSubRegion & subRegion ) +{ + GEOS_MARK_FUNCTION; + + real64 maxPhaseVolFrac = updateSubRegionState( subRegion ); + + return maxPhaseVolFrac; + +} + +void CompositionalMultiphaseWell::updateState( DomainPartition & domain ) +{ + GEOS_MARK_FUNCTION; + //tjb + real64 maxPhaseVolFrac = 0.0; + forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &, + MeshLevel & mesh, + string_array const & regionNames ) + { + mesh.getElemManager().forElementSubRegions< WellElementSubRegion >( regionNames, [&]( localIndex const, + WellElementSubRegion & subRegion ) + { WellControls & wellControls = getWellControls( subRegion ); - if( wellControls.getWellStatus() == WellControls::Status::OPEN ) + if( wellControls.getWellState()) { +#if 1 + real64 const maxRegionPhaseVolFrac = updateWellState( subRegion ); +#else real64 const maxRegionPhaseVolFrac = updateSubRegionState( subRegion ); + +#endif maxPhaseVolFrac = LvArray::math::max( maxRegionPhaseVolFrac, maxPhaseVolFrac ); } } ); @@ -1028,30 +985,279 @@ void CompositionalMultiphaseWell::updateState( DomainPartition & domain ) real64 CompositionalMultiphaseWell::updateSubRegionState( WellElementSubRegion & subRegion ) { - // update properties - updateGlobalComponentFraction( subRegion ); + real64 maxPhaseVolChange=0.0; + WellControls & wellControls = getWellControls( subRegion ); + if( wellControls.getWellState()) + { + if( m_useNewCode ) + { + // update properties + updateGlobalComponentFraction( subRegion ); + + // update densities, phase fractions, phase volume fractions - // update volumetric rates for the well constraints - // note: this must be called before updateFluidModel - updateVolRatesForConstraint( subRegion ); + updateFluidModel( subRegion ); // Calculate fluid properties - // update densities, phase fractions, phase volume fractions + updateSeparator( subRegion ); // Calculate fluid properties at control conditions - updateFluidModel( subRegion ); // Calculate fluid properties; - real64 maxPhaseVolChange = updatePhaseVolumeFraction( subRegion ); - updateTotalMassDensity( subRegion ); - // update the current BHP pressure - updateBHPForConstraint( subRegion ); + updateVolRatesForConstraint( subRegion ); // remove tjb ?? + + 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 = + wellControls.getReference< real64 >( CompositionalMultiphaseWell::viewKeyStruct::currentBHPString() ); + array1d< real64 > currentPhaseVolRate = + wellControls.getReference< array1d< real64 > >( CompositionalMultiphaseWell::viewKeyStruct::currentPhaseVolRateString() ); + real64 & currentTotalVolRate = + wellControls.getReference< real64 >( CompositionalMultiphaseWell::viewKeyStruct::currentTotalVolRateString() ); + real64 & currentMassRate = + wellControls.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() ) + { + wellControls.getReference< array1d< real64 > >( + CompositionalMultiphaseWell::viewKeyStruct::currentPhaseVolRateString() ) =currentPhaseVolRate; + + + } + } + else + { + // update properties + updateGlobalComponentFraction( subRegion ); + + // update volumetric rates for the well constraints + // note: this must be called before updateFluidModel + + updateVolRatesForConstraint( subRegion ); + + // update densities, phase fractions, phase volume fractions + + updateFluidModel( subRegion ); // Calculate fluid properties; + maxPhaseVolChange = updatePhaseVolumeFraction( subRegion ); + updateTotalMassDensity( subRegion ); + // update the current BHP pressure + updateBHPForConstraint( subRegion ); + } + + } return maxPhaseVolChange; } -void CompositionalMultiphaseWell::initializeWells( DomainPartition & domain, real64 const & time_n ) +void CompositionalMultiphaseWell::initializeWell( DomainPartition & domain, MeshLevel & mesh, WellElementSubRegion & subRegion, real64 const & time_n ) { GEOS_MARK_FUNCTION; - + GEOS_UNUSED_VAR( domain ); integer const numComp = m_numComponents; integer const numPhase = m_numPhases; + m_nextDt = -1; + // TODO: change the way we access the flowSolver here + CompositionalMultiphaseBase const & flowSolver = getParent().getGroup< CompositionalMultiphaseBase >( getFlowSolverName() ); + + + compositionalMultiphaseWellKernels::PresTempCompFracInitializationKernel::CompFlowAccessors + resCompFlowAccessors( mesh.getElemManager(), flowSolver.getName() ); + compositionalMultiphaseWellKernels::PresTempCompFracInitializationKernel::MultiFluidAccessors + resMultiFluidAccessors( mesh.getElemManager(), flowSolver.getName() ); + + WellControls & wellControls = getWellControls( subRegion ); + PerforationData const & perforationData = *subRegion.getPerforationData(); + arrayView2d< real64 const > const compPerfRate = perforationData.getField< fields::well::compPerforationRate >(); + + bool const hasNonZeroRate = MpiWrapper::max< integer >( hasNonZero( compPerfRate )); + + if( time_n <= 0.0 || ( wellControls.isWellOpen( ) && !hasNonZeroRate ) ) + { + wellControls.setWellState( true ); + if( wellControls.getCurrentConstraint() == nullptr ) + { + // tjb needed for backward compatibility. and these 2 lists must be consistent + ConstraintTypeId inputControl = ConstraintTypeId( wellControls.getInputControl()); + if( wellControls.isProducer() ) + { + wellControls.forSubGroups< MinimumBHPConstraint, ProductionConstraint< VolumeRateConstraint >, ProductionConstraint< MassRateConstraint >, + ProductionConstraint< PhaseVolumeRateConstraint > >( [&]( auto & constraint ) + { + if( constraint.getControl() == inputControl ) + { + wellControls.setCurrentConstraint( &constraint ); + wellControls.setControl( static_cast< WellControls::Control >(inputControl) ); // tjb old + } + + } ); + } + else + { + + wellControls.forSubGroups< MaximumBHPConstraint, InjectionConstraint< VolumeRateConstraint >, InjectionConstraint< MassRateConstraint >, + InjectionConstraint< PhaseVolumeRateConstraint > >( [&]( auto & constraint ) + { + if( constraint.getControl() == inputControl ) + { + wellControls.setCurrentConstraint( &constraint ); + wellControls.setControl( static_cast< WellControls::Control >(inputControl) ); // tjb old + } + } ); + } + } + // get well primary variables on well elements + arrayView1d< real64 > const & wellElemPressure = subRegion.getField< well::pressure >(); + arrayView1d< real64 > const & wellElemTemp = subRegion.getField< well::temperature >(); + arrayView2d< real64, compflow::USD_COMP > const & wellElemCompDens = subRegion.getField< well::globalCompDensity >(); + arrayView1d< real64 > const & connRate = subRegion.getField< well::mixtureConnectionRate >(); + + // get the info stored on well elements + arrayView2d< real64, compflow::USD_COMP > const & wellElemCompFrac = subRegion.getField< well::globalCompFraction >(); + arrayView1d< real64 const > const & wellElemGravCoef = subRegion.getField< well::gravityCoefficient >(); + + // get the element region, subregion, index + arrayView1d< localIndex const > const resElementRegion = perforationData.getField< perforation::reservoirElementRegion >(); + arrayView1d< localIndex const > const resElementSubRegion = perforationData.getField< perforation::reservoirElementSubRegion >(); + arrayView1d< localIndex const > const resElementIndex = perforationData.getField< perforation::reservoirElementIndex >(); + + arrayView1d< real64 const > const & perfGravCoef = perforationData.getField< fields::well::gravityCoefficient >(); + arrayView1d< integer const > const & perfStatus = perforationData.getField< fields::perforation::perforationStatus >(); + + // 1) Loop over all perforations to compute an average mixture density and component fraction + // 2) Initialize the reference pressure + // 3) Estimate the pressures in the well elements using the average density + compositionalMultiphaseWellKernels:: + PresTempCompFracInitializationKernel:: + launch( perforationData.size(), + subRegion.size(), + numComp, + numPhase, + wellControls, + 0.0, // initialization done at t = 0 + resCompFlowAccessors.get( flow::pressure{} ), + resCompFlowAccessors.get( flow::temperature{} ), + resCompFlowAccessors.get( flow::globalCompDensity{} ), + resCompFlowAccessors.get( flow::phaseVolumeFraction{} ), + resMultiFluidAccessors.get( fields::multifluid::phaseMassDensity{} ), + resElementRegion, + resElementSubRegion, + resElementIndex, + perfGravCoef, + perfStatus, + wellElemGravCoef, + wellElemPressure, + wellElemTemp, + wellElemCompFrac ); + + // get well secondary variables on well elements + string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString() ); + MultiFluidBase & fluid = getConstitutiveModel< MultiFluidBase >( subRegion, fluidName ); + arrayView3d< real64 const, constitutive::multifluid::USD_PHASE > const & wellElemPhaseDens = fluid.phaseDensity(); + arrayView2d< real64 const, constitutive::multifluid::USD_FLUID > const & wellElemTotalDens = fluid.totalDensity(); + + // 4) Back calculate component densities + constitutive::constitutiveUpdatePassThru( fluid, [&] ( auto & castedFluid ) + { + typename TYPEOFREF( castedFluid ) ::KernelWrapper fluidWrapper = castedFluid.createKernelWrapper(); + + thermalCompositionalMultiphaseBaseKernels:: + FluidUpdateKernel:: + launch< serialPolicy >( subRegion.size(), + fluidWrapper, + wellElemPressure, + wellElemTemp, + wellElemCompFrac ); + } ); + + compositionalMultiphaseWellKernels:: + CompDensInitializationKernel::launch( subRegion.size(), + numComp, + wellElemCompFrac, + wellElemTotalDens, + wellElemCompDens ); + + // 5) Recompute the pressure-dependent properties + updateSubRegionState( subRegion ); + + // 6) Estimate the well rates + // TODO: initialize rates using perforation rates + compositionalMultiphaseWellKernels:: + RateInitializationKernel:: + launch( subRegion.size(), + wellControls, + time_n, // initialization done at time_n + wellElemPhaseDens, + wellElemTotalDens, + connRate ); + + updateVolRatesForConstraint( subRegion ); + // Since this is a well manager class the rates need to be pushed into the WellControls class, which represnets the well + WellConstraintBase * constraint = wellControls.getCurrentConstraint(); + constraint->setBHP ( wellControls.getReference< real64 >( CompositionalMultiphaseWell::viewKeyStruct::currentBHPString() )); + constraint->setPhaseVolumeRates ( wellControls.getReference< array1d< real64 > >( + CompositionalMultiphaseWell::viewKeyStruct::currentPhaseVolRateString() ) ); + constraint->setTotalVolumeRate ( wellControls.getReference< real64 >( + CompositionalMultiphaseWell::viewKeyStruct::currentTotalVolRateString() )); + constraint->setMassRate( wellControls.getReference< real64 >( CompositionalMultiphaseWell::viewKeyStruct::currentMassRateString() )); + // 7) Copy well / fluid dofs to "prop"_n variables + saveState( subRegion ); + } + else if( !hasNonZeroRate ) + { + wellControls.setWellState( false ); + GEOS_LOG_RANK_0( "tjb shut wells "<< subRegion.getName()); + } + else + { + wellControls.setWellState( true ); + // setup if restart + if( wellControls.getCurrentConstraint() == nullptr ) + { + updateSubRegionState( subRegion ); + if( wellControls.isProducer() ) + { + wellControls.forSubGroups< MinimumBHPConstraint, ProductionConstraint< VolumeRateConstraint >, ProductionConstraint< MassRateConstraint >, + ProductionConstraint< PhaseVolumeRateConstraint > >( [&]( + auto + & constraint ) + { + if( ConstraintTypeId( wellControls.getControl()) == constraint.getControl() ) + { + wellControls.setCurrentConstraint( &constraint ); + } + } ); + } + else + { + wellControls.forSubGroups< MaximumBHPConstraint, InjectionConstraint< VolumeRateConstraint >, InjectionConstraint< MassRateConstraint >, InjectionConstraint< PhaseVolumeRateConstraint > >( [&]( + auto + & + constraint ) + { + if( ConstraintTypeId( wellControls.getControl()) == constraint.getControl() ) + { + wellControls.setCurrentConstraint( &constraint ); + } + } ); + } + } + + } +} + + +void CompositionalMultiphaseWell::initializeWells( DomainPartition & domain, real64 const & time_n ) +{ + GEOS_MARK_FUNCTION; + m_nextDt = -1; // TODO: change the way we access the flowSolver here CompositionalMultiphaseBase const & flowSolver = getParent().getGroup< CompositionalMultiphaseBase >( getFlowSolverName() ); @@ -1071,103 +1277,68 @@ void CompositionalMultiphaseWell::initializeWells( DomainPartition & domain, rea [&]( localIndex const, WellElementSubRegion & subRegion ) { - WellControls & wellControls = getWellControls( subRegion ); - PerforationData const & perforationData = *subRegion.getPerforationData(); - arrayView2d< real64 const > const compPerfRate = perforationData.getField< fields::well::compPerforationRate >(); - bool const hasNonZeroRate = MpiWrapper::max< integer >( hasNonZero( compPerfRate )); + initializeWell( domain, mesh, subRegion, time_n ); + } ); - if( wellControls.isWellOpen() && !hasNonZeroRate ) - { - // get well primary variables on well elements - arrayView1d< real64 > const & wellElemPressure = subRegion.getField< well::pressure >(); - arrayView1d< real64 > const & wellElemTemp = subRegion.getField< well::temperature >(); - arrayView2d< real64, compflow::USD_COMP > const & wellElemCompDens = subRegion.getField< well::globalCompDensity >(); - arrayView1d< real64 > const & connRate = subRegion.getField< well::mixtureConnectionRate >(); - - // get the info stored on well elements - arrayView2d< real64, compflow::USD_COMP > const & wellElemCompFrac = subRegion.getField< well::globalCompFraction >(); - arrayView1d< real64 const > const & wellElemGravCoef = subRegion.getField< well::gravityCoefficient >(); - - // get the element region, subregion, index - arrayView1d< localIndex const > const resElementRegion = perforationData.getField< perforation::reservoirElementRegion >(); - arrayView1d< localIndex const > const resElementSubRegion = perforationData.getField< perforation::reservoirElementSubRegion >(); - arrayView1d< localIndex const > const resElementIndex = perforationData.getField< perforation::reservoirElementIndex >(); - - arrayView1d< real64 const > const & perfGravCoef = perforationData.getField< fields::well::gravityCoefficient >(); - arrayView1d< integer const > const & perfStatus = perforationData.getField< fields::perforation::perforationStatus >(); - - // 1) Loop over all perforations to compute an average mixture density and component fraction - // 2) Initialize the reference pressure - // 3) Estimate the pressures in the well elements using the average density - compositionalMultiphaseWellKernels:: - PresTempCompFracInitializationKernel:: - launch( perforationData.size(), - subRegion.size(), - numComp, - numPhase, - wellControls, - 0.0, // initialization done at t = 0 - resCompFlowAccessors.get( flow::pressure{} ), - resCompFlowAccessors.get( flow::temperature{} ), - resCompFlowAccessors.get( flow::globalCompDensity{} ), - resCompFlowAccessors.get( flow::phaseVolumeFraction{} ), - resMultiFluidAccessors.get( fields::multifluid::phaseMassDensity{} ), - resElementRegion, - resElementSubRegion, - resElementIndex, - perfGravCoef, - perfStatus, - wellElemGravCoef, - wellElemPressure, - wellElemTemp, - wellElemCompFrac ); - - // get well secondary variables on well elements - string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString() ); - MultiFluidBase & fluid = getConstitutiveModel< MultiFluidBase >( subRegion, fluidName ); - arrayView3d< real64 const, constitutive::multifluid::USD_PHASE > const & wellElemPhaseDens = fluid.phaseDensity(); - arrayView2d< real64 const, constitutive::multifluid::USD_FLUID > const & wellElemTotalDens = fluid.totalDensity(); + } ); +} - // 4) Back calculate component densities - constitutive::constitutiveUpdatePassThru( fluid, [&] ( auto & castedFluid ) - { - typename TYPEOFREF( castedFluid ) ::KernelWrapper fluidWrapper = castedFluid.createKernelWrapper(); - - thermalCompositionalMultiphaseBaseKernels:: - FluidUpdateKernel:: - launch< serialPolicy >( subRegion.size(), - fluidWrapper, - wellElemPressure, - wellElemTemp, - wellElemCompFrac ); - } ); +void CompositionalMultiphaseWell::assembleWellFluxTerms( real64 const & time, + real64 const & dt, + WellElementSubRegion const & subRegion, + DofManager const & dofManager, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) +{ + GEOS_MARK_FUNCTION; + GEOS_UNUSED_VAR( time ); - compositionalMultiphaseWellKernels:: - CompDensInitializationKernel::launch( subRegion.size(), - numComp, - wellElemCompFrac, - wellElemTotalDens, - wellElemCompDens ); + BitFlags< isothermalCompositionalMultiphaseBaseKernels::KernelFlags > kernelFlags; + if( m_useTotalMassEquation ) + kernelFlags.set( isothermalCompositionalMultiphaseBaseKernels::KernelFlags::TotalMassEquation ); - // 5) Recompute the pressure-dependent properties - updateSubRegionState( subRegion ); + string const wellDofKey = dofManager.getKey( wellElementDofName()); + + WellControls const & well_controls = getWellControls( subRegion ); + if( well_controls.isWellOpen( ) && !m_keepVariablesConstantDuringInitStep ) + { + string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString()); + MultiFluidBase const & fluid = getConstitutiveModel< MultiFluidBase >( subRegion, fluidName ); + int numComponents = fluid.numFluidComponents(); + + if( isThermal() ) + { + thermalCompositionalMultiphaseWellKernels:: + FaceBasedAssemblyKernelFactory:: + createAndLaunch< parallelDevicePolicy<> >( numComponents, + dt, + dofManager.rankOffset(), + kernelFlags, + wellDofKey, + well_controls, + subRegion, + fluid, + localMatrix, + localRhs ); + } + else + { + compositionalMultiphaseWellKernels:: + FaceBasedAssemblyKernelFactory:: + createAndLaunch< parallelDevicePolicy<> >( numComponents, + dt, + dofManager.rankOffset(), + kernelFlags, + wellDofKey, + well_controls, + subRegion, + localMatrix, + localRhs ); + } + } - // 6) Estimate the well rates - // TODO: initialize rates using perforation rates - compositionalMultiphaseWellKernels:: - RateInitializationKernel:: - launch( subRegion.size(), - m_targetPhaseIndex, - wellControls, - time_n, // initialization done at time_n - wellElemPhaseDens, - wellElemTotalDens, - connRate ); - } - } ); - } ); } void CompositionalMultiphaseWell::assembleFluxTerms( real64 const & time, @@ -1193,43 +1364,9 @@ void CompositionalMultiphaseWell::assembleFluxTerms( real64 const & time, [&]( localIndex const, WellElementSubRegion & subRegion ) { - WellControls const & well_controls = getWellControls( subRegion ); - if( well_controls.getWellStatus() == WellControls::Status::OPEN && !m_keepVariablesConstantDuringInitStep ) - { - string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString()); - MultiFluidBase const & fluid = getConstitutiveModel< MultiFluidBase >( subRegion, fluidName ); - int const numComponents = fluid.numFluidComponents(); - if( isThermal() ) - { - thermalCompositionalMultiphaseWellKernels:: - FaceBasedAssemblyKernelFactory:: - createAndLaunch< parallelDevicePolicy<> >( numComponents, - dt, - dofManager.rankOffset(), - kernelFlags, - wellDofKey, - well_controls, - subRegion, - fluid, - localMatrix, - localRhs ); - } - else - { - compositionalMultiphaseWellKernels:: - FaceBasedAssemblyKernelFactory:: - createAndLaunch< parallelDevicePolicy<> >( numComponents, - dt, - dofManager.rankOffset(), - kernelFlags, - wellDofKey, - well_controls, - subRegion, - localMatrix, - localRhs ); - } - } + assembleWellFluxTerms( time, dt, subRegion, dofManager, localMatrix, localRhs ); + } ); } ); @@ -1246,119 +1383,271 @@ void CompositionalMultiphaseWell::assembleAccumulationTerms( real64 const & time GEOS_UNUSED_VAR( time ); GEOS_UNUSED_VAR( dt ); - BitFlags< isothermalCompositionalMultiphaseBaseKernels::KernelFlags > kernelFlags; - if( m_useTotalMassEquation ) - kernelFlags.set( isothermalCompositionalMultiphaseBaseKernels::KernelFlags::TotalMassEquation ); + BitFlags< isothermalCompositionalMultiphaseBaseKernels::KernelFlags > kernelFlags; + if( m_useTotalMassEquation ) + kernelFlags.set( isothermalCompositionalMultiphaseBaseKernels::KernelFlags::TotalMassEquation ); + + string const wellDofKey = dofManager.getKey( wellElementDofName() ); + forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &, + MeshLevel & mesh, + string_array const & regionNames ) + { + mesh.getElemManager().forElementSubRegions< WellElementSubRegion >( regionNames, + [&]( localIndex const, + WellElementSubRegion & subRegion ) + { + + assembleWellAccumulationTerms( time, dt, subRegion, dofManager, localMatrix, localRhs ); + } ); + } ); + + +} + +void CompositionalMultiphaseWell::assembleWellAccumulationTerms( real64 const & time, + real64 const & dt, + WellElementSubRegion & subRegion, + DofManager const & dofManager, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) +{ + GEOS_MARK_FUNCTION; + GEOS_UNUSED_VAR( time ); + GEOS_UNUSED_VAR( dt ); + + BitFlags< isothermalCompositionalMultiphaseBaseKernels::KernelFlags > kernelFlags; + if( m_useTotalMassEquation ) + kernelFlags.set( isothermalCompositionalMultiphaseBaseKernels::KernelFlags::TotalMassEquation ); + + string const wellDofKey = dofManager.getKey( wellElementDofName() ); + + string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString()); + MultiFluidBase const & fluid = getConstitutiveModel< MultiFluidBase >( subRegion, fluidName ); + integer const numPhases = fluid.numFluidPhases(); + integer const numComponents = fluid.numFluidComponents(); + WellControls & wellControls = getWellControls( subRegion ); + if( wellControls.getWellStatus() == WellControls::Status::OPEN && !m_keepVariablesConstantDuringInitStep ) + { + if( isThermal() ) + { + + 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 ) + { + if( elemStatus[ei]==WellElementSubRegion::WellElemStatus::CLOSED ) + { + mixConnRate[ei] = 0.0; + globalIndex const dofIndex = wellElemDofNumber[ei]; + localIndex const localRow = dofIndex - rank_offset; + + real64 const unity = 1.0; + for( integer i=0; i < m_numDofPerWellElement; i++ ) + { + globalIndex const rindex = localRow+i; + globalIndex const cindex =dofIndex + i; + localMatrix.template addToRow< serialAtomic >( rindex, + &cindex, + &unity, + 1 ); + localRhs[rindex] = 0.0; + } + } + } + } ); + + } + else + { + arrayView1d< globalIndex const > const & wellElemDofNumber = + subRegion.getReference< array1d< globalIndex > >( wellDofKey ); + arrayView1d< integer const > const wellElemGhostRank = subRegion.ghostRank(); + + arrayView1d< real64 > 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 ) + { + 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; + } + } + } ); + // zero out current state constraint quantities + wellControls.getReference< real64 >( CompositionalMultiphaseWell::viewKeyStruct::currentBHPString() )=0.0; + wellControls.getReference< array1d< real64 > >( + CompositionalMultiphaseWell::viewKeyStruct::currentPhaseVolRateString() ).zero(); + wellControls.getReference< real64 >( + CompositionalMultiphaseWell::viewKeyStruct::currentTotalVolRateString() )=0.0; + wellControls.getReference< real64 >( CompositionalMultiphaseWell::viewKeyStruct::currentMassRateString() )=0.0; + } +} + + +real64 +CompositionalMultiphaseWell::calculateWellResidualNorm( real64 const & time_n, + real64 const & dt, + WellElementSubRegion const & subRegion, + DofManager const & dofManager, + arrayView1d< real64 const > const & localRhs ) +{ + GEOS_MARK_FUNCTION; + integer numNorm = 1; // mass balance + array1d< real64 > localResidualNorm; + array1d< real64 > localResidualNormalizer; + + if( isThermal() ) + { + numNorm = 2; // mass balance and energy balance + } + localResidualNorm.resize( numNorm ); + localResidualNormalizer.resize( numNorm ); + + globalIndex const rankOffset = dofManager.rankOffset(); string const wellDofKey = dofManager.getKey( wellElementDofName() ); - forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &, - MeshLevel & mesh, - string_array const & regionNames ) + + + + string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString() ); + MultiFluidBase const & fluid = subRegion.getConstitutiveModel< MultiFluidBase >( fluidName ); + + WellControls const & wellControls = getWellControls( subRegion ); + + if( wellControls.isWellOpen( ) ) { - mesh.getElemManager().forElementSubRegions< WellElementSubRegion >( regionNames, - [&]( localIndex const, - WellElementSubRegion & subRegion ) + // step 1: compute the norm in the subRegion + if( isThermal() ) { - 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 ) + real64 subRegionResidualNorm[2]{}; + + thermalCompositionalMultiphaseWellKernels::ResidualNormKernelFactory:: + createAndLaunch< parallelDevicePolicy<> >( m_numComponents, + rankOffset, + wellDofKey, + localRhs, + subRegion, + fluid, + wellControls, + time_n, + dt, + m_nonlinearSolverParameters.m_minNormalizer, + subRegionResidualNorm ); + // step 2: reduction across meshBodies/regions/subRegions + + for( integer i=0; i >( numComponents, - numPhases, - wellControls.isProducer(), - dofManager.rankOffset(), - kernelFlags, - wellDofKey, - subRegion, - fluid, - localMatrix, - localRhs ); - } - else + if( subRegionResidualNorm[i] > localResidualNorm[i] ) { - compositionalMultiphaseWellKernels:: - ElementBasedAssemblyKernelFactory:: - createAndLaunch< parallelDevicePolicy<> >( numComponents, - numPhases, - wellControls.isProducer(), - dofManager.rankOffset(), - kernelFlags, - wellDofKey, - subRegion, - fluid, - localMatrix, - localRhs ); + localResidualNorm[i] = subRegionResidualNorm[i]; } - // get the degrees of freedom and ghosting info - arrayView1d< globalIndex const > const & wellElemDofNumber = - subRegion.getReference< array1d< globalIndex > >( wellDofKey ); - arrayView1d< integer const > const wellElemGhostRank = subRegion.ghostRank(); - arrayView1d< integer const > const elemStatus = subRegion.getLocalWellElementStatus(); - arrayView1d< real64 > const mixConnRate = subRegion.getField< fields::well::mixtureConnectionRate >(); - localIndex rank_offset = dofManager.rankOffset(); - forAll< parallelDevicePolicy<> >( subRegion.size(), [=] GEOS_HOST_DEVICE ( localIndex const ei ) - { - if( wellElemGhostRank[ei] < 0 ) - { - if( elemStatus[ei]==WellElementSubRegion::WellElemStatus::CLOSED ) - { - mixConnRate[ei] = 0.0; - globalIndex const dofIndex = wellElemDofNumber[ei]; - localIndex const localRow = dofIndex - rank_offset; - - real64 const unity = 1.0; - for( integer i=0; i < m_numDofPerWellElement; i++ ) - { - globalIndex const rindex = localRow+i; - globalIndex const cindex =dofIndex + i; - localMatrix.template addToRow< serialAtomic >( rindex, - &cindex, - &unity, - 1 ); - localRhs[rindex] = 0.0; - } - } - } - } ); } - else - { - // 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 ) - { - 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; - } - } - } ); + } + else + { + real64 subRegionResidualNorm[1]{}; + compositionalMultiphaseWellKernels::ResidualNormKernelFactory:: + createAndLaunch< parallelDevicePolicy<> >( m_numComponents, + numDofPerWellElement(), + rankOffset, + wellDofKey, + localRhs, + subRegion, + fluid, + wellControls, + time_n, + dt, + m_nonlinearSolverParameters.m_minNormalizer, + subRegionResidualNorm ); + + + + // step 2: reduction across meshBodies/regions/subRegions + + if( subRegionResidualNorm[0] > localResidualNorm[0] ) + { + localResidualNorm[0] = subRegionResidualNorm[0]; } - } ); // forElementSubRegions - } ); // forDiscretizationOnMeshTargets + } + } + else + { + 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 + thermalCompositionalMultiphaseWellKernels::ResidualNormKernelFactory:: + createAndLaunch< parallelDevicePolicy<> >( m_numComponents, + rankOffset, + wellDofKey, + localRhs, + subRegion, + fluid, + wellControls, + time_n, + dt, + m_nonlinearSolverParameters.m_minNormalizer, + subRegionResidualNorm ); + // step 2: reduction across meshBodies/regions/subRegions - for( integer i=0; i localResidualNorm[i] ) + for( integer i=0; i localResidualNorm[i] ) + { + localResidualNorm[i] = subRegionResidualNorm[i]; + } } - } - } - else - { - real64 subRegionResidualNorm[1]{}; - compositionalMultiphaseWellKernels::ResidualNormKernelFactory:: - createAndLaunch< parallelDevicePolicy<> >( m_numComponents, - numDofPerWellElement(), - m_targetPhaseIndex, - rankOffset, - wellDofKey, - localRhs, - subRegion, - fluid, - wellControls, - time_n, - dt, - m_nonlinearSolverParameters.m_minNormalizer, - subRegionResidualNorm ); + } + else + { + real64 subRegionResidualNorm[1]{}; + compositionalMultiphaseWellKernels::ResidualNormKernelFactory:: + createAndLaunch< parallelDevicePolicy<> >( m_numComponents, + numDofPerWellElement(), + rankOffset, + wellDofKey, + localRhs, + subRegion, + fluid, + wellControls, + time_n, + dt, + m_nonlinearSolverParameters.m_minNormalizer, + subRegionResidualNorm ); - // step 2: reduction across meshBodies/regions/subRegions + // step 2: reduction across meshBodies/regions/subRegions - if( subRegionResidualNorm[0] > localResidualNorm[0] ) + if( subRegionResidualNorm[0] > localResidualNorm[0] ) + { + localResidualNorm[0] = subRegionResidualNorm[0]; + } + } + } + else + { + for( integer i=0; i const & localSolution ) +{ + GEOS_MARK_FUNCTION; + + string const wellDofKey = dofManager.getKey( wellElementDofName() ); + + real64 scalingFactor = 1.0; + real64 maxDeltaPres = 0.0, maxDeltaCompDens = 0.0, maxDeltaTemp = 0.0; + real64 minPresScalingFactor = 1.0, minCompDensScalingFactor = 1.0, minTempScalingFactor = 1.0; + + arrayView1d< real64 const > const pressure = subRegion.getField< fields::well::pressure >(); + arrayView1d< real64 const > const temperature = subRegion.getField< fields::well::temperature >(); + arrayView2d< real64 const, compflow::USD_COMP > const compDens = subRegion.getField< fields::well::globalCompDensity >(); + arrayView1d< real64 > pressureScalingFactor = subRegion.getField< fields::well::pressureScalingFactor >(); + arrayView1d< real64 > temperatureScalingFactor = subRegion.getField< fields::well::temperatureScalingFactor >(); + arrayView1d< real64 > compDensScalingFactor = subRegion.getField< fields::well::globalCompDensityScalingFactor >(); + const integer temperatureOffset = m_numComponents+2; + auto const subRegionData = + m_isThermal + ? thermalCompositionalMultiphaseBaseKernels:: + SolutionScalingKernelFactory:: + createAndLaunch< parallelDevicePolicy<> >( m_maxRelativePresChange, + m_maxAbsolutePresChange, + m_maxRelativeTempChange, + m_maxCompFracChange, + m_maxRelativeCompDensChange, + pressure, + temperature, + compDens, + pressureScalingFactor, + compDensScalingFactor, + temperatureScalingFactor, + dofManager.rankOffset(), + m_numComponents, + wellDofKey, + subRegion, + localSolution, + temperatureOffset ) + : isothermalCompositionalMultiphaseBaseKernels:: + SolutionScalingKernelFactory:: + createAndLaunch< parallelDevicePolicy<> >( m_maxRelativePresChange, + m_maxAbsolutePresChange, + m_maxCompFracChange, + m_maxRelativeCompDensChange, + pressure, + compDens, + pressureScalingFactor, + compDensScalingFactor, + dofManager.rankOffset(), + m_numComponents, + wellDofKey, + subRegion, + localSolution ); + + + scalingFactor = std::min( subRegionData.localMinVal, scalingFactor ); + + maxDeltaPres = std::max( maxDeltaPres, subRegionData.localMaxDeltaPres ); + maxDeltaCompDens = std::max( maxDeltaCompDens, subRegionData.localMaxDeltaCompDens ); + maxDeltaTemp = std::max( maxDeltaTemp, subRegionData.localMaxDeltaTemp ); + minPresScalingFactor = std::min( minPresScalingFactor, subRegionData.localMinPresScalingFactor ); + minCompDensScalingFactor = std::min( minCompDensScalingFactor, subRegionData.localMinCompDensScalingFactor ); + minTempScalingFactor = std::min( minTempScalingFactor, subRegionData.localMinTempScalingFactor ); + + + scalingFactor = MpiWrapper::min( scalingFactor ); + maxDeltaPres = MpiWrapper::max( maxDeltaPres ); + maxDeltaCompDens = MpiWrapper::max( maxDeltaCompDens ); + minPresScalingFactor = MpiWrapper::min( minPresScalingFactor ); + minCompDensScalingFactor = MpiWrapper::min( minCompDensScalingFactor ); + + string const massUnit = m_useMass ? "kg/m3" : "mol/m3"; + GEOS_LOG_LEVEL_RANK_0( logInfo::Solution, + GEOS_FMT( " {}: Max well pressure change: {} Pa (before scaling)", + getName(), GEOS_FMT( "{:.{}f}", maxDeltaPres, 3 ) ) ); + GEOS_LOG_LEVEL_RANK_0( logInfo::Solution, + GEOS_FMT( " {}: Max well component density change: {} {} (before scaling)", + getName(), GEOS_FMT( "{:.{}f}", maxDeltaCompDens, 3 ), massUnit ) ); + + if( m_isThermal ) + { + maxDeltaTemp = MpiWrapper::max( maxDeltaTemp ); + minTempScalingFactor = MpiWrapper::min( minTempScalingFactor ); + GEOS_LOG_LEVEL_RANK_0( logInfo::Solution, + GEOS_FMT( " {}: Max well temperature change: {} K (before scaling)", + getName(), GEOS_FMT( "{:.{}f}", maxDeltaTemp, 3 ) ) ); + } + + + GEOS_LOG_LEVEL_RANK_0( logInfo::Solution, + GEOS_FMT( " {}: Min well pressure scaling factor: {}", + getName(), minPresScalingFactor ) ); + GEOS_LOG_LEVEL_RANK_0( logInfo::Solution, + GEOS_FMT( " {}: Min well component density scaling factor: {}", + getName(), minCompDensScalingFactor ) ); + if( m_isThermal ) + { + GEOS_LOG_LEVEL_RANK_0( logInfo::Solution, + GEOS_FMT( " {}: Min well temperature scaling factor: {}", + getName(), minTempScalingFactor ) ); + } + + + return LvArray::math::max( scalingFactor, m_minScalingFactor ); + +} + real64 CompositionalMultiphaseWell::scalingForSystemSolution( DomainPartition & domain, DofManager const & dofManager, @@ -1611,186 +2018,303 @@ CompositionalMultiphaseWell::scalingForSystemSolution( DomainPartition & domain, } bool -CompositionalMultiphaseWell::checkSystemSolution( DomainPartition & domain, - DofManager const & dofManager, - arrayView1d< real64 const > const & localSolution, - real64 const scalingFactor ) +CompositionalMultiphaseWell::checkWellSystemSolution( ElementSubRegionBase & subRegion, + DofManager const & dofManager, + arrayView1d< real64 const > const & localSolution, + real64 const scalingFactor ) { GEOS_MARK_FUNCTION; string const wellDofKey = dofManager.getKey( wellElementDofName() ); integer localCheck = 1; + + real64 minPres = 0.0, minDens = 0.0, minTotalDens = 0.0; integer numNegPres = 0, numNegDens = 0, numNegTotalDens = 0; - forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &, - MeshLevel & mesh, - string_array const & regionNames ) - { - mesh.getElemManager().forElementSubRegions( regionNames, - [&]( localIndex const, - ElementSubRegionBase & subRegion ) - { - //integer const m_allowCompDensChopping(true); - integer const m_allowNegativePressure( false ); - compositionalMultiphaseUtilities::ScalingType const m_scalingType( compositionalMultiphaseUtilities::ScalingType::Global ); - arrayView1d< real64 const > const pressure = - subRegion.getField< well::pressure >(); - arrayView1d< real64 const > const temperature = - subRegion.getField< well::temperature >(); - arrayView2d< real64 const, compflow::USD_COMP > const compDens = - subRegion.getField< well::globalCompDensity >(); - arrayView1d< real64 > pressureScalingFactor = subRegion.getField< well::pressureScalingFactor >(); - arrayView1d< real64 > temperatureScalingFactor = subRegion.getField< well::temperatureScalingFactor >(); - arrayView1d< real64 > compDensScalingFactor = subRegion.getField< well::globalCompDensityScalingFactor >(); - - // check that pressure and component densities are non-negative - // for thermal, check that temperature is above 273.15 K - const integer temperatureOffset = m_numComponents+2; - auto const subRegionData = - m_isThermal + const std::string wellName = subRegion.getName(); + + //integer const m_allowCompDensChopping(true); + integer const m_allowNegativePressure( false ); + compositionalMultiphaseUtilities::ScalingType const m_scalingType( compositionalMultiphaseUtilities::ScalingType::Global ); + arrayView1d< real64 const > const pressure = + subRegion.getField< fields::well::pressure >(); + arrayView1d< real64 const > const temperature = + subRegion.getField< fields::well::temperature >(); + arrayView2d< real64 const, compflow::USD_COMP > const compDens = + subRegion.getField< fields::well::globalCompDensity >(); + arrayView1d< real64 > pressureScalingFactor = subRegion.getField< fields::well::pressureScalingFactor >(); + arrayView1d< real64 > temperatureScalingFactor = subRegion.getField< fields::well::temperatureScalingFactor >(); + arrayView1d< real64 > compDensScalingFactor = subRegion.getField< fields::well::globalCompDensityScalingFactor >(); + + // check that pressure and component densities are non-negative + // for thermal, check that temperature is above 273.15 K + const integer temperatureOffset = m_numComponents+2; + auto const subRegionData = + m_isThermal ? thermalCompositionalMultiphaseBaseKernels:: - SolutionCheckKernelFactory:: - createAndLaunch< parallelDevicePolicy<> >( m_allowCompDensChopping, - m_allowNegativePressure, - m_scalingType, - scalingFactor, - pressure, - temperature, - compDens, - pressureScalingFactor, - temperatureScalingFactor, - compDensScalingFactor, - dofManager.rankOffset(), - m_numComponents, - wellDofKey, - subRegion, - localSolution, - temperatureOffset ) + SolutionCheckKernelFactory:: + createAndLaunch< parallelDevicePolicy<> >( m_allowCompDensChopping, + m_allowNegativePressure, + m_scalingType, + scalingFactor, + pressure, + temperature, + compDens, + pressureScalingFactor, + temperatureScalingFactor, + compDensScalingFactor, + dofManager.rankOffset(), + m_numComponents, + wellDofKey, + subRegion, + localSolution, + temperatureOffset ) : isothermalCompositionalMultiphaseBaseKernels:: - SolutionCheckKernelFactory:: - createAndLaunch< parallelDevicePolicy<> >( m_allowCompDensChopping, - m_allowNegativePressure, - m_scalingType, - scalingFactor, - pressure, - compDens, - pressureScalingFactor, - compDensScalingFactor, - dofManager.rankOffset(), - m_numComponents, - wellDofKey, - subRegion, - localSolution ); + SolutionCheckKernelFactory:: + createAndLaunch< parallelDevicePolicy<> >( m_allowCompDensChopping, + m_allowNegativePressure, + m_scalingType, + scalingFactor, + pressure, + compDens, + pressureScalingFactor, + compDensScalingFactor, + dofManager.rankOffset(), + m_numComponents, + wellDofKey, + subRegion, + localSolution ); + + localCheck = std::min( localCheck, subRegionData.localMinVal ); + + 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 ); + minTotalDens = MpiWrapper::min( minTotalDens ); + numNegPres = MpiWrapper::sum( numNegPres ); + numNegDens = MpiWrapper::sum( numNegDens ); + numNegTotalDens = MpiWrapper::sum( numNegTotalDens ); + + if( numNegPres > 0 ) + GEOS_LOG_LEVEL_RANK_0( logInfo::Solution, + GEOS_FMT( " {}: Number of negative well pressure values: {}, minimum value: {} Pa", + 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: {} {} ", + 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: {} {} ", + subRegion.getName(), minTotalDens, fmt::format( "{:.{}f}", minDens, 3 ), massUnit ) ); + + + + return MpiWrapper::min( localCheck ); +} + +bool +CompositionalMultiphaseWell::checkSystemSolution( DomainPartition & domain, + DofManager const & dofManager, + arrayView1d< real64 const > const & localSolution, + real64 const scalingFactor ) +{ + GEOS_MARK_FUNCTION; - localCheck = std::min( localCheck, subRegionData.localMinVal ); + string const wellDofKey = dofManager.getKey( wellElementDofName() ); + integer globalCheck = 1; - 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; + forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &, + MeshLevel & mesh, + string_array const & regionNames ) + { + mesh.getElemManager().forElementSubRegions( regionNames, + [&]( localIndex const, + ElementSubRegionBase & subRegion ) + { + integer localCheck = checkWellSystemSolution( subRegion, dofManager, localSolution, scalingFactor ); + globalCheck = MpiWrapper::min( localCheck ); } ); } ); + return globalCheck; +} - minPres = MpiWrapper::min( minPres ); - minDens = MpiWrapper::min( minDens ); - minTotalDens = MpiWrapper::min( minTotalDens ); - numNegPres = MpiWrapper::sum( numNegPres ); - numNegDens = MpiWrapper::sum( numNegDens ); - numNegTotalDens = MpiWrapper::sum( numNegTotalDens ); +void CompositionalMultiphaseWell::computeWellPerforationRates( real64 const & time_n, + real64 const & GEOS_UNUSED_PARAM( dt ), + ElementRegionManager const & elemManager, + WellElementSubRegion & subRegion ) +{ + GEOS_MARK_FUNCTION; + GEOS_UNUSED_VAR( time_n ); + + CompositionalMultiphaseBase const & flowSolver = getParent().getGroup< CompositionalMultiphaseBase >( getFlowSolverName() ); + + PerforationData * const perforationData = subRegion.getPerforationData(); + WellControls const & wellControls = getWellControls( subRegion ); + if( wellControls.isWellOpen() && !m_keepVariablesConstantDuringInitStep ) + { + + string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString() ); + MultiFluidBase const & fluid = getConstitutiveModel< MultiFluidBase >( subRegion, fluidName ); + bool isThermal = fluid.isThermal(); + + if( isThermal ) + { + thermalPerforationFluxKernels:: + PerforationFluxKernelFactory:: + createAndLaunch< parallelDevicePolicy<> >( m_numComponents, + m_numPhases, + flowSolver.getName(), + perforationData, + subRegion, + fluid, + elemManager, + wellControls.isInjector(), + wellControls.isCrossflowEnabled()); + } + else + { + isothermalPerforationFluxKernels:: + PerforationFluxKernelFactory:: + createAndLaunch< parallelDevicePolicy<> >( m_numComponents, + m_numPhases, + flowSolver.getName(), + perforationData, + subRegion, + elemManager, + wellControls.isInjector(), + wellControls.isCrossflowEnabled() ); + } + } + else + { +// Zero completion flow rate + arrayView2d< real64 > const compPerfRate = perforationData->getField< fields::well::compPerforationRate >(); + for( integer iperf=0; iperfsize(); iperf++ ) + { + for( integer ic = 0; ic < m_numComponents; ++ic ) + { + compPerfRate[iperf][ic] = 0.0; + } + } + } - 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 ) ) ); - 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 ) ); - 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 ) ); - return MpiWrapper::min( localCheck ); } + void CompositionalMultiphaseWell::computePerforationRates( real64 const & time_n, - real64 const & GEOS_UNUSED_PARAM( dt ), + real64 const & dt, DomainPartition & domain ) { GEOS_MARK_FUNCTION; - GEOS_UNUSED_VAR( time_n ); - - forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &, - MeshLevel & mesh, - string_array const & regionNames ) + GEOS_UNUSED_VAR( dt ); + forDiscretizationOnMeshTargets ( domain.getMeshBodies(), [&] ( string const &, + MeshLevel & mesh, + string_array const & regionNames ) { // TODO: change the way we access the flowSolver here - CompositionalMultiphaseBase const & flowSolver = getParent().getGroup< CompositionalMultiphaseBase >( getFlowSolverName() ); + ElementRegionManager & elemManager = mesh.getElemManager(); elemManager.forElementSubRegions< WellElementSubRegion >( regionNames, [&]( localIndex const, WellElementSubRegion & subRegion ) { - PerforationData * const perforationData = subRegion.getPerforationData(); - WellControls const & wellControls = getWellControls( subRegion ); - if( wellControls.getWellStatus() == WellControls::Status::OPEN && !m_keepVariablesConstantDuringInitStep ) - { - string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString() ); - MultiFluidBase const & fluid = getConstitutiveModel< MultiFluidBase >( subRegion, fluidName ); - bool const isThermal = fluid.isThermal(); + computeWellPerforationRates( time_n, dt, elemManager, subRegion ); - if( isThermal ) - { - thermalPerforationFluxKernels:: - PerforationFluxKernelFactory:: - createAndLaunch< parallelDevicePolicy<> >( m_numComponents, - m_numPhases, - flowSolver.getName(), - perforationData, - subRegion, - fluid, - elemManager, - wellControls.isInjector(), - wellControls.isCrossflowEnabled() ); - } - else - { - isothermalPerforationFluxKernels:: - PerforationFluxKernelFactory:: - createAndLaunch< parallelDevicePolicy<> >( m_numComponents, - m_numPhases, - flowSolver.getName(), - perforationData, - subRegion, - elemManager, - wellControls.isInjector(), - wellControls.isCrossflowEnabled() ); - } - } - else - { - // Zero completion flow rate - arrayView2d< real64 > const compPerfRate = perforationData->getField< well::compPerforationRate >(); - for( integer iperf=0; iperfsize(); iperf++ ) - { - for( integer ic = 0; ic < m_numComponents; ++ic ) - { - compPerfRate[iperf][ic] = 0.0; - } - } - } } ); } ); } +void +CompositionalMultiphaseWell::applyWellSystemSolution( DofManager const & dofManager, + arrayView1d< real64 const > const & localSolution, + real64 const scalingFactor, + real64 const dt, + DomainPartition & domain, + MeshLevel & mesh, + WellElementSubRegion & subRegion ) +{ + + GEOS_UNUSED_VAR( domain ); + DofManager::CompMask pressureMask( m_numDofPerWellElement, 0, 1 ); + DofManager::CompMask componentMask( m_numDofPerWellElement, 1, numFluidComponents()+1 ); + DofManager::CompMask connRateMask( m_numDofPerWellElement, numFluidComponents()+1, numFluidComponents()+2 ); + GEOS_UNUSED_VAR( dt ); + // update all the fields using the global damping coefficients + dofManager.addVectorToField( localSolution, + wellElementDofName(), + fields::well::pressure::key(), + scalingFactor, + pressureMask ); + + dofManager.addVectorToField( localSolution, + wellElementDofName(), + fields::well::globalCompDensity::key(), + scalingFactor, + componentMask ); + + dofManager.addVectorToField( localSolution, + wellElementDofName(), + fields::well::mixtureConnectionRate::key(), + scalingFactor, + connRateMask ); + + if( isThermal() ) + { + DofManager::CompMask temperatureMask( m_numDofPerWellElement, numFluidComponents()+2, numFluidComponents()+3 ); + dofManager.addVectorToField( localSolution, + wellElementDofName(), + 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( subRegion ); + } +#endif + + // synchronize + FieldIdentifiers fieldsToBeSync; + if( isThermal() ) + { + fieldsToBeSync.addElementFields( { fields::well::pressure::key(), + fields::well::globalCompDensity::key(), + fields::well::mixtureConnectionRate::key(), + fields::well::temperature::key() }, + getTargetRegionNames() ); + } + else + { + fieldsToBeSync.addElementFields( { fields::well::pressure::key(), + fields::well::globalCompDensity::key(), + fields::well::mixtureConnectionRate::key() }, + getTargetRegionNames() ); + } + CommunicationTools::getInstance().synchronizeFields( fieldsToBeSync, + mesh, + domain.getNeighbors(), + true ); + +} void CompositionalMultiphaseWell::applySystemSolution( DofManager const & dofManager, @@ -1872,6 +2396,34 @@ CompositionalMultiphaseWell::applySystemSolution( DofManager const & dofManager, } +void CompositionalMultiphaseWell::chopNegativeDensities( WellElementSubRegion & subRegion ) +{ + integer const numComp = m_numComponents; + + + arrayView1d< integer const > const & wellElemGhostRank = subRegion.ghostRank(); + + arrayView2d< real64, compflow::USD_COMP > const & wellElemCompDens = + subRegion.getField< fields::well::globalCompDensity >(); + + forAll< parallelDevicePolicy<> >( subRegion.size(), [=] GEOS_HOST_DEVICE ( localIndex const iwelem ) + { + if( wellElemGhostRank[iwelem] < 0 ) + { + for( integer ic = 0; ic < numComp; ++ic ) + { + // we allowed for some densities to be slightly negative in CheckSystemSolution + // if the new density is negative, chop back to zero + if( wellElemCompDens[iwelem][ic] < 0 ) + { + wellElemCompDens[iwelem][ic] = 0.0; + } + } + } + } ); + +} + void CompositionalMultiphaseWell::chopNegativeDensities( DomainPartition & domain ) { integer const numComp = m_numComponents; @@ -1963,15 +2515,158 @@ void CompositionalMultiphaseWell::resetStateToBeginningOfStep( DomainPartition & } ); } +void CompositionalMultiphaseWell::assembleWellConstraintTerms( real64 const & time_n, + real64 const & GEOS_UNUSED_PARAM( dt ), + WellElementSubRegion const & subRegion, + DofManager const & dofManager, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) +{ + GEOS_MARK_FUNCTION; + + + // the rank that owns the reference well element is responsible for the calculations below. + if( !subRegion.isLocallyOwned() ) + { + return; + } + WellControls & wellControls = getWellControls( subRegion ); + + if( wellControls.isProducer() ) + { + wellControls.forSubGroups< MinimumBHPConstraint, ProductionConstraint< PhaseVolumeRateConstraint >, ProductionConstraint< MassRateConstraint >, ProductionConstraint< VolumeRateConstraint >, + ProductionConstraint< LiquidRateConstraint > + >( [&]( auto & constraint ) + { + if( constraint.getName() == wellControls.getCurrentConstraint()->getName()) + { + // found limiting constraint + + // fluid data + constitutive::MultiFluidBase & fluidSeparator = wellControls.getMultiFluidSeparator(); + integer isThermal = fluidSeparator.isThermal(); + integer const numComp = fluidSeparator.numFluidComponents(); + geos::internal::kernelLaunchSelectorCompThermSwitch( numComp, isThermal, [&] ( auto NC, auto ISTHERMAL ) + { + integer constexpr NUM_COMP = NC(); + integer constexpr IS_THERMAL = ISTHERMAL(); + + wellConstraintKernels::ConstraintHelper< NUM_COMP, IS_THERMAL >::assembleConstraintEquation( time_n, + wellControls, + constraint, + subRegion, + dofManager.getKey( wellElementDofName() ), + dofManager.rankOffset(), + localMatrix, + localRhs ); + } ); + } + } ); + } + else + { + wellControls.forSubGroups< MaximumBHPConstraint, InjectionConstraint< PhaseVolumeRateConstraint >, InjectionConstraint< MassRateConstraint >, + InjectionConstraint< VolumeRateConstraint >, + InjectionConstraint< LiquidRateConstraint > + >( [&]( auto & constraint ) + { + if( constraint.getName() == wellControls.getCurrentConstraint()->getName()) + { + // found limiting constraint + + // fluid data + constitutive::MultiFluidBase & fluidSeparator = wellControls.getMultiFluidSeparator(); + integer isThermal = fluidSeparator.isThermal(); + integer const numComp = fluidSeparator.numFluidComponents(); + geos::internal::kernelLaunchSelectorCompThermSwitch( numComp, isThermal, [&] ( auto NC, auto ISTHERMAL ) + { + integer constexpr NUM_COMP = NC(); + integer constexpr IS_THERMAL = ISTHERMAL(); + + wellConstraintKernels::ConstraintHelper< NUM_COMP, IS_THERMAL >::assembleConstraintEquation( time_n, + wellControls, + constraint, + subRegion, + dofManager.getKey( wellElementDofName() ), + dofManager.rankOffset(), + localMatrix, + localRhs ); + } ); + } + } ); + } +} + +void CompositionalMultiphaseWell::assembleWellPressureRelations( real64 const & GEOS_UNUSED_PARAM( time_n ), + real64 const & GEOS_UNUSED_PARAM( dt ), + WellElementSubRegion const & subRegion, + DofManager const & dofManager, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) +{ + GEOS_MARK_FUNCTION; + + + WellControls & wellControls = getWellControls( subRegion ); + + if( wellControls.isWellOpen( ) && !m_keepVariablesConstantDuringInitStep ) + { + string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString() ); + MultiFluidBase const & fluid = getConstitutiveModel< MultiFluidBase >( subRegion, fluidName ); + bool const isThermal = fluid.isThermal(); + // get the degrees of freedom, depth info, next welem index + string const wellDofKey = dofManager.getKey( wellElementDofName() ); + arrayView1d< globalIndex const > const & wellElemDofNumber = + subRegion.getReference< array1d< globalIndex > >( wellDofKey ); + arrayView1d< real64 const > const & wellElemGravCoef = + subRegion.getField< well::gravityCoefficient >(); + arrayView1d< localIndex const > const & nextWellElemIndex = + subRegion.getReference< array1d< localIndex > >( WellElementSubRegion::viewKeyStruct::nextWellElementIndexString() ); + + // get primary variables on well elements + arrayView1d< real64 const > const & wellElemPres = + subRegion.getField< well::pressure >(); + + // get total mass density on well elements (for potential calculations) + arrayView1d< real64 const > const & wellElemTotalMassDens = + subRegion.getField< well::totalMassDensity >(); + arrayView2d< real64 const, compflow::USD_FLUID_DC > const & dWellElemTotalMassDens = + subRegion.getField< well::dTotalMassDensity >(); + + // segment status + arrayView1d< integer const > const elemStatus =subRegion.getLocalWellElementStatus(); + + bool controlHasSwitched = false; + isothermalCompositionalMultiphaseBaseKernels:: + KernelLaunchSelectorCompTherm< compositionalMultiphaseWellKernels::PressureRelationKernel > + ( numFluidComponents(), + isThermal, + subRegion.size(), + dofManager.rankOffset(), + elemStatus, + wellElemDofNumber, + wellElemGravCoef, + nextWellElemIndex, + wellElemPres, + wellElemTotalMassDens, + dWellElemTotalMassDens, + controlHasSwitched, + localMatrix, + localRhs ); + + } + +} + void CompositionalMultiphaseWell::assemblePressureRelations( real64 const & time_n, - real64 const & GEOS_UNUSED_PARAM( dt ), + real64 const & dt, DomainPartition const & domain, DofManager const & dofManager, CRSMatrixView< real64, globalIndex const > const & localMatrix, arrayView1d< real64 > const & localRhs ) { GEOS_MARK_FUNCTION; - + GEOS_UNUSED_PARAM( dt ) forDiscretizationOnMeshTargets ( domain.getMeshBodies(), [&] ( string const &, MeshLevel const & mesh, string_array const & regionNames ) @@ -1984,109 +2679,57 @@ void CompositionalMultiphaseWell::assemblePressureRelations( real64 const & time WellElementSubRegion const & subRegion ) { - WellControls & wellControls = getWellControls( subRegion ); + assembleWellPressureRelations( time_n, dt, subRegion, dofManager, localMatrix, localRhs ); + } ); + } ); - if( wellControls.isWellOpen( ) && !m_keepVariablesConstantDuringInitStep ) - { - string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString() ); - MultiFluidBase const & fluid = getConstitutiveModel< MultiFluidBase >( subRegion, fluidName ); - bool const isThermal = fluid.isThermal(); - // get the degrees of freedom, depth info, next welem index - string const wellDofKey = dofManager.getKey( wellElementDofName() ); - arrayView1d< globalIndex const > const & wellElemDofNumber = - subRegion.getReference< array1d< globalIndex > >( wellDofKey ); - arrayView1d< real64 const > const & wellElemGravCoef = - subRegion.getField< well::gravityCoefficient >(); - arrayView1d< localIndex const > const & nextWellElemIndex = - subRegion.getReference< array1d< localIndex > >( WellElementSubRegion::viewKeyStruct::nextWellElementIndexString() ); - - // get primary variables on well elements - arrayView1d< real64 const > const & wellElemPres = - subRegion.getField< well::pressure >(); - - // get total mass density on well elements (for potential calculations) - arrayView1d< real64 const > const & wellElemTotalMassDens = - subRegion.getField< well::totalMassDensity >(); - arrayView2d< real64 const, compflow::USD_FLUID_DC > const & dWellElemTotalMassDens = - subRegion.getField< well::dTotalMassDensity >(); - - // segment status - arrayView1d< integer const > const elemStatus =subRegion.getLocalWellElementStatus(); - - bool controlHasSwitched = false; - isothermalCompositionalMultiphaseBaseKernels:: - KernelLaunchSelectorCompTherm< compositionalMultiphaseWellKernels::PressureRelationKernel > - ( numFluidComponents(), - isThermal, - subRegion.size(), - dofManager.rankOffset(), - subRegion.isLocallyOwned(), - subRegion.getTopWellElementIndex(), - m_targetPhaseIndex, - wellControls, - time_n, // controls evaluated with BHP/rate of the beginning of step - elemStatus, - wellElemDofNumber, - wellElemGravCoef, - nextWellElemIndex, - wellElemPres, - wellElemTotalMassDens, - dWellElemTotalMassDens, - controlHasSwitched, - localMatrix, - localRhs ); - - if( controlHasSwitched ) - { - // TODO: move the switch logic into wellControls - // TODO: implement a more general switch when more then two constraints per well type are allowed +} - if( wellControls.getControl() == WellControls::Control::BHP ) - { - if( wellControls.isProducer() ) - { - wellControls.switchToPhaseRateControl( wellControls.getTargetPhaseRate( time_n ) ); - GEOS_LOG_LEVEL_RANK_0( logInfo::WellControl, - GEOS_FMT( "Control switch for well {} from BHP constraint to phase volumetric rate constraint", subRegion.getName() ) ); - } - else if( wellControls.getInputControl() == WellControls::Control::MASSRATE ) - { - wellControls.switchToMassRateControl( wellControls.getTargetMassRate( time_n ) ); - GEOS_LOG_LEVEL_RANK_0( logInfo::WellControl, - GEOS_FMT( "Control switch for well {} from BHP constraint to mass rate constraint", subRegion.getName()) ); - } - else - { - wellControls.switchToTotalRateControl( wellControls.getTargetTotalRate( time_n ) ); - GEOS_LOG_LEVEL_RANK_0( logInfo::WellControl, - GEOS_FMT( "Control switch for well {} from BHP constraint to total volumetric rate constraint", subRegion.getName()) ); - } - } - else - { - wellControls.switchToBHPControl( wellControls.getTargetBHP( time_n ) ); - GEOS_LOG_LEVEL_RANK_0( logInfo::WellControl, - GEOS_FMT( "Control switch for well {} from rate constraint to BHP constraint", subRegion.getName() ) ); - } - } +void CompositionalMultiphaseWell::saveState( WellElementSubRegion & subRegion ) +{ - // If a well is opened and then timestep is cut resulting in the well being shut, if the well is opened - // the well initialization code requires control type to by synced - integer owner = -1; - // Only subregion owner evaluates well control and control changes need to be broadcast to all ranks - if( subRegion.isLocallyOwned() ) - { - owner = MpiWrapper::commRank( MPI_COMM_GEOS ); - } - owner = MpiWrapper::max( owner ); - WellControls::Control wellControl = wellControls.getControl(); - MpiWrapper::broadcast( wellControl, owner ); - wellControls.setControl( wellControl ); - } + // get a reference to the primary variables on well elements + arrayView1d< real64 const > const & wellElemPressure = + subRegion.getField< fields::well::pressure >(); + arrayView2d< real64 const, compflow::USD_COMP > const & wellElemGlobalCompDensity = + subRegion.getField< fields::well::globalCompDensity >(); + arrayView1d< real64 const > const & wellElemTemperature = + subRegion.getField< fields::well::temperature >(); + + arrayView1d< real64 > const & wellElemPressure_n = + subRegion.getField< fields::well::pressure_n >(); + wellElemPressure_n.setValues< parallelDevicePolicy<> >( wellElemPressure ); + + if( isThermal() ) + { + + arrayView1d< real64 > const & wellElemTemperature_n = + subRegion.getField< fields::well::temperature_n >(); + wellElemTemperature_n.setValues< parallelDevicePolicy<> >( wellElemTemperature ); + } + + arrayView2d< real64, compflow::USD_COMP > const & wellElemGlobalCompDensity_n = + subRegion.getField< fields::well::globalCompDensity_n >(); + wellElemGlobalCompDensity_n.setValues< parallelDevicePolicy<> >( wellElemGlobalCompDensity ); + + arrayView1d< real64 const > const & connRate = + subRegion.getField< fields::well::mixtureConnectionRate >(); + arrayView1d< real64 > const & connRate_n = + subRegion.getField< fields::well::mixtureConnectionRate_n >(); + connRate_n.setValues< parallelDevicePolicy<> >( connRate ); + + arrayView2d< real64 const, compflow::USD_PHASE > const wellElemPhaseVolFrac = + subRegion.getField< fields::well::phaseVolumeFraction >(); + arrayView2d< real64, compflow::USD_PHASE > const wellElemPhaseVolFrac_n = + subRegion.getField< fields::well::phaseVolumeFraction_n >(); + wellElemPhaseVolFrac_n.setValues< parallelDevicePolicy<> >( wellElemPhaseVolFrac ); + + string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString() ); + MultiFluidBase const & fluid = getConstitutiveModel< MultiFluidBase >( subRegion, fluidName ); + fluid.saveConvergedState(); + - } ); - } ); } void CompositionalMultiphaseWell::implicitStepSetup( real64 const & time_n, @@ -2316,5 +2959,75 @@ void CompositionalMultiphaseWell::printRates( real64 const & time_n, } ); } + +bool CompositionalMultiphaseWell::evaluateConstraints( real64 const & time_n, + WellElementSubRegion & subRegion ) +{ + WellControls & wellControls = getWellControls( subRegion ); + // create list of all constraints to process + std::vector< WellConstraintBase * > constraintList; + if( wellControls.isProducer() ) + { + constraintList = wellControls.getProdRateConstraints(); + // Solve minimum bhp constraint first + constraintList.insert( constraintList.begin(), wellControls.getMinBHPConstraint() ); + } + else + { + constraintList = wellControls.getInjRateConstraints(); + // Solve maximum bhp constraint first; + constraintList.insert( constraintList.begin(), wellControls.getMaxBHPConstraint() ); + } + // Get current constraint + WellConstraintBase * limitingConstraint = nullptr; + for( auto & constraint : constraintList ) + { + if( constraint->getName() == wellControls.getCurrentConstraint()->getName()) + { + limitingConstraint = constraint; + // tjb. this is likely not needed. set in update state + constraint->setBHP ( wellControls.getReference< real64 >( CompositionalMultiphaseWell::viewKeyStruct::currentBHPString() )); + constraint->setPhaseVolumeRates ( wellControls.getReference< array1d< real64 > >( + CompositionalMultiphaseWell::viewKeyStruct::currentPhaseVolRateString() ) ); + constraint->setTotalVolumeRate ( wellControls.getReference< real64 >( + CompositionalMultiphaseWell::viewKeyStruct::currentTotalVolRateString() )); + constraint->setMassRate( wellControls.getReference< real64 >( CompositionalMultiphaseWell::viewKeyStruct::currentMassRateString() )); + GEOS_LOG_RANK_IF ( getLogLevel() > 4 && subRegion.isLocallyOwned(), + " Well " << subRegion.getName() << " Limiting Constraint " << limitingConstraint->getName() << " " << limitingConstraint->bottomHolePressure() << " " << + limitingConstraint->phaseVolumeRates() << " " << + limitingConstraint->totalVolumeRate() << " " << limitingConstraint->massRate()); + } + } + + + // Check current against other constraints + for( auto & constraint : constraintList ) + { + + if( limitingConstraint->getName() != constraint->getName()) + { + if( constraint->checkViolation( *limitingConstraint, time_n ) ) + { + limitingConstraint = constraint; + wellControls.setControl( static_cast< WellControls::Control >(constraint->getControl()) ); // tjb old + wellControls.setCurrentConstraint( constraint ); + constraint->setBHP ( wellControls.getReference< real64 >( CompositionalMultiphaseWell::viewKeyStruct::currentBHPString() )); + constraint->setPhaseVolumeRates ( wellControls.getReference< array1d< real64 > >( + CompositionalMultiphaseWell::viewKeyStruct::currentPhaseVolRateString() ) ); + constraint->setTotalVolumeRate ( wellControls.getReference< real64 >( + CompositionalMultiphaseWell::viewKeyStruct::currentTotalVolRateString() )); + constraint->setMassRate( wellControls.getReference< real64 >( CompositionalMultiphaseWell::viewKeyStruct::currentMassRateString() )); + GEOS_LOG_RANK_IF ( getLogLevel() > 4 && subRegion.isLocallyOwned(), + " Well " << subRegion.getName() << " New Limiting Constraint " << constraint->getName() << " " << constraint->getConstraintValue( time_n ) ); + } + } + } + GEOS_LOG_RANK_IF ( getLogLevel() > 4 && subRegion.isLocallyOwned(), + " Well " << subRegion.getName() << " Limiting Constraint " << limitingConstraint->getName() << " " << limitingConstraint->bottomHolePressure() << " " << + limitingConstraint->phaseVolumeRates() << " " << + limitingConstraint->totalVolumeRate() << " " << limitingConstraint->massRate()); + + return true; +} REGISTER_CATALOG_ENTRY( PhysicsSolverBase, CompositionalMultiphaseWell, string const &, Group * const ) -} // namespace geos +} // namespace geos diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/CompositionalMultiphaseWell.hpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/CompositionalMultiphaseWell.hpp index fd874ec5a0e..23b39915b21 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/wells/CompositionalMultiphaseWell.hpp +++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/CompositionalMultiphaseWell.hpp @@ -25,6 +25,8 @@ #include "physicsSolvers/fluidFlow/wells/WellSolverBase.hpp" #include "physicsSolvers/fluidFlow/CompositionalMultiphaseBase.hpp" +#include "physicsSolvers/fluidFlow/wells/WellConstraintsBase.hpp" + namespace geos { @@ -92,6 +94,13 @@ class CompositionalMultiphaseWell : public WellSolverBase /**@{*/ + virtual real64 + calculateWellResidualNorm( real64 const & time_n, + real64 const & dt, + WellElementSubRegion const & subRegion, + DofManager const & dofManager, + arrayView1d< real64 const > const & localRhs ) override; + virtual real64 calculateResidualNorm( real64 const & time_n, real64 const & dt, @@ -99,17 +108,37 @@ class CompositionalMultiphaseWell : public WellSolverBase DofManager const & dofManager, arrayView1d< real64 const > const & localRhs ) override; + virtual real64 + scalingForWellSystemSolution( ElementSubRegionBase & subRegion, + DofManager const & dofManager, + arrayView1d< real64 const > const & localSolution ) override; + virtual real64 scalingForSystemSolution( DomainPartition & domain, DofManager const & dofManager, arrayView1d< real64 const > const & localSolution ) override; + virtual bool + checkWellSystemSolution( ElementSubRegionBase & subRegion, + DofManager const & dofManager, + arrayView1d< real64 const > const & localSolution, + real64 const scalingFactor ) override; + virtual bool checkSystemSolution( DomainPartition & domain, DofManager const & dofManager, arrayView1d< real64 const > const & localSolution, real64 const scalingFactor ) override; + virtual void + applyWellSystemSolution( DofManager const & dofManager, + arrayView1d< real64 const > const & localSolution, + real64 const scalingFactor, + real64 const dt, + DomainPartition & domain, + MeshLevel & mesh, + WellElementSubRegion & subRegion ) override; + virtual void applySystemSolution( DofManager const & dofManager, arrayView1d< real64 const > const & localSolution, @@ -159,6 +188,21 @@ class CompositionalMultiphaseWell : public WellSolverBase */ void updateFluidModel( WellElementSubRegion & subRegion ); + /** + * @brief Update well separator using current values of pressure and composition at the reference element + * @param subRegion the well subregion containing all the primary and dependent fields + * @param targetIndex the targetIndex of the subRegion + */ + void updateSeparator( WellElementSubRegion & subRegion ); + + /** + * @brief Calculate well rates at reference element + * @param subRegion the well subregion containing all the primary and dependent fields + * @param targetIndex the targetIndex of the subRegion + */ + + void calculateReferenceElementRates( WellElementSubRegion & subRegion ); + /** * @brief Recompute phase volume fractions (saturations) from constitutive and primary variables * @param subRegion the well subregion containing all the primary and dependent fields @@ -172,6 +216,16 @@ class CompositionalMultiphaseWell : public WellSolverBase */ void updateTotalMassDensity( WellElementSubRegion & subRegion ) const; + /** + * @brief Recompute the perforation rates for all the wells + * @param domain the domain containing the mesh and fields + */ + virtual void computeWellPerforationRates( real64 const & time_n, + real64 const & GEOS_UNUSED_PARAM( dt ), + ElementRegionManager const & elemManager, + WellElementSubRegion & subRegion )override; + + /** * @brief Recompute the perforation rates for all the wells * @param domain the domain containing the mesh and fields @@ -183,6 +237,7 @@ class CompositionalMultiphaseWell : public WellSolverBase * @brief Recompute all dependent quantities from primary variables (including constitutive models) * @param subRegion the well subregion containing all the primary and dependent fields */ + virtual real64 updateWellState( WellElementSubRegion & subRegion ) override; virtual void updateState( DomainPartition & domain ) override; virtual real64 updateSubRegionState( WellElementSubRegion & subRegion ) override; @@ -197,6 +252,13 @@ class CompositionalMultiphaseWell : public WellSolverBase integer useTotalMassEquation() const { return m_useTotalMassEquation; } + virtual void assembleWellFluxTerms( real64 const & time, + real64 const & dt, + WellElementSubRegion const & subRegion, + DofManager const & dofManager, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) override; + /** * @brief assembles the flux terms for all connections between well elements * @param time_n previous time value @@ -213,6 +275,14 @@ class CompositionalMultiphaseWell : public WellSolverBase DofManager const & dofManager, CRSMatrixView< real64, globalIndex const > const & localMatrix, arrayView1d< real64 > const & localRhs )override; + + virtual void assembleWellAccumulationTerms( real64 const & time, + real64 const & dt, + WellElementSubRegion & subRegion, + DofManager const & dofManager, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) override; + /** * @brief assembles the accumulation term for all the well elements * @param domain the physical domain object @@ -227,6 +297,21 @@ class CompositionalMultiphaseWell : public WellSolverBase CRSMatrixView< real64, globalIndex const > const & localMatrix, arrayView1d< real64 > const & localRhs ) override; + virtual void assembleWellConstraintTerms( real64 const & time_n, + real64 const & dt, + WellElementSubRegion const & subRegion, + DofManager const & dofManager, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) override; + + + virtual void assembleWellPressureRelations( real64 const & time_n, + real64 const & dt, + WellElementSubRegion const & subRegion, + DofManager const & dofManager, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) override; + /** * @brief assembles the pressure relations at all connections between well elements except at the well head * @param time_n time at the beginning of the time step @@ -249,6 +334,15 @@ class CompositionalMultiphaseWell : public WellSolverBase */ void chopNegativeDensities( DomainPartition & domain ); + void chopNegativeDensities( WellElementSubRegion & subRegion ); + + /** + * @brief Initialize all the primary and secondary variables in all the wells + * @param domain the domain containing the well manager to access individual wells + */ + void initializeWells( DomainPartition & domain, real64 const & time_n ) override; + void initializeWell( DomainPartition & domain, MeshLevel & mesh, WellElementSubRegion & subRegion, real64 const & time_n ) override; + struct viewKeyStruct : WellSolverBase::viewKeyStruct { static constexpr char const * dofFieldString() { return "compositionalWellVars"; } @@ -276,32 +370,13 @@ class CompositionalMultiphaseWell : public WellSolverBase static constexpr char const * massDensityString() { return "massDensity";} static constexpr char const * currentBHPString() { return "currentBHP"; } - static constexpr char const * dCurrentBHPString() { return "dCurrentBHP"; } - - static constexpr char const * dCurrentBHP_dPresString() { return "dCurrentBHP_dPres"; } - static constexpr char const * dCurrentBHP_dCompDensString() { return "dCurrentBHP_dCompDens"; } static constexpr char const * currentPhaseVolRateString() { return "currentPhaseVolumetricRate"; } - static constexpr char const * dCurrentPhaseVolRateString() { return "dCurrentPhaseVolumetricRate"; } - - - static constexpr char const * dCurrentPhaseVolRate_dPresString() { return "dCurrentPhaseVolumetricRate_dPres"; } - - static constexpr char const * dCurrentPhaseVolRate_dCompDensString() { return "dCurrentPhaseVolumetricRate_dCompDens"; } - - static constexpr char const * dCurrentPhaseVolRate_dRateString() { return "dCurrentPhaseVolumetricRate_dRate"; } static constexpr char const * currentTotalVolRateString() { return "currentTotalVolumetricRate"; } - static constexpr char const * dCurrentTotalVolRateString() { return "dCurrentTotalVolumetricRate"; } static constexpr char const * currentMassRateString() { return "currentMassRate"; } - static constexpr char const * dCurrentTotalVolRate_dPresString() { return "dCurrentTotalVolumetricRate_dPres"; } - - static constexpr char const * dCurrentTotalVolRate_dCompDensString() { return "dCurrentTotalVolumetricRate_dCompDens"; } - - static constexpr char const * dCurrentTotalVolRate_dRateString() { return "dCurrentTotalVolumetricRate_dRate"; } - } viewKeysCompMultiphaseWell; protected: @@ -312,6 +387,7 @@ class CompositionalMultiphaseWell : public WellSolverBase virtual void initializePostInitialConditionsPreSubGroups() override; + void saveState( WellElementSubRegion & subRegion ); virtual void postRestartInitialization() override final; /* * @brief Utility function that checks the consistency of the constitutive models @@ -330,11 +406,6 @@ class CompositionalMultiphaseWell : public WellSolverBase void validateWellControlsForFluid( WellControls const & wellControls, constitutive::MultiFluidBase const & fluid ) const; - /** - * @brief Checks injection streams for validity (compositions sum to one) - * @param subRegion the well subRegion - */ - void validateInjectionStreams( WellElementSubRegion const & subRegion ) const; /** * @brief Make sure that the well constraints are compatible @@ -357,16 +428,26 @@ class CompositionalMultiphaseWell : public WellSolverBase real64 const & dt, DomainPartition & domain ) override; -private: + virtual bool evaluateConstraints( real64 const & time_n, + WellElementSubRegion & subRegion ) 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; +private: virtual void setConstitutiveNames( ElementSubRegionBase & subRegion ) const override; + template< typename ... GROUPTYPES > + void selectLimitingConstraint( real64 const & time_n, integer const coupledIterationNumber, WellElementSubRegion & subRegion ); + + void solveConstraint( std::shared_ptr< WellConstraintBase > constraint, + real64 const & time_n, + real64 const & dt, + integer const cycleNumber, + integer const coupledIterationNumber, + DomainPartition & domain, + MeshLevel & mesh, + ElementRegionManager & elemManager, + WellElementSubRegion & subRegion, + DofManager const & dofManager ); /// flag indicating whether mass or molar formulation should be used diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/SinglePhaseWell.cpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/SinglePhaseWell.cpp index ce588ad3d5c..9463eb335bc 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/wells/SinglePhaseWell.cpp +++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/SinglePhaseWell.cpp @@ -44,7 +44,7 @@ #include "physicsSolvers/fluidFlow/kernels/singlePhase/FluidUpdateKernel.hpp" #include "physicsSolvers/fluidFlow/kernels/singlePhase/SolutionCheckKernel.hpp" #include "physicsSolvers/fluidFlow/SinglePhaseStatistics.hpp" - +#include "physicsSolvers/fluidFlow/wells/kernels/SinglePhaseWellConstraintKernels.hpp" namespace geos { @@ -143,6 +143,7 @@ void SinglePhaseWell::validateWellConstraints( real64 const & time_n, WellElementSubRegion const & subRegion, ElementRegionManager const & elemManager ) { + GEOS_UNUSED_VAR( time_n ); // tjb this will be needed with validation against tables WellControls & wellControls = getWellControls( subRegion ); if( !wellControls.useSurfaceConditions() ) { @@ -176,7 +177,9 @@ void SinglePhaseWell::validateWellConstraints( real64 const & time_n, wellControls.setRegionAverageTemperature( stats.averageTemperature ); } } + #if 0 // tjb vix this WellControls::Control currentControl = wellControls.getControl(); + real64 const targetTotalRate = wellControls.getTargetTotalRate( time_n ); real64 const targetPhaseRate = wellControls.getTargetPhaseRate( time_n ); GEOS_THROW_IF( currentControl == WellControls::Control::PHASEVOLRATE, @@ -192,7 +195,38 @@ void SinglePhaseWell::validateWellConstraints( real64 const & time_n, GEOS_THROW_IF( !isZero( targetPhaseRate ), "WellControls " << wellControls.getDataContext() << ": Target phase rate cannot be used for SinglePhaseWell", - InputError, wellControls.getDataContext() ); + InputError ); +#endif +} + +void SinglePhaseWell::initializePostInitialConditionsPreSubGroups() +{ + WellSolverBase::initializePostInitialConditionsPreSubGroups(); + createSeparator(); +} +void SinglePhaseWell::createSeparator() +{ + DomainPartition & domain = this->getGroupByPath< DomainPartition >( "/Problem/domain" ); + forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &, + MeshLevel & mesh, + string_array const & regionNames ) + { + + // loop over the wells + mesh.getElemManager().forElementSubRegions< WellElementSubRegion >( regionNames, [&]( localIndex const, + WellElementSubRegion & subRegion ) + { + string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString() ); + SingleFluidBase & fluid = subRegion.getConstitutiveModel< SingleFluidBase >( fluidName ); + // setup fluid separator + WellControls & wellControls = getWellControls( subRegion ); + string const fluidSeparatorName = wellControls.getName() + "Separator"; + std::unique_ptr< constitutive::ConstitutiveBase > fluidSeparatorPtr = fluid.deliverClone( fluidSeparatorName, &wellControls ); + fluidSeparatorPtr->allocateConstitutiveData( wellControls, 1 ); + fluidSeparatorPtr->resize( 1 ); + wellControls.setFluidSeparator( std::move( fluidSeparatorPtr )); + } ); + } ); } void SinglePhaseWell::updateBHPForConstraint( WellElementSubRegion & subRegion ) @@ -262,7 +296,7 @@ void SinglePhaseWell::updateBHPForConstraint( WellElementSubRegion & subRegion ) } -void SinglePhaseWell::updateVolRateForConstraint( WellElementSubRegion & subRegion ) +void SinglePhaseWell::calculateReferenceElementRates( WellElementSubRegion & subRegion ) { GEOS_MARK_FUNCTION; @@ -276,9 +310,6 @@ void SinglePhaseWell::updateVolRateForConstraint( WellElementSubRegion & subRegi // subRegion data - arrayView1d< real64 const > const pres = - subRegion.getField< well::pressure >(); - arrayView1d< real64 const > const & connRate = subRegion.getField< well::connectionRate >(); @@ -287,14 +318,71 @@ void SinglePhaseWell::updateVolRateForConstraint( WellElementSubRegion & subRegi string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString() ); SingleFluidBase & fluid = subRegion.getConstitutiveModel< SingleFluidBase >( fluidName ); arrayView2d< real64 const, constitutive::singlefluid::USD_FLUID > const & dens = fluid.density(); - arrayView3d< real64 const, constitutive::singlefluid::USD_FLUID_DER > const & dDens = fluid.dDensity(); + // control data + WellControls & wellControls = getWellControls( subRegion ); + + real64 & currentVolRate = + wellControls.getReference< real64 >( SinglePhaseWell::viewKeyStruct::currentVolRateString() ); + + // bring everything back to host, capture the scalars by reference + forAll< serialPolicy >( 1, [connRate, + dens, + ¤tVolRate, + &iwelemRef] ( localIndex const ) + { + real64 const densInv = 1.0 / dens[iwelemRef][0]; + currentVolRate = connRate[iwelemRef] * densInv; + // tjb compute mass + } ); + + +} + +void SinglePhaseWell::updateFluidModel( WellElementSubRegion & subRegion ) const +{ + GEOS_MARK_FUNCTION; + + arrayView1d< real64 const > const pres = subRegion.getField< well::pressure >(); + arrayView1d< real64 const > const temp = subRegion.getField< well::temperature >(); + + string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString() ); + SingleFluidBase & fluid = subRegion.getConstitutiveModel< SingleFluidBase >( fluidName ); + + constitutiveUpdatePassThru( fluid, [&]( auto & castedFluid ) + { + typename TYPEOFREF( castedFluid ) ::KernelWrapper fluidWrapper = castedFluid.createKernelWrapper(); + singlePhaseBaseKernels::FluidUpdateKernel::launch( fluidWrapper, pres, temp ); + } ); +} +void SinglePhaseWell::updateSeparator( WellElementSubRegion & subRegion ) +{ + GEOS_MARK_FUNCTION; + + // the rank that owns the reference well element is responsible for the calculations below. + if( !subRegion.isLocallyOwned() ) + { + return; + } + + localIndex const iwelemRef = subRegion.getTopWellElementIndex(); + + // subRegion data + arrayView1d< real64 const > const pres = + subRegion.getField< well::pressure >(); + + // control data WellControls & wellControls = getWellControls( subRegion ); string const wellControlsName = wellControls.getName(); bool const logSurfaceCondition = isLogLevelActive< logInfo::WellControl >( wellControls.getLogLevel()); integer const useSurfaceConditions = wellControls.useSurfaceConditions(); + + // fluid data + constitutive::SingleFluidBase & fluidSeparator = wellControls.getSingleFluidSeparator(); + arrayView2d< real64 const, constitutive::singlefluid::USD_FLUID > const & dens = fluidSeparator.density(); + real64 flashPressure; if( useSurfaceConditions ) { @@ -311,31 +399,22 @@ void SinglePhaseWell::updateVolRateForConstraint( WellElementSubRegion & subRegi flashPressure = pres[iwelemRef]; } } - real64 & currentVolRate = - wellControls.getReference< real64 >( SinglePhaseWell::viewKeyStruct::currentVolRateString() ); - arrayView1d< real64 > const & dCurrentVolRate = - wellControls.getReference< array1d< real64 > >( SinglePhaseWell::viewKeyStruct::dCurrentVolRateString() ); - - constitutiveUpdatePassThru( fluid, [&]( auto & castedFluid ) + constitutiveUpdatePassThru( fluidSeparator, [&]( auto & castedFluid ) { - typename TYPEOFREF( castedFluid ) ::KernelWrapper fluidWrapper = castedFluid.createKernelWrapper(); + typename TYPEOFREF( castedFluid ) ::KernelWrapper fluidSeparatorWrapper = castedFluid.createKernelWrapper(); geos::internal::kernelLaunchSelectorThermalSwitch( isThermal(), [&] ( auto ISTHERMAL ) { integer constexpr IS_THERMAL = ISTHERMAL(); - using COFFSET_WJ = singlePhaseWellKernels::ColOffset_WellJac< IS_THERMAL >; + GEOS_UNUSED_VAR( IS_THERMAL ); // bring everything back to host, capture the scalars by reference - forAll< serialPolicy >( 1, [fluidWrapper, + forAll< serialPolicy >( 1, [fluidSeparatorWrapper, pres, - connRate, dens, - dDens, logSurfaceCondition, &useSurfaceConditions, &flashPressure, - ¤tVolRate, - dCurrentVolRate, &iwelemRef, &wellControlsName] ( localIndex const ) { @@ -346,7 +425,7 @@ void SinglePhaseWell::updateVolRateForConstraint( WellElementSubRegion & subRegi if( useSurfaceConditions ) { // we need to compute the surface density - fluidWrapper.update( iwelemRef, 0, flashPressure ); + fluidSeparatorWrapper.update( iwelemRef, 0, flashPressure ); if( logSurfaceCondition ) { @@ -361,62 +440,204 @@ void SinglePhaseWell::updateVolRateForConstraint( WellElementSubRegion & subRegi } else { - real64 const refPres = pres[iwelemRef]; - fluidWrapper.update( iwelemRef, 0, refPres ); - } - - real64 const densInv = 1.0 / dens[iwelemRef][0]; - currentVolRate = connRate[iwelemRef] * densInv; - - dCurrentVolRate[COFFSET_WJ::dP] = -( useSurfaceConditions == 0 ) * dDens[iwelemRef][0][DerivOffset::dP] * currentVolRate * densInv; - dCurrentVolRate[COFFSET_WJ::dQ] = densInv; - if constexpr ( IS_THERMAL ) - { - dCurrentVolRate[COFFSET_WJ::dT] = -( useSurfaceConditions == 0 ) * dDens[iwelemRef][0][DerivOffset::dT] * currentVolRate * densInv; - } - if( logSurfaceCondition && useSurfaceConditions ) - { - GEOS_LOG_RANK( GEOS_FMT( "{}: total fluid density at surface conditions = {} kg/sm3, total rate = {} kg/s, total surface volumetric rate = {} sm3/s", - wellControlsName, dens[iwelemRef][0], connRate[iwelemRef], currentVolRate ) ); + fluidSeparatorWrapper.update( iwelemRef, 0, flashPressure ); } } ); } ); } ); } -void SinglePhaseWell::updateFluidModel( WellElementSubRegion & subRegion ) const +real64 SinglePhaseWell::updateSubRegionState( WellElementSubRegion & subRegion ) { - GEOS_MARK_FUNCTION; + WellControls & wellControls = getWellControls( subRegion ); + if( wellControls.getWellState()) + { + if( m_useNewCode ) + { + // update volumetric rates for the well constraints + // Warning! This must be called before updating the fluid model + //calculateReferenceElementRates( subRegion ); + + // update density in the well elements + updateFluidModel( subRegion ); + updateSeparator( subRegion ); // Calculate fluid properties at control conditions + + // Calculate the reference element rates + calculateReferenceElementRates( subRegion ); + // update the current BHP + updateBHPForConstraint( subRegion ); + } + else + { + // update volumetric rates for the well constraints + // Warning! This must be called before updating the fluid model + calculateReferenceElementRates( subRegion ); - arrayView1d< real64 const > const pres = subRegion.getField< well::pressure >(); - arrayView1d< real64 const > const temp = subRegion.getField< well::temperature >(); + // update density in the well elements + updateFluidModel( subRegion ); - string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString() ); - SingleFluidBase & fluid = subRegion.getConstitutiveModel< SingleFluidBase >( fluidName ); + // update the current BHP + updateBHPForConstraint( subRegion ); + } - constitutiveUpdatePassThru( fluid, [&]( auto & castedFluid ) - { - typename TYPEOFREF( castedFluid ) ::KernelWrapper fluidWrapper = castedFluid.createKernelWrapper(); - singlePhaseBaseKernels::FluidUpdateKernel::launch( fluidWrapper, pres, temp ); - } ); + } + return 0.0; // change in phasevolume fraction doesnt apply } - -real64 SinglePhaseWell::updateSubRegionState( WellElementSubRegion & subRegion ) +void SinglePhaseWell::initializeWell( DomainPartition & domain, MeshLevel & mesh, WellElementSubRegion & subRegion, real64 const & time_n ) { - // update volumetric rates for the well constraints - // Warning! This must be called before updating the fluid model - updateVolRateForConstraint( subRegion ); + GEOS_UNUSED_VAR( domain ); + WellControls & wellControls = getWellControls( subRegion ); + PerforationData const & perforationData = *subRegion.getPerforationData(); - // update density in the well elements - updateFluidModel( subRegion ); + // get the info stored on well elements + arrayView1d< real64 const > const wellElemGravCoef = + subRegion.getField< well::gravityCoefficient >(); - // update the current BHP - updateBHPForConstraint( subRegion ); + // get well primary variables on well elements + arrayView1d< real64 > const wellElemPressure = + subRegion.getField< well::pressure >(); + arrayView1d< real64 > const connRate = + subRegion.getField< well::connectionRate >(); + arrayView1d< real64 > const wellElemTemperature = + subRegion.getField< well::temperature >(); + // get the element region, subregion, index + arrayView1d< localIndex const > const resElementRegion = + perforationData.getField< perforation::reservoirElementRegion >(); + arrayView1d< localIndex const > const resElementSubRegion = + perforationData.getField< perforation::reservoirElementSubRegion >(); + arrayView1d< localIndex const > const resElementIndex = + perforationData.getField< perforation::reservoirElementIndex >(); + + arrayView1d< real64 const > const & perfGravCoef = + perforationData.getField< well::gravityCoefficient >(); + + bool const hasNonZeroRate = MpiWrapper::max< integer >( hasNonZero( connRate )); + + if( time_n <= 0.0 || (wellControls.isWellOpen() && !hasNonZeroRate ) ) + { + wellControls.setWellState( true ); + if( wellControls.getCurrentConstraint() == nullptr ) + { + if( wellControls.isProducer() ) + { + wellControls.forSubGroups< MinimumBHPConstraint, ProductionConstraint< VolumeRateConstraint >, ProductionConstraint< MassRateConstraint >, + ProductionConstraint< PhaseVolumeRateConstraint > >( [&]( auto & constraint ) + { + if( ConstraintTypeId( wellControls.getControl()) == constraint.getControl() ) + { + wellControls.setCurrentConstraint( &constraint ); + wellControls.setControl( static_cast< WellControls::Control >(constraint.getControl()) ); // tjb old + } + } ); + } + else + { + wellControls.forSubGroups< MaximumBHPConstraint, InjectionConstraint< VolumeRateConstraint >, InjectionConstraint< MassRateConstraint >, + InjectionConstraint< PhaseVolumeRateConstraint > >( [&]( auto & constraint ) + { + if( ConstraintTypeId( wellControls.getControl()) == constraint.getControl() ) + { + wellControls.setCurrentConstraint( &constraint ); + wellControls.setControl( static_cast< WellControls::Control >(constraint.getControl()) ); // tjb old + } + } ); + } + } - // note: the perforation rates are updated separately - return 0.0; // change in phasevolume fraction doesnt apply -} + // TODO: change the way we access the flowSolver here + SinglePhaseBase const & flowSolver = getParent().getGroup< SinglePhaseBase >( getFlowSolverName() ); + PresTempInitializationKernel::SinglePhaseFlowAccessors resSinglePhaseFlowAccessors( mesh.getElemManager(), flowSolver.getName() ); + PresTempInitializationKernel::SingleFluidAccessors resSingleFluidAccessors( mesh.getElemManager(), flowSolver.getName() ); + + // 1) Loop over all perforations to compute an average density + // 2) Initialize the reference pressure + // 3) Estimate the pressures in the well elements using the average density + PresTempInitializationKernel:: + launch( isThermal(), + perforationData.size(), + subRegion.size(), + perforationData.getNumPerforationsGlobal(), + wellControls, + 0.0, // initialization done at t = 0 + resSinglePhaseFlowAccessors.get( flow::pressure{} ), + resSinglePhaseFlowAccessors.get( flow::temperature{} ), + resSingleFluidAccessors.get( fields::singlefluid::density{} ), + resElementRegion, + resElementSubRegion, + resElementIndex, + perfGravCoef, + wellElemGravCoef, + wellElemPressure, + wellElemTemperature ); + + // 4) Recompute the pressure-dependent properties + // Note: I am leaving that here because I would like to use the perforationRates (computed in UpdateState) + // to better initialize the rates + updateSubRegionState( subRegion ); + + string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString() ); + SingleFluidBase & fluid = subRegion.getConstitutiveModel< SingleFluidBase >( fluidName ); + arrayView2d< real64 const, constitutive::singlefluid::USD_FLUID > const & wellElemDens = fluid.density(); + + // 5) Estimate the well rates + RateInitializationKernel::launch( subRegion.size(), + wellControls, + 0.0, // initialization done at t = 0 + wellElemDens, + connRate ); + + calculateReferenceElementRates( subRegion ); + WellConstraintBase * constraint = wellControls.getCurrentConstraint(); + constraint->setBHP ( wellControls.getReference< real64 >( SinglePhaseWell::viewKeyStruct::currentBHPString() )); + constraint->setTotalVolumeRate ( wellControls.getReference< real64 >( + SinglePhaseWell::viewKeyStruct::currentVolRateString() )); + //constraint->setMassRate( wellControls.getReference< real64 >( SinglePhaseWell::viewKeyStruct::currentMassRateString() )); + // 7) Copy well / fluid dofs to "prop"_n variables + saveState( subRegion ); + } + else if( !hasNonZeroRate ) + { + wellControls.setWellState( false ); + GEOS_LOG_RANK_0( "tjb shut wells "<< subRegion.getName()); + } + else + { + wellControls.setWellState( true ); + // setup for restart + if( wellControls.getCurrentConstraint() == nullptr ) + { + updateSubRegionState( subRegion ); + if( wellControls.isProducer() ) + { + wellControls.forSubGroups< MinimumBHPConstraint, ProductionConstraint< VolumeRateConstraint >, ProductionConstraint< MassRateConstraint >, + ProductionConstraint< PhaseVolumeRateConstraint > >( [&]( + auto + & constraint ) + { + if( ConstraintTypeId( wellControls.getControl()) == constraint.getControl() ) + { + wellControls.setCurrentConstraint( &constraint ); + } + } ); + } + else + { + wellControls.forSubGroups< MaximumBHPConstraint, InjectionConstraint< VolumeRateConstraint >, InjectionConstraint< MassRateConstraint >, InjectionConstraint< PhaseVolumeRateConstraint > >( [&]( + auto + & + constraint ) + { + if( ConstraintTypeId( wellControls.getControl()) == constraint.getControl() ) + { + wellControls.setCurrentConstraint( &constraint ); + } + } ); + } + } + } + +} void SinglePhaseWell::initializeWells( DomainPartition & domain, real64 const & time_n ) { GEOS_MARK_FUNCTION; @@ -424,87 +645,16 @@ void SinglePhaseWell::initializeWells( DomainPartition & domain, real64 const & // loop over the wells forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &, - MeshLevel & meshLevel, + MeshLevel & mesh, string_array const & regionNames ) { - ElementRegionManager & elemManager = meshLevel.getElemManager(); + ElementRegionManager & elemManager = mesh.getElemManager(); elemManager.forElementSubRegions< WellElementSubRegion >( regionNames, [&]( localIndex const, WellElementSubRegion & subRegion ) { - WellControls const & wellControls = getWellControls( subRegion ); - PerforationData const & perforationData = *subRegion.getPerforationData(); - - // get the info stored on well elements - arrayView1d< real64 const > const wellElemGravCoef = - subRegion.getField< well::gravityCoefficient >(); - - // get well primary variables on well elements - arrayView1d< real64 > const wellElemPressure = - subRegion.getField< well::pressure >(); - arrayView1d< real64 > const connRate = - subRegion.getField< well::connectionRate >(); - arrayView1d< real64 > const wellElemTemperature = - subRegion.getField< well::temperature >(); - // get the element region, subregion, index - arrayView1d< localIndex const > const resElementRegion = - perforationData.getField< perforation::reservoirElementRegion >(); - arrayView1d< localIndex const > const resElementSubRegion = - perforationData.getField< perforation::reservoirElementSubRegion >(); - arrayView1d< localIndex const > const resElementIndex = - perforationData.getField< perforation::reservoirElementIndex >(); - - arrayView1d< real64 const > const & perfGravCoef = - perforationData.getField< well::gravityCoefficient >(); - - bool const hasNonZeroRate = MpiWrapper::max< integer >( hasNonZero( connRate )); - - if( wellControls.isWellOpen() && !hasNonZeroRate ) - { - // TODO: change the way we access the flowSolver here - SinglePhaseBase const & flowSolver = getParent().getGroup< SinglePhaseBase >( getFlowSolverName() ); - PresTempInitializationKernel::SinglePhaseFlowAccessors resSinglePhaseFlowAccessors( meshLevel.getElemManager(), flowSolver.getName() ); - PresTempInitializationKernel::SingleFluidAccessors resSingleFluidAccessors( meshLevel.getElemManager(), flowSolver.getName() ); - - // 1) Loop over all perforations to compute an average density - // 2) Initialize the reference pressure - // 3) Estimate the pressures in the well elements using the average density - PresTempInitializationKernel:: - launch( isThermal(), - perforationData.size(), - subRegion.size(), - perforationData.getNumPerforationsGlobal(), - wellControls, - 0.0, // initialization done at t = 0 - resSinglePhaseFlowAccessors.get( flow::pressure{} ), - resSinglePhaseFlowAccessors.get( flow::temperature{} ), - resSingleFluidAccessors.get( fields::singlefluid::density{} ), - resElementRegion, - resElementSubRegion, - resElementIndex, - perfGravCoef, - wellElemGravCoef, - wellElemPressure, - wellElemTemperature ); - - // 4) Recompute the pressure-dependent properties - // Note: I am leaving that here because I would like to use the perforationRates (computed in UpdateState) - // to better initialize the rates - updateSubRegionState( subRegion ); - - string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString() ); - SingleFluidBase & fluid = subRegion.getConstitutiveModel< SingleFluidBase >( fluidName ); - arrayView2d< real64 const, constitutive::singlefluid::USD_FLUID > const & wellElemDens = fluid.density(); - - // 5) Estimate the well rates - RateInitializationKernel::launch( subRegion.size(), - wellControls, - 0.0, // initialization done at t = 0 - wellElemDens, - connRate ); - } - + initializeWell( domain, mesh, subRegion, time_n ); } ); } ); @@ -585,6 +735,33 @@ void SinglePhaseWell::shutDownWell( real64 const time_n, } ); } +real64 SinglePhaseWell::updateWellState( WellElementSubRegion & subRegion ) +{ + GEOS_MARK_FUNCTION; + + updateSubRegionState( subRegion ); + return 0.0; +} +void SinglePhaseWell::updateState( DomainPartition & domain ) +{ + GEOS_MARK_FUNCTION; + + forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &, + MeshLevel & mesh, + string_array const & regionNames ) + { + mesh.getElemManager().forElementSubRegions< WellElementSubRegion >( regionNames, [&]( localIndex const, + WellElementSubRegion & subRegion ) + { + WellControls & wellControls = getWellControls( subRegion ); + if( wellControls.getWellState()) + { + updateWellState( subRegion ); + } + } ); + } ); + +} void SinglePhaseWell::assembleSystem( real64 const time, real64 const dt, DomainPartition & domain, @@ -594,11 +771,40 @@ void SinglePhaseWell::assembleSystem( real64 const time, { string const wellDofKey = dofManager.getKey( wellElementDofName()); + if( m_useNewCode ) + { + // selects constraints one of 2 ways + // wellEstimator flag set to 0 => orginal logic rates are computed during update state and constraints are selected every newton + // iteration + // wellEstimator flag > 0 => well esitmator solved for each constraint and then selects the constraint + // => estimator solve only performed first "wellEstimator" iterations + NonlinearSolverParameters const & nonlinearParams = getNonlinearSolverParameters(); + selectWellConstraint( time, dt, nonlinearParams.m_numNewtonIterations, domain ); + } + // assemble the accumulation term in the mass balance equations assembleAccumulationTerms( time, dt, domain, dofManager, localMatrix, localRhs ); // then assemble the pressure relations between well elements assemblePressureRelations( time, dt, domain, dofManager, localMatrix, localRhs ); + { + forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &, + MeshLevel & mesh, + string_array const & regionNames ) + { + ElementRegionManager & elementRegionManager = mesh.getElemManager(); + elementRegionManager.forElementRegions< WellElementRegion >( regionNames, + [&]( localIndex const, + WellElementRegion & region ) + { + WellElementSubRegion & subRegion = region.getGroup( ElementRegionBase::viewKeyStruct::elementSubRegions() ) + .getGroup< WellElementSubRegion >( region.getSubRegionName() ); + WellControls & wellControls = getWellControls( subRegion ); + if( !wellControls.getConstraintSwitch() ) + assembleWellConstraintTerms( time, dt, subRegion, dofManager, localMatrix.toViewConstSizes(), localRhs ); + } ); + } ); + } // then compute the perforation rates (later assembled by the coupled solver) computePerforationRates( time, dt, domain ); @@ -611,6 +817,48 @@ void SinglePhaseWell::assembleSystem( real64 const time, shutDownWell( time, domain, dofManager, localMatrix, localRhs ); } +void SinglePhaseWell::assembleWellFluxTerms( real64 const & time, + real64 const & dt, + WellElementSubRegion const & subRegion, + DofManager const & dofManager, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) +{ + GEOS_MARK_FUNCTION; + GEOS_UNUSED_VAR( time ); + WellControls const & wellControls = getWellControls( subRegion ); + // get a reference to the degree-of-freedom numbers + string const wellDofKey = dofManager.getKey( wellElementDofName() ); + + if( isThermal() ) + { + string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString() ); + SingleFluidBase const & fluid = subRegion.getConstitutiveModel< SingleFluidBase >( fluidName ); + thermalSinglePhaseWellKernels:: + FaceBasedAssemblyKernelFactory:: + createAndLaunch< parallelDevicePolicy<> >( dt, + dofManager.rankOffset(), + wellDofKey, + wellControls, + subRegion, + fluid, + localMatrix, + localRhs ); + } + else + { + singlePhaseWellKernels:: + FaceBasedAssemblyKernelFactory:: + createAndLaunch< parallelDevicePolicy<> >( dt, + dofManager.rankOffset(), + wellDofKey, + wellControls, + subRegion, + localMatrix, + localRhs ); + } + +} void SinglePhaseWell::assembleFluxTerms( real64 const & time_n, real64 const & dt, DomainPartition & domain, @@ -619,8 +867,6 @@ void SinglePhaseWell::assembleFluxTerms( real64 const & time_n, arrayView1d< real64 > const & localRhs ) { GEOS_MARK_FUNCTION; - GEOS_UNUSED_VAR( time_n ); - GEOS_UNUSED_VAR( dt ); // loop over the wells forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &, @@ -635,45 +881,106 @@ void SinglePhaseWell::assembleFluxTerms( real64 const & time_n, WellElementSubRegion const & subRegion ) { - WellControls const & wellControls = getWellControls( subRegion ); - // get a reference to the degree-of-freedom numbers - string const wellDofKey = dofManager.getKey( wellElementDofName() ); + assembleWellFluxTerms( time_n, dt, subRegion, dofManager, localMatrix, localRhs ); + } ); - if( isThermal() ) - { - string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString() ); - SingleFluidBase const & fluid = subRegion.getConstitutiveModel< SingleFluidBase >( fluidName ); - thermalSinglePhaseWellKernels:: - FaceBasedAssemblyKernelFactory:: - createAndLaunch< parallelDevicePolicy<> >( dt, - dofManager.rankOffset(), - wellDofKey, - wellControls, - subRegion, - fluid, - localMatrix, - localRhs ); - } - else + } ); +} + + +void SinglePhaseWell::assembleWellConstraintTerms( real64 const & time_n, + real64 const & GEOS_UNUSED_PARAM( dt ), + WellElementSubRegion const & subRegion, + DofManager const & dofManager, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) +{ + GEOS_MARK_FUNCTION; + + + // the rank that owns the reference well element is responsible for the calculations below. + if( !subRegion.isLocallyOwned() ) + { + return; + } + WellControls & wellControls = getWellControls( subRegion ); + + { + // tjb wellControls.forSubGroups< BHPConstraint, MassConstraint, VolumeRateConstraint >( [&]( auto & constraint ) + wellControls.forSubGroups< BHPConstraint, InjectionConstraint< VolumeRateConstraint >, ProductionConstraint< VolumeRateConstraint > >( [&]( auto & constraint ) + { + if( constraint.getName() == wellControls.getCurrentConstraint()->getName()) { - singlePhaseWellKernels:: - FaceBasedAssemblyKernelFactory:: - createAndLaunch< parallelDevicePolicy<> >( dt, - dofManager.rankOffset(), - wellDofKey, - wellControls, - subRegion, - localMatrix, - localRhs ); + // found limiting constraint + + // fluid data + constitutive::SingleFluidBase & fluidSeparator = wellControls.getSingleFluidSeparator(); + integer isThermal = fluidSeparator.isThermal(); + + geos::internal::kernelLaunchSelectorThermalSwitch( isThermal, [&] ( auto ISTHERMAL ) + { + integer constexpr IS_THERMAL = ISTHERMAL(); + + singlePhaseWellConstraintKernels::ConstraintHelper< IS_THERMAL >::assembleConstraintEquation( time_n, + wellControls, + constraint, + subRegion, + dofManager.getKey( wellElementDofName() ), + dofManager.rankOffset(), + localMatrix, + localRhs ); + } ); } } ); + } - } ); } +void SinglePhaseWell::assembleWellPressureRelations( real64 const & GEOS_UNUSED_PARAM( time_n ), + real64 const & GEOS_UNUSED_PARAM( dt ), + WellElementSubRegion const & subRegion, + DofManager const & dofManager, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) +{ + + // get the degrees of freedom numbers, depth, next well elem index + string const wellDofKey = dofManager.getKey( wellElementDofName() ); + arrayView1d< globalIndex const > const & wellElemDofNumber = + subRegion.getReference< array1d< globalIndex > >( wellDofKey ); + arrayView1d< real64 const > const & wellElemGravCoef = + subRegion.getField< well::gravityCoefficient >(); + arrayView1d< localIndex const > const & nextWellElemIndex = + subRegion.getReference< array1d< localIndex > >( WellElementSubRegion::viewKeyStruct::nextWellElementIndexString() ); + + // get primary variables on well elements + arrayView1d< real64 const > const & wellElemPressure = + subRegion.getField< well::pressure >(); + + // get well constitutive data + string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString() ); + SingleFluidBase const & fluid = subRegion.getConstitutiveModel< SingleFluidBase >( fluidName ); + arrayView2d< real64 const, constitutive::singlefluid::USD_FLUID > const & wellElemDensity = fluid.density(); + arrayView3d< real64 const, constitutive::singlefluid::USD_FLUID_DER > const & dWellElemDensity = fluid.dDensity(); + + geos::internal::kernelLaunchSelectorThermalSwitch( isThermal(), [&] ( auto ISTHERMAL ) + { + PressureRelationKernel::launch< ISTHERMAL >( subRegion.size(), + dofManager.rankOffset(), + wellElemDofNumber, + wellElemGravCoef, + nextWellElemIndex, + wellElemPressure, + wellElemDensity, + dWellElemDensity, + localMatrix, + localRhs ); + } ); + +} void SinglePhaseWell::assemblePressureRelations( real64 const & time_n, - real64 const & GEOS_UNUSED_PARAM( dt ), + real64 const & dt, DomainPartition const & domain, DofManager const & dofManager, CRSMatrixView< real64, globalIndex const > const & localMatrix, @@ -694,68 +1001,53 @@ void SinglePhaseWell::assemblePressureRelations( real64 const & time_n, WellElementSubRegion const & subRegion ) { - WellControls & wellControls = getWellControls( subRegion ); - - // get the degrees of freedom numbers, depth, next well elem index - string const wellDofKey = dofManager.getKey( wellElementDofName() ); - arrayView1d< globalIndex const > const & wellElemDofNumber = - subRegion.getReference< array1d< globalIndex > >( wellDofKey ); - arrayView1d< real64 const > const & wellElemGravCoef = - subRegion.getField< well::gravityCoefficient >(); - arrayView1d< localIndex const > const & nextWellElemIndex = - subRegion.getReference< array1d< localIndex > >( WellElementSubRegion::viewKeyStruct::nextWellElementIndexString() ); + assembleWellPressureRelations( time_n, dt, subRegion, dofManager, localMatrix, localRhs ); + } ); + } ); +} +void SinglePhaseWell::assembleWellAccumulationTerms( real64 const & time, + real64 const & dt, + WellElementSubRegion & subRegion, + DofManager const & dofManager, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) - // get primary variables on well elements - arrayView1d< real64 const > const & wellElemPressure = - subRegion.getField< well::pressure >(); +{ + GEOS_UNUSED_VAR( time ); + GEOS_UNUSED_VAR( dt ); + // get a reference to the degree-of-freedom numbers + string const wellElemDofKey = dofManager.getKey( wellElementDofName() ); - // get well constitutive data - string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString() ); - SingleFluidBase const & fluid = subRegion.getConstitutiveModel< SingleFluidBase >( fluidName ); - arrayView2d< real64 const, constitutive::singlefluid::USD_FLUID > const & wellElemDensity = fluid.density(); - arrayView3d< real64 const, constitutive::singlefluid::USD_FLUID_DER > const & dWellElemDensity = fluid.dDensity(); + WellControls const & wellControls = getWellControls( subRegion ); + string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString() ); + SingleFluidBase const & fluid = subRegion.getConstitutiveModel< SingleFluidBase >( fluidName ); - geos::internal::kernelLaunchSelectorThermalSwitch( isThermal(), [&] ( auto ISTHERMAL ) - { - localIndex controlHasSwitched=0; - controlHasSwitched = PressureRelationKernel::launch< ISTHERMAL >( subRegion.size(), - dofManager.rankOffset(), - subRegion.isLocallyOwned(), - subRegion.getTopWellElementIndex(), - wellControls, - time_n, - wellElemDofNumber, - wellElemGravCoef, - nextWellElemIndex, - wellElemPressure, - wellElemDensity, - dWellElemDensity, - localMatrix, - localRhs ); - - if( controlHasSwitched == 1 ) - { - // Note: if BHP control is not viable, we switch to TOTALVOLRATE - // if TOTALVOLRATE is not viable, we switch to BHP + if( isThermal() ) + { + thermalSinglePhaseWellKernels:: + ElementBasedAssemblyKernelFactory:: + createAndLaunch< parallelDevicePolicy<> >( wellControls.isProducer(), + dofManager.rankOffset(), + wellElemDofKey, + subRegion, + fluid, + localMatrix, + localRhs ); + } + else + { + singlePhaseWellKernels:: + ElementBasedAssemblyKernelFactory:: + createAndLaunch< parallelDevicePolicy<> >( dofManager.rankOffset(), + wellElemDofKey, + subRegion, + fluid, + localMatrix, + localRhs ); + } +} - if( wellControls.getControl() == WellControls::Control::BHP ) - { - wellControls.switchToTotalRateControl( wellControls.getTargetTotalRate( time_n ) ); - GEOS_LOG_LEVEL_RANK_0( logInfo::WellControl, - GEOS_FMT( "Control switch for well {} from BHP constraint to rate constraint", subRegion.getName()) ); - } - else - { - wellControls.switchToBHPControl( wellControls.getTargetBHP( time_n ) ); - GEOS_LOG_LEVEL_RANK_0( logInfo::WellControl, - GEOS_FMT( "Control switch for well {} from rate constraint to BHP constraint", subRegion.getName()) ); - } - } - } ); - } ); - } ); -} void SinglePhaseWell::assembleAccumulationTerms( real64 const & time_n, real64 const & dt, @@ -765,54 +1057,22 @@ void SinglePhaseWell::assembleAccumulationTerms( real64 const & time_n, arrayView1d< real64 > const & localRhs ) { GEOS_MARK_FUNCTION; - GEOS_UNUSED_VAR( time_n ); - GEOS_UNUSED_VAR( dt ); + forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &, - MeshLevel const & mesh, + MeshLevel & mesh, string_array const & regionNames ) { - ElementRegionManager const & elemManager = mesh.getElemManager(); + ElementRegionManager & elemManager = mesh.getElemManager(); elemManager.forElementSubRegions< WellElementSubRegion >( regionNames, [&]( localIndex const, - WellElementSubRegion const & subRegion ) + WellElementSubRegion & subRegion ) { - - // get a reference to the degree-of-freedom numbers - string const wellElemDofKey = dofManager.getKey( wellElementDofName() ); - - WellControls const & wellControls = getWellControls( subRegion ); - string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString() ); - SingleFluidBase const & fluid = subRegion.getConstitutiveModel< SingleFluidBase >( fluidName ); - - if( isThermal() ) - { - thermalSinglePhaseWellKernels:: - ElementBasedAssemblyKernelFactory:: - createAndLaunch< parallelDevicePolicy<> >( wellControls.isProducer(), - dofManager.rankOffset(), - wellElemDofKey, - subRegion, - fluid, - localMatrix, - localRhs ); - } - else - { - singlePhaseWellKernels:: - ElementBasedAssemblyKernelFactory:: - createAndLaunch< parallelDevicePolicy<> >( dofManager.rankOffset(), - wellElemDofKey, - subRegion, - fluid, - localMatrix, - localRhs ); - } + assembleWellAccumulationTerms( time_n, dt, subRegion, dofManager, localMatrix, localRhs ); } ); } ); - // then assemble the volume balance equations - assembleVolumeBalanceTerms( domain, dofManager, localMatrix, localRhs ); + } void SinglePhaseWell::assembleVolumeBalanceTerms( DomainPartition const & GEOS_UNUSED_PARAM( domain ), @@ -887,6 +1147,123 @@ void SinglePhaseWell::computePerforationRates( real64 const & time_n, } +real64 +SinglePhaseWell::calculateWellResidualNorm( real64 const & time_n, + real64 const & dt, + WellElementSubRegion const & subRegion, + DofManager const & dofManager, + arrayView1d< real64 const > const & localRhs ) +{ + GEOS_MARK_FUNCTION; + integer numNorm = 1; // mass balance + array1d< real64 > localResidualNorm; + array1d< real64 > localResidualNormalizer; + + if( isThermal() ) + { + numNorm = 2; // mass balance and energy balance + } + localResidualNorm.resize( numNorm ); + localResidualNormalizer.resize( numNorm ); + + + globalIndex const rankOffset = dofManager.rankOffset(); + string const wellDofKey = dofManager.getKey( wellElementDofName() ); + + + + string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString() ); + SingleFluidBase const & fluid = subRegion.getConstitutiveModel< SingleFluidBase >( fluidName ); + + WellControls const & wellControls = getWellControls( subRegion ); + + // step 1: compute the norm in the subRegion + if( isThermal() ) + { + real64 subRegionResidualNorm[2]{}; + thermalSinglePhaseWellKernels::ResidualNormKernelFactory:: + createAndLaunch< parallelDevicePolicy<> >( rankOffset, + wellDofKey, + localRhs, + subRegion, + fluid, + wellControls, + time_n, + dt, + m_nonlinearSolverParameters.m_minNormalizer, + subRegionResidualNorm ); + // step 2: reduction across meshBodies/regions/subRegions + + for( integer i=0; i localResidualNorm[i] ) + { + localResidualNorm[i] = subRegionResidualNorm[i]; + } + } + } + else + { + real64 subRegionResidualNorm[1]{}; + ResidualNormKernelFactory:: + createAndLaunch< parallelDevicePolicy<> >( rankOffset, + wellDofKey, + localRhs, + subRegion, + fluid, + wellControls, + time_n, + dt, + m_nonlinearSolverParameters.m_minNormalizer, + subRegionResidualNorm ); + + // step 2: reduction across meshBodies/regions/subRegions + + if( subRegionResidualNorm[0] > localResidualNorm[0] ) + { + localResidualNorm[0] = subRegionResidualNorm[0]; + } + } + + + real64 resNorm=localResidualNorm[0]; + if( isThermal() ) + { + real64 globalResidualNorm[2]{}; + globalResidualNorm[0] = MpiWrapper::max( localResidualNorm[0] ); + globalResidualNorm[1] = MpiWrapper::max( localResidualNorm[1] ); + resNorm= std::sqrt( globalResidualNorm[0] * globalResidualNorm[0] + globalResidualNorm[1] * globalResidualNorm[1] ); + + GEOS_LOG_LEVEL_RANK_0_NLR( logInfo::ResidualNorm, GEOS_FMT( " ( R{} ) = ( {:4.2e} ) ( Renergy ) = ( {:4.2e} )", + coupledSolverAttributePrefix(), globalResidualNorm[0], globalResidualNorm[1] )); + + getConvergenceStats().setResidualValue( GEOS_FMT( "R{}", coupledSolverAttributePrefix()), globalResidualNorm[0] ); + getConvergenceStats().setResidualValue( "Renergy", globalResidualNorm[1] ); + } + else + { + resNorm = MpiWrapper::max( resNorm ); + + GEOS_LOG_LEVEL_RANK_0_NLR( logInfo::ResidualNorm, GEOS_FMT( " ( R{} ) = ( {:4.2e} )", + coupledSolverAttributePrefix(), resNorm )); + getConvergenceStats().setResidualValue( GEOS_FMT( "R{}", coupledSolverAttributePrefix()), resNorm ); + } + + return resNorm; +} +real64 +SinglePhaseWell::scalingForWellSystemSolution( ElementSubRegionBase & subRegion, + DofManager const & dofManager, + arrayView1d< real64 const > const & localSolution ) +{ + GEOS_MARK_FUNCTION; + GEOS_UNUSED_VAR( subRegion ); + GEOS_UNUSED_VAR( dofManager ); + GEOS_UNUSED_VAR( localSolution ); + + return 1.0; +} + real64 SinglePhaseWell::calculateResidualNorm( real64 const & time_n, real64 const & dt, @@ -895,13 +1272,13 @@ SinglePhaseWell::calculateResidualNorm( real64 const & time_n, arrayView1d< real64 const > const & localRhs ) { GEOS_MARK_FUNCTION; - integer numNorm = 1; // mass balance + integer numNorm = 1; // mass balance array1d< real64 > localResidualNorm; array1d< real64 > localResidualNormalizer; if( isThermal() ) { - numNorm = 2; // mass balance and energy balance + numNorm = 2; // mass balance and energy balance } localResidualNorm.resize( numNorm ); localResidualNormalizer.resize( numNorm ); @@ -928,51 +1305,54 @@ SinglePhaseWell::calculateResidualNorm( real64 const & time_n, WellControls const & wellControls = getWellControls( subRegion ); - // step 1: compute the norm in the subRegion - if( isThermal() ) + if( wellControls.isWellOpen() ) { - real64 subRegionResidualNorm[2]{}; - thermalSinglePhaseWellKernels::ResidualNormKernelFactory:: - createAndLaunch< parallelDevicePolicy<> >( rankOffset, - wellDofKey, - localRhs, - subRegion, - fluid, - wellControls, - time_n, - dt, - m_nonlinearSolverParameters.m_minNormalizer, - subRegionResidualNorm ); - // step 2: reduction across meshBodies/regions/subRegions - - for( integer i=0; i localResidualNorm[i] ) + real64 subRegionResidualNorm[2]{}; + thermalSinglePhaseWellKernels::ResidualNormKernelFactory:: + createAndLaunch< parallelDevicePolicy<> >( rankOffset, + wellDofKey, + localRhs, + subRegion, + fluid, + wellControls, + time_n, + dt, + m_nonlinearSolverParameters.m_minNormalizer, + subRegionResidualNorm ); + // step 2: reduction across meshBodies/regions/subRegions + + for( integer i=0; i localResidualNorm[i] ) + { + localResidualNorm[i] = subRegionResidualNorm[i]; + } } } - } - else - { - real64 subRegionResidualNorm[1]{}; - ResidualNormKernelFactory:: - createAndLaunch< parallelDevicePolicy<> >( rankOffset, - wellDofKey, - localRhs, - subRegion, - fluid, - wellControls, - time_n, - dt, - m_nonlinearSolverParameters.m_minNormalizer, - subRegionResidualNorm ); - - // step 2: reduction across meshBodies/regions/subRegions - - if( subRegionResidualNorm[0] > localResidualNorm[0] ) + else { - localResidualNorm[0] = subRegionResidualNorm[0]; + real64 subRegionResidualNorm[1]{}; + ResidualNormKernelFactory:: + createAndLaunch< parallelDevicePolicy<> >( rankOffset, + wellDofKey, + localRhs, + subRegion, + fluid, + wellControls, + time_n, + dt, + m_nonlinearSolverParameters.m_minNormalizer, + subRegionResidualNorm ); + + // step 2: reduction across meshBodies/regions/subRegions + + if( subRegionResidualNorm[0] > localResidualNorm[0] ) + { + localResidualNorm[0] = subRegionResidualNorm[0]; + } } } } ); @@ -1003,6 +1383,47 @@ SinglePhaseWell::calculateResidualNorm( real64 const & time_n, return resNorm; } +bool SinglePhaseWell::checkWellSystemSolution( ElementSubRegionBase & subRegion, + DofManager const & dofManager, + arrayView1d< real64 const > const & localSolution, + real64 const scalingFactor ) +{ + GEOS_MARK_FUNCTION; + + string const wellDofKey = dofManager.getKey( wellElementDofName() ); + integer numNegativePressures = 0; + real64 minPressure = 0.0; + + + globalIndex const rankOffset = dofManager.rankOffset(); + // get the degree of freedom numbers on well elements + arrayView1d< globalIndex const > const & dofNumber = + subRegion.getReference< array1d< globalIndex > >( wellDofKey ); + arrayView1d< integer const > const & ghostRank = subRegion.ghostRank(); + + // get a reference to the primary variables on well elements + arrayView1d< real64 const > const & pres = + subRegion.getField< well::pressure >(); + + auto const statistics = + singlePhaseBaseKernels::SolutionCheckKernel:: + launch< parallelDevicePolicy<> >( localSolution, rankOffset, dofNumber, ghostRank, pres, scalingFactor ); + + numNegativePressures += statistics.first; + minPressure = std::min( minPressure, statistics.second ); + + + numNegativePressures = MpiWrapper::sum( numNegativePressures ); + + if( numNegativePressures > 0 ) + { + GEOS_LOG_LEVEL_RANK_0( logInfo::Solution, + GEOS_FMT( " {}: Number of negative pressure values: {}, minimum value: {} Pa", + getName(), numNegativePressures, fmt::format( "{:.{}f}", minPressure, 3 ) ) ); + } + + return (m_allowNegativePressure || numNegativePressures == 0) ? 1 : 0; +} bool SinglePhaseWell::checkSystemSolution( DomainPartition & domain, DofManager const & dofManager, @@ -1037,7 +1458,7 @@ bool SinglePhaseWell::checkSystemSolution( DomainPartition & domain, arrayView1d< real64 const > const & pres = subRegion.getField< well::pressure >(); - auto const statistics = + std::pair< integer, real64 > statistics = singlePhaseBaseKernels::SolutionCheckKernel:: launch< parallelDevicePolicy<> >( localSolution, rankOffset, dofNumber, ghostRank, pres, scalingFactor ); @@ -1058,6 +1479,66 @@ bool SinglePhaseWell::checkSystemSolution( DomainPartition & domain, return (m_allowNegativePressure || numNegativePressures == 0) ? 1 : 0; } +void +SinglePhaseWell::applyWellSystemSolution( DofManager const & dofManager, + arrayView1d< real64 const > const & localSolution, + real64 const scalingFactor, + real64 const dt, + DomainPartition & domain, + MeshLevel & mesh, + WellElementSubRegion & subRegion ) +{ + GEOS_UNUSED_VAR( dt ); + GEOS_UNUSED_VAR( subRegion ); + DofManager::CompMask pressureMask( m_numDofPerWellElement, 0, 1 ); + DofManager::CompMask connRateMask( m_numDofPerWellElement, 1, 2 ); + dofManager.addVectorToField( localSolution, + wellElementDofName(), + well::pressure::key(), + scalingFactor, + pressureMask ); + + dofManager.addVectorToField( localSolution, + wellElementDofName(), + well::connectionRate::key(), + scalingFactor, + connRateMask ); + + if( isThermal() ) + { + DofManager::CompMask temperatureMask( m_numDofPerWellElement, 2, 3 ); + + dofManager.addVectorToField( localSolution, + wellElementDofName(), + fields::well::temperature::key(), + scalingFactor, + temperatureMask ); + + } + + + FieldIdentifiers fieldsToBeSync; + if( isThermal() ) + { + fieldsToBeSync.addElementFields( { well::pressure::key(), + well::connectionRate::key(), + well::temperature::key() }, + getTargetRegionNames() ); + } + else + { + fieldsToBeSync.addElementFields( { well::pressure::key(), + well::connectionRate::key() }, + getTargetRegionNames() ); + } + CommunicationTools::getInstance().synchronizeFields( fieldsToBeSync, + mesh, + domain.getNeighbors(), + true ); + + +} + void SinglePhaseWell::applySystemSolution( DofManager const & dofManager, arrayView1d< real64 const > const & localSolution, @@ -1157,6 +1638,26 @@ void SinglePhaseWell::resetStateToBeginningOfStep( DomainPartition & domain ) } ); } +void SinglePhaseWell::saveState( WellElementSubRegion & subRegion ) +{ + arrayView1d< real64 const > const wellElemPressure = subRegion.getField< well::pressure >(); + arrayView1d< real64 > const wellElemPressure_n = subRegion.getField< well::pressure_n >(); + wellElemPressure_n.setValues< parallelDevicePolicy<> >( wellElemPressure ); + + if( isThermal() ) + { + arrayView1d< real64 const > const wellElemTemperature = subRegion.getField< well::temperature >(); + arrayView1d< real64 > const wellElemTemperature_n = subRegion.getField< well::temperature_n >(); + wellElemTemperature_n.setValues< parallelDevicePolicy<> >( wellElemTemperature ); + } + arrayView1d< real64 const > const connRate = subRegion.getField< well::connectionRate >(); + arrayView1d< real64 > const connRate_n = subRegion.getField< well::connectionRate_n >(); + connRate_n.setValues< parallelDevicePolicy<> >( connRate ); + + SingleFluidBase const & fluid = + getConstitutiveModel< SingleFluidBase >( subRegion, subRegion.getReference< string >( viewKeyStruct::fluidNamesString() ) ); + fluid.saveConvergedState(); +} void SinglePhaseWell::implicitStepSetup( real64 const & time, real64 const & dt, @@ -1300,5 +1801,66 @@ void SinglePhaseWell::printRates( real64 const & time_n, } ); } +bool SinglePhaseWell::evaluateConstraints( real64 const & time_n, + WellElementSubRegion & subRegion ) +{ + WellControls & wellControls = getWellControls( subRegion ); + // create list of all constraints to process + std::vector< WellConstraintBase * > constraintList; + if( wellControls.isProducer() ) + { + constraintList = wellControls.getProdRateConstraints(); + // Solve minimum bhp constraint first + constraintList.insert( constraintList.begin(), wellControls.getMinBHPConstraint() ); + } + else + { + constraintList = wellControls.getInjRateConstraints(); + // Solve maximum bhp constraint first; + constraintList.insert( constraintList.begin(), wellControls.getMaxBHPConstraint() ); + } + // Get current constraint + WellConstraintBase * limitingConstraint = nullptr; + for( auto & constraint : constraintList ) + { + if( constraint->getName() == wellControls.getCurrentConstraint()->getName()) + { + limitingConstraint = constraint; + // tjb. this is likely not needed. set in update state + constraint->setBHP ( wellControls.getReference< real64 >( SinglePhaseWell::viewKeyStruct::currentBHPString() )); + constraint->setTotalVolumeRate ( wellControls.getReference< real64 >( + SinglePhaseWell::viewKeyStruct::currentVolRateString() )); + + GEOS_LOG_RANK_IF ( getLogLevel() > 4 && subRegion.isLocallyOwned(), + " Well " << subRegion.getName() << " Limiting Constraint " << limitingConstraint->getName() << " " << limitingConstraint->bottomHolePressure() << " " << + limitingConstraint->totalVolumeRate() ); + } + } + + + // Check current against other constraints + for( auto & constraint : constraintList ) + { + + if( limitingConstraint->getName() != constraint->getName()) + { + //std::cout << "Use estimator " << useEstimator << " Evaluating constraint " << constraint.getName() << " against constraint " << + // limitingConstraint->getName() << std::endl; + if( constraint->checkViolation( *limitingConstraint, time_n ) ) + { + wellControls.setControl( static_cast< WellControls::Control >(constraint->getControl()) ); // tjb old + wellControls.setCurrentConstraint( constraint ); + GEOS_LOG_RANK_IF ( getLogLevel() > 4 && subRegion.isLocallyOwned(), + " Well " << subRegion.getName() << " New Limiting Constraint " << constraint->getName() << " " << constraint->getConstraintValue( time_n ) ); + } + } + } + GEOS_LOG_RANK_IF ( getLogLevel() > 4 && subRegion.isLocallyOwned(), + " Well " << subRegion.getName() << " Limiting Constraint " << limitingConstraint->getName() << " " << limitingConstraint->bottomHolePressure() << " " << limitingConstraint->phaseVolumeRates() << " " << + limitingConstraint->totalVolumeRate() << " " << limitingConstraint->massRate()); + + return true; +} + REGISTER_CATALOG_ENTRY( PhysicsSolverBase, SinglePhaseWell, string const &, Group * const ) }// namespace geos diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/SinglePhaseWell.hpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/SinglePhaseWell.hpp index 7bfeb996aa4..fc3b259bc95 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/wells/SinglePhaseWell.hpp +++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/SinglePhaseWell.hpp @@ -95,6 +95,15 @@ class SinglePhaseWell : public WellSolverBase */ /**@{*/ + virtual real64 scalingForWellSystemSolution( ElementSubRegionBase & subRegion, + DofManager const & dofManager, + arrayView1d< real64 const > const & localSolution ) override; + virtual real64 + calculateWellResidualNorm( real64 const & time_n, + real64 const & dt, + WellElementSubRegion const & subRegion, + DofManager const & dofManager, + arrayView1d< real64 const > const & localRhs ) override; virtual real64 calculateResidualNorm( real64 const & time_n, real64 const & dt, @@ -102,6 +111,12 @@ class SinglePhaseWell : public WellSolverBase DofManager const & dofManager, arrayView1d< real64 const > const & localRhs ) override; + virtual bool + checkWellSystemSolution( ElementSubRegionBase & subRegion, + DofManager const & dofManager, + arrayView1d< real64 const > const & localSolution, + real64 const scalingFactor ) override; + virtual bool checkSystemSolution( DomainPartition & domain, DofManager const & dofManager, @@ -109,6 +124,14 @@ class SinglePhaseWell : public WellSolverBase real64 const scalingFactor ) override; virtual void + applyWellSystemSolution( DofManager const & dofManager, + arrayView1d< real64 const > const & localSolution, + real64 const scalingFactor, + real64 const dt, + DomainPartition & domain, + MeshLevel & mesh, + WellElementSubRegion & subRegion ) override; + virtual void applySystemSolution( DofManager const & dofManager, arrayView1d< real64 const > const & localSolution, real64 const scalingFactor, @@ -142,7 +165,7 @@ class SinglePhaseWell : public WellSolverBase * @brief Recompute the volumetric rate that are used in the well constraints * @param subRegion the well subregion containing all the primary and dependent fields */ - virtual void updateVolRateForConstraint( WellElementSubRegion & subRegion ); + virtual void calculateReferenceElementRates( WellElementSubRegion & subRegion ); /** * @brief Recompute the BHP pressure that is used in the well constraints @@ -155,6 +178,11 @@ class SinglePhaseWell : public WellSolverBase * @param subRegion the well subRegion containing the well elements and their associated fields */ virtual void updateFluidModel( WellElementSubRegion & subRegion ) const; + /** + * @brief Update separator model state + * @param subRegion the well subRegion containing the separator + */ + void updateSeparator( WellElementSubRegion & subRegion ); /** * @brief Recompute the perforation rates for all the wells @@ -163,6 +191,14 @@ class SinglePhaseWell : public WellSolverBase virtual void computePerforationRates( real64 const & time_n, real64 const & dt, DomainPartition & domain ) override; + /** + * @brief Recompute all dependent quantities from primary variables (including constitutive + * models) + * @param domain the domain containing the mesh and fields + */ + virtual real64 updateWellState( WellElementSubRegion & subRegion ) override; + virtual void updateState( DomainPartition & domain ) override; + /** * @brief Recompute all dependent quantities from primary variables (including constitutive models) on the well * @param subRegion the well subRegion containing the well elements and their associated fields @@ -185,6 +221,21 @@ class SinglePhaseWell : public WellSolverBase CRSMatrixView< real64, globalIndex const > const & localMatrix, arrayView1d< real64 > const & localRhs ) override; + /** + * @brief assembles the flux terms for all connections between well elements + * @param time_n previous time value + * @param dt time step + * @param domain the physical domain object + * @param dofManager degree-of-freedom manager associated with the linear system + * @param matrix the system matrix + * @param rhs the system right-hand side vector + */ + virtual void assembleWellFluxTerms( real64 const & time, + real64 const & dt, + WellElementSubRegion const & subRegion, + DofManager const & dofManager, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) override; /** * @brief assembles the flux terms for all connections between well elements * @param time_n previous time value @@ -208,6 +259,14 @@ class SinglePhaseWell : public WellSolverBase * @param matrix the system matrix * @param rhs the system right-hand side vector */ + + virtual void assembleWellAccumulationTerms( real64 const & time, + real64 const & dt, + WellElementSubRegion & subRegion, + DofManager const & dofManager, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) override; + virtual void assembleAccumulationTerms( real64 const & time_n, real64 const & dt, DomainPartition & domain, DofManager const & dofManager, @@ -226,6 +285,22 @@ class SinglePhaseWell : public WellSolverBase CRSMatrixView< real64, globalIndex const > const & localMatrix, arrayView1d< real64 > const & localRhs ); + virtual void assembleWellConstraintTerms( real64 const & time_n, + real64 const & dt, + WellElementSubRegion const & subRegion, + DofManager const & dofManager, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) override; + + + virtual void assembleWellPressureRelations( real64 const & time_n, + real64 const & dt, + WellElementSubRegion const & subRegion, + DofManager const & dofManager, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) override; + + /** * @brief assembles the pressure relations at all connections between well elements except at the well head * @param time_n time at the beginning of the time step @@ -242,6 +317,13 @@ class SinglePhaseWell : public WellSolverBase CRSMatrixView< real64, globalIndex const > const & localMatrix, arrayView1d< real64 > const & localRhs ) override; + /** + * @brief Initialize all the primary and secondary variables in all the wells + * @param domain the domain containing the well manager to access individual wells + */ + void initializeWells( DomainPartition & domain, real64 const & time_n ) override; + void initializeWell( DomainPartition & domain, MeshLevel & mesh, WellElementSubRegion & subRegion, real64 const & time_n ) override; + /* * @brief apply a special treatment to the wells that are shut * @param time_n the time at the previous converged time step @@ -268,6 +350,10 @@ class SinglePhaseWell : public WellSolverBase protected: + virtual void initializePostInitialConditionsPreSubGroups() override; + + void saveState( WellElementSubRegion & subRegion ); + void printRates( real64 const & time_n, real64 const & dt, DomainPartition & domain ) override; @@ -279,11 +365,6 @@ class SinglePhaseWell : public WellSolverBase virtual void setConstitutiveNames( ElementSubRegionBase & subRegion ) const override; - /** - * @brief Initialize all the primary and secondary variables in all the wells - * @param domain the domain containing the well manager to access individual wells - */ - void initializeWells( DomainPartition & domain, real64 const & time_n ) override; /** * @brief Make sure that the well constraints are compatible @@ -297,6 +378,14 @@ class SinglePhaseWell : public WellSolverBase WellElementSubRegion const & subRegion, ElementRegionManager const & elemManager ) override; + virtual bool evaluateConstraints( real64 const & time_n, + WellElementSubRegion & subRegion ) override; + + /** + * @brief Create well separator + */ + void createSeparator(); + }; } // namespace geos diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/WellBHPConstraints.cpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellBHPConstraints.cpp new file mode 100644 index 00000000000..70f6e79b86a --- /dev/null +++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellBHPConstraints.cpp @@ -0,0 +1,116 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * SPDX-License-Identifier: LGPL-2.1-only + * + * Copyright (c) 2016-2024 Lawrence Livermore National Security LLC + * Copyright (c) 2018-2024 TotalEnergies + * Copyright (c) 2018-2024 The Board of Trustees of the Leland Stanford Junior University + * Copyright (c) 2023-2024 Chevron + * Copyright (c) 2019- GEOS/GEOSX Contributors + * All rights reserved + * + * See top level LICENSE, COPYRIGHT, CONTRIBUTORS, NOTICE, and ACKNOWLEDGEMENTS files for details. + * ------------------------------------------------------------------------------------------------------------ + */ + +/* + * @file WellBHPConstraints.cpp + */ + +#include "LogLevelsInfo.hpp" +#include "WellBHPConstraints.hpp" +#include "WellConstants.hpp" +#include "dataRepository/InputFlags.hpp" +#include "functions/FunctionManager.hpp" + + +namespace geos +{ + +using namespace dataRepository; + +BHPConstraint::BHPConstraint( string const & name, Group * const parent ) + : WellConstraintBase( name, parent ), + m_refElevation( 0.0 ), + m_refGravCoef( 0.0 ) +{ + setInputFlags( InputFlags::OPTIONAL_NONUNIQUE ); + + registerWrapper( viewKeyStruct::targetBHPString(), &m_constraintValue ). + setDefaultValue( 0.0 ). + setInputFlag( InputFlags::OPTIONAL ). + setRestartFlags( RestartFlags::WRITE_AND_READ ). + setDescription( "Minimun bottom-hole production pressure [Pa]" ); + + registerWrapper( viewKeyStruct::refElevString(), &m_refElevation ). + setDefaultValue( -1 ). + setInputFlag( InputFlags::REQUIRED ). + setDescription( "Reference elevation where BHP control is enforced [m]" ); + +} + + +BHPConstraint::~BHPConstraint() +{} + +void BHPConstraint::postInputInitialization() +{ + + WellConstraintBase::postInputInitialization(); + +} + +MinimumBHPConstraint::MinimumBHPConstraint( string const & name, Group * const parent ) + : BHPConstraint( name, parent ) +{ + setInputFlags( InputFlags::OPTIONAL_NONUNIQUE ); + + registerWrapper( viewKeyStruct::targetBHPString(), &m_constraintValue ). + setDefaultValue( 0.0 ). + setInputFlag( InputFlags::OPTIONAL ). + setRestartFlags( RestartFlags::WRITE_AND_READ ). + setDescription( "Minimun bottom-hole production pressure [Pa]" ); +} + + +MinimumBHPConstraint::~MinimumBHPConstraint() +{} + +void MinimumBHPConstraint::postInputInitialization() +{ + + BHPConstraint::postInputInitialization(); + +} + +bool MinimumBHPConstraint::checkViolation( WellConstraintBase const & currentConstraint, real64 const & currentTime ) const +{ + return currentConstraint.bottomHolePressure() < getConstraintValue( currentTime ); +} + +MaximumBHPConstraint::MaximumBHPConstraint( string const & name, Group * const parent ) + : BHPConstraint( name, parent ) +{ + setInputFlags( InputFlags::OPTIONAL_NONUNIQUE ); + +} + + +MaximumBHPConstraint::~MaximumBHPConstraint() +{} + +void MaximumBHPConstraint::postInputInitialization() +{ + // Validate value and table options + BHPConstraint::postInputInitialization(); + +} +bool MaximumBHPConstraint::checkViolation( WellConstraintBase const & currentConstraint, real64 const & currentTime ) const +{ + return currentConstraint.bottomHolePressure() > getConstraintValue( currentTime ); +} + +REGISTER_CATALOG_ENTRY( WellConstraintBase, MinimumBHPConstraint, string const &, Group * const ) +REGISTER_CATALOG_ENTRY( WellConstraintBase, MaximumBHPConstraint, string const &, Group * const ) + +} //namespace geos diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/WellBHPConstraints.hpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellBHPConstraints.hpp new file mode 100644 index 00000000000..3002d303e39 --- /dev/null +++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellBHPConstraints.hpp @@ -0,0 +1,334 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * SPDX-License-Identifier: LGPL-2.1-only + * + * Copyright (c) 2016-2024 Lawrence Livermore National Security LLC + * Copyright (c) 2018-2024 TotalEnergies + * Copyright (c) 2018-2024 The Board of Trustees of the Leland Stanford Junior University + * Copyright (c) 2023-2024 Chevron + * Copyright (c) 2019- GEOS/GEOSX Contributors + * All rights reserved + * + * See top level LICENSE, COPYRIGHT, CONTRIBUTORS, NOTICE, and ACKNOWLEDGEMENTS files for details. + * ------------------------------------------------------------------------------------------------------------ + */ + +/* + * @file WellBHPConstraints.hpp + */ + + +#ifndef GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_WELLBHPCONSTRAINTS_HPP +#define GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_WELLBHPCONSTRAINTS_HPP + +#include "common/format/EnumStrings.hpp" +#include "dataRepository/Group.hpp" +#include "functions/TableFunction.hpp" +#include "WellConstraintsBase.hpp" +namespace geos +{ + +/** + * @class BHPConstraint + * @brief This class describes a minimum pressure constraint used to control a injection well. + */ +class BHPConstraint : public WellConstraintBase +{ +public: + + /** + * @name Constructor / Destructor + */ + ///@{ + + /** + * @brief Constructor for WellControls Objects. + * @param[in] name the name of this instantiation of WellControls in the repository + * @param[in] parent the parent group of this instantiation of WellControls + */ + explicit BHPConstraint( string const & name, dataRepository::Group * const parent ); + + + /** + * @brief Default destructor. + */ + ~BHPConstraint() override; + + /** + * @brief Deleted default constructor. + */ + BHPConstraint() = delete; + + /** + * @brief Deleted copy constructor. + */ + BHPConstraint( BHPConstraint const & ) = delete; + + /** + * @brief Deleted move constructor. + */ + BHPConstraint( BHPConstraint && ) = delete; + + /** + * @brief Deleted assignment operator. + * @return a reference to a constraint object + */ + BHPConstraint & operator=( BHPConstraint const & ) = delete; + + /** + * @brief Deleted move operator. + * @return a reference to a constraint object + */ + BHPConstraint & operator=( BHPConstraint && ) = delete; + + ///@} + + /** + * @name Getters / Setters + */ + ///@{ + + // Temp interface - tjb + virtual ConstraintTypeId getControl() const override { return ConstraintTypeId::BHP; }; + + ///@} + /** + * @brief Struct to serve as a container for variable strings and keys. + * @struct viewKeyStruct + */ + struct viewKeyStruct + { + /// String key for the well target BHP + static constexpr char const * targetBHPString() { return "targetBHP"; } + /// String key for the well reference elevation (for BHP control) + static constexpr char const * refElevString() { return "referenceElevation"; } + } + viewKeysWellBHPConstraint; + + //virtual bool checkViolation( WellConstraintBase const & currentConstraint, real64 const & currentTime ) const override; + + /** + * @brief Getter for the reference elevation where the BHP control is enforced + * @return the reference elevation + */ + real64 getReferenceElevation() const { return m_refElevation; } + + /** + * @brief Set the reference elevation where the BHP control is enforced + * @return the reference elevation + */ + void setReferenceElevation( real64 const & refElevation ) { m_refElevation=refElevation; } + + /** + * @brief Getter for the reference gravity coefficient + * @return the reference gravity coefficient + */ + real64 getReferenceGravityCoef() const { return m_refGravCoef; } + + /** + * @brief Setter for the reference gravity + */ + void setReferenceGravityCoef( real64 const & refGravCoef ) { m_refGravCoef = refGravCoef; } + +protected: + + virtual void postInputInitialization() override; + + /// Reference elevation + real64 m_refElevation; + + /// Gravity coefficient of the reference elevation + real64 m_refGravCoef; + +}; + +/** + * @class MinimumBHPConstraint + * @brief This class describes a minimum pressure constraint used to control a injection well. + */ +class MinimumBHPConstraint : public BHPConstraint +{ +public: + + /** + * @name Constructor / Destructor + */ + ///@{ + + /** + * @brief Constructor for WellControls Objects. + * @param[in] name the name of this instantiation of WellControls in the repository + * @param[in] parent the parent group of this instantiation of WellControls + */ + explicit MinimumBHPConstraint( string const & name, dataRepository::Group * const parent ); + + + /** + * @brief Default destructor. + */ + ~MinimumBHPConstraint() override; + + /** + * @brief Deleted default constructor. + */ + MinimumBHPConstraint() = delete; + + /** + * @brief Deleted copy constructor. + */ + MinimumBHPConstraint( MinimumBHPConstraint const & ) = delete; + + /** + * @brief Deleted move constructor. + */ + MinimumBHPConstraint( MinimumBHPConstraint && ) = delete; + + /** + * @brief Deleted assignment operator. + * @return a reference to a constraint object + */ + MinimumBHPConstraint & operator=( MinimumBHPConstraint const & ) = delete; + + /** + * @brief Deleted move operator. + * @return a reference to a constraint object + */ + MinimumBHPConstraint & operator=( MinimumBHPConstraint && ) = delete; + + ///@} + + /** + * @brief name of the node manager in the object catalog + * @return string that contains the catalog name to generate a new Constraint object through the object catalog. + */ + static string catalogName() + { + return "MinimumBHPConstraint"; + } + virtual string getCatalogName() const override { return catalogName(); } + /** + * @name Getters / Setters + */ + ///@{ + + + /** + * @brief Struct to serve as a container for variable strings and keys. + * @struct viewKeyStruct + */ + struct viewKeyStruct + { + /// String key for the well target BHP + static constexpr char const * targetBHPString() { return "targetBHP"; } + } + viewKeysWellBHPConstraint; + + virtual bool checkViolation( WellConstraintBase const & currentConstraint, real64 const & currentTime ) const override; + +protected: + + virtual void postInputInitialization() override; + + +}; + +/** + * @class WellMinimumBHPConstraint + * @brief This class describes a maximum pressure constraint used to control a injection well. + */ +class MaximumBHPConstraint : public BHPConstraint +{ +public: + + /** + * @name Constructor / Destructor + */ + ///@{ + + /** + * @brief Constructor for WellControls Objects. + * @param[in] name the name of this instantiation of WellControls in the repository + * @param[in] parent the parent group of this instantiation of WellControls + */ + explicit MaximumBHPConstraint( string const & name, dataRepository::Group * const parent ); + + + /** + * @brief Default destructor. + */ + ~MaximumBHPConstraint() override; + + /** + * @brief Deleted default constructor. + */ + MaximumBHPConstraint() = delete; + + /** + * @brief Deleted copy constructor. + */ + MaximumBHPConstraint( MaximumBHPConstraint const & ) = delete; + + /** + * @brief Deleted move constructor. + */ + MaximumBHPConstraint( MaximumBHPConstraint && ) = delete; + + /** + * @brief Deleted assignment operator. + * @return a reference to a constraint object + */ + MaximumBHPConstraint & operator=( MaximumBHPConstraint const & ) = delete; + + /** + * @brief Deleted move operator. + * @return a reference to a constraint object + */ + MaximumBHPConstraint & operator=( MaximumBHPConstraint && ) = delete; + + ///@} + + /** + * @brief name of the node manager in the object catalog + * @return string that contains the catalog name to generate a new Constraint object through the object catalog. + */ + static string catalogName() + { + return "MaximumBHPConstraint"; + } + virtual string getCatalogName() const override { return catalogName(); } + /** + * @name Getters / Setters + */ + ///@{ + // Temp interface - tjb + virtual ConstraintTypeId getControl() const override { return ConstraintTypeId::BHP; }; + + + ///@} + /** + * @brief Struct to serve as a container for variable strings and keys. + * @struct viewKeyStruct + */ + struct viewKeyStruct + { + /// String key for the well target BHP + static constexpr char const * targetBHPString() { return "targetBHP"; } + } + viewKeysWellBHPConstraint; + + virtual bool checkViolation( WellConstraintBase const & currentConstraint, real64 const & currentTime ) const override; +protected: + + virtual void postInputInitialization() override; + + + +private: + + +}; + + +} //namespace geos + +#endif //GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_WELLBHPCONSTRAINTS_HPP diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/WellConstants.hpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellConstants.hpp index 58b8b421c3a..83eadb7c2ff 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/wells/WellConstants.hpp +++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellConstants.hpp @@ -36,6 +36,11 @@ struct WellConstants static constexpr real64 defaultInjectorBHP = 1.01325e8; }; +enum class WellTypes : integer +{ + PRODUCER, /**< A production well */ + INJECTOR /**< An injection well */ +}; } //namespace geos #endif //GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_WELLCONSTANTS_HPP diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/WellConstraintsBase.cpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellConstraintsBase.cpp new file mode 100644 index 00000000000..cdc89300129 --- /dev/null +++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellConstraintsBase.cpp @@ -0,0 +1,134 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * 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( true ), + 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" ); + +} + + +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..586f61a17d3 --- /dev/null +++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellConstraintsBase.hpp @@ -0,0 +1,289 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * 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"; } + + + } + /// 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 + bool 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 098d80beac0..5d69896b204 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/wells/WellControls.cpp +++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellControls.cpp @@ -32,24 +32,18 @@ using namespace dataRepository; WellControls::WellControls( string const & name, Group * const parent ) : Group( name, parent ), m_type( Type::PRODUCER ), - m_refElevation( 0.0 ), - m_refGravCoef( 0.0 ), m_inputControl( Control::UNINITIALIZED ), - m_currentControl( Control::UNINITIALIZED ), - m_targetBHP( 0.0 ), - m_targetTotalRate( 0.0 ), - m_targetPhaseRate( 0.0 ), - m_targetMassRate( 0.0 ), + m_currentControl( Control::UNINITIALIZED ), // tjb remove m_useSurfaceConditions( 0 ), - m_surfacePres( 0.0 ), - m_surfaceTemp( 0.0 ), + m_surfacePres( -1.0 ), + m_surfaceTemp( -1.0 ), m_isCrossflowEnabled( 1 ), m_initialPressureCoefficient( 0.1 ), m_rateSign( -1.0 ), - m_targetTotalRateTable( nullptr ), - m_targetPhaseRateTable( nullptr ), - m_targetBHPTable( nullptr ), m_statusTable( nullptr ), + m_wellOpen( false ), + m_constraintSwitch( true ), + m_currentConstraint( nullptr ), m_wellStatus( WellControls::Status::OPEN ), m_regionAveragePressure( -1 ) { @@ -59,61 +53,14 @@ WellControls::WellControls( string const & name, Group * const parent ) setInputFlag( InputFlags::REQUIRED ). setDescription( "Well type. Valid options:\n* " + EnumStrings< Type >::concat( "\n* " ) ); - registerWrapper( viewKeyStruct::inputControlString(), &m_inputControl ). - setInputFlag( InputFlags::REQUIRED ). - setDescription( "Well control. Valid options:\n* " + EnumStrings< Control >::concat( "\n* " ) ); - registerWrapper( viewKeyStruct::currentControlString(), &m_currentControl ). setDefaultValue( Control::UNINITIALIZED ). setInputFlag( InputFlags::FALSE ). setDescription( "Current well control" ); - registerWrapper( viewKeyStruct::targetBHPString(), &m_targetBHP ). - setDefaultValue( 0.0 ). - setInputFlag( InputFlags::OPTIONAL ). - setRestartFlags( RestartFlags::WRITE_AND_READ ). - setDescription( "Target bottom-hole pressure [Pa]" ); - - registerWrapper( viewKeyStruct::targetTotalRateString(), &m_targetTotalRate ). - setDefaultValue( 0.0 ). - setInputFlag( InputFlags::OPTIONAL ). - setRestartFlags( RestartFlags::WRITE_AND_READ ). - setDescription( "Target total volumetric rate (if useSurfaceConditions: [surface m^3/s]; else [reservoir m^3/s])" ); - - registerWrapper( viewKeyStruct::targetPhaseRateString(), &m_targetPhaseRate ). - setDefaultValue( 0.0 ). - setInputFlag( InputFlags::OPTIONAL ). - setRestartFlags( RestartFlags::WRITE_AND_READ ). - setDescription( "Target phase volumetric rate (if useSurfaceConditions: [surface m^3/s]; else [reservoir m^3/s])" ); - - registerWrapper( viewKeyStruct::targetMassRateString(), &m_targetMassRate ). - setDefaultValue( 0.0 ). - setInputFlag( InputFlags::OPTIONAL ). - setRestartFlags( RestartFlags::WRITE_AND_READ ). - setDescription( "Target Mass Rate rate ( [kg^3/s])" ); - - registerWrapper( viewKeyStruct::targetPhaseNameString(), &m_targetPhaseName ). - setRTTypeName( rtTypes::CustomTypes::groupNameRef ). - setDefaultValue( "" ). - setInputFlag( InputFlags::OPTIONAL ). - setRestartFlags( RestartFlags::WRITE_AND_READ ). - setDescription( "Name of the target phase" ); - - registerWrapper( viewKeyStruct::refElevString(), &m_refElevation ). - setDefaultValue( -1 ). + registerWrapper( viewKeyStruct::inputControlString(), &m_inputControl ). setInputFlag( InputFlags::REQUIRED ). - setDescription( "Reference elevation where BHP control is enforced [m]" ); - - registerWrapper( viewKeyStruct::injectionStreamString(), &m_injectionStream ). - setDefaultValue( -1 ). - setSizedFromParent( 0 ). - setInputFlag( InputFlags::OPTIONAL ). - setDescription( "Global component densities of the injection stream [moles/m^3 or kg/m^3]" ); - - registerWrapper( viewKeyStruct::injectionTemperatureString(), &m_injectionTemperature ). - setDefaultValue( -1 ). - setInputFlag( InputFlags::OPTIONAL ). - setDescription( "Temperature of the injection stream [K]" ); + setDescription( "Well control. Valid options:\n* " + EnumStrings< Control >::concat( "\n* " ) ); registerWrapper( viewKeyStruct::useSurfaceConditionsString(), &m_useSurfaceConditions ). setDefaultValue( 0 ). @@ -154,31 +101,6 @@ WellControls::WellControls( string const & name, Group * const parent ) " - Injector pressure at reference depth initialized as: (1+initialPressureCoefficient)*reservoirPressureAtClosestPerforation + density*g*( zRef - zPerf ) \n" " - Producer pressure at reference depth initialized as: (1-initialPressureCoefficient)*reservoirPressureAtClosestPerforation + density*g*( zRef - zPerf ) " ); - registerWrapper( viewKeyStruct::targetBHPTableNameString(), &m_targetBHPTableName ). - setRTTypeName( rtTypes::CustomTypes::groupNameRef ). - setInputFlag( InputFlags::OPTIONAL ). - setDescription( "Name of the BHP table when the rate is a time dependent function" ); - - registerWrapper( viewKeyStruct::targetTotalRateTableNameString(), &m_targetTotalRateTableName ). - setRTTypeName( rtTypes::CustomTypes::groupNameRef ). - setInputFlag( InputFlags::OPTIONAL ). - setDescription( "Name of the total rate table when the rate is a time dependent function" ); - - registerWrapper( viewKeyStruct::targetPhaseRateTableNameString(), &m_targetPhaseRateTableName ). - setRTTypeName( rtTypes::CustomTypes::groupNameRef ). - 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." ); addLogLevel< logInfo::WellControl >(); } @@ -187,28 +109,80 @@ WellControls::WellControls( string const & name, Group * const parent ) WellControls::~WellControls() {} -void WellControls::switchToBHPControl( real64 const & val ) +Group * WellControls::createChild( string const & childKey, string const & childName ) { - m_currentControl = Control::BHP; - m_targetBHP = val; -} + GEOS_LOG_RANK_0( GEOS_FMT( "{}: adding {} {}", getName(), childKey, childName ) ); + ////const auto childTypes = { viewKeyStruct::perforationString() }; + //GEOS_ERROR_IF( childKey != viewKeyStruct::perforationString(), + // CatalogInterface::unknownTypeError( childKey, getDataContext(), childTypes ) ); -void WellControls::switchToTotalRateControl( real64 const & val ) -{ - m_currentControl = Control::TOTALVOLRATE; - m_targetTotalRate = val; -} + Group * constraint = nullptr; + if( childKey == viewKeyStruct::minimumBHPConstraintString() ) + { + MinimumBHPConstraint & bhpConstraint = registerGroup< MinimumBHPConstraint >( childName ); + m_minBHPConstraint = &bhpConstraint; + constraint = &bhpConstraint; + } + else if( childKey == viewKeyStruct::maximumBHPConstraintString() ) + { + MaximumBHPConstraint & bhpConstraint = registerGroup< MaximumBHPConstraint >( childName ); + m_maxBHPConstraint = &bhpConstraint; + constraint = &bhpConstraint; + } + else if( childKey == viewKeyStruct::productionPhaseVolumeRateConstraintString() ) + { + ProductionConstraint< PhaseVolumeRateConstraint > & phaseConstraint = registerGroup< ProductionConstraint< PhaseVolumeRateConstraint > >( childName ); + m_productionRateConstraintList.emplace_back( &phaseConstraint ); + constraint = &phaseConstraint; + } + else if( childKey == viewKeyStruct::injectionPhaseVolumeRateConstraint() ) + { -void WellControls::switchToPhaseRateControl( real64 const & val ) -{ - m_currentControl = Control::PHASEVOLRATE; - m_targetPhaseRate = val; + InjectionConstraint< PhaseVolumeRateConstraint > & phaseConstraint = registerGroup< InjectionConstraint< PhaseVolumeRateConstraint > >( childName ); + m_injectionRateConstraintList.emplace_back( &phaseConstraint ); + constraint = &phaseConstraint; + } + else if( childKey == viewKeyStruct::productionVolumeRateConstraint() ) + { + ProductionConstraint< VolumeRateConstraint > & volConstraint = registerGroup< ProductionConstraint< VolumeRateConstraint > >( childName ); + m_productionRateConstraintList.emplace_back( &volConstraint ); + constraint = &volConstraint; + } + else if( childKey == viewKeyStruct::injectionVolumeRateConstraint() ) + { + InjectionConstraint< VolumeRateConstraint > & volConstraint = registerGroup< InjectionConstraint< VolumeRateConstraint > >( childName ); + m_injectionRateConstraintList.emplace_back( &volConstraint ); + constraint = &volConstraint; + } + else if( childKey == viewKeyStruct::productionMassRateConstraint() ) + { + ProductionConstraint< MassRateConstraint > & massConstraint = registerGroup< ProductionConstraint< MassRateConstraint > >( childName ); + m_productionRateConstraintList.emplace_back( &massConstraint ); + constraint = &massConstraint; + + } + else if( childKey == viewKeyStruct::injectionMassRateConstraint() ) + { + InjectionConstraint< MassRateConstraint > & massConstraint = registerGroup< InjectionConstraint< MassRateConstraint > >( childName ); + m_injectionRateConstraintList.emplace_back( &massConstraint ); + constraint = &massConstraint; + } + else if( childKey == viewKeyStruct::productionLiquidRateConstraint() ) + { + ProductionConstraint< LiquidRateConstraint > & liquidConstraint = registerGroup< ProductionConstraint< LiquidRateConstraint > >( childName ); + m_productionRateConstraintList.emplace_back( &liquidConstraint ); + constraint = &liquidConstraint; + } + return constraint; } -void WellControls::switchToMassRateControl( real64 const & val ) +void WellControls::expandObjectCatalogs() { - m_currentControl = Control::MASSRATE; - m_targetMassRate = val; + // During schema generation, register one of each type derived from ConstitutiveBase here + for( auto & catalogIter: WellConstraintBase::getCatalog()) + { + createChild( catalogIter.first, catalogIter.first ); + } } namespace @@ -236,6 +210,7 @@ TableFunction * createWellTable( string const & tableName, void WellControls::postInputInitialization() { + // 0) Assign the value of the current well control // When the simulation starts from a restart file, we don't want to use the inputControl, // because the control may have switched in the simulation that generated the restart @@ -249,119 +224,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() ); - - // 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_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() ); + // 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 - 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 ); - 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 @@ -377,79 +260,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()) { @@ -480,12 +290,31 @@ void WellControls::setWellStatus( real64 const & currentTime, WellControls::Stat m_wellStatus = status; if( m_wellStatus == WellControls::Status::OPEN ) { - - if( isZero( getTargetTotalRate( currentTime ) ) && isZero( getTargetPhaseRate( currentTime ) ) - && isZero( getTargetMassRate( currentTime ) ) ) + if( isProducer()) { - m_wellStatus = WellControls::Status::CLOSED; + std::vector< WellConstraintBase * > const constraints = getProdRateConstraints(); + for( auto const & constraint : constraints ) + { + if( isZero( constraint->getConstraintValue( currentTime ) ) ) + { + m_wellStatus = WellControls::Status::CLOSED; + break; + } + } + } + else + { + std::vector< WellConstraintBase * > const constraints = getInjRateConstraints(); + for( auto const & constraint : constraints ) + { + if( isZero( constraint->getConstraintValue( currentTime ) ) ) + { + m_wellStatus = WellControls::Status::CLOSED; + break; + } + } } + if( m_statusTable->evaluate( ¤tTime ) < LvArray::NumericLimits< real64 >::epsilon ) { m_wellStatus = WellControls::Status::CLOSED; @@ -498,13 +327,46 @@ 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::setConstraintSwitch( bool constraintSwitch ) +{ + m_constraintSwitch = constraintSwitch; +} + +bool WellControls::getConstraintSwitch() const +{ + return m_constraintSwitch; +} + 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() ) + { + 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 ); } @@ -522,4 +384,80 @@ void WellControls::setNextDtFromTable( TableFunction const * table, real64 const } } +real64 WellControls::getTargetBHP( real64 const & targetTime ) const +{ + if( isProducer()) + { + return m_minBHPConstraint->getConstraintValue( targetTime ); + } + return m_maxBHPConstraint->getConstraintValue( targetTime ); +} + + +real64 WellControls::getInjectionTemperature() const +{ + real64 injectionTemperature = 0.0; + this->forInjectionConstraints< InjectionConstraint< PhaseVolumeRateConstraint >, InjectionConstraint< VolumeRateConstraint >, InjectionConstraint< MassRateConstraint > >( [&] ( auto & constraint ) + { + if( constraint.isConstraintActive()) + { + injectionTemperature = constraint.getInjectionTemperature(); + return; + } + } ); + return injectionTemperature; +} + + +arrayView1d< real64 const > WellControls::getInjectionStream() const +{ + arrayView1d< real64 const > injectionStream; + forInjectionConstraints< InjectionConstraint< PhaseVolumeRateConstraint >, InjectionConstraint< VolumeRateConstraint >, InjectionConstraint< MassRateConstraint > >( [&] ( auto & constraint ) + { + if( constraint.isConstraintActive() ) + { + injectionStream = constraint.getInjectionStream(); + return; + } + } ); + + return injectionStream; +} + +integer WellControls::getConstraintPhaseIndex() const +{ + integer phaseIndex = -1; + + if( isProducer() ) + { + forProductionConstraints< ProductionConstraint< PhaseVolumeRateConstraint > >( [&] ( auto & constraint ) + { + if( constraint.isConstraintActive() ) + { + phaseIndex = constraint.getPhaseIndex(); + } + } ); + } + else + { + forInjectionConstraints< InjectionConstraint< PhaseVolumeRateConstraint > >( [&] ( auto & constraint ) + { + if( constraint.isConstraintActive() ) + { + phaseIndex = constraint.getPhaseIndex(); + } + } ); + } + + return phaseIndex; +} + +real64 WellControls::getReferenceElevation() const +{ + if( isProducer () ) + { + return getMinBHPConstraint()->getReferenceElevation(); + } + return getMaxBHPConstraint()->getReferenceElevation(); +} } //namespace geos diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/WellControls.hpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellControls.hpp index 301c3c42f7c..625891c00ff 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/wells/WellControls.hpp +++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellControls.hpp @@ -26,6 +26,15 @@ #include "functions/TableFunction.hpp" #include "constitutive/fluid/multifluid/MultiFluidBase.hpp" +#include "physicsSolvers/fluidFlow/wells/WellInjectionConstraint.hpp" +#include "physicsSolvers/fluidFlow/wells/WellProductionConstraint.hpp" +#include "physicsSolvers/fluidFlow/wells/WellBHPConstraints.hpp" +#include "physicsSolvers/fluidFlow/wells/WellVolumeRateConstraint.hpp" +#include "physicsSolvers/fluidFlow/wells/WellPhaseVolumeRateConstraint.hpp" +#include "physicsSolvers/fluidFlow/wells/WellMassRateConstraint.hpp" +#include "physicsSolvers/fluidFlow/wells/WellLiquidRateConstraint.hpp" +#include "constitutive/fluid/multifluid/MultiFluidBase.hpp" +#include "constitutive/fluid/singlefluid/SingleFluidBase.hpp" namespace geos { namespace dataRepository @@ -124,33 +133,127 @@ class WellControls : public dataRepository::Group ///@} /** - * @name Getters / Setters + * @brief Create a new geometric object (box, plane, etc) as a child of this group. + * @param childKey the catalog key of the new geometric object to create + * @param childName the name of the new geometric object in the repository + * @return the group child */ - ///@{ + virtual Group * createChild( string const & childKey, string const & childName ) override; + /// Expand catalog for schema generation + + virtual void expandObjectCatalogs() override; /** - * @brief Set the control type to BHP and set a numerical value for the control. - * @param[in] val value for the BHP control + * @brief Apply a given functor to a container if the container can be + * cast to one of the specified types. + * @tparam CASTTYPE the first type that will be used in the attempted casting of container + * @tparam CASTTYPES a variadic list of types that will be used in the attempted casting of container + * @tparam CONTAINERTYPE the type of container + * @tparam LAMBDA the type of lambda function to call in the function + * @param[in] container a pointer to the container which will be passed to the lambda function + * @param[in] lambda the lambda function to call in the function + * @return a boolean to indicate whether the lambda was successfully applied to the container. */ - void switchToBHPControl( real64 const & val ); + template< typename T0, typename T1, typename ... CASTTYPES, typename CONTAINERTYPE, typename LAMBDA > + static bool applyLambdaToContainer( CONTAINERTYPE container, LAMBDA && lambda ) + { + using Pointee = std::remove_pointer_t< std::remove_reference_t< CONTAINERTYPE > >; + using T = std::conditional_t< std::is_const< Pointee >::value, T0 const, T0 >; + T * const castedContainer = dynamic_cast< T * >( container ); + + if( castedContainer != nullptr ) + { + lambda( *castedContainer ); + return true; + } + + return applyLambdaToContainer< T1, CASTTYPES... >( container, std::forward< LAMBDA >( lambda ) ); + } + + // Base case: no more types to try + template< typename CONTAINERTYPE, typename LAMBDA > + static bool applyLambdaToContainer( CONTAINERTYPE /*container*/, LAMBDA && /*lambda*/ ) + { + return false; + } + + // Single-type overload: try only T0 and stop + template< typename T0, typename CONTAINERTYPE, typename LAMBDA > + static bool applyLambdaToContainer( CONTAINERTYPE container, LAMBDA && lambda ) + { + using Pointee = std::remove_pointer_t< std::remove_reference_t< CONTAINERTYPE > >; + using T = std::conditional_t< std::is_const< Pointee >::value, T0 const, T0 >; + T * const castedContainer = dynamic_cast< T * >( container ); + + if( castedContainer != nullptr ) + { + lambda( *castedContainer ); + return true; + } + + return false; + } + /** - * @brief Set the control type to total rate and set a numerical value for the control. - * @param[in] val value for the total volumetric rate + * @copydoc forInjectionConstraints(LAMBDA &&) */ - void switchToTotalRateControl( real64 const & val ); + template< typename GROUPTYPE = Group, typename ... GROUPTYPES, typename LAMBDA > + void forInjectionConstraints( LAMBDA && lambda ) const + { + for( auto const * constraintIter : m_injectionRateConstraintList ) + { + applyLambdaToContainer< GROUPTYPE, GROUPTYPES... >( constraintIter, [&]( auto const & castedSubGroup ) + { + lambda( castedSubGroup ); + } ); + } + } + // non-const overload + template< typename GROUPTYPE = Group, typename ... GROUPTYPES, typename LAMBDA > + void forInjectionConstraints( LAMBDA && lambda ) + { + for( auto * constraintIter : m_injectionRateConstraintList ) + { + applyLambdaToContainer< GROUPTYPE, GROUPTYPES... >( constraintIter, [&]( auto & castedSubGroup ) + { + lambda( castedSubGroup ); + } ); + } + } /** - * @brief Set the control type to mass rate and set a numerical value for the control. - * @param[in] val value for the mass rate + * @copydoc forProductionConstraints(LAMBDA &&) */ - void switchToMassRateControl( real64 const & val ); + template< typename GROUPTYPE = Group, typename ... GROUPTYPES, typename LAMBDA > + void forProductionConstraints( LAMBDA && lambda ) const + { + for( auto const * constraintIter : m_productionRateConstraintList ) + { + applyLambdaToContainer< GROUPTYPE, GROUPTYPES... >( constraintIter, [&]( auto const & castedSubGroup ) + { + lambda( castedSubGroup ); + } ); + } + } + // non-const overload + template< typename GROUPTYPE = Group, typename ... GROUPTYPES, typename LAMBDA > + void forProductionConstraints( LAMBDA && lambda ) + { + for( auto * constraintIter : m_productionRateConstraintList ) + { + applyLambdaToContainer< GROUPTYPE, GROUPTYPES... >( constraintIter, [&]( auto & castedSubGroup ) + { + lambda( castedSubGroup ); + } ); + } + } /** - * @brief Set the control type to phase rate and set a numerical value for the control. - * @param[in] val value for the phase volumetric rate + * @name Getters / Setters */ - void switchToPhaseRateControl( real64 const & val ); + ///@{ + /** * @brief Get the control type for the well. @@ -170,11 +273,6 @@ 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 - */ - real64 getReferenceElevation() const { return m_refElevation; } /** * @brief Getter for the reference gravity coefficient @@ -189,58 +287,38 @@ class WellControls : public dataRepository::Group /** - * @brief Get the target bottom hole pressure value. - * @return a value for the target bottom hole pressure + * @brief Returns the target bottom hole pressure value. + * @param[in] targetTime time at which to evaluate the constraint + * @return the injector maximum bottom hole pressure or producer minimum bottom hole pressure */ - real64 getTargetBHP( real64 const & currentTime ) const - { - return m_targetBHPTable->evaluate( ¤tTime ); - } + real64 getTargetBHP( real64 const & targetTime ) const; - /** - * @brief Get the target total rate - * @return the target total rate - */ - real64 getTargetTotalRate( real64 const & currentTime ) const - { - return m_rateSign * m_targetTotalRateTable->evaluate( ¤tTime ); - } /** - * @brief Get the target phase rate - * @return the target phase rate + * @brief Const accessor for the temperature of the injection stream + * @return the temperature of the injection stream */ - real64 getTargetPhaseRate( real64 const & currentTime ) const - { - return m_rateSign * m_targetPhaseRateTable->evaluate( ¤tTime ); - } + real64 getInjectionTemperature() const; /** - * @brief Get the target mass rate - * @return the target mass rate + * @brief Const accessor for the injection stream + * @return the injection stream */ - real64 getTargetMassRate( real64 const & currentTime ) const - { - return m_rateSign * m_targetMassRateTable->evaluate( ¤tTime ); - } + arrayView1d< real64 const > getInjectionStream() const; - /** - * @brief Get the target phase name - * @return the target phase name - */ - const string & getTargetPhaseName() const { return m_targetPhaseName; } /** - * @brief Const accessor for the composition of the injection stream - * @return a global component fraction vector + * @brief Const accessor for the phase constraint index + * @return phase index associated with phase constraint */ - arrayView1d< real64 const > getInjectionStream() const { return m_injectionStream; } + integer getConstraintPhaseIndex() const; /** - * @brief Const accessor for the temperature of the injection stream - * @return the temperature of the injection stream + * @brief Return the reference elvation where pressure constraint is measured + * @return vertical location of constraint */ - real64 getInjectionTemperature() const { return m_injectionTemperature; } + real64 getReferenceElevation() const; + /** * @brief Getter for the flag specifying whether we check rates at surface or reservoir conditions @@ -284,6 +362,18 @@ class WellControls : public dataRepository::Group */ bool isWellOpen() const; + void setWellState( bool open ); + bool getWellState() const; + + + void setConstraintSwitch( bool constraintSwitch ); + bool getConstraintSwitch() const; + + void setCurrentConstraint( WellConstraintBase * currentConstraint ) { m_currentConstraint = currentConstraint;} + WellConstraintBase * getCurrentConstraint() { return m_currentConstraint; } + WellConstraintBase const * getCurrentConstraint() const { return m_currentConstraint; } + + /** * @brief Getter for the flag to enable crossflow * @return the flag deciding whether crossflow is allowed or not @@ -303,6 +393,7 @@ class WellControls : public dataRepository::Group */ void setNextDtFromTables( real64 const & currentTime, real64 & nextDt ); + /** * @brief setter for multi fluid separator * @param[in] fluidSeparatorPtr single or multiphase separator @@ -314,6 +405,13 @@ class WellControls : public dataRepository::Group */ constitutive::MultiFluidBase & getMultiFluidSeparator() { return dynamicCast< constitutive::MultiFluidBase & >( *m_fluidSeparatorPtr ); } + /** + * @brief Getter for single fluid separator + * @return reference to separator + */ + constitutive::SingleFluidBase & getSingleFluidSeparator() { return dynamicCast< constitutive::SingleFluidBase & >( *m_fluidSeparatorPtr ); } + + /** * @brief Getter for the reservoir average pressure when m_useSurfaceConditions == 0 * @return the pressure @@ -352,6 +450,7 @@ class WellControls : public dataRepository::Group WellControls::Status getWellStatus () const { return m_wellStatus; } ///@} + /** * @brief Struct to serve as a container for variable strings and keys. * @struct viewKeyStruct @@ -362,24 +461,10 @@ class WellControls : public dataRepository::Group static constexpr char const * refElevString() { return "referenceElevation"; } /// String key for the well type static constexpr char const * typeString() { return "type"; } - /// String key for the well input control - static constexpr char const * inputControlString() { return "control"; } /// String key for the well current control static constexpr char const * currentControlString() { return "currentControl"; } - /// String key for the well target BHP - static constexpr char const * targetBHPString() { return "targetBHP"; } - /// String key for the well target rate - static constexpr char const * targetTotalRateString() { return "targetTotalRate"; } - /// String key for the well target phase rate - static constexpr char const * targetPhaseRateString() { return "targetPhaseRate"; } - /// String key for the well target phase name - static constexpr char const * targetPhaseNameString() { return "targetPhaseName"; } - /// String key for the well target phase name - static constexpr char const * targetMassRateString() { return "targetMassRate"; } - /// String key for the well injection stream - static constexpr char const * injectionStreamString() { return "injectionStream"; } - /// String key for the well injection temperature - static constexpr char const * injectionTemperatureString() { return "injectionTemperature"; } + /// String key for the well input control + static constexpr char const * inputControlString() { return "control"; } /// String key for checking the rates at surface conditions static constexpr char const * useSurfaceConditionsString() { return "useSurfaceConditions"; } /// String key for reference reservoir region @@ -388,14 +473,7 @@ class WellControls : public dataRepository::Group static constexpr char const * surfacePressureString() { return "surfacePressure"; } /// String key for the surface temperature static constexpr char const * surfaceTemperatureString() { return "surfaceTemperature"; } - /// string key for total rate table name - static constexpr char const * targetTotalRateTableNameString() { return "targetTotalRateTableName"; } - /// string key for phase rate table name - static constexpr char const * targetPhaseRateTableNameString() { return "targetPhaseRateTableName"; } - /// string key for mass rate table name - static constexpr char const * targetMassRateTableNameString() { return "targetMassRateTableName"; } - /// string key for BHP table name - static constexpr char const * targetBHPTableNameString() { return "targetBHPTableName"; } + /// string key for status table name static constexpr char const * statusTableNameString() { return "statusTableName"; } /// string key for perforation status table name @@ -404,13 +482,53 @@ class WellControls : public dataRepository::Group static constexpr char const * enableCrossflowString() { return "enableCrossflow"; } /// string key for the initial pressure coefficient static constexpr char const * initialPressureCoefficientString() { return "initialPressureCoefficient"; } - + /// string key for the esitmate well solution flag + static constexpr char const * estimateWellSolutionString() { return "estimateWellSolution"; } + + /// string key for the minimum BHP presssure for a producer + static constexpr char const * minimumBHPConstraintString() { return "MinimumBHPConstraint"; } + /// string key for the maximum BHP presssure for a injection + static constexpr char const * maximumBHPConstraintString() { return "MaximumBHPConstraint"; } + /// string key for the maximum phase rate for a producer + static constexpr char const * productionPhaseVolumeRateConstraintString() { return "ProductionPhaseVolumeRateConstraint"; } + /// string key for the maximum phase rate for a injection + static constexpr char const * injectionPhaseVolumeRateConstraint() { return "InjectionPhaseVolumeRateConstraint"; } + /// string key for the maximum volume rate for a producer + static constexpr char const * productionVolumeRateConstraint() { return "ProductionVolumeRateConstraint"; } + /// string key for the maximum volume rate for a injector + static constexpr char const * injectionVolumeRateConstraint() { return "InjectionVolumeRateConstraint"; } + /// string key for the maximum mass rate for a producer + static constexpr char const * productionMassRateConstraint() { return "ProductionMassRateConstraint"; } + /// string key for the maximum mass rate for a injector + static constexpr char const * injectionMassRateConstraint() { return "InjectionMassRateConstraint"; } + /// string key for the liquid rate for a producer + static constexpr char const * productionLiquidRateConstraint() { return "ProductionLiquidRateConstraint"; } } /// ViewKey struct for the WellControls class viewKeysWellControls; static void setNextDtFromTable( TableFunction const * table, real64 const currentTime, real64 & nextDt ); + /** + * @brief Create a constraint + * @tparam ConstraintType the type of constraint to create + * @param[in] constraintName name to assign to the constraint + */ + template< typename ConstraintType > void createConstraint ( string const & constraintName ); + + + /** + * @brief Getters for constraints + */ + MinimumBHPConstraint * getMinBHPConstraint() { return m_minBHPConstraint; }; + MinimumBHPConstraint * getMinBHPConstraint() const { return m_minBHPConstraint; }; + MaximumBHPConstraint * getMaxBHPConstraint() { return m_maxBHPConstraint; }; + MaximumBHPConstraint * getMaxBHPConstraint() const { return m_maxBHPConstraint; }; + // Lists of rate constraints + std::vector< WellConstraintBase * > getProdRateConstraints() { return m_productionRateConstraintList; }; + std::vector< WellConstraintBase * > getProdRateConstraints() const { return m_productionRateConstraintList; }; + std::vector< WellConstraintBase * > getInjRateConstraints() { return m_injectionRateConstraintList; } + std::vector< WellConstraintBase * > getInjRateConstraints() const { return m_injectionRateConstraintList; } protected: virtual void postInputInitialization() override; @@ -434,27 +552,6 @@ class WellControls : public dataRepository::Group /// Well controls as a Control enum Control m_currentControl; - /// Target bottom hole pressure value - real64 m_targetBHP; - - /// Target rate value - real64 m_targetTotalRate; - - /// Target phase rate value - real64 m_targetPhaseRate; - - /// Name of the targeted phase - string m_targetPhaseName; - - /// Target MassRate - real64 m_targetMassRate; - - /// Vector with global component fractions at the injector - array1d< real64 > m_injectionStream; - - /// Temperature at the injector - real64 m_injectionTemperature; - /// Flag to decide whether rates are controlled at rates or surface conditions integer m_useSurfaceConditions; @@ -470,17 +567,6 @@ class WellControls : public dataRepository::Group /// Surface temperature real64 m_surfaceTemp; - /// Total rate table name - string m_targetTotalRateTableName; - - /// Phase rate table name - string m_targetPhaseRateTableName; - - /// Mass rate table name - string m_targetMassRateTableName; - - /// BHP table name - string m_targetBHPTableName; /// Well status table name string m_statusTableName; @@ -497,25 +583,33 @@ class WellControls : public dataRepository::Group /// Rate sign. +1 for injector, -1 for producer real64 m_rateSign; - /// Total rate table - TableFunction const * m_targetTotalRateTable; - /// Phase rate table - TableFunction const * m_targetPhaseRateTable; + /// Status table + TableFunction const * m_statusTable; + + bool m_wellOpen; - /// Mass rate table - TableFunction const * m_targetMassRateTable; - /// BHP table - TableFunction const * m_targetBHPTable; - /// Status table - TableFunction const * m_statusTable; + /// List of constraints + //constraint_array m_ConstraintList; + // Bool to trigger old/new constraint switch logic + bool m_constraintSwitch; + + // Current constraint + WellConstraintBase * m_currentConstraint; + // Minimum and maximum BHP and WHP constraints + MinimumBHPConstraint * m_minBHPConstraint; + MaximumBHPConstraint * m_maxBHPConstraint; + + + // Lists of rate constraints + std::vector< WellConstraintBase * > m_productionRateConstraintList; + std::vector< WellConstraintBase * > m_injectionRateConstraintList; /// Well status WellControls::Status m_wellStatus; - /// Region average pressure used in volume rate constraint calculations real64 m_regionAveragePressure; @@ -524,11 +618,15 @@ class WellControls : public dataRepository::Group }; -ENUM_STRINGS( WellControls::Type, + +// Use local aliases to avoid accidental macro expansion of the tokens 'Type' or 'Control' +using WellControls_Type = WellControls::Type; +ENUM_STRINGS( WellControls_Type, "producer", "injector" ); -ENUM_STRINGS( WellControls::Control, +using WellControls_Control = WellControls::Control; +ENUM_STRINGS( WellControls_Control, "BHP", "phaseVolRate", "totalVolRate", diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/WellInjectionConstraint.cpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellInjectionConstraint.cpp new file mode 100644 index 00000000000..57145a752dc --- /dev/null +++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellInjectionConstraint.cpp @@ -0,0 +1,108 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * SPDX-License-Identifier: LGPL-2.1-only + * + * Copyright (c) 2016-2024 Lawrence Livermore National Security LLC + * Copyright (c) 2018-2024 TotalEnergies + * Copyright (c) 2018-2024 The Board of Trustees of the Leland Stanford Junior University + * Copyright (c) 2023-2024 Chevron + * Copyright (c) 2019- GEOS/GEOSX Contributors + * All rights reserved + * + * See top level LICENSE, COPYRIGHT, CONTRIBUTORS, NOTICE, and ACKNOWLEDGEMENTS files for details. + * ------------------------------------------------------------------------------------------------------------ + */ + +/* + * @file WellInjectionConstraint.cpp + */ + +#include "LogLevelsInfo.hpp" +#include "WellInjectionConstraint.hpp" +#include "WellConstants.hpp" +#include "dataRepository/InputFlags.hpp" +#include "functions/FunctionManager.hpp" + +#include "WellLiquidRateConstraint.hpp" +#include "WellMassRateConstraint.hpp" +#include "WellPhaseVolumeRateConstraint.hpp" +#include "WellVolumeRateConstraint.hpp" + +namespace geos +{ +using namespace dataRepository; + +template< typename ConstraintRateType > +InjectionConstraint< ConstraintRateType >::InjectionConstraint( string const & name, Group * const parent ) + : ConstraintRateType( name, parent ) +{ + // set rate sign for injectors (base class member) + this->m_rateSign = 1.0; + classtype::registerWrapper( injectionStreamKey::injectionStreamString(), &m_injectionStream ). + setDefaultValue( -1 ). + setSizedFromParent( 0 ). + setInputFlag( InputFlags::OPTIONAL ). + setDescription( "Global component densities of the injection stream [moles/m^3 or kg/m^3]" ); + + InjectionConstraint< ConstraintRateType >::registerWrapper( injectionStreamKey::injectionTemperatureString(), &m_injectionTemperature ). + setDefaultValue( -1 ). + setInputFlag( InputFlags::OPTIONAL ). + setDescription( "Temperature of the injection stream [K]" ); +} +template< typename ConstraintRateType > +InjectionConstraint< ConstraintRateType >::~InjectionConstraint() +{} +template< typename ConstraintRateType > +void InjectionConstraint< ConstraintRateType >::postInputInitialization() +{ + // Validate value and table options + ConstraintRateType::postInputInitialization(); + +// Validate the injection stream and temperature + validateInjectionStream( ); + +} +template< typename ConstraintRateType > +void InjectionConstraint< ConstraintRateType >::validateInjectionStream( ) +{ + GEOS_THROW_IF( (m_injectionStream.empty() && m_injectionTemperature >= 0) || + (!m_injectionStream.empty() && m_injectionTemperature < 0), + this->getName() << " " << this->getDataContext() << ": Both " + << injectionStreamKey::injectionStreamString() << " and " << injectionStreamKey::injectionTemperatureString() + << " must be specified for multiphase simulations", + InputError ); + + if( !m_injectionStream.empty()) + { + real64 sum = 0.0; + for( localIndex ic = 0; ic < m_injectionStream.size(); ++ic ) + { + GEOS_ERROR_IF( m_injectionStream[ic] < 0.0 || m_injectionStream[ic] > 1.0, + classtype::getWrapperDataContext( injectionStreamKey::injectionStreamString() ) << ": Invalid injection stream" ); + sum += m_injectionStream[ic]; + } + GEOS_THROW_IF( LvArray::math::abs( 1.0 - sum ) > std::numeric_limits< real64 >::epsilon(), + classtype::getWrapperDataContext( injectionStreamKey::injectionStreamString() ) << ": Invalid injection stream", + InputError ); + } +} + +// Register concrete wrapper constraint types and instantiate templates. +template class InjectionConstraint< LiquidRateConstraint >; +using InjectionLiquidRateConstraint = InjectionConstraint< LiquidRateConstraint >; +REGISTER_CATALOG_ENTRY( WellConstraintBase, InjectionLiquidRateConstraint, string const &, Group * const ) + +template class InjectionConstraint< MassRateConstraint >; +using InjectionMassRateConstraint = InjectionConstraint< MassRateConstraint >; +REGISTER_CATALOG_ENTRY( WellConstraintBase, InjectionMassRateConstraint, string const &, Group * const ) + +template class InjectionConstraint< PhaseVolumeRateConstraint >; +using InjectionPhaseVolumeRateConstraint = InjectionConstraint< PhaseVolumeRateConstraint >; +REGISTER_CATALOG_ENTRY( WellConstraintBase, InjectionPhaseVolumeRateConstraint, string const &, Group * const ) + +template class InjectionConstraint< VolumeRateConstraint >; +using InjectionVolumeRateConstraint = InjectionConstraint< VolumeRateConstraint >; +REGISTER_CATALOG_ENTRY( WellConstraintBase, InjectionVolumeRateConstraint, string const &, Group * const ) + + +} diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/WellInjectionConstraint.hpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellInjectionConstraint.hpp new file mode 100644 index 00000000000..1707f5f20e7 --- /dev/null +++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellInjectionConstraint.hpp @@ -0,0 +1,139 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * SPDX-License-Identifier: LGPL-2.1-only + * + * Copyright (c) 2016-2024 Lawrence Livermore National Security LLC + * Copyright (c) 2018-2024 TotalEnergies + * Copyright (c) 2018-2024 The Board of Trustees of the Leland Stanford Junior University + * Copyright (c) 2023-2024 Chevron + * Copyright (c) 2019- GEOS/GEOSX Contributors + * All rights reserved + * + * See top level LICENSE, COPYRIGHT, CONTRIBUTORS, NOTICE, and ACKNOWLEDGEMENTS files for details. + * ------------------------------------------------------------------------------------------------------------ + */ + +/* + * @file WellInjectionConstraint.hpp + */ + + +#ifndef GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_WELLINJECTIONCONSTRAINT_HPP +#define GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_WELLINJECTIONCONSTRAINT_HPP + +#include "common/format/EnumStrings.hpp" +#include "dataRepository/Group.hpp" +#include "functions/TableFunction.hpp" + +namespace geos +{ + +using namespace dataRepository; +/** + * @class InjectionConstraint + * @brief This class describes constraint used to control a injection well. + */ + +template< typename ConstraintType > +class InjectionConstraint : public ConstraintType +{ +public: + typedef InjectionConstraint< ConstraintType > classtype; + /** + * @name Constructor / Destructor + */ + ///@{ + + /** + * @brief Constructor for WellControls Objects. + * @param[in] name the name of this instantiation of WellControls in the repository + * @param[in] parent the parent group of this instantiation of WellControls + */ + explicit InjectionConstraint( string const & name, dataRepository::Group * const parent ); + + /** + * @brief Default destructor. + */ + ~InjectionConstraint() override; + + /** + * @brief Deleted default constructor. + */ + InjectionConstraint() = delete; + + /** + * @brief Deleted copy constructor. + */ + InjectionConstraint( InjectionConstraint const & ) = delete; + + /** + * @brief Deleted move constructor. + */ + InjectionConstraint( InjectionConstraint && ) = delete; + + /** + * @brief Deleted assignment operator. + * @return a reference to a constraint object + */ + InjectionConstraint & operator=( InjectionConstraint const & ) = delete; + + /** + * @brief Deleted move operator. + * @return a reference to a constraint object + */ + InjectionConstraint & operator=( InjectionConstraint && ) = delete; + + ///@} + + /** + * @brief name of the node manager in the object catalog + * @return string that contains the catalog name to generate a new Constraint object through the object catalog. + */ + static string catalogName() + { + return "Injection"+ConstraintType::catalogName(); + } + + virtual string getCatalogName() const override { return catalogName(); } + + struct injectionStreamKey + { + /// String key for the well injection stream + static constexpr char const * injectionStreamString() { return "injectionStream"; } + /// String key for the well injection temperature + static constexpr char const * injectionTemperatureString() { return "injectionTemperature"; } + }; + + /** + * @brief Const accessor for the composition of the injection stream + * @return a global component fraction vector + */ + arrayView1d< real64 const > getInjectionStream() const { return m_injectionStream; } + + /** + * @brief Const accessor for the temperature of the injection stream + * @return the temperature of the injection stream + */ + real64 getInjectionTemperature() const { return m_injectionTemperature; } + +protected: + + virtual void postInputInitialization() override; + static bool isViolated( const real64 & currentValue, const real64 & constraintValue ) + { return currentValue > constraintValue; } + + void validateInjectionStream(); +private: + + /// Vector with global component fractions at the injector + array1d< real64 > m_injectionStream; + + /// Temperature at the injector + real64 m_injectionTemperature; + +}; + + +} //namespace geos + +#endif //GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_WELLINJECTIONCONSTRAINT_HPP diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/WellLiquidRateConstraint.cpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellLiquidRateConstraint.cpp new file mode 100644 index 00000000000..54424d9224e --- /dev/null +++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellLiquidRateConstraint.cpp @@ -0,0 +1,85 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * SPDX-License-Identifier: LGPL-2.1-only + * + * Copyright (c) 2016-2024 Lawrence Livermore National Security LLC + * Copyright (c) 2018-2024 TotalEnergies + * Copyright (c) 2018-2024 The Board of Trustees of the Leland Stanford Junior University + * Copyright (c) 2023-2024 Chevron + * Copyright (c) 2019- GEOS/GEOSX Contributors + * All rights reserved + * + * See top level LICENSE, COPYRIGHT, CONTRIBUTORS, NOTICE, and ACKNOWLEDGEMENTS files for details. + * ------------------------------------------------------------------------------------------------------------ + */ + +/* + * @file WellLiquidRateConstraint.cpp + */ + +#include "LogLevelsInfo.hpp" +#include "WellLiquidRateConstraint.hpp" +#include "WellConstants.hpp" +#include "dataRepository/InputFlags.hpp" +#include "functions/FunctionManager.hpp" + +namespace geos +{ + +using namespace dataRepository; + + +LiquidRateConstraint::LiquidRateConstraint( string const & name, Group * const parent ) + : WellConstraintBase( name, parent ) +{ + this->registerWrapper( viewKeyStruct::liquidRateString(), &this->m_constraintValue ). + setDefaultValue( 0.0 ). + setInputFlag( InputFlags::OPTIONAL ). + setRestartFlags( RestartFlags::WRITE_AND_READ ). + setDescription( "Phase rate, (if useSurfaceCondSitions: [surface m^3/s]; else [reservoir m^3/s]) " ); + + this->registerWrapper( viewKeyStruct::phaseNamesString(), &m_phaseNames ). + setRTTypeName( rtTypes::CustomTypes::groupNameRefArray ). + setInputFlag( InputFlags::REQUIRED ). + setDescription( "List of fluid phase names defining the liquid" ); +} + +LiquidRateConstraint::~LiquidRateConstraint() +{} + +void LiquidRateConstraint::postInputInitialization() +{ + // Validate table options + WellConstraintBase::postInputInitialization(); + + // check constraint value + GEOS_THROW_IF( m_constraintValue < 0, + getWrapperDataContext( viewKeyStruct::liquidRateString() ) << ": Target value is negative", + InputError ); + + GEOS_THROW_IF ((m_constraintValue <= 0.0 && m_constraintScheduleTableName.empty()), + getName() << " " << getDataContext() << ": You need to specify a liquid rate constraint. \n" << + "The rate constraint can be specified using " << + "either " << viewKeyStruct::liquidRateString() << + " or " << WellConstraintBase::viewKeyStruct::constraintScheduleTableNameString(), + InputError ); +} + + +bool LiquidRateConstraint::checkViolation( WellConstraintBase const & currentConstraint, real64 const & currentTime ) const +{ + real64 const currentValue = currentConstraint.liquidRate(); + real64 const constraintValue = this->getConstraintValue( currentTime ); + if( this->m_rateSign < 0.0 ) + { + // production: violated when current < constraint + return currentValue < constraintValue; + } + else + { + // injection: violated when current > constraint + return currentValue > constraintValue; + } +} + +} //namespace geos diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/WellLiquidRateConstraint.hpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellLiquidRateConstraint.hpp new file mode 100644 index 00000000000..45ef0b58a98 --- /dev/null +++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellLiquidRateConstraint.hpp @@ -0,0 +1,170 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * SPDX-License-Identifier: LGPL-2.1-only + * + * Copyright (c) 2016-2024 Lawrence Livermore National Security LLC + * Copyright (c) 2018-2024 TotalEnergies + * Copyright (c) 2018-2024 The Board of Trustees of the Leland Stanford Junior University + * Copyright (c) 2023-2024 Chevron + * Copyright (c) 2019- GEOS/GEOSX Contributors + * All rights reserved + * + * See top level LICENSE, COPYRIGHT, CONTRIBUTORS, NOTICE, and ACKNOWLEDGEMENTS files for details. + * ------------------------------------------------------------------------------------------------------------ + */ + +/* + * @file WellLiquidRateConstraint.hpp + */ + + +#ifndef GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_WELLLIQUIDRATECONSTRAINT_HPP +#define GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_WELLLIQUIDRATECONSTRAINT_HPP + +#include "common/format/EnumStrings.hpp" + +#include "functions/TableFunction.hpp" +#include "WellConstraintsBase.hpp" + +namespace geos +{ + + +/** + * @class LiquidRateConstraint + * @brief This class describes a Liquid rate constraint used to control of type WellConstraintType + */ + + +class LiquidRateConstraint : public WellConstraintBase +{ +public: + + + /** + * @name Constructor / Destructor + */ + ///@{ + + /** + * @brief Constructor for WellControls Objects. + * @param[in] name the name of this instantiation of WellControls in the repository + * @param[in] parent the parent group of this instantiation of WellControls + */ + explicit LiquidRateConstraint( string const & name, dataRepository::Group * const parent ); + + + /** + * @brief Default destructor. + */ + ~LiquidRateConstraint() override; + + /** + * @brief Deleted default constructor. + */ + LiquidRateConstraint() = delete; + + /** + * @brief Deleted copy constructor. + */ + LiquidRateConstraint( LiquidRateConstraint const & ) = delete; + + /** + * @brief Deleted move constructor. + */ + LiquidRateConstraint( LiquidRateConstraint && ) = delete; + + /** + * @brief Deleted assignment operator. + * @return a reference to a constraint object + */ + LiquidRateConstraint & operator=( LiquidRateConstraint const & ) = delete; + + /** + * @brief Deleted move operator. + * @return a reference to a constraint object + */ + LiquidRateConstraint & operator=( LiquidRateConstraint && ) = delete; + + /** + * @brief name of the node manager in the object catalog + * @return string that contains the catalog name to generate a new Constraint object through the object catalog. + */ + static string catalogName() + { + return "LiquidRateConstraint"; + } + ///@} + + /** + * @name Getters / Setters + */ + ///@{ + /** + * @brief Get the target phase name + * @return the target phase name + */ + const string_array & getPhaseNames() const { return m_phaseNames; } + + /** + * @brief Set phases associated with liquid constraint + * @param array of phase names + */ + void setPhaseNames( const string_array & phaseNames ) { m_phaseNames=phaseNames; } + + /** + * @brief Get the phase indices + * @return array of phase indices + */ + const array1d< integer > & getPhaseIndices() const { return m_phaseIndices; } + + ///@} + /** + * @brief Struct to serve as a container for variable strings and keys. + * @struct viewKeyStruct + */ + struct viewKeyStruct + { + /// String key for the liquid rate + static constexpr char const * liquidRateString() { return "liquidRate"; } + /// String key for the phases names + static constexpr char const * phaseNamesString() { return "phaseNames"; } + }; + + // Temp interface - tjb + virtual ConstraintTypeId getControl() const override { return ConstraintTypeId::LIQUIDRATE; }; + + virtual bool checkViolation( WellConstraintBase const & currentConstraint, real64 const & currentTime ) const override; + +protected: + + virtual void postInputInitialization() override; + + template< typename T > + void validateLiquidType( T const & fluidModel ) + { + m_phaseIndices.resize( m_phaseNames.size()); + for( size_t ip =0; ip m_phaseIndices; + +}; + + + +} //namespace geos + +#endif //GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_WELLLiquidRateConstraint_HPP diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/WellMassRateConstraint.cpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellMassRateConstraint.cpp new file mode 100644 index 00000000000..6851a033f8b --- /dev/null +++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellMassRateConstraint.cpp @@ -0,0 +1,81 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * SPDX-License-Identifier: LGPL-2.1-only + * + * Copyright (c) 2016-2024 Lawrence Livermore National Security LLC + * Copyright (c) 2018-2024 TotalEnergies + * Copyright (c) 2018-2024 The Board of Trustees of the Leland Stanford Junior University + * Copyright (c) 2023-2024 Chevron + * Copyright (c) 2019- GEOS/GEOSX Contributors + * All rights reserved + * + * See top level LICENSE, COPYRIGHT, CONTRIBUTORS, NOTICE, and ACKNOWLEDGEMENTS files for details. + * ------------------------------------------------------------------------------------------------------------ + */ + +/* + * @file WellMassRateConstraints.cpp + */ + +#include "LogLevelsInfo.hpp" +#include "WellMassRateConstraint.hpp" +#include "WellConstants.hpp" +#include "dataRepository/InputFlags.hpp" +#include "functions/FunctionManager.hpp" + +namespace geos +{ + +using namespace dataRepository; + +MassRateConstraint::MassRateConstraint( string const & name, Group * const parent ) + : WellConstraintBase( name, parent ) +{ + this->setInputFlags( InputFlags::OPTIONAL_NONUNIQUE ); + + this->registerWrapper( viewKeyStruct::massRateString(), &this->m_constraintValue ). + setDefaultValue( 0.0 ). + setInputFlag( InputFlags::OPTIONAL ). + setRestartFlags( RestartFlags::WRITE_AND_READ ). + setDescription( "Maximum mass rate (kg/s)" ); +} + +MassRateConstraint::~MassRateConstraint() +{} + + +void MassRateConstraint::postInputInitialization() +{ + // Validate table options + WellConstraintBase::postInputInitialization(); + + // check constraint value + GEOS_THROW_IF( m_constraintValue < 0, + getWrapperDataContext( viewKeyStruct::massRateString() ) << ": Target value is negative", + InputError ); + + GEOS_THROW_IF ((m_constraintValue <= 0.0 && m_constraintScheduleTableName.empty()), + getName() << " " << getDataContext() << ": You need to specify a mass rate constraint. \n" << + "The rate constraint can be specified using " << + "either " << viewKeyStruct::massRateString() << + " or " << WellConstraintBase::viewKeyStruct::constraintScheduleTableNameString(), + InputError ); +} + + +bool MassRateConstraint::checkViolation( WellConstraintBase const & currentConstraint, real64 const & currentTime )const +{ + // isViolated is defined as a static method on the specific WellConstraintType (Injection/Production) + // Evaluate violation according to the sign set for injectors/producers + real64 const currentValue = currentConstraint.massRate(); + real64 const constraintValue = this->getConstraintValue( currentTime ); + if( this->m_rateSign < 0.0 ) + { + return currentValue < constraintValue; + } + else + { + return currentValue > constraintValue; + } +} +} //namespace geos diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/WellMassRateConstraint.hpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellMassRateConstraint.hpp new file mode 100644 index 00000000000..6cd6703ba36 --- /dev/null +++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellMassRateConstraint.hpp @@ -0,0 +1,120 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * SPDX-License-Identifier: LGPL-2.1-only + * + * Copyright (c) 2016-2024 Lawrence Livermore National Security LLC + * Copyright (c) 2018-2024 TotalEnergies + * Copyright (c) 2018-2024 The Board of Trustees of the Leland Stanford Junior University + * Copyright (c) 2023-2024 Chevron + * Copyright (c) 2019- GEOS/GEOSX Contributors + * All rights reserved + * + * See top level LICENSE, COPYRIGHT, CONTRIBUTORS, NOTICE, and ACKNOWLEDGEMENTS files for details. + * ------------------------------------------------------------------------------------------------------------ + */ + +/* + * @file WellMassRateConstraint.hpp + */ + + +#ifndef GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_WELLMASSRATECONSTRAINT_HPP +#define GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_WELLMASSRATECONSTRAINT_HPP + +#include "common/format/EnumStrings.hpp" +#include "dataRepository/Group.hpp" +#include "functions/TableFunction.hpp" +#include "WellConstraintsBase.hpp" +namespace geos +{ + +/** + * @class MassRateConstraint + * @brief This class describes a mass rate constraint used to control a well. + */ + +class MassRateConstraint : public WellConstraintBase +{ +public: + + /** + * @name Constructor / Destructor + */ + ///@{ + + /** + * @brief Constructor for WellControls Objects. + * @param[in] name the name of this instantiation of WellControls in the repository + * @param[in] parent the parent group of this instantiation of WellControls + */ + explicit MassRateConstraint( string const & name, dataRepository::Group * const parent ); + + + /** + * @brief Default destructor. + */ + ~MassRateConstraint() override; + + /** + * @brief Deleted default constructor. + */ + MassRateConstraint() = delete; + + /** + * @brief Deleted copy constructor. + */ + MassRateConstraint( MassRateConstraint const & ) = delete; + + /** + * @brief Deleted move constructor. + */ + MassRateConstraint( MassRateConstraint && ) = delete; + + /** + * @brief Deleted assignment operator. + * @return a reference to a constraint object + */ + MassRateConstraint & operator=( MassRateConstraint const & ) = delete; + + /** + * @brief Deleted move operator. + * @return a reference to a constraint object + */ + MassRateConstraint & operator=( MassRateConstraint && ) = delete; + + /** + * @brief name of the node manager in the object catalog + * @return string that contains the catalog name to generate a new Constraint object through the object catalog. + */ + static string catalogName() + { + return "MassRateConstraint"; + } + ///@} + + struct viewKeyStruct + { + /// String key for the well target rate + static constexpr char const * massRateString() { return "massRate"; } + }; + + /** + * @name Getters / Setters + */ + + // Temp interface - tjb + virtual ConstraintTypeId getControl() const override { return ConstraintTypeId::MASSRATE; }; + ///@} + + virtual bool checkViolation( WellConstraintBase const & currentConstraint, real64 const & currentTime ) const override; + +protected: + + virtual void postInputInitialization() override; + +}; + + +} //namespace geos + +#endif //GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_WELLMASSRATECONSTRAINT_HPP diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/WellPhaseVolumeRateConstraint.cpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellPhaseVolumeRateConstraint.cpp new file mode 100644 index 00000000000..533848b97a3 --- /dev/null +++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellPhaseVolumeRateConstraint.cpp @@ -0,0 +1,88 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * SPDX-License-Identifier: LGPL-2.1-only + * + * Copyright (c) 2016-2024 Lawrence Livermore National Security LLC + * Copyright (c) 2018-2024 TotalEnergies + * Copyright (c) 2018-2024 The Board of Trustees of the Leland Stanford Junior University + * Copyright (c) 2023-2024 Chevron + * Copyright (c) 2019- GEOS/GEOSX Contributors + * All rights reserved + * + * See top level LICENSE, COPYRIGHT, CONTRIBUTORS, NOTICE, and ACKNOWLEDGEMENTS files for details. + * ------------------------------------------------------------------------------------------------------------ + */ + +/* + * @file WellPhaseVolumeRateConstraint.cpp + */ + +#include "LogLevelsInfo.hpp" +#include "WellPhaseVolumeRateConstraint.hpp" +#include "WellConstants.hpp" +#include "dataRepository/InputFlags.hpp" +#include "functions/FunctionManager.hpp" + + +namespace geos +{ + +using namespace dataRepository; + + +PhaseVolumeRateConstraint::PhaseVolumeRateConstraint( string const & name, Group * const parent ) + : WellConstraintBase( name, parent ) +{ + this->setInputFlags( InputFlags::OPTIONAL_NONUNIQUE ); + + this->registerWrapper( viewKeyStruct::phaseRateString(), &this->m_constraintValue ). + setDefaultValue( 0.0 ). + setInputFlag( InputFlags::OPTIONAL ). + setRestartFlags( RestartFlags::WRITE_AND_READ ). + setDescription( "Phase rate, (if useSurfaceConditions: [surface m^3/s]; else [reservoir m^3/s]) " ); + + this->registerWrapper( viewKeyStruct::phaseNameString(), &this->m_phaseName ). + setRTTypeName( rtTypes::CustomTypes::groupNameRef ). + setDefaultValue( "" ). + setInputFlag( InputFlags::OPTIONAL ). + setRestartFlags( RestartFlags::WRITE_AND_READ ). + setDescription( "Name of the target phase" ); +} + +PhaseVolumeRateConstraint::~PhaseVolumeRateConstraint() +{} + +void PhaseVolumeRateConstraint::postInputInitialization() +{ + // Validate table options + WellConstraintBase::postInputInitialization(); + + // check constraint value + GEOS_THROW_IF( m_constraintValue < 0, + getWrapperDataContext( viewKeyStruct::phaseRateString() ) << ": Target value is negative", + InputError ); + + + GEOS_THROW_IF ((m_constraintValue <= 0.0 && m_constraintScheduleTableName.empty()), + getName() << " " << getDataContext() << ": You need to specify a phase rate constraint. \n" << + "The rate constraint can be specified using " << + "either " << viewKeyStruct::phaseRateString() << + " or " << WellConstraintBase::viewKeyStruct::constraintScheduleTableNameString(), + InputError ); +} + +bool PhaseVolumeRateConstraint::checkViolation( WellConstraintBase const & currentConstraint, real64 const & currentTime ) const +{ + real64 const currentValue = currentConstraint.phaseVolumeRates()[m_phaseIndex]; + real64 const constraintValue = getConstraintValue( currentTime ); + if( m_rateSign < 0.0 ) + { + return currentValue < constraintValue; + } + else + { + return currentValue > constraintValue; + } +} + +} //namespace geos diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/WellPhaseVolumeRateConstraint.hpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellPhaseVolumeRateConstraint.hpp new file mode 100644 index 00000000000..bb7880cfc0c --- /dev/null +++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellPhaseVolumeRateConstraint.hpp @@ -0,0 +1,179 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * SPDX-License-Identifier: LGPL-2.1-only + * + * Copyright (c) 2016-2024 Lawrence Livermore National Security LLC + * Copyright (c) 2018-2024 TotalEnergies + * Copyright (c) 2018-2024 The Board of Trustees of the Leland Stanford Junior University + * Copyright (c) 2023-2024 Chevron + * Copyright (c) 2019- GEOS/GEOSX Contributors + * All rights reserved + * + * See top level LICENSE, COPYRIGHT, CONTRIBUTORS, NOTICE, and ACKNOWLEDGEMENTS files for details. + * ------------------------------------------------------------------------------------------------------------ + */ + +/* + * @file WellPhaseVolumeRateConstraint.hpp + */ + + +#ifndef GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_WELLPHASEVOLUMERATECONSTRAINT_HPP +#define GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_WELLPHASEVOLUMERATECONSTRAINT_HPP + +#include "common/format/EnumStrings.hpp" +#include "dataRepository/Group.hpp" +#include "functions/TableFunction.hpp" +#include "WellConstraintsBase.hpp" +#include "WellConstants.hpp" + +namespace geos +{ + + +template< typename T > +localIndex getPhaseIndexFromFluidModel( T const & fluidModel, std::string const & inputPhase ) +{ + localIndex phaseIndex=-1; + // Find target phase index for phase rate constraint + for( integer ip = 0; ip < fluidModel.numFluidPhases(); ++ip ) + { + if( fluidModel.phaseNames()[ip] == inputPhase ) + { + phaseIndex = ip; + } + } + return phaseIndex; +} + +/** + * @class PhaseVolumeRateConstraint + * @brief This class describes a phase rate constraint used to control a well of WellConstraintType type (Injection or Production). + */ + +class PhaseVolumeRateConstraint : public WellConstraintBase +{ +public: + + + /** + * @name Constructor / Destructor + */ + ///@{ + + /** + * @brief Constructor for WellControls Objects. + * @param[in] name the name of this instantiation of WellControls in the repository + * @param[in] parent the parent group of this instantiation of WellControls + */ + explicit PhaseVolumeRateConstraint( string const & name, dataRepository::Group * const parent ); + + /** + * @brief Default destructor. + */ + ~PhaseVolumeRateConstraint() override; + + /** + * @brief Deleted default constructor. + */ + PhaseVolumeRateConstraint() = delete; + + /** + * @brief Deleted copy constructor. + */ + PhaseVolumeRateConstraint( PhaseVolumeRateConstraint const & ) = delete; + + /** + * @brief Deleted move constructor. + */ + PhaseVolumeRateConstraint( PhaseVolumeRateConstraint && ) = delete; + + /** + * @brief Deleted assignment operator. + * @return a reference to a constraint object + */ + PhaseVolumeRateConstraint & operator=( PhaseVolumeRateConstraint const & ) = delete; + + /** + * @brief Deleted move operator. + * @return a reference to a constraint object + */ + PhaseVolumeRateConstraint & operator=( PhaseVolumeRateConstraint && ) = delete; + + ///@} + + /** + * @brief name of the node manager in the object catalog + * @return string that contains the catalog name to generate a new Constraint object through the object catalog. + */ + static string catalogName() + { + return "PhaseVolumeRateConstraint"; + } + + /** + * @name Getters / Setters + */ + ///@{ + + // Temp interface - tjb + virtual ConstraintTypeId getControl() const override { return ConstraintTypeId::PHASEVOLRATE; }; + + /** + * @brief Get the target phase name + * @return the target phase name + */ + const string & getPhaseName() const { return m_phaseName; } + + /** + * @brief Get the target phase index + * @return the target phase index + */ + const localIndex & getPhaseIndex() const { return m_phaseIndex; } + + ///@} + + struct viewKeyStruct + { + /// String key for the well target phase rate + static constexpr char const * phaseRateString() { return "phaseRate"; } + /// String key for the well target phase name + static constexpr char const * phaseNameString() { return "phaseName"; } + }; + + /** + * @brief Validate phase type is consistent with fluidmodel + */ + template< typename T > void validatePhaseType( T const & fluidModel ); + ///@} + + virtual bool checkViolation( WellConstraintBase const & currentConstraint, real64 const & currentTime ) const override; +protected: + + virtual void postInputInitialization() override; + +private: + + /// Name of the targeted phase + string m_phaseName; + + /// Index of the target phase, used to impose the phase rate constraint + localIndex m_phaseIndex; + +}; + +template< typename T > +void PhaseVolumeRateConstraint::validatePhaseType( T const & fluidModel ) +{ + // Find target phase index for phase rate constraint + m_phaseIndex = getPhaseIndexFromFluidModel( fluidModel, this->template getReference< string >( viewKeyStruct::phaseNameString())); + + GEOS_THROW_IF( m_phaseIndex == -1, + "PhaseVolumeRateConstraint " << this->template getReference< string >( viewKeyStruct::phaseNameString()) << + ": Invalid phase type for simulation fluid model", + InputError ); +} + +} //namespace geos + +#endif //GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_WELLPHASEVOLUMERATECONSTRAINT_HPP diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/WellProductionConstraint.cpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellProductionConstraint.cpp new file mode 100644 index 00000000000..4ec3ec3299d --- /dev/null +++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellProductionConstraint.cpp @@ -0,0 +1,70 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * SPDX-License-Identifier: LGPL-2.1-only + * + * Copyright (c) 2016-2024 Lawrence Livermore National Security LLC + * Copyright (c) 2018-2024 TotalEnergies + * Copyright (c) 2018-2024 The Board of Trustees of the Leland Stanford Junior University + * Copyright (c) 2023-2024 Chevron + * Copyright (c) 2019- GEOS/GEOSX Contributors + * All rights reserved + * + * See top level LICENSE, COPYRIGHT, CONTRIBUTORS, NOTICE, and ACKNOWLEDGEMENTS files for details. + * ------------------------------------------------------------------------------------------------------------ + */ + +/* + * @file WellProductionConstraint.cpp + */ + +#include "LogLevelsInfo.hpp" +#include "WellProductionConstraint.hpp" +#include "WellConstants.hpp" +#include "dataRepository/InputFlags.hpp" +#include "functions/FunctionManager.hpp" + +#include "WellLiquidRateConstraint.hpp" +#include "WellMassRateConstraint.hpp" +#include "WellPhaseVolumeRateConstraint.hpp" +#include "WellVolumeRateConstraint.hpp" + +namespace geos +{ + +template< typename ConstraintRateType > +ProductionConstraint< ConstraintRateType >::ProductionConstraint( string const & name, Group * const parent ) + : ConstraintRateType( name, parent ) +{ + // set rate sign for producers (base class member) + this->m_rateSign = -1.0; +} +template< typename ConstraintRateType > +ProductionConstraint< ConstraintRateType >::~ProductionConstraint() +{} + +template< typename ConstraintRateType > +void ProductionConstraint< ConstraintRateType >::postInputInitialization() +{ + // Validate value and table options + ConstraintRateType::postInputInitialization(); + +} +// Register concrete wrapper constraint types and instantiate templates. + +template class ProductionConstraint< LiquidRateConstraint >; +using ProductionLiquidRateConstraint = ProductionConstraint< LiquidRateConstraint >; +REGISTER_CATALOG_ENTRY( WellConstraintBase, ProductionLiquidRateConstraint, string const &, Group * const ) + +template class ProductionConstraint< MassRateConstraint >; +using ProductionMassRateConstraint = ProductionConstraint< MassRateConstraint >; +REGISTER_CATALOG_ENTRY( WellConstraintBase, ProductionMassRateConstraint, string const &, Group * const ) + +template class ProductionConstraint< PhaseVolumeRateConstraint >; +using ProductionPhaseVolumeRateConstraint = ProductionConstraint< PhaseVolumeRateConstraint >; +REGISTER_CATALOG_ENTRY( WellConstraintBase, ProductionPhaseVolumeRateConstraint, string const &, Group * const ) + +template class ProductionConstraint< VolumeRateConstraint >; +using ProductionVolumeRateConstraint = ProductionConstraint< VolumeRateConstraint >; +REGISTER_CATALOG_ENTRY( WellConstraintBase, ProductionVolumeRateConstraint, string const &, Group * const ) + +} //namespace geos diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/WellProductionConstraint.hpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellProductionConstraint.hpp new file mode 100644 index 00000000000..ce1ca16de14 --- /dev/null +++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellProductionConstraint.hpp @@ -0,0 +1,106 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * SPDX-License-Identifier: LGPL-2.1-only + * + * Copyright (c) 2016-2024 Lawrence Livermore National Security LLC + * Copyright (c) 2018-2024 TotalEnergies + * Copyright (c) 2018-2024 The Board of Trustees of the Leland Stanford Junior University + * Copyright (c) 2023-2024 Chevron + * Copyright (c) 2019- GEOS/GEOSX Contributors + * All rights reserved + * + * See top level LICENSE, COPYRIGHT, CONTRIBUTORS, NOTICE, and ACKNOWLEDGEMENTS files for details. + * ------------------------------------------------------------------------------------------------------------ + */ + +/* + * @file WellProductionConstraints.hpp + */ + + +#ifndef GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_WELLPRODUCTIONCONSTRAINT_HPP +#define GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_WELLPRODUCTIONCONSTRAINT_HPP + +#include "common/format/EnumStrings.hpp" +#include "dataRepository/Group.hpp" +#include "functions/TableFunction.hpp" + +namespace geos +{ +using namespace dataRepository; +/** + * @class ProductionConstraint + * @brief This class describes constraint used to control a production well. + */ + +template< typename ConstraintType > +class ProductionConstraint : public ConstraintType +{ +public: + /** + * @name Constructor / Destructor + */ + ///@{ + + /** + * @brief Constructor for WellControls Objects. + * @param[in] name the name of this instantiation of WellControls in the repository + * @param[in] parent the parent group of this instantiation of WellControls + */ + explicit ProductionConstraint( string const & name, dataRepository::Group * const parent ); + + /** + * @brief Default destructor. + */ + ~ProductionConstraint() override; + + /** + * @brief Deleted default constructor. + */ + ProductionConstraint() = delete; + + /** + * @brief Deleted copy constructor. + */ + ProductionConstraint( ProductionConstraint const & ) = delete; + + /** + * @brief Deleted move constructor. + */ + ProductionConstraint( ProductionConstraint && ) = delete; + + /** + * @brief Deleted assignment operator. + * @return a reference to a constraint object + */ + ProductionConstraint & operator=( ProductionConstraint const & ) = delete; + + /** + * @brief Deleted move operator. + * @return a reference to a constraint object + */ + ProductionConstraint & operator=( ProductionConstraint && ) = delete; + + ///@} + + /** + * @brief name of the node manager in the object catalog + * @return string that contains the catalog name to generate a new Constraint object through the object catalog. + */ + static string catalogName() + { + return "Production"+ConstraintType::catalogName(); + } + virtual string getCatalogName() const override { return catalogName(); } +protected: + + virtual void postInputInitialization() override; + + static bool isViolated( const real64 & currentValue, const real64 & constraintValue ) + { return currentValue < constraintValue; } +}; + + +} //namespace geos + +#endif //GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_WELLPRODUCTIONCONSTRAINT_HPP diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/WellSolverBase.cpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellSolverBase.cpp index daeeffef3c1..79470d0865a 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/wells/WellSolverBase.cpp +++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellSolverBase.cpp @@ -43,7 +43,9 @@ WellSolverBase::WellSolverBase( string const & name, m_numDofPerResElement( 0 ), m_isThermal( 0 ), m_ratesOutputDir( joinPath( OutputBase::getOutputDirectory(), name + "_rates" ) ), - m_keepVariablesConstantDuringInitStep( false ) + m_keepVariablesConstantDuringInitStep( false ), + + m_useNewCode( true ) { registerWrapper( viewKeyStruct::isThermalString(), &m_isThermal ). setApplyDefaultValue( 0 ). @@ -63,6 +65,12 @@ WellSolverBase::WellSolverBase( string const & name, setInputFlag( dataRepository::InputFlags::OPTIONAL ). setDescription( "Choose time step to honor rates/bhp tables time intervals" ); + this->registerWrapper( viewKeyStruct::useNewCodeString(), &m_useNewCode ). + setApplyDefaultValue( 1 ). + setInputFlag( InputFlags::OPTIONAL ). + setDescription( "Use new code" ); + + addLogLevel< logInfo::WellControl >(); } @@ -294,8 +302,73 @@ void WellSolverBase::implicitStepSetup( real64 const & time_n, setPerforationStatus( time_n, domain ); // Initialize the primary and secondary variables for the first time step + if( !m_useNewCode ) + { + initializeWells( domain, time_n ); + } + +} + +void WellSolverBase::selectWellConstraint( real64 const & time_n, + real64 const & dt, + const integer coupledIterationNumber, + DomainPartition & domain ) +{ + GEOS_MARK_FUNCTION; + GEOS_UNUSED_VAR( dt ); + GEOS_UNUSED_VAR( coupledIterationNumber ); + + forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const & meshBodyName, + MeshLevel & meshLevel, + string_array const & regionNames ) + { + GEOS_UNUSED_VAR( meshBodyName ); + ElementRegionManager & elementRegionManager = meshLevel.getElemManager(); + elementRegionManager.forElementRegions< WellElementRegion >( regionNames, + [&]( localIndex const, + WellElementRegion & region ) + { + WellElementSubRegion & subRegion = region.getGroup( ElementRegionBase::viewKeyStruct::elementSubRegions() ) + .getGroup< WellElementSubRegion >( region.getSubRegionName() ); + WellControls & wellControls = getWellControls( subRegion ); + if( wellControls.isWellOpen() ) + { + if( !wellControls.getWellState() ) + { + wellControls.setWellState( 1 ); + + initializeWell( domain, meshLevel, subRegion, time_n ); + } + } + else + { + wellControls.setWellState( 0 ); + } + + + if( wellControls.getWellState()) + { + wellControls.setConstraintSwitch( false ); + + 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 = wellControls.getControl(); + MpiWrapper::broadcast( wellControl, owner ); + wellControls.setControl( wellControl ); + } + } ); + } ); - initializeWells( domain, time_n ); } void WellSolverBase::updateState( DomainPartition & domain ) @@ -319,13 +392,43 @@ void WellSolverBase::assembleSystem( real64 const time, CRSMatrixView< real64, globalIndex const > const & localMatrix, arrayView1d< real64 > const & localRhs ) { - string const wellDofKey = dofManager.getKey( wellElementDofName()); + + if( m_useNewCode ) + { + // selects constraints one of 2 ways + // wellEstimator flag set to 0 => orginal logic rates are computed during update state and constraints are selected every newton + // iteration + // wellEstimator flag > 0 => well esitmator solved for each constraint and then selects the constraint + // => estimator solve only performed first "wellEstimator" iterations + NonlinearSolverParameters const & nonlinearParams = getNonlinearSolverParameters(); + selectWellConstraint( time, dt, nonlinearParams.m_numNewtonIterations, domain ); + } + // assemble the accumulation term in the mass balance equations assembleAccumulationTerms( time, dt, domain, dofManager, localMatrix, localRhs ); // then assemble the pressure relations between well elements assemblePressureRelations( time, dt, domain, dofManager, localMatrix, localRhs ); + //if( false && m_useNewCode ) + { + forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &, + MeshLevel & mesh, + string_array const & regionNames ) + { + ElementRegionManager & elementRegionManager = mesh.getElemManager(); + elementRegionManager.forElementRegions< WellElementRegion >( regionNames, + [&]( localIndex const, + WellElementRegion & region ) + { + WellElementSubRegion & subRegion = region.getGroup( ElementRegionBase::viewKeyStruct::elementSubRegions() ) + .getGroup< WellElementSubRegion >( region.getSubRegionName() ); + WellControls & wellControls = getWellControls( subRegion ); + if( !wellControls.getConstraintSwitch() ) + assembleWellConstraintTerms( time, dt, subRegion, dofManager, localMatrix.toViewConstSizes(), localRhs ); + } ); + } ); + } // then compute the perforation rates (later assembled by the coupled solver) computePerforationRates( time, dt, domain ); @@ -387,8 +490,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 a8159378b50..ae1ff3194db 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/wells/WellSolverBase.hpp +++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellSolverBase.hpp @@ -159,6 +159,12 @@ class WellSolverBase : public PhysicsSolverBase virtual void registerDataOnMesh( Group & meshBodies ) override; + void selectWellConstraint( real64 const & time_n, + real64 const & dt, + integer const coupledIterationNumber, + DomainPartition & domain ); + + virtual void setupDofs( DomainPartition const & domain, DofManager & dofManager ) const override; @@ -180,15 +186,6 @@ class WellSolverBase : public PhysicsSolverBase /**@}*/ - /** - * @brief function to assemble the linear system matrix and rhs - * @param time the time at the beginning of the step - * @param dt the desired timestep - * @param domain the domain partition - * @param dofManager degree-of-freedom manager associated with the linear system - * @param matrix the system matrix - * @param rhs the system right-hand side vector - */ virtual void assembleSystem( real64 const time, real64 const dt, DomainPartition & domain, @@ -196,6 +193,14 @@ class WellSolverBase : public PhysicsSolverBase CRSMatrixView< real64, globalIndex const > const & localMatrix, arrayView1d< real64 > const & localRhs ) override; + virtual void assembleWellFluxTerms( real64 const & GEOS_UNUSED_PARAM( time ), + real64 const & GEOS_UNUSED_PARAM( dt ), + WellElementSubRegion const & GEOS_UNUSED_PARAM( subRegion ), + DofManager const & GEOS_UNUSED_PARAM( dofManager ), + CRSMatrixView< real64, globalIndex const > const & GEOS_UNUSED_PARAM( localMatrix ), + arrayView1d< real64 > const & GEOS_UNUSED_PARAM( localRhs ) ) = 0; + + /** * @brief assembles the flux terms for all connections between well elements * @param time_n previous time value @@ -212,6 +217,16 @@ class WellSolverBase : public PhysicsSolverBase CRSMatrixView< real64, globalIndex const > const & localMatrix, arrayView1d< real64 > const & localRhs ) = 0; + + + virtual void assembleWellAccumulationTerms( real64 const & time, + real64 const & dt, + WellElementSubRegion & subRegion, + DofManager const & dofManager, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) = 0; + + /** * @brief assembles the accumulation term for all the well elements * @param domain the physical domain object @@ -226,8 +241,22 @@ class WellSolverBase : public PhysicsSolverBase CRSMatrixView< real64, globalIndex const > const & localMatrix, arrayView1d< real64 > const & localRhs ) = 0; + virtual void assembleWellConstraintTerms( real64 const & GEOS_UNUSED_PARAM( time ), + real64 const & GEOS_UNUSED_PARAM( dt ), + WellElementSubRegion const & GEOS_UNUSED_PARAM( subRegion ), + DofManager const & GEOS_UNUSED_PARAM( dofManager ), + CRSMatrixView< real64, globalIndex const > const & GEOS_UNUSED_PARAM( localMatrix ), + arrayView1d< real64 > const & GEOS_UNUSED_PARAM( localRhs ) ) = 0; + + virtual void assembleWellPressureRelations ( real64 const & GEOS_UNUSED_PARAM( time ), + real64 const & GEOS_UNUSED_PARAM( dt ), + WellElementSubRegion const & GEOS_UNUSED_PARAM( subRegion ), + DofManager const & GEOS_UNUSED_PARAM( dofManager ), + CRSMatrixView< real64, globalIndex const > const & GEOS_UNUSED_PARAM( localMatrix ), + arrayView1d< real64 > const & GEOS_UNUSED_PARAM( localRhs ) ) = 0; /** - * @brief assembles the pressure relations at all connections between well elements except at the well head + * @brief assembles the pressure relations at all connections between well elements except at + * the well head * @param time_n time at the beginning of the time step * @param dt the time step size * @param domain the physical domain object @@ -243,17 +272,34 @@ class WellSolverBase : public PhysicsSolverBase arrayView1d< real64 > const & localRhs ) = 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 real64 updateWellState( WellElementSubRegion & subRegion ) = 0; virtual void updateState( DomainPartition & domain ) override; /** - * @brief Recompute all dependent quantities from primary variables (including constitutive models) - * @param subRegion the well subRegion containing the well elements and their associated fields + * @brief Initialize all the primary and secondary variables in all the wells + * @param domain the domain containing the well manager to access individual wells + */ + virtual void initializeWells( DomainPartition & domain, real64 const & time_n ) = 0; + virtual void initializeWell( DomainPartition & domain, MeshLevel & mesh, WellElementSubRegion & subRegion, real64 const & time_n ) = 0; + + /** + * @brief Recompute all dependent quantities from primary variables (including constitutive + * models) + * @param subRegion the well subRegion containing the well elements and their associated + * fields */ virtual real64 updateSubRegionState( WellElementSubRegion & subRegion ) = 0; + + virtual void computeWellPerforationRates( real64 const & GEOS_UNUSED_PARAM( time_n ), + real64 const & GEOS_UNUSED_PARAM( dt ), + ElementRegionManager const & GEOS_UNUSED_PARAM( elemManager ), + WellElementSubRegion & GEOS_UNUSED_PARAM( subRegion ) ){} + /** * @brief Recompute the perforation rates for all the wells * @param domain the domain containing the mesh and fields @@ -262,6 +308,67 @@ class WellSolverBase : public PhysicsSolverBase real64 const & dt, DomainPartition & domain ) = 0; + virtual real64 + calculateWellResidualNorm( real64 const & GEOS_UNUSED_PARAM( time_n ), + real64 const & GEOS_UNUSED_PARAM( dt ), + WellElementSubRegion const & GEOS_UNUSED_PARAM( subRegion ), + DofManager const & GEOS_UNUSED_PARAM( dofManager ), + arrayView1d< real64 const > const & GEOS_UNUSED_PARAM( localRhs ) ) = 0; + + virtual real64 + scalingForWellSystemSolution( ElementSubRegionBase & GEOS_UNUSED_PARAM( subRegion ), + DofManager const & GEOS_UNUSED_PARAM( dofManager ), + arrayView1d< real64 const > const & GEOS_UNUSED_PARAM( localSolution ) ) = 0; + + virtual bool + checkWellSystemSolution( ElementSubRegionBase & GEOS_UNUSED_PARAM( subRegion ), + DofManager const & GEOS_UNUSED_PARAM( dofManager ), + arrayView1d< real64 const > const & GEOS_UNUSED_PARAM( localSolution ), + real64 const GEOS_UNUSED_PARAM( scalingFactor ) ) = 0; + virtual void + applyWellSystemSolution( DofManager const & GEOS_UNUSED_PARAM( dofManager ), + arrayView1d< real64 const > const & GEOS_UNUSED_PARAM( localSolution ), + real64 const GEOS_UNUSED_PARAM( scalingFactor ), + real64 const GEOS_UNUSED_PARAM( dt ), + DomainPartition & GEOS_UNUSED_PARAM( domain ), + MeshLevel & GEOS_UNUSED_PARAM( mesh ), + WellElementSubRegion & GEOS_UNUSED_PARAM( subRegion ) ) = 0; + + /** + * @brief Function to perform line search + * @param time_n time at the beginning of the step + * @param dt the perscribed timestep + * @param cycleNumber the current cycle number + * @param domain the domain object + * @param dofManager degree-of-freedom manager associated with the linear system + * @param localMatrix the system matrix + * @param rhs the system right-hand side vector + * @param solution the solution vector + * @param scaleFactor the scaling factor to apply to the solution + * @param lastResidual (in) target value below which to reduce residual norm, (out) achieved + * residual norm + * @return return true if line search succeeded, false otherwise + * + * This function implements a nonlinear newton method for implicit problems. It requires that + * the + * other functions in the solver interface are implemented in the derived physics solver. The + * nonlinear loop includes a simple line search algorithm, and will cut the timestep if + * convergence is not achieved according to the parameters in linearSolverParameters member. + */ + bool + lineSearch1( real64 const & time_n, + real64 const & dt, + integer const cycleNumber, + DomainPartition & domain, + ElementRegionManager & elemManager, + WellElementSubRegion & subRegion, + MeshLevel & mesh, + DofManager const & dofManager, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + ParallelVector & rhs, + ParallelVector & solution, + real64 const scaleFactor, + real64 & lastResidual ); /** * @brief function to set the next time step size * @param[in] currentTime the current time @@ -274,9 +381,12 @@ class WellSolverBase : public PhysicsSolverBase DomainPartition & domain ) override; /** - * @brief Utility function to keep the well variables during a time step (used in poromechanics simulations) - * @param[in] keepVariablesConstantDuringInitStep flag to tell the solver to freeze its primary variables during a time step - * @detail This function is meant to be called by a specific task before/after the initialization step + * @brief Utility function to keep the well variables during a time step (used in + * poromechanics simulations) + * @param[in] keepVariablesConstantDuringInitStep flag to tell the solver to freeze its + * primary variables during a time step + * @detail This function is meant to be called by a specific task before/after the + * initialization step */ void setKeepVariablesConstantDuringInitStep( bool const keepVariablesConstantDuringInitStep ) { m_keepVariablesConstantDuringInitStep = keepVariablesConstantDuringInitStep; } @@ -286,6 +396,7 @@ class WellSolverBase : public PhysicsSolverBase static constexpr char const * isThermalString() { return "isThermal"; } static constexpr char const * writeCSVFlagString() { return "writeCSV"; } static constexpr char const * timeStepFromTablesFlagString() { return "timeStepFromTables"; } + static constexpr char const * useNewCodeString() { return "useNewCode"; } static constexpr char const * fluidNamesString() { return "fluidNames"; } }; @@ -306,11 +417,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 +433,10 @@ class WellSolverBase : public PhysicsSolverBase real64 const & dt, DomainPartition & domain ) = 0; + virtual bool evaluateConstraints( real64 const & time_n, + WellElementSubRegion & subRegion ) = 0; + + /// name of the flow solver string m_flowSolverName; @@ -357,6 +467,13 @@ 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; + + + integer m_useNewCode; + }; } diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/WellVolumeRateConstraint.cpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellVolumeRateConstraint.cpp new file mode 100644 index 00000000000..db11dd85ef2 --- /dev/null +++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellVolumeRateConstraint.cpp @@ -0,0 +1,80 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * SPDX-License-Identifier: LGPL-2.1-only + * + * Copyright (c) 2016-2024 Lawrence Livermore National Security LLC + * Copyright (c) 2018-2024 TotalEnergies + * Copyright (c) 2018-2024 The Board of Trustees of the Leland Stanford Junior University + * Copyright (c) 2023-2024 Chevron + * Copyright (c) 2019- GEOS/GEOSX Contributors + * All rights reserved + * + * See top level LICENSE, COPYRIGHT, CONTRIBUTORS, NOTICE, and ACKNOWLEDGEMENTS files for details. + * ------------------------------------------------------------------------------------------------------------ + */ + +/* + * @file WellVolumeRateConstraint.cpp + */ + +#include "LogLevelsInfo.hpp" +#include "WellVolumeRateConstraint.hpp" +#include "WellConstants.hpp" +#include "dataRepository/InputFlags.hpp" +#include "functions/FunctionManager.hpp" + +namespace geos +{ + +using namespace dataRepository; + +VolumeRateConstraint::VolumeRateConstraint( string const & name, Group * const parent ) + : WellConstraintBase( name, parent ) +{ + this->setInputFlags( InputFlags::OPTIONAL_NONUNIQUE ); + + this->registerWrapper( viewKeyStruct::volumeRateString(), &this->m_constraintValue ). + setDefaultValue( 0.0 ). + setInputFlag( InputFlags::OPTIONAL ). + setRestartFlags( RestartFlags::WRITE_AND_READ ). + setDescription( "Volumetric rate (if useSurfaceConditions: [surface m^3/s]; else [reservoir m^3/s])" ); + +} + +VolumeRateConstraint::~VolumeRateConstraint() +{} + + +void VolumeRateConstraint::postInputInitialization() +{ + // Validate table options + WellConstraintBase::postInputInitialization(); + + // check constraint value + GEOS_THROW_IF( m_constraintValue < 0, + getWrapperDataContext( viewKeyStruct::volumeRateString() ) << ": Target value is negative", + InputError ); + + GEOS_THROW_IF ((m_constraintValue <= 0.0 && m_constraintScheduleTableName.empty()), + getName() << " " << getDataContext() << ": You need to specify a volume rate constraint. \n" << + "The rate constraint can be specified using " << + "either " << viewKeyStruct::volumeRateString() << + " or " << WellConstraintBase::viewKeyStruct::constraintScheduleTableNameString(), + InputError ); +} + +bool VolumeRateConstraint::checkViolation( WellConstraintBase const & currentConstraint, real64 const & currentTime ) const +{ + real64 const currentValue = currentConstraint.totalVolumeRate(); + real64 const constraintValue = this->getConstraintValue( currentTime ); + if( this->m_rateSign < 0.0 ) + { + return currentValue < constraintValue; + } + else + { + return currentValue > constraintValue; + } +} + +} //namespace geos diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/WellVolumeRateConstraint.hpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellVolumeRateConstraint.hpp new file mode 100644 index 00000000000..1ed14597ca3 --- /dev/null +++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellVolumeRateConstraint.hpp @@ -0,0 +1,124 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * SPDX-License-Identifier: LGPL-2.1-only + * + * Copyright (c) 2016-2024 Lawrence Livermore National Security LLC + * Copyright (c) 2018-2024 TotalEnergies + * Copyright (c) 2018-2024 The Board of Trustees of the Leland Stanford Junior University + * Copyright (c) 2023-2024 Chevron + * Copyright (c) 2019- GEOS/GEOSX Contributors + * All rights reserved + * + * See top level LICENSE, COPYRIGHT, CONTRIBUTORS, NOTICE, and ACKNOWLEDGEMENTS files for details. + * ------------------------------------------------------------------------------------------------------------ + */ + +/* + * @file WellVolumeRateConstraints.hpp + */ + + +#ifndef GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_WELLTOTALVOLRATECONSTRAINTS_HPP +#define GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_WELLTOTALVOLRATECONSTRAINTS_HPP + +#include "common/format/EnumStrings.hpp" +#include "dataRepository/Group.hpp" +#include "functions/TableFunction.hpp" +#include "WellConstraintsBase.hpp" +namespace geos +{ + + + +/** + * @class VolumeRateConstraint + * @brief This class describes a volume rate constraint used to control a well. + */ + +class VolumeRateConstraint : public WellConstraintBase +{ +public: + + /** + * @name Constructor / Destructor + */ + ///@{ + + /** + * @brief Constructor for WellControls Objects. + * @param[in] name the name of this instantiation of WellControls in the repository + * @param[in] parent the parent group of this instantiation of WellControls + */ + explicit VolumeRateConstraint( string const & name, dataRepository::Group * const parent ); + + + /** + * @brief Default destructor. + */ + ~VolumeRateConstraint() override; + + /** + * @brief Deleted default constructor. + */ + VolumeRateConstraint() = delete; + + /** + * @brief Deleted copy constructor. + */ + VolumeRateConstraint( VolumeRateConstraint const & ) = delete; + + /** + * @brief Deleted move constructor. + */ + VolumeRateConstraint( VolumeRateConstraint && ) = delete; + + /** + * @brief Deleted assignment operator. + * @return a reference to a constraint object + */ + VolumeRateConstraint & operator=( VolumeRateConstraint const & ) = delete; + + /** + * @brief Deleted move operator. + * @return a reference to a constraint object + */ + VolumeRateConstraint & operator=( VolumeRateConstraint && ) = delete; + + /** + * @brief name of the node manager in the object catalog + * @return string that contains the catalog name to generate a new Constraint object through the object catalog. + */ + static string catalogName() + { + return "VolumeRateConstraint"; + } + ///@} + /** + * @brief Struct to serve as a container for variable strings and keys. + * @struct viewKeyStruct + */ + struct viewKeyStruct + { + /// String key for the volume rate + static constexpr char const * volumeRateString() { return "volumeRate"; } + }; + /** + * @name Getters / Setters + */ + ///@{ + + // Temp interface - tjb + virtual ConstraintTypeId getControl() const override { return ConstraintTypeId::TOTALVOLRATE; }; + ///@} + + virtual bool checkViolation( WellConstraintBase const & currentConstraint, real64 const & currentTime ) const override; +protected: + + virtual void postInputInitialization() override; + +}; + + +} //namespace geos + +#endif //GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_WELLCONSTRAINT_HPP diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/CompositionalMultiphaseWellConstraintKernels.hpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/CompositionalMultiphaseWellConstraintKernels.hpp new file mode 100644 index 00000000000..485545cd2d9 --- /dev/null +++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/CompositionalMultiphaseWellConstraintKernels.hpp @@ -0,0 +1,555 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * SPDX-License-Identifier: LGPL-2.1-only + * + * Copyright (c) 2016-2024 Lawrence Livermore National Security LLC + * Copyright (c) 2018-2024 TotalEnergies + * Copyright (c) 2018-2024 The Board of Trustees of the Leland Stanford Junior University + * Copyright (c) 2023-2024 Chevron + * Copyright (c) 2019- GEOS/GEOSX Contributors + * All rights reserved + * + * See top level LICENSE, COPYRIGHT, CONTRIBUTORS, NOTICE, and ACKNOWLEDGEMENTS files for details. + * ------------------------------------------------------------------------------------------------------------ + */ + +/** + * @file CompositionalMultiphaseWellConstraintKernels.hpp + */ + +#ifndef GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_WELLCONSTRAINTKERNELS_HPP +#define GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_WELLCONSTRAINTKERNELS_HPP + +#include "codingUtilities/Utilities.hpp" +#include "constitutive/fluid/multifluid/MultiFluidBase.hpp" +#include "constitutive/fluid/multifluid/MultiFluidFields.hpp" + + +#include "physicsSolvers/fluidFlow/wells/WellControls.hpp" +#include "physicsSolvers/fluidFlow/wells/WellBHPConstraints.hpp" +#include "physicsSolvers/fluidFlow/wells/WellVolumeRateConstraint.hpp" +#include "physicsSolvers/fluidFlow/wells/WellPhaseVolumeRateConstraint.hpp" +#include "physicsSolvers/fluidFlow/wells/WellMassRateConstraint.hpp" +#include "physicsSolvers/fluidFlow/wells/WellLiquidRateConstraint.hpp" +namespace geos +{ + +namespace wellConstraintKernels +{ + +/******************************** ControlEquationHelper ********************************/ +//template< integer NC, integer IS_THERMAL, typname S, typename T > +//struct ConstraintHelper< NC, IS_THERMAL > {}; + +template< integer NC, integer IS_THERMAL, typename CONSTRAINT = BHPConstraint > +struct ConstraintHelper +{ + static void assembleConstraintEquation( real64 const & time_n, + WellControls & wellControls, + BHPConstraint & constraint, + WellElementSubRegion const & subRegion, + string const & wellDofKey, + localIndex const & rankOffset, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) + { + // subRegion data + localIndex const iwelemRef = subRegion.getTopWellElementIndex(); + arrayView1d< globalIndex const > const & wellElemDofNumber = subRegion.getReference< array1d< globalIndex > >( wellDofKey ); + arrayView1d< real64 const > const & pres = subRegion.getField< fields::well::pressure >(); + arrayView1d< real64 const > const & totalMassDens = subRegion.getField< fields::well::totalMassDensity >(); + arrayView2d< real64 const > const & dTotalMassDens = subRegion.getField< fields::well::dTotalMassDensity >(); + arrayView1d< real64 const > const wellElemGravCoef = subRegion.getField< fields::well::gravityCoefficient >(); + + // setup row/column indices for constraint equation + using COFFSET_WJ = compositionalMultiphaseWellKernels::ColOffset_WellJac< NC, IS_THERMAL >; + using WJ_ROFFSET = compositionalMultiphaseWellKernels::RowOffset_WellJac< NC, IS_THERMAL >; + using Deriv = constitutive::multifluid::DerivativeOffset; + + localIndex const eqnRowIndex = wellElemDofNumber[iwelemRef] + WJ_ROFFSET::CONTROL - rankOffset; + globalIndex dofColIndices[COFFSET_WJ::nDer]{}; + for( integer ic = 0; ic < COFFSET_WJ::nDer; ++ic ) + { + dofColIndices[ ic ] = wellElemDofNumber[iwelemRef] + ic; + } + + // constraint data + real64 const & targetBHP = constraint.getConstraintValue( time_n ); + real64 const & refGravCoef = constraint.getReferenceGravityCoef(); + + // current constraint value + real64 const & currentBHP = + wellControls.getReference< real64 >( CompositionalMultiphaseWell::viewKeyStruct::currentBHPString() ); + + // residual + real64 controlEqn = currentBHP - targetBHP; + + // setup Jacobian terms + real64 dControlEqn[NC+2+IS_THERMAL]{}; + + // bring everything back to host, capture the scalars by reference + forAll< serialPolicy >( 1, [pres, + totalMassDens, + dTotalMassDens, + wellElemGravCoef, + &dControlEqn, + &iwelemRef, + &refGravCoef] ( localIndex const ) + { + real64 const diffGravCoef = refGravCoef - wellElemGravCoef[iwelemRef]; + dControlEqn[COFFSET_WJ::dP] = 1 + dTotalMassDens[iwelemRef][Deriv::dP] * diffGravCoef; + for( integer ic = 0; ic < NC; ++ic ) + { + dControlEqn[COFFSET_WJ::dC+ic] = dTotalMassDens[iwelemRef][Deriv::dC+ic] * diffGravCoef; + } + if constexpr ( IS_THERMAL ) + { + dControlEqn[COFFSET_WJ::dT] = dTotalMassDens[iwelemRef][Deriv::dT] * diffGravCoef; + } + } ); + + // add solver matrices + localRhs[eqnRowIndex] += controlEqn; + localMatrix.addToRowBinarySearchUnsorted< serialAtomic >( eqnRowIndex, + dofColIndices, + dControlEqn, + COFFSET_WJ::nDer ); + } + + + template< template< typename U > class T, typename U=PhaseVolumeRateConstraint > + static void assembleConstraintEquation( real64 const & time_n, + WellControls & wellControls, + T< PhaseVolumeRateConstraint > & constraint, + WellElementSubRegion const & subRegion, + string const & wellDofKey, + localIndex const & rankOffset, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) + { + // subRegion data + + localIndex const iwelemRef = subRegion.getTopWellElementIndex(); + arrayView1d< globalIndex const > const & wellElemDofNumber = subRegion.getReference< array1d< globalIndex > >( wellDofKey ); + arrayView1d< real64 const > const & connRate = subRegion.getField< fields::well::mixtureConnectionRate >(); + arrayView2d< real64 const, compflow::USD_COMP > const & compFrac = subRegion.getField< fields::well::globalCompFraction >(); + arrayView3d< real64 const, compflow::USD_COMP_DC > const & dCompFrac_dCompDens = subRegion.getField< fields::well::dGlobalCompFraction_dGlobalCompDensity >(); + + // setup row/column indices for constraint equation + using COFFSET_WJ = compositionalMultiphaseWellKernels::ColOffset_WellJac< NC, IS_THERMAL >; + using WJ_ROFFSET = compositionalMultiphaseWellKernels::RowOffset_WellJac< NC, IS_THERMAL >; + using Deriv = constitutive::multifluid::DerivativeOffset; + + localIndex const eqnRowIndex = wellElemDofNumber[iwelemRef] + WJ_ROFFSET::CONTROL - rankOffset; + globalIndex dofColIndices[COFFSET_WJ::nDer]{}; + for( integer ic = 0; ic < COFFSET_WJ::nDer; ++ic ) + { + dofColIndices[ ic ] = wellElemDofNumber[iwelemRef] + ic; + } + + // fluid data + constitutive::MultiFluidBase & fluidSeparator = wellControls.getMultiFluidSeparator(); + + arrayView3d< real64 const, constitutive::multifluid::USD_PHASE > const & phaseFrac = fluidSeparator.phaseFraction(); + arrayView4d< real64 const, constitutive::multifluid::USD_PHASE_DC > const & dPhaseFrac = fluidSeparator.dPhaseFraction(); + arrayView3d< real64 const, constitutive::multifluid::USD_PHASE > const & phaseDens = fluidSeparator.phaseDensity(); + arrayView4d< real64 const, constitutive::multifluid::USD_PHASE_DC > const & dPhaseDens = fluidSeparator.dPhaseDensity(); + + // constraint data + integer ip = getPhaseIndexFromFluidModel( fluidSeparator, constraint.getPhaseName()); + real64 const & targetPhaseRate = constraint.getConstraintValue( time_n ); + + // current constraint value + arrayView1d< real64 > const & currentPhaseVolRate = + wellControls.getReference< array1d< real64 > >( CompositionalMultiphaseWell::viewKeyStruct::currentPhaseVolRateString() ); + integer const useSurfaceConditions = wellControls.useSurfaceConditions(); + + // residual + real64 controlEqn = currentPhaseVolRate[ip] - targetPhaseRate; + + // setup Jacobian terms + real64 dControlEqn[NC+2+IS_THERMAL]{}; + + // bring everything back to host, capture the scalars by reference + forAll< serialPolicy >( 1, [&ip, + connRate, + phaseDens, + dPhaseDens, + phaseFrac, + dPhaseFrac, + compFrac, + dCompFrac_dCompDens, + &dControlEqn, + &useSurfaceConditions, + &iwelemRef] ( localIndex const ) + { + // skip the rest of this function if phase ip is absent + bool const phaseExists = (phaseFrac[iwelemRef][0][ip] > 0); + if( phaseExists ) + { + stackArray1d< real64, NC > work( NC ); + real64 const currentTotalRate = connRate[iwelemRef]; + + real64 const phaseDensInv = 1.0 / phaseDens[iwelemRef][0][ip]; + real64 const phaseFracTimesPhaseDensInv = phaseFrac[iwelemRef][0][ip] * phaseDensInv; + real64 const dPhaseFracTimesPhaseDensInv_dPres = dPhaseFrac[iwelemRef][0][ip][Deriv::dP] * phaseDensInv + - dPhaseDens[iwelemRef][0][ip][Deriv::dP] * phaseFracTimesPhaseDensInv * phaseDensInv; + + // divide the total mass/molar rate by the (phase density * phase fraction) to get the phase volumetric rate + dControlEqn[COFFSET_WJ::dP] = ( useSurfaceConditions == 0 ) * currentTotalRate * dPhaseFracTimesPhaseDensInv_dPres; + dControlEqn[COFFSET_WJ::dQ] = phaseFracTimesPhaseDensInv; + if constexpr (IS_THERMAL ) + { + real64 const dPhaseFracTimesPhaseDensInv_dTemp = dPhaseFrac[iwelemRef][0][ip][Deriv::dT] * phaseDensInv + - dPhaseDens[iwelemRef][0][ip][Deriv::dT] * phaseFracTimesPhaseDensInv * phaseDensInv; + dControlEqn[COFFSET_WJ::dT] = ( useSurfaceConditions == 0 ) * currentTotalRate * dPhaseFracTimesPhaseDensInv_dTemp; + } + + for( integer ic = 0; ic < NC; ++ic ) + { + dControlEqn[COFFSET_WJ::dC+ic] = -phaseFracTimesPhaseDensInv * dPhaseDens[iwelemRef][0][ip][Deriv::dC+ic] * phaseDensInv; + dControlEqn[COFFSET_WJ::dC+ic] += dPhaseFrac[iwelemRef][0][ip][Deriv::dC+ic] * phaseDensInv; + dControlEqn[COFFSET_WJ::dC+ic] *= currentTotalRate; + } + applyChainRuleInPlace( NC, dCompFrac_dCompDens[iwelemRef], &dControlEqn[COFFSET_WJ::dC], work.data() ); + + } + } ); + + // add solver matrices + localRhs[eqnRowIndex] += controlEqn; + localMatrix.addToRowBinarySearchUnsorted< serialAtomic >( eqnRowIndex, + dofColIndices, + dControlEqn, + COFFSET_WJ::nDer ); + } + + template< template< typename U > class T, typename U=LiquidRateConstraint > + static void assembleConstraintEquation( real64 const & time_n, + WellControls & wellControls, + T< LiquidRateConstraint > & constraint, + WellElementSubRegion const & subRegion, + string const & wellDofKey, + localIndex const & rankOffset, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) + { + // subRegion data + + localIndex const iwelemRef = subRegion.getTopWellElementIndex(); + arrayView1d< globalIndex const > const & wellElemDofNumber = subRegion.getReference< array1d< globalIndex > >( wellDofKey ); + arrayView1d< real64 const > const & connRate = subRegion.getField< fields::well::mixtureConnectionRate >(); + arrayView2d< real64 const, compflow::USD_COMP > const & compFrac = subRegion.getField< fields::well::globalCompFraction >(); + arrayView3d< real64 const, compflow::USD_COMP_DC > const & dCompFrac_dCompDens = subRegion.getField< fields::well::dGlobalCompFraction_dGlobalCompDensity >(); + + // setup row/column indices for constraint equation + using COFFSET_WJ = compositionalMultiphaseWellKernels::ColOffset_WellJac< NC, IS_THERMAL >; + using WJ_ROFFSET = compositionalMultiphaseWellKernels::RowOffset_WellJac< NC, IS_THERMAL >; + using Deriv = constitutive::multifluid::DerivativeOffset; + + localIndex const eqnRowIndex = wellElemDofNumber[iwelemRef] + WJ_ROFFSET::CONTROL - rankOffset; + globalIndex dofColIndices[COFFSET_WJ::nDer]{}; + for( integer ic = 0; ic < COFFSET_WJ::nDer; ++ic ) + { + dofColIndices[ ic ] = wellElemDofNumber[iwelemRef] + ic; + } + + // fluid data + constitutive::MultiFluidBase & fluidSeparator = wellControls.getMultiFluidSeparator(); + + arrayView3d< real64 const, constitutive::multifluid::USD_PHASE > const & phaseFrac = fluidSeparator.phaseFraction(); + arrayView4d< real64 const, constitutive::multifluid::USD_PHASE_DC > const & dPhaseFrac = fluidSeparator.dPhaseFraction(); + arrayView3d< real64 const, constitutive::multifluid::USD_PHASE > const & phaseDens = fluidSeparator.phaseDensity(); + arrayView4d< real64 const, constitutive::multifluid::USD_PHASE_DC > const & dPhaseDens = fluidSeparator.dPhaseDensity(); + + // constraint data + real64 const & targetPhaseRate = constraint.getConstraintValue( time_n ); + const array1d< int > & phaseIndices = constraint.getPhaseIndices(); + + + // current constraint value + arrayView1d< real64 > const & currentPhaseVolRate = + wellControls.getReference< array1d< real64 > >( CompositionalMultiphaseWell::viewKeyStruct::currentPhaseVolRateString() ); + integer const useSurfaceConditions = wellControls.useSurfaceConditions(); + + // residual + real64 controlEqn = -targetPhaseRate; + for( integer ip= 0; ip < phaseIndices.size(); ++ip ) + { + controlEqn += currentPhaseVolRate[phaseIndices[ip]]; + } + + // setup Jacobian terms + real64 dControlEqn[NC+2+IS_THERMAL]{}; + + // bring everything back to host, capture the scalars by reference + forAll< serialPolicy >( 1, [phaseIndices, + connRate, + phaseDens, + dPhaseDens, + phaseFrac, + dPhaseFrac, + compFrac, + dCompFrac_dCompDens, + &dControlEqn, + &useSurfaceConditions, + &iwelemRef] ( localIndex const ) + { + + stackArray1d< real64, NC > work( NC ); + for( integer i= 0; i < phaseIndices.size(); ++i ) + { + integer ip = phaseIndices[i]; + bool const phaseExists = (phaseFrac[iwelemRef][0][ip] > 0); + // skip the rest of this function if phase ip is absent + if( phaseExists ) + { + + real64 const currentTotalRate = connRate[iwelemRef]; + + real64 const phaseDensInv = 1.0 / phaseDens[iwelemRef][0][ip]; + real64 const phaseFracTimesPhaseDensInv = phaseFrac[iwelemRef][0][ip] * phaseDensInv; + real64 const dPhaseFracTimesPhaseDensInv_dPres = dPhaseFrac[iwelemRef][0][ip][Deriv::dP] * phaseDensInv + - dPhaseDens[iwelemRef][0][ip][Deriv::dP] * phaseFracTimesPhaseDensInv * phaseDensInv; + + // divide the total mass/molar rate by the (phase density * phase fraction) to get the phase volumetric rate + dControlEqn[COFFSET_WJ::dP] += ( useSurfaceConditions == 0 ) * currentTotalRate * dPhaseFracTimesPhaseDensInv_dPres; + dControlEqn[COFFSET_WJ::dQ] += phaseFracTimesPhaseDensInv; + if constexpr (IS_THERMAL ) + { + real64 const dPhaseFracTimesPhaseDensInv_dTemp = dPhaseFrac[iwelemRef][0][ip][Deriv::dT] * phaseDensInv + - dPhaseDens[iwelemRef][0][ip][Deriv::dT] * phaseFracTimesPhaseDensInv * phaseDensInv; + dControlEqn[COFFSET_WJ::dT] += ( useSurfaceConditions == 0 ) * currentTotalRate * dPhaseFracTimesPhaseDensInv_dTemp; + } + + for( integer ic = 0; ic < NC; ++ic ) + { + real64 temp = -phaseFracTimesPhaseDensInv * dPhaseDens[iwelemRef][0][ip][Deriv::dC+ic] * phaseDensInv; + temp += dPhaseFrac[iwelemRef][0][ip][Deriv::dC+ic] * phaseDensInv; + temp *= currentTotalRate; + dControlEqn[COFFSET_WJ::dC+ic] += temp; + } + } + } + applyChainRuleInPlace( NC, dCompFrac_dCompDens[iwelemRef], &dControlEqn[COFFSET_WJ::dC], work.data() ); + } ); + + // add solver matrices + localRhs[eqnRowIndex] += controlEqn; + localMatrix.addToRowBinarySearchUnsorted< serialAtomic >( eqnRowIndex, + dofColIndices, + dControlEqn, + COFFSET_WJ::nDer ); + } + template< template< typename U > class T, typename U=VolumeRateConstraint > + static void assembleConstraintEquation( real64 const & time_n, + WellControls & wellControls, + T< VolumeRateConstraint > & constraint, + WellElementSubRegion const & subRegion, + string const & wellDofKey, + localIndex const & rankOffset, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) + { + // subRegion data + + localIndex const iwelemRef = subRegion.getTopWellElementIndex(); + arrayView1d< globalIndex const > const & wellElemDofNumber = subRegion.getReference< array1d< globalIndex > >( wellDofKey ); + arrayView1d< real64 const > const & connRate = subRegion.getField< fields::well::mixtureConnectionRate >(); + arrayView2d< real64 const, compflow::USD_COMP > const & compFrac = subRegion.getField< fields::well::globalCompFraction >(); + arrayView3d< real64 const, compflow::USD_COMP_DC > const & dCompFrac_dCompDens = subRegion.getField< fields::well::dGlobalCompFraction_dGlobalCompDensity >(); + + // setup row/column indices for constraint equation + using COFFSET_WJ = compositionalMultiphaseWellKernels::ColOffset_WellJac< NC, IS_THERMAL >; + using WJ_ROFFSET = compositionalMultiphaseWellKernels::RowOffset_WellJac< NC, IS_THERMAL >; + using Deriv = constitutive::multifluid::DerivativeOffset; + + localIndex const eqnRowIndex = wellElemDofNumber[iwelemRef] + WJ_ROFFSET::CONTROL - rankOffset; + globalIndex dofColIndices[COFFSET_WJ::nDer]{}; + for( integer ic = 0; ic < COFFSET_WJ::nDer; ++ic ) + { + dofColIndices[ ic ] = wellElemDofNumber[iwelemRef] + ic; + } + + // fluid data + constitutive::MultiFluidBase & fluidSeparator = wellControls.getMultiFluidSeparator(); + + arrayView2d< real64 const, constitutive::multifluid::USD_FLUID > const & totalDens = fluidSeparator.totalDensity(); + arrayView3d< real64 const, constitutive::multifluid::USD_FLUID_DC > const & dTotalDens = fluidSeparator.dTotalDensity(); + + // constraint data + real64 const & targetTotalVolRate = constraint.getConstraintValue( time_n ); + + // current constraint value + real64 const & currentTotalVolRate = + wellControls.getReference< real64 >( CompositionalMultiphaseWell::viewKeyStruct::currentTotalVolRateString() ); + integer const useSurfaceConditions = wellControls.useSurfaceConditions(); + + // residual + real64 controlEqn = currentTotalVolRate - targetTotalVolRate; + + // setup Jacobian terms + real64 dControlEqn[NC+2+IS_THERMAL]{}; + + // bring everything back to host, capture the scalars by reference + forAll< serialPolicy >( 1, [connRate, + totalDens, + dTotalDens, + compFrac, + dCompFrac_dCompDens, + &dControlEqn, + &useSurfaceConditions, + &iwelemRef] ( localIndex const ) + { + stackArray1d< real64, NC > work( NC ); + + real64 const currentTotalRate = connRate[iwelemRef]; + + // compute the inverse of the total density and derivatives + + real64 const totalDensInv = 1.0 / totalDens[iwelemRef][0]; + + stackArray1d< real64, NC > dTotalDensInv_dCompDens( NC ); + for( integer ic = 0; ic < NC; ++ic ) + { + dTotalDensInv_dCompDens[ic] = -dTotalDens[iwelemRef][0][Deriv::dC+ic] * totalDensInv * totalDensInv; + } + applyChainRuleInPlace( NC, dCompFrac_dCompDens[iwelemRef], dTotalDensInv_dCompDens, work.data() ); + + // Step 2.2: divide the total mass/molar rate by the total density to get the total volumetric rate + + // Compute derivatives dP dT + real64 const dTotalDensInv_dPres = -dTotalDens[iwelemRef][0][Deriv::dP] * totalDensInv * totalDensInv; + dControlEqn[COFFSET_WJ::dP] = ( useSurfaceConditions == 0 ) * currentTotalRate * dTotalDensInv_dPres; + if constexpr ( IS_THERMAL ) + { + dControlEqn[COFFSET_WJ::dT] = ( useSurfaceConditions == 0 ) * currentTotalRate * -dTotalDens[iwelemRef][0][Deriv::dT] * totalDensInv * totalDensInv; + } + + dControlEqn[COFFSET_WJ::dQ] = totalDensInv; + for( integer ic = 0; ic < NC; ++ic ) + { + dControlEqn[COFFSET_WJ::dC+ic] = currentTotalRate * dTotalDensInv_dCompDens[ic]; + } + + } ); + + // add solver matrices + localRhs[eqnRowIndex] += controlEqn; + localMatrix.addToRowBinarySearchUnsorted< serialAtomic >( eqnRowIndex, + dofColIndices, + dControlEqn, + COFFSET_WJ::nDer ); + } + template< template< typename U > class T, typename U=MassRateConstraint > + static void assembleConstraintEquation( real64 const & time_n, + WellControls & wellControls, + T< MassRateConstraint > & constraint, + WellElementSubRegion const & subRegion, + string const & wellDofKey, + localIndex const & rankOffset, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) + { + // subRegion data + + localIndex const iwelemRef = subRegion.getTopWellElementIndex(); + arrayView1d< globalIndex const > const & wellElemDofNumber = subRegion.getReference< array1d< globalIndex > >( wellDofKey ); + arrayView1d< real64 const > const & connRate = subRegion.getField< fields::well::mixtureConnectionRate >(); + arrayView2d< real64 const, compflow::USD_COMP > const & compFrac = subRegion.getField< fields::well::globalCompFraction >(); + arrayView3d< real64 const, compflow::USD_COMP_DC > const & dCompFrac_dCompDens = subRegion.getField< fields::well::dGlobalCompFraction_dGlobalCompDensity >(); + + // setup row/column indices for constraint equation + using COFFSET_WJ = compositionalMultiphaseWellKernels::ColOffset_WellJac< NC, IS_THERMAL >; + using WJ_ROFFSET = compositionalMultiphaseWellKernels::RowOffset_WellJac< NC, IS_THERMAL >; + using Deriv = constitutive::multifluid::DerivativeOffset; + + localIndex const eqnRowIndex = wellElemDofNumber[iwelemRef] + WJ_ROFFSET::CONTROL - rankOffset; + globalIndex dofColIndices[COFFSET_WJ::nDer]{}; + for( integer ic = 0; ic < COFFSET_WJ::nDer; ++ic ) + { + dofColIndices[ ic ] = wellElemDofNumber[iwelemRef] + ic; + } + + // fluid data + constitutive::MultiFluidBase & fluidSeparator = wellControls.getMultiFluidSeparator(); + arrayView2d< real64 const, constitutive::multifluid::USD_FLUID > const & totalDens = fluidSeparator.totalDensity(); + arrayView3d< real64 const, constitutive::multifluid::USD_FLUID_DC > const & dTotalDens = fluidSeparator.dTotalDensity(); + + // constraint data + real64 const & targetMassRate = constraint.getConstraintValue( time_n ); + + // current constraint value + real64 const & massDensity = + wellControls.getReference< real64 >( CompositionalMultiphaseWell::viewKeyStruct::massDensityString() ); + + // fix to use stored massrate + real64 const & currentTotalVolRate = + wellControls.getReference< real64 >( CompositionalMultiphaseWell::viewKeyStruct::currentTotalVolRateString() ); + integer const useSurfaceConditions = wellControls.useSurfaceConditions(); + + // residual + real64 controlEqn = massDensity*currentTotalVolRate - targetMassRate; + + // setup Jacobian terms + real64 dControlEqn[NC+2+IS_THERMAL]{}; + + // bring everything back to host, capture the scalars by reference + forAll< serialPolicy >( 1, [connRate, + massDensity, + totalDens, + dTotalDens, + compFrac, + dCompFrac_dCompDens, + &dControlEqn, + &useSurfaceConditions, + &iwelemRef] ( localIndex const ) + { + stackArray1d< real64, NC > work( NC ); + + real64 const currentTotalRate = connRate[iwelemRef]; + + // compute the inverse of the total density and derivatives + + real64 const totalDensInv = 1.0 / totalDens[iwelemRef][0]; + + stackArray1d< real64, NC > dTotalDensInv_dCompDens( NC ); + for( integer ic = 0; ic < NC; ++ic ) + { + dTotalDensInv_dCompDens[ic] = -dTotalDens[iwelemRef][0][Deriv::dC+ic] * totalDensInv * totalDensInv; + } + applyChainRuleInPlace( NC, dCompFrac_dCompDens[iwelemRef], dTotalDensInv_dCompDens, work.data() ); + + // Step 2.2: divide the total mass/molar rate by the total density to get the total volumetric rate + + // Compute derivatives dP dT + real64 const dTotalDensInv_dPres = -dTotalDens[iwelemRef][0][Deriv::dP] * totalDensInv * totalDensInv; + dControlEqn[COFFSET_WJ::dP] = ( useSurfaceConditions == 0 )*massDensity * currentTotalRate * dTotalDensInv_dPres; + if constexpr ( IS_THERMAL ) + { + dControlEqn[COFFSET_WJ::dT] = ( useSurfaceConditions == 0 ) * massDensity* currentTotalRate * -dTotalDens[iwelemRef][0][Deriv::dT] * totalDensInv * totalDensInv; + } + + dControlEqn[COFFSET_WJ::dQ] = massDensity*totalDensInv; + for( integer ic = 0; ic < NC; ++ic ) + { + dControlEqn[COFFSET_WJ::dC+ic] = massDensity* currentTotalRate * dTotalDensInv_dCompDens[ic]; + } + + } ); + + // add solver matrices + localRhs[eqnRowIndex] += controlEqn; + localMatrix.addToRowBinarySearchUnsorted< serialAtomic >( eqnRowIndex, + dofColIndices, + dControlEqn, + COFFSET_WJ::nDer ); + } +}; + + +} // end namespace wellConstraintKernels + +} // end namespace geos + +#endif //GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_WELLCONSTRAINTKERNELS_HPP diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/CompositionalMultiphaseWellKernels.cpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/CompositionalMultiphaseWellKernels.cpp index ee04ffa8c73..5c2d4589705 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/CompositionalMultiphaseWellKernels.cpp +++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/CompositionalMultiphaseWellKernels.cpp @@ -37,19 +37,19 @@ GEOS_HOST_DEVICE inline void ControlEquationHelper:: - switchControl( bool const isProducer, - WellControls::Control const & inputControl, - WellControls::Control const & currentControl, - integer const phasePhaseIndex, - real64 const & targetBHP, - real64 const & targetPhaseRate, - real64 const & targetTotalRate, - real64 const & targetMassRate, - real64 const & currentBHP, - arrayView1d< real64 const > const & currentPhaseVolRate, - real64 const & currentTotalVolRate, - real64 const & currentMassRate, - WellControls::Control & newControl ) + selectLimitingConstraint( bool const isProducer, + WellControls::Control const & inputControl, + WellControls::Control const & currentControl, + integer const phasePhaseIndex, + real64 const & targetBHP, + real64 const & targetPhaseRate, + real64 const & targetTotalRate, + real64 const & targetMassRate, + real64 const & currentBHP, + arrayView1d< real64 const > const & currentPhaseVolRate, + real64 const & currentTotalVolRate, + real64 const & currentMassRate, + WellControls::Control & newControl ) { // if isViable is true at the end of the following checks, no need to switch bool controlIsViable = false; @@ -140,6 +140,7 @@ ControlEquationHelper:: real64 const & targetPhaseRate, real64 const & targetTotalRate, real64 const & targetMassRate, + real64 const & targetValue, real64 const & currentBHP, arrayView1d< real64 const > const & dCurrentBHP, arrayView1d< real64 const > const & currentPhaseVolRate, @@ -152,7 +153,7 @@ ControlEquationHelper:: CRSMatrixView< real64, globalIndex const > const & localMatrix, arrayView1d< real64 > const & localRhs ) { - + GEOS_UNUSED_VAR( targetValue ); // tjb keeping this around if needed to compare with old constraint eqn jacgen using COFFSET_WJ = compositionalMultiphaseWellKernels::ColOffset_WellJac< NC, IS_THERMAL >; using Deriv = multifluid::DerivativeOffset; @@ -300,11 +301,6 @@ void PressureRelationKernel:: launch( localIndex const size, globalIndex const rankOffset, - bool const isLocallyOwned, - localIndex const iwelemControl, - integer const targetPhaseIndex, - WellControls const & wellControls, - real64 const & time, arrayView1d< integer const > const elemStatus, arrayView1d< globalIndex const > const & wellElemDofNumber, arrayView1d< real64 const > const & wellElemGravCoef, @@ -317,36 +313,6 @@ PressureRelationKernel:: arrayView1d< real64 > const & localRhs ) { using COFFSET_WJ = compositionalMultiphaseWellKernels::ColOffset_WellJac< NC, IS_THERMAL >; - // static well control data - bool const isProducer = wellControls.isProducer(); - WellControls::Control const currentControl = wellControls.getControl(); - WellControls::Control const inputControl = wellControls.getInputControl(); - real64 const targetBHP = wellControls.getTargetBHP( time ); - real64 const targetTotalRate = wellControls.getTargetTotalRate( time ); - real64 const targetPhaseRate = wellControls.getTargetPhaseRate( time ); - real64 const targetMassRate = wellControls.getTargetMassRate( time ); - - // dynamic well control data - real64 const & currentBHP = - wellControls.getReference< real64 >( CompositionalMultiphaseWell::viewKeyStruct::currentBHPString() ); - arrayView1d< real64 const > const & dCurrentBHP = - wellControls.getReference< array1d< real64 > >( CompositionalMultiphaseWell::viewKeyStruct::dCurrentBHPString() ); - - arrayView1d< real64 const > const & currentPhaseVolRate = - wellControls.getReference< array1d< real64 > >( CompositionalMultiphaseWell::viewKeyStruct::currentPhaseVolRateString() ); - arrayView2d< real64 const > const & dCurrentPhaseVolRate = - wellControls.getReference< array2d< real64 > >( CompositionalMultiphaseWell::viewKeyStruct::dCurrentPhaseVolRateString() ); - - real64 const & currentTotalVolRate = - wellControls.getReference< real64 >( CompositionalMultiphaseWell::viewKeyStruct::currentTotalVolRateString() ); - arrayView1d< real64 const > const & dCurrentTotalVolRate = - wellControls.getReference< array1d< real64 > >( CompositionalMultiphaseWell::viewKeyStruct::dCurrentTotalVolRateString() ); - - real64 const & currentMassRate = - wellControls.getReference< real64 >( CompositionalMultiphaseWell::viewKeyStruct::currentMassRateString() ); - - real64 const & massDensity = - wellControls.getReference< real64 >( CompositionalMultiphaseWell::viewKeyStruct::massDensityString() ); RAJA::ReduceMax< parallelDeviceReduce, localIndex > switchControl( 0 ); @@ -360,48 +326,7 @@ PressureRelationKernel:: localIndex const iwelemNext = nextWellElemIndex[iwelem]; - if( iwelemNext < 0 && isLocallyOwned ) // if iwelemNext < 0, form control equation - { - - WellControls::Control newControl = currentControl; - ControlEquationHelper::switchControl( isProducer, - inputControl, - currentControl, - targetPhaseIndex, - targetBHP, - targetPhaseRate, - targetTotalRate, - targetMassRate, - currentBHP, - currentPhaseVolRate, - currentTotalVolRate, - currentMassRate, - newControl ); - if( currentControl != newControl ) - { - switchControl.max( 1 ); - } - ControlEquationHelper::compute< NC, IS_THERMAL >( rankOffset, - newControl, - targetPhaseIndex, - targetBHP, - targetPhaseRate, - targetTotalRate, - targetMassRate, - currentBHP, - dCurrentBHP, - currentPhaseVolRate, - dCurrentPhaseVolRate, - currentTotalVolRate, - dCurrentTotalVolRate, - massDensity, - wellElemDofNumber[iwelemControl], - localMatrix, - localRhs ); - // TODO: for consistency, we should assemble here, not in compute... - - } - else if( iwelemNext >= 0 ) // if iwelemNext >= 0, form momentum equation + if( iwelemNext >= 0 ) // if iwelemNext >= 0, form momentum equation { real64 localPresRel = 0; @@ -455,11 +380,6 @@ PressureRelationKernel:: void PressureRelationKernel:: \ launch< NC, IS_THERMAL >( localIndex const size, \ globalIndex const rankOffset, \ - bool const isLocallyOwned, \ - localIndex const iwelemControl, \ - integer const targetPhaseIndex, \ - WellControls const & wellControls, \ - real64 const & time, \ arrayView1d< integer const > const elemStatus, \ arrayView1d< globalIndex const > const & wellElemDofNumber, \ arrayView1d< real64 const > const & wellElemGravCoef, \ @@ -716,62 +636,74 @@ CompDensInitializationKernel:: void RateInitializationKernel:: launch( localIndex const subRegionSize, - integer const targetPhaseIndex, WellControls const & wellControls, real64 const & time, arrayView3d< real64 const, multifluid::USD_PHASE > const & phaseDens, arrayView2d< real64 const, multifluid::USD_FLUID > const & totalDens, arrayView1d< real64 > const & connRate ) { - WellControls::Control const control = wellControls.getControl(); - bool const isProducer = wellControls.isProducer(); - real64 const targetTotalRate = wellControls.getTargetTotalRate( time ); - real64 const targetPhaseRate = wellControls.getTargetPhaseRate( time ); - real64 const targetMassRate = wellControls.getTargetMassRate( time ); - - // Estimate the connection rates - forAll< parallelDevicePolicy<> >( subRegionSize, [=] GEOS_HOST_DEVICE ( localIndex const iwelem ) + if( wellControls.isProducer() ) { - if( control == WellControls::Control::BHP ) + std::vector< WellConstraintBase * > const constraints = wellControls.getProdRateConstraints(); + // Use first rate constraint to set initial connection rates + real64 const constraintVal = constraints[0]->getConstraintValue( time ); + ConstraintTypeId const controlType = constraints[0]->getControl(); + if( controlType == ConstraintTypeId::PHASEVOLRATE ) { - // if BHP constraint set rate below the absolute max rate - // with the appropriate sign (negative for prod, positive for inj) - if( isProducer ) + integer const targetPhaseIndex = wellControls.getConstraintPhaseIndex(); + + forAll< parallelDevicePolicy<> >( subRegionSize, [=] GEOS_HOST_DEVICE ( localIndex const iwelem ) { - connRate[iwelem] = LvArray::math::max( 0.1 * targetPhaseRate * phaseDens[iwelem][0][targetPhaseIndex], -1e3 ); - } - else + connRate[iwelem] = LvArray::math::max( 0.1 * constraintVal * phaseDens[iwelem][0][targetPhaseIndex], -1e3 ); + } ); + } + else if( controlType == ConstraintTypeId::TOTALVOLRATE ) + { + forAll< parallelDevicePolicy<> >( subRegionSize, [=] GEOS_HOST_DEVICE ( localIndex const iwelem ) { - if( isZero( targetMassRate ) ) - { - connRate[iwelem] = LvArray::math::min( 0.1 * targetTotalRate * totalDens[iwelem][0], 1e3 ); - } - else - { - connRate[iwelem] = targetMassRate; - } - - } + connRate[iwelem] = LvArray::math::max( 0.1 * constraintVal * totalDens[iwelem][0], -1e3 ); + } ); } - else if( control == WellControls::Control::MASSRATE ) + else if( controlType == ConstraintTypeId::MASSRATE ) { - connRate[iwelem] = targetMassRate; + forAll< parallelDevicePolicy<> >( subRegionSize, [=] GEOS_HOST_DEVICE ( localIndex const iwelem ) + { + connRate[iwelem] = constraintVal; + } ); } - else + } + else + { + std::vector< WellConstraintBase * > const constraints = wellControls.getInjRateConstraints(); + // Use first rate constraint to set initial connection rates + real64 const constraintVal = constraints[0]->getConstraintValue( time ); + ConstraintTypeId const controlType = constraints[0]->getControl(); + if( controlType == ConstraintTypeId::PHASEVOLRATE ) { - if( isProducer ) + integer const targetPhaseIndex = wellControls.getConstraintPhaseIndex(); + + forAll< parallelDevicePolicy<> >( subRegionSize, [=] GEOS_HOST_DEVICE ( localIndex const iwelem ) { - connRate[iwelem] = targetPhaseRate * phaseDens[iwelem][0][targetPhaseIndex]; - } - else + connRate[iwelem] = LvArray::math::max( 0.1 * constraintVal * phaseDens[iwelem][0][targetPhaseIndex], 1e3 ); + } ); + } + else if( controlType == ConstraintTypeId::TOTALVOLRATE ) + { + forAll< parallelDevicePolicy<> >( subRegionSize, [=] GEOS_HOST_DEVICE ( localIndex const iwelem ) { - connRate[iwelem] = targetTotalRate * totalDens[iwelem][0]; - } + connRate[iwelem] = constraintVal * totalDens[iwelem][0]; + } ); } - } ); + else if( controlType == ConstraintTypeId::MASSRATE ) + { + forAll< parallelDevicePolicy<> >( subRegionSize, [=] GEOS_HOST_DEVICE ( localIndex const iwelem ) + { + connRate[iwelem] = constraintVal; + } ); + } + } } - } // 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..b01d8adf0ab 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/CompositionalMultiphaseWellKernels.hpp +++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/CompositionalMultiphaseWellKernels.hpp @@ -40,6 +40,7 @@ #include "physicsSolvers/fluidFlow/wells/CompositionalMultiphaseWellFields.hpp" #include "physicsSolvers/fluidFlow/wells/WellControls.hpp" #include "physicsSolvers/fluidFlow/wells/WellSolverBaseFields.hpp" +#include "physicsSolvers/fluidFlow/wells/WellPhaseVolumeRateConstraint.hpp" namespace geos { @@ -134,19 +135,19 @@ struct ControlEquationHelper inline static void - switchControl( bool const isProducer, - WellControls::Control const & inputControl, - WellControls::Control const & currentControl, - integer const phasePhaseIndex, - real64 const & targetBHP, - real64 const & targetPhaseRate, - real64 const & targetTotalRate, - real64 const & targetMassRate, - real64 const & currentBHP, - arrayView1d< real64 const > const & currentPhaseVolRate, - real64 const & currentTotalVolRate, - real64 const & currentMassRate, - WellControls::Control & newControl ); + selectLimitingConstraint( bool const isProducer, + WellControls::Control const & inputControl, + WellControls::Control const & currentControl, + integer const phasePhaseIndex, + real64 const & targetBHP, + real64 const & targetPhaseRate, + real64 const & targetTotalRate, + real64 const & targetMassRate, + real64 const & currentBHP, + arrayView1d< real64 const > const & currentPhaseVolRate, + real64 const & currentTotalVolRate, + real64 const & currentMassRate, + WellControls::Control & newControl ); template< integer NC, integer IS_THERMAL > GEOS_HOST_DEVICE @@ -160,6 +161,7 @@ struct ControlEquationHelper real64 const & targetTotalRate, real64 const & targetMassRate, real64 const & currentBHP, + real64 const & targetValue, arrayView1d< real64 const > const & dCurrentBHP, arrayView1d< real64 const > const & currentPhaseVolRate, arrayView2d< real64 const > const & dCurrentPhaseVolRate, @@ -200,11 +202,6 @@ struct PressureRelationKernel static void launch( localIndex const size, globalIndex const rankOffset, - bool const isLocallyOwned, - localIndex const iwelemControl, - integer const targetPhaseIndex, - WellControls const & wellControls, - real64 const & time, arrayView1d< integer const > const elemStatus, arrayView1d< globalIndex const > const & wellElemDofNumber, arrayView1d< real64 const > const & wellElemGravCoef, @@ -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,41 @@ 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 ) + { + 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 ); + + // 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 = -1; + m_constraintValue = wellControls.getInjRateConstraints()[0]->getConstraintValue( time ); + + } + + + } GEOS_HOST_DEVICE virtual void computeLinf( localIndex const iwelem, @@ -561,17 +577,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 +602,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 +627,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 +674,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 +690,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 +715,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 +729,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 +744,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 ); } diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/PerforationFluxKernels.hpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/PerforationFluxKernels.hpp index 18a48ee1e63..e8862990a2d 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/PerforationFluxKernels.hpp +++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/PerforationFluxKernels.hpp @@ -569,7 +569,7 @@ class PerforationFluxKernelFactory string const flowSolverName, PerforationData * const perforationData, ElementSubRegionBase const & subRegion, - ElementRegionManager & elemManager, + ElementRegionManager const & elemManager, bool const isInjector, bool const isCrossflowEnabled ) { @@ -848,7 +848,7 @@ class PerforationFluxKernelFactory PerforationData * const perforationData, ElementSubRegionBase const & subRegion, MultiFluidBase const & fluid, - ElementRegionManager & elemManager, + ElementRegionManager const & elemManager, bool const isInjector, bool const isCrossflowEnabled ) { diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/SinglePhaseWellConstraintKernels.hpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/SinglePhaseWellConstraintKernels.hpp new file mode 100644 index 00000000000..a7f7af725ce --- /dev/null +++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/SinglePhaseWellConstraintKernels.hpp @@ -0,0 +1,195 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * SPDX-License-Identifier: LGPL-2.1-only + * + * Copyright (c) 2016-2024 Lawrence Livermore National Security LLC + * Copyright (c) 2018-2024 TotalEnergies + * Copyright (c) 2018-2024 The Board of Trustees of the Leland Stanford Junior University + * Copyright (c) 2023-2024 Chevron + * Copyright (c) 2019- GEOS/GEOSX Contributors + * All rights reserved + * + * See top level LICENSE, COPYRIGHT, CONTRIBUTORS, NOTICE, and ACKNOWLEDGEMENTS files for details. + * ------------------------------------------------------------------------------------------------------------ + */ + +/** + * @file SinglePhaseWellConstraintKernels.hpp + */ + +#ifndef GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_SINGLEPHASEWELLCONSTRAINTKERNELS_HPP +#define GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_SINGLEPHASEWELLCONSTRAINTKERNELS_HPP + +#include "codingUtilities/Utilities.hpp" +#include "constitutive/fluid/singlefluid/SingleFluidBase.hpp" +#include "constitutive/fluid/singlefluid/SingleFluidFields.hpp" + + +#include "physicsSolvers/fluidFlow/wells/WellControls.hpp" +#include "physicsSolvers/fluidFlow/wells/WellBHPConstraints.hpp" +#include "physicsSolvers/fluidFlow/wells/WellVolumeRateConstraint.hpp" + + +namespace geos +{ + +namespace singlePhaseWellConstraintKernels +{ + +/******************************** ControlEquationHelper ********************************/ + + +template< integer IS_THERMAL > +struct ConstraintHelper +{ + static void assembleConstraintEquation( real64 const & time_n, + WellControls & wellControls, + BHPConstraint & constraint, + WellElementSubRegion const & subRegion, + string const & wellDofKey, + localIndex const & rankOffset, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) + { + // subRegion data + localIndex const iwelemRef = subRegion.getTopWellElementIndex(); + arrayView1d< globalIndex const > const & wellElemDofNumber = subRegion.getReference< array1d< globalIndex > >( wellDofKey ); + arrayView1d< real64 const > const & pres = subRegion.getField< fields::well::pressure >(); + + constitutive::SingleFluidBase & fluidSeparator = wellControls.getSingleFluidSeparator(); + arrayView2d< real64 const, constitutive::singlefluid::USD_FLUID > const & density = fluidSeparator.density(); + arrayView3d< real64 const, constitutive::singlefluid::USD_FLUID_DER > const & dDensity = fluidSeparator.dDensity(); + + arrayView1d< real64 const > const wellElemGravCoef = subRegion.getField< fields::well::gravityCoefficient >(); + + // setup row/column indices for constraint equation + using ROFFSET_WJ = singlePhaseWellKernels::RowOffset_WellJac< IS_THERMAL >; + using COFFSET_WJ = singlePhaseWellKernels::ColOffset_WellJac< IS_THERMAL >; + using Deriv = constitutive::singlefluid::DerivativeOffsetC< IS_THERMAL >; + + localIndex const eqnRowIndex = wellElemDofNumber[iwelemRef] + ROFFSET_WJ::CONTROL - rankOffset; + globalIndex dofColIndices[COFFSET_WJ::nDer]{}; + for( integer i = 0; i < COFFSET_WJ::nDer; ++i ) + { + dofColIndices[ i ] = wellElemDofNumber[iwelemRef] + i; + } + // constraint data + real64 const & targetBHP = constraint.getConstraintValue( time_n ); + real64 const & refGravCoef = constraint.getReferenceGravityCoef(); + + // current constraint value + real64 const & currentBHP = + wellControls.getReference< real64 >( SinglePhaseWell::viewKeyStruct::currentBHPString() ); + + // residual + real64 controlEqn = currentBHP - targetBHP; + + // setup Jacobian terms + real64 dControlEqn[2+IS_THERMAL]{}; + + // bring everything back to host, capture the scalars by reference + forAll< serialPolicy >( 1, [pres, + density, + dDensity, + wellElemGravCoef, + &dControlEqn, + &iwelemRef, + &refGravCoef] ( localIndex const ) + { + real64 const diffGravCoef = refGravCoef - wellElemGravCoef[iwelemRef]; + dControlEqn[COFFSET_WJ::dP] = 1.0 + dDensity[iwelemRef][0][Deriv::dP] *diffGravCoef; + if constexpr ( IS_THERMAL ) + { + dControlEqn[COFFSET_WJ::dT] = dDensity[iwelemRef][0][Deriv::dT] * diffGravCoef; + } + } ); + + // add solver matrices + localRhs[eqnRowIndex] += controlEqn; + localMatrix.addToRowBinarySearchUnsorted< serialAtomic >( eqnRowIndex, + dofColIndices, + dControlEqn, + COFFSET_WJ::nDer ); + } + template< template< typename U > class T, typename U=VolumeRateConstraint > + static void assembleConstraintEquation( real64 const & time_n, + WellControls & wellControls, + T< VolumeRateConstraint > & constraint, + WellElementSubRegion const & subRegion, + string const & wellDofKey, + localIndex const & rankOffset, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) + { + // subRegion data + + localIndex const iwelemRef = subRegion.getTopWellElementIndex(); + arrayView1d< globalIndex const > const & wellElemDofNumber = subRegion.getReference< array1d< globalIndex > >( wellDofKey ); + + + // setup row/column indices for constraint equation + using ROFFSET_WJ = singlePhaseWellKernels::RowOffset_WellJac< IS_THERMAL >; + using COFFSET_WJ = singlePhaseWellKernels::ColOffset_WellJac< IS_THERMAL >; + using Deriv = constitutive::singlefluid::DerivativeOffsetC< IS_THERMAL >; + + localIndex const eqnRowIndex = wellElemDofNumber[iwelemRef] + ROFFSET_WJ::CONTROL - rankOffset; + globalIndex dofColIndices[COFFSET_WJ::nDer]{}; + for( integer i = 0; i < COFFSET_WJ::nDer; ++i ) + { + dofColIndices[ i ] = wellElemDofNumber[iwelemRef] + i; + } + + // fluid data + constitutive::SingleFluidBase & fluidSeparator = wellControls.getSingleFluidSeparator(); + arrayView2d< real64 const, constitutive::singlefluid::USD_FLUID > const & density = fluidSeparator.density(); + arrayView3d< real64 const, constitutive::singlefluid::USD_FLUID_DER > const & dDensity = fluidSeparator.dDensity(); + + // constraint data + real64 const & targetVolRate = constraint.getConstraintValue( time_n ); + + // current constraint value + real64 & currentVolRate = + wellControls.getReference< real64 >( SinglePhaseWell::viewKeyStruct::currentVolRateString() ); + + integer const useSurfaceConditions = wellControls.useSurfaceConditions(); + + // residual + real64 controlEqn = currentVolRate - targetVolRate; + + // setup Jacobian terms + real64 dControlEqn[2+IS_THERMAL]{}; + + // bring everything back to host, capture the scalars by reference + forAll< serialPolicy >( 1, [currentVolRate, + density, + dDensity, + &dControlEqn, + &useSurfaceConditions, + &iwelemRef] ( localIndex const ) + { + // compute the inverse of the total density and derivatives + real64 const densInv = 1.0 / density[iwelemRef][0]; + + dControlEqn[COFFSET_WJ::dP] = -( useSurfaceConditions == 0 ) * dDensity[iwelemRef][0][Deriv::dP] * currentVolRate * densInv; + dControlEqn[COFFSET_WJ::dQ] = densInv; + if constexpr ( IS_THERMAL ) + { + dControlEqn[COFFSET_WJ::dT] = -( useSurfaceConditions == 0 ) * dDensity[iwelemRef][0][Deriv::dT] * currentVolRate * densInv; + } + + } ); + + // add solver matrices + localRhs[eqnRowIndex] += controlEqn; + localMatrix.addToRowBinarySearchUnsorted< serialAtomic >( eqnRowIndex, + dofColIndices, + dControlEqn, + COFFSET_WJ::nDer ); + } +}; + +} // end namespace wellConstraintKernels + +} // end namespace geos + +#endif //GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_WELLCONSTRAINTKERNELS_HPP diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/SinglePhaseWellKernels.cpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/SinglePhaseWellKernels.cpp index 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..dcb8b61f9be 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/SinglePhaseWellKernels.hpp +++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/SinglePhaseWellKernels.hpp @@ -20,18 +20,21 @@ #ifndef GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_SINGLEPHASEWELLKERNELS_HPP #define GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_SINGLEPHASEWELLKERNELS_HPP +#include "common/DataTypes.hpp" +#include "common/GEOS_RAJA_Interface.hpp" +#include "physicsSolvers/PhysicsSolverBaseKernels.hpp" + #include "constitutive/fluid/singlefluid/SingleFluidFields.hpp" #include "constitutive/fluid/singlefluid/SingleFluidBase.hpp" #include "constitutive/fluid/singlefluid/SingleFluidLayouts.hpp" #include "constitutive/fluid/singlefluid/SingleFluidLayouts.hpp" -#include "common/DataTypes.hpp" -#include "common/GEOS_RAJA_Interface.hpp" + #include "mesh/ElementRegionManager.hpp" #include "physicsSolvers/fluidFlow/FlowSolverBaseFields.hpp" #include "physicsSolvers/fluidFlow/StencilAccessors.hpp" #include "physicsSolvers/fluidFlow/wells/WellControls.hpp" #include "physicsSolvers/fluidFlow/wells/SinglePhaseWellFields.hpp" -#include "physicsSolvers/PhysicsSolverBaseKernels.hpp" + #include "physicsSolvers/KernelLaunchSelectors.hpp" namespace geos @@ -185,13 +188,9 @@ struct PressureRelationKernel using TAG = singlePhaseWellKernels::ElemTag; template< integer IS_THERMAL > - static localIndex + static void launch( localIndex const size, globalIndex const rankOffset, - bool const isLocallyOwned, - localIndex const iwelemControl, - WellControls const & wellControls, - real64 const & time, arrayView1d< globalIndex const > const & wellElemDofNumber, arrayView1d< real64 const > const & wellElemGravCoef, arrayView1d< localIndex const > const & nextWellElemIndex, @@ -811,12 +810,19 @@ class ResidualNormKernel : public physicsSolverBaseKernels::ResidualNormKernelBa m_isLocallyOwned( subRegion.isLocallyOwned() ), m_iwelemControl( subRegion.getTopWellElementIndex() ), m_currentControl( wellControls.getControl() ), - m_targetBHP( wellControls.getTargetBHP( time ) ), - m_targetRate( wellControls.getTargetTotalRate( time ) ), - m_targetMassRate( wellControls.getTargetMassRate( time ) ), + m_constraintValue ( wellControls.getCurrentConstraint()->getConstraintValue( time )), m_volume( subRegion.getElementVolume() ), m_density_n( fluid.density_n() ) - {} + { + if( wellControls.isProducer() ) + { + m_targetBHP = wellControls.getMinBHPConstraint()->getConstraintValue( time ); + } + else + { + m_targetBHP = wellControls.getMaxBHPConstraint()->getConstraintValue( time ); + } + } GEOS_HOST_DEVICE virtual void computeLinf( localIndex const iwelem, @@ -838,12 +844,12 @@ class ResidualNormKernel : public physicsSolverBaseKernels::ResidualNormKernelBa else if( m_currentControl == WellControls::Control::TOTALVOLRATE ) { // this residual entry is in volume / time units - normalizer = LvArray::math::max( LvArray::math::abs( m_targetRate ), m_minNormalizer ); + normalizer = LvArray::math::max( LvArray::math::abs( m_constraintValue ), m_minNormalizer ); } else if( m_currentControl == WellControls::Control::MASSRATE ) { // the residual entry is in volume / time units - normalizer = LvArray::math::max( LvArray::math::abs( m_targetMassRate ), m_minNormalizer ); + normalizer = LvArray::math::max( LvArray::math::abs( m_constraintValue ), m_minNormalizer ); } } // for the pressure difference equation, always normalize by the BHP @@ -856,7 +862,7 @@ class ResidualNormKernel : public physicsSolverBaseKernels::ResidualNormKernelBa else // SinglePhaseWell::RowOffset::MASSBAL { // this residual entry is in mass units - normalizer = m_dt * LvArray::math::abs( m_targetRate ) * m_density_n[iwelem][0]; + normalizer = m_dt * LvArray::math::abs( m_constraintValue ) * m_density_n[iwelem][0]; // to make sure that everything still works well if the rate is zero, we add this check normalizer = LvArray::math::max( normalizer, m_volume[iwelem] * m_density_n[iwelem][0] ); @@ -894,9 +900,9 @@ class ResidualNormKernel : public physicsSolverBaseKernels::ResidualNormKernelBa /// Controls WellControls::Control const m_currentControl; - real64 const m_targetBHP; - real64 const m_targetRate; - real64 const m_targetMassRate; + real64 const m_constraintValue; + real64 m_targetBHP; + /// View on the volume arrayView1d< real64 const > const m_volume; diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/ThermalCompositionalMultiphaseWellKernels.hpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/ThermalCompositionalMultiphaseWellKernels.hpp index 54b7819e8bf..a949203843a 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 { @@ -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,37 @@ 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_constraintValue ( wellControls.getCurrentConstraint()->getConstraintValue( 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 + m_targetBHP = wellControls.getMinBHPConstraint()->getConstraintValue( time ); + if( m_currentControl == WellControls::Control::PHASEVOLRATE ) + { + m_targetPhaseIndex = wellControls.getConstraintPhaseIndex(); + } + } + else + { + m_targetBHP = wellControls.getMaxBHPConstraint()->getConstraintValue( time ); + if( m_currentControl == WellControls::Control::PHASEVOLRATE ) + { + m_targetPhaseIndex = wellControls.getConstraintPhaseIndex(); + } + } + + } GEOS_HOST_DEVICE @@ -232,17 +249,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 +274,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 +299,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 +356,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 +372,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 const m_constraintValue; + real64 m_targetBHP; + /// View on the volume arrayView1d< real64 const > const m_volume; @@ -383,7 +399,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 +412,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 +432,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 ); } ); } @@ -645,8 +659,7 @@ 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(); 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/schema/schema.xsd b/src/coreComponents/schema/schema.xsd index e29fdc5fd0b..641e4b1ae98 100644 --- a/src/coreComponents/schema/schema.xsd +++ b/src/coreComponents/schema/schema.xsd @@ -222,18 +222,10 @@ - - - - - - - - @@ -1536,26 +1528,10 @@ stress - traction is applied to the faces as specified by the inner product of i - - - - - - - - - - - - - - - - @@ -1564,18 +1540,6 @@ stress - traction is applied to the faces as specified by the inner product of i - - - - - - - - - - - - @@ -3532,7 +3496,44 @@ When set to `all` output both convergence & iteration information to a csv.--> - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -3573,6 +3574,8 @@ Information output from lower logLevels is added with the desired log level + + @@ -3589,6 +3592,17 @@ When set to `all` output both convergence & iteration information to a csv.--> + + + + + + + + + + + - Injector pressure at reference depth initialized as: (1+initialPressureCoefficient)*reservoirPressureAtClosestPerforation + density*g*( zRef - zPerf ) - Producer pressure at reference depth initialized as: (1-initialPressureCoefficient)*reservoirPressureAtClosestPerforation + density*g*( zRef - zPerf ) --> - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -3655,6 +3642,109 @@ See note on referenceReservoirRegion for reservoir condition options--> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -4060,7 +4150,7 @@ Information output from lower logLevels is added with the desired log level - + @@ -4089,7 +4179,7 @@ When set to `all` output both convergence & iteration information to a csv.--> - + @@ -5358,7 +5448,44 @@ When set to `all` output both convergence & iteration information to a csv.--> - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -5387,6 +5514,8 @@ Information output from lower logLevels is added with the desired log level + + diff --git a/src/coreComponents/schema/schema.xsd.other b/src/coreComponents/schema/schema.xsd.other index 33a2ec00f2c..a427cacdbd4 100644 --- a/src/coreComponents/schema/schema.xsd.other +++ b/src/coreComponents/schema/schema.xsd.other @@ -341,15 +341,11 @@ - - - - @@ -854,9 +850,29 @@ + + + + + + + + + + + + + + + + + + + + @@ -3423,7 +3439,7 @@ - + @@ -3451,7 +3467,7 @@ - + @@ -3470,11 +3486,11 @@ - + - + @@ -3484,7 +3500,7 @@ - + @@ -3494,11 +3510,11 @@ - + - + @@ -3508,7 +3524,7 @@ - + @@ -3518,7 +3534,7 @@ - + @@ -3528,7 +3544,7 @@ - + @@ -3552,7 +3568,7 @@ - + @@ -3570,7 +3586,7 @@ - + @@ -3582,7 +3598,7 @@ - + @@ -3594,7 +3610,7 @@ - + @@ -3602,11 +3618,11 @@ - + - + @@ -3629,7 +3645,7 @@ - + @@ -3655,7 +3671,7 @@ - + @@ -3676,7 +3692,7 @@ - + @@ -3706,7 +3722,7 @@ - + @@ -3720,7 +3736,7 @@ - + @@ -3751,7 +3767,7 @@ - + @@ -3796,7 +3812,7 @@ - +