diff --git a/.github/workflows/ci_tests.yml b/.github/workflows/ci_tests.yml index 622294f4bbf..896de338ccd 100644 --- a/.github/workflows/ci_tests.yml +++ b/.github/workflows/ci_tests.yml @@ -166,6 +166,12 @@ jobs: ENABLE_HYPRE: OFF ENABLE_TRILINOS: ON GEOS_ENABLE_BOUNDS_CHECK: ON + RUNS_ON: streak2 + NPROC: 8 + DOCKER_RUN_ARGS: "--cpus=8 --memory=256g --runtime=nvidia -v /etc/pki/ca-trust/source/anchors/:/usr/local/share/ca-certificates/llnl:ro" + DOCKER_CERTS_DIR: "/usr/local/share/ca-certificates" + DOCKER_CERTS_UPDATE_COMMAND: "update-ca-certificates" + HOST_CONFIG: /spack-generated.cmake - name: Ubuntu (20.04, gcc 10.5.0, open-mpi 4.0.3) - github codespaces @@ -233,12 +239,16 @@ jobs: CMAKE_BUILD_TYPE: ${{ matrix.CMAKE_BUILD_TYPE }} DOCKER_IMAGE_TAG: ${{ needs.is_not_draft_pull_request.outputs.DOCKER_IMAGE_TAG }} DOCKER_REPOSITORY: ${{ matrix.DOCKER_REPOSITORY }} + DOCKER_CERTS_DIR: ${{ matrix.DOCKER_CERTS_DIR || '' }} + DOCKER_CERTS_UPDATE_COMMAND: ${{ matrix.DOCKER_CERTS_UPDATE_COMMAND || '' }} + DOCKER_RUN_ARGS: ${{ matrix.DOCKER_RUN_ARGS || '' }} ENABLE_HYPRE: ${{ matrix.ENABLE_HYPRE }} ENABLE_TRILINOS: ${{ matrix.ENABLE_TRILINOS }} GEOS_ENABLE_BOUNDS_CHECK: ${{ matrix.GEOS_ENABLE_BOUNDS_CHECK }} GCP_BUCKET: ${{ matrix.GCP_BUCKET }} HOST_CONFIG: ${{ matrix.HOST_CONFIG }} - RUNS_ON: ubuntu-22.04 + NPROC: ${{ matrix.NPROC || '' }} + RUNS_ON: ${{ matrix.RUNS_ON || 'ubuntu-22.04' }} secrets: inherit # If the 'ci: run integrated tests' PR label is found, the integrated tests will be run immediately after the cpu jobs. diff --git a/.gitmodules b/.gitmodules index 5552178f5ce..59e5fe8aebf 100644 --- a/.gitmodules +++ b/.gitmodules @@ -10,3 +10,6 @@ [submodule "src/coreComponents/fileIO/coupling/hdf5_interface"] path = src/coreComponents/fileIO/coupling/hdf5_interface url = ../../GEOS-DEV/hdf5_interface.git +[submodule "src/coreComponents/constitutive/HPCReact"] + path = src/coreComponents/constitutive/HPCReact + url = ../../GEOS-DEV/HPCReact.git diff --git a/.integrated_tests.yaml b/.integrated_tests.yaml index 7bc489e447b..751b91fc845 100644 --- a/.integrated_tests.yaml +++ b/.integrated_tests.yaml @@ -1,6 +1,6 @@ baselines: bucket: geosx - baseline: integratedTests/baseline_integratedTests-pr3795-15047-606f4ac + baseline: integratedTests/baseline_integratedTests-pr3634-15105-6ab70ef allow_fail: all: '' diff --git a/BASELINE_NOTES.md b/BASELINE_NOTES.md index 10890889767..f5b66fdacee 100644 --- a/BASELINE_NOTES.md +++ b/BASELINE_NOTES.md @@ -6,6 +6,10 @@ This file is designed to track changes to the integrated test baselines. Any developer who updates the baseline ID in the .integrated_tests.yaml file is expected to create an entry in this file with the pull request number, date, and their justification for rebaselining. These notes should be in reverse-chronological order, and use the following time format: (YYYY-MM-DD). +PR #3634 (2025-12-31) +===================== +Add singlephase reactive transport solver integrated with HPCReact + PR #3795 (2025-12-19) ===================== Addition of multiphase initialisation diff --git a/inputFiles/singlePhaseReactiveTransport/ChainSerial_1DAllKinetic_base.xml b/inputFiles/singlePhaseReactiveTransport/ChainSerial_1DAllKinetic_base.xml new file mode 100644 index 00000000000..27df60a6124 --- /dev/null +++ b/inputFiles/singlePhaseReactiveTransport/ChainSerial_1DAllKinetic_base.xml @@ -0,0 +1,74 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/inputFiles/singlePhaseReactiveTransport/ChainSerial_1DAllKinetic_benchmark.xml b/inputFiles/singlePhaseReactiveTransport/ChainSerial_1DAllKinetic_benchmark.xml new file mode 100644 index 00000000000..355a4289718 --- /dev/null +++ b/inputFiles/singlePhaseReactiveTransport/ChainSerial_1DAllKinetic_benchmark.xml @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inputFiles/singlePhaseReactiveTransport/ChainSerial_base.xml b/inputFiles/singlePhaseReactiveTransport/ChainSerial_base.xml new file mode 100644 index 00000000000..4bc502df425 --- /dev/null +++ b/inputFiles/singlePhaseReactiveTransport/ChainSerial_base.xml @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inputFiles/singlePhaseReactiveTransport/ChainSerial_initialAggregate.xml b/inputFiles/singlePhaseReactiveTransport/ChainSerial_initialAggregate.xml new file mode 100644 index 00000000000..a72597cb050 --- /dev/null +++ b/inputFiles/singlePhaseReactiveTransport/ChainSerial_initialAggregate.xml @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + + + + + + diff --git a/inputFiles/singlePhaseReactiveTransport/ChainSerial_logConc.xml b/inputFiles/singlePhaseReactiveTransport/ChainSerial_logConc.xml new file mode 100644 index 00000000000..0cb9d89de44 --- /dev/null +++ b/inputFiles/singlePhaseReactiveTransport/ChainSerial_logConc.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + + + diff --git a/inputFiles/singlePhaseReactiveTransport/MoMaSEasy_1DAdvective_base.xml b/inputFiles/singlePhaseReactiveTransport/MoMaSEasy_1DAdvective_base.xml new file mode 100644 index 00000000000..220cbce513c --- /dev/null +++ b/inputFiles/singlePhaseReactiveTransport/MoMaSEasy_1DAdvective_base.xml @@ -0,0 +1,102 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/inputFiles/singlePhaseReactiveTransport/MoMaSEasy_1DAdvective_benchmark.xml b/inputFiles/singlePhaseReactiveTransport/MoMaSEasy_1DAdvective_benchmark.xml new file mode 100644 index 00000000000..e880c80c505 --- /dev/null +++ b/inputFiles/singlePhaseReactiveTransport/MoMaSEasy_1DAdvective_benchmark.xml @@ -0,0 +1,86 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inputFiles/singlePhaseReactiveTransport/MoMaSEasy_1DDiffusive_base.xml b/inputFiles/singlePhaseReactiveTransport/MoMaSEasy_1DDiffusive_base.xml new file mode 100644 index 00000000000..311fa6e9d4a --- /dev/null +++ b/inputFiles/singlePhaseReactiveTransport/MoMaSEasy_1DDiffusive_base.xml @@ -0,0 +1,102 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inputFiles/singlePhaseReactiveTransport/MoMaSEasy_1DDiffusive_benchmark.xml b/inputFiles/singlePhaseReactiveTransport/MoMaSEasy_1DDiffusive_benchmark.xml new file mode 100644 index 00000000000..b24149087c9 --- /dev/null +++ b/inputFiles/singlePhaseReactiveTransport/MoMaSEasy_1DDiffusive_benchmark.xml @@ -0,0 +1,86 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inputFiles/singlePhaseReactiveTransport/MoMaSEasy_1D_base.xml b/inputFiles/singlePhaseReactiveTransport/MoMaSEasy_1D_base.xml new file mode 100644 index 00000000000..aa3cf8c3932 --- /dev/null +++ b/inputFiles/singlePhaseReactiveTransport/MoMaSEasy_1D_base.xml @@ -0,0 +1,133 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inputFiles/singlePhaseReactiveTransport/MoMaSEasy_2DAdvective_base.xml b/inputFiles/singlePhaseReactiveTransport/MoMaSEasy_2DAdvective_base.xml new file mode 100644 index 00000000000..25c52b14b60 --- /dev/null +++ b/inputFiles/singlePhaseReactiveTransport/MoMaSEasy_2DAdvective_base.xml @@ -0,0 +1,103 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/inputFiles/singlePhaseReactiveTransport/MoMaSEasy_2DAdvective_benchmark.xml b/inputFiles/singlePhaseReactiveTransport/MoMaSEasy_2DAdvective_benchmark.xml new file mode 100644 index 00000000000..6172f57b089 --- /dev/null +++ b/inputFiles/singlePhaseReactiveTransport/MoMaSEasy_2DAdvective_benchmark.xml @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inputFiles/singlePhaseReactiveTransport/MoMaSEasy_2D_base.xml b/inputFiles/singlePhaseReactiveTransport/MoMaSEasy_2D_base.xml new file mode 100644 index 00000000000..7c4059813cc --- /dev/null +++ b/inputFiles/singlePhaseReactiveTransport/MoMaSEasy_2D_base.xml @@ -0,0 +1,81 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inputFiles/singlePhaseReactiveTransport/MoMaSEasy_initialAggregate.xml b/inputFiles/singlePhaseReactiveTransport/MoMaSEasy_initialAggregate.xml new file mode 100644 index 00000000000..d17545e2d17 --- /dev/null +++ b/inputFiles/singlePhaseReactiveTransport/MoMaSEasy_initialAggregate.xml @@ -0,0 +1,143 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inputFiles/singlePhaseReactiveTransport/MoMaSEasy_initialPrimaryConc.xml b/inputFiles/singlePhaseReactiveTransport/MoMaSEasy_initialPrimaryConc.xml new file mode 100644 index 00000000000..0c996c4f429 --- /dev/null +++ b/inputFiles/singlePhaseReactiveTransport/MoMaSEasy_initialPrimaryConc.xml @@ -0,0 +1,143 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inputFiles/singlePhaseReactiveTransport/MoMaSEasy_logConc.xml b/inputFiles/singlePhaseReactiveTransport/MoMaSEasy_logConc.xml new file mode 100644 index 00000000000..b694ac4c669 --- /dev/null +++ b/inputFiles/singlePhaseReactiveTransport/MoMaSEasy_logConc.xml @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + diff --git a/inputFiles/singlePhaseReactiveTransport/MoMaSMedium_1DAdvective_base.xml b/inputFiles/singlePhaseReactiveTransport/MoMaSMedium_1DAdvective_base.xml new file mode 100644 index 00000000000..2e180c91c2c --- /dev/null +++ b/inputFiles/singlePhaseReactiveTransport/MoMaSMedium_1DAdvective_base.xml @@ -0,0 +1,102 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/inputFiles/singlePhaseReactiveTransport/MoMaSMedium_1DAdvective_benchmark.xml b/inputFiles/singlePhaseReactiveTransport/MoMaSMedium_1DAdvective_benchmark.xml new file mode 100644 index 00000000000..56c8677fe2b --- /dev/null +++ b/inputFiles/singlePhaseReactiveTransport/MoMaSMedium_1DAdvective_benchmark.xml @@ -0,0 +1,93 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inputFiles/singlePhaseReactiveTransport/MoMaSMedium_base.xml b/inputFiles/singlePhaseReactiveTransport/MoMaSMedium_base.xml new file mode 100644 index 00000000000..f1ed51c7534 --- /dev/null +++ b/inputFiles/singlePhaseReactiveTransport/MoMaSMedium_base.xml @@ -0,0 +1,151 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inputFiles/singlePhaseReactiveTransport/MoMaSMedium_initialAggregate.xml b/inputFiles/singlePhaseReactiveTransport/MoMaSMedium_initialAggregate.xml new file mode 100644 index 00000000000..2fb76a9a089 --- /dev/null +++ b/inputFiles/singlePhaseReactiveTransport/MoMaSMedium_initialAggregate.xml @@ -0,0 +1,143 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inputFiles/singlePhaseReactiveTransport/MoMaSMedium_initialPrimaryConc.xml b/inputFiles/singlePhaseReactiveTransport/MoMaSMedium_initialPrimaryConc.xml new file mode 100644 index 00000000000..01742f28856 --- /dev/null +++ b/inputFiles/singlePhaseReactiveTransport/MoMaSMedium_initialPrimaryConc.xml @@ -0,0 +1,143 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inputFiles/singlePhaseReactiveTransport/MoMaSMedium_logConc.xml b/inputFiles/singlePhaseReactiveTransport/MoMaSMedium_logConc.xml new file mode 100644 index 00000000000..1b39e7f1cfa --- /dev/null +++ b/inputFiles/singlePhaseReactiveTransport/MoMaSMedium_logConc.xml @@ -0,0 +1,89 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inputFiles/singlePhaseReactiveTransport/mixedReactionCarbonateSystemPorosityUpdateThermal_1D_base.xml b/inputFiles/singlePhaseReactiveTransport/mixedReactionCarbonateSystemPorosityUpdateThermal_1D_base.xml new file mode 100644 index 00000000000..dec9240eb9f --- /dev/null +++ b/inputFiles/singlePhaseReactiveTransport/mixedReactionCarbonateSystemPorosityUpdateThermal_1D_base.xml @@ -0,0 +1,205 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inputFiles/singlePhaseReactiveTransport/mixedReactionCarbonateSystemPorosityUpdateThermal_1D_smoke.xml b/inputFiles/singlePhaseReactiveTransport/mixedReactionCarbonateSystemPorosityUpdateThermal_1D_smoke.xml new file mode 100644 index 00000000000..f56e8be7ee9 --- /dev/null +++ b/inputFiles/singlePhaseReactiveTransport/mixedReactionCarbonateSystemPorosityUpdateThermal_1D_smoke.xml @@ -0,0 +1,85 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inputFiles/singlePhaseReactiveTransport/mixedReactionCarbonateSystemPorosityUpdate_1D_base.xml b/inputFiles/singlePhaseReactiveTransport/mixedReactionCarbonateSystemPorosityUpdate_1D_base.xml new file mode 100644 index 00000000000..b432a533571 --- /dev/null +++ b/inputFiles/singlePhaseReactiveTransport/mixedReactionCarbonateSystemPorosityUpdate_1D_base.xml @@ -0,0 +1,166 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inputFiles/singlePhaseReactiveTransport/mixedReactionCarbonateSystemPorosityUpdate_1D_smoke.xml b/inputFiles/singlePhaseReactiveTransport/mixedReactionCarbonateSystemPorosityUpdate_1D_smoke.xml new file mode 100644 index 00000000000..f91c2e86ae9 --- /dev/null +++ b/inputFiles/singlePhaseReactiveTransport/mixedReactionCarbonateSystemPorosityUpdate_1D_smoke.xml @@ -0,0 +1,85 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inputFiles/singlePhaseReactiveTransport/mixedReactionCarbonateSystem_1D_base.xml b/inputFiles/singlePhaseReactiveTransport/mixedReactionCarbonateSystem_1D_base.xml new file mode 100644 index 00000000000..2caed030de9 --- /dev/null +++ b/inputFiles/singlePhaseReactiveTransport/mixedReactionCarbonateSystem_1D_base.xml @@ -0,0 +1,153 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inputFiles/singlePhaseReactiveTransport/mixedReactionCarbonateSystem_1D_smoke.xml b/inputFiles/singlePhaseReactiveTransport/mixedReactionCarbonateSystem_1D_smoke.xml new file mode 100644 index 00000000000..2a0deb5d713 --- /dev/null +++ b/inputFiles/singlePhaseReactiveTransport/mixedReactionCarbonateSystem_1D_smoke.xml @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inputFiles/singlePhaseReactiveTransport/mixedReactionCarbonateSystem_initialAggregate.xml b/inputFiles/singlePhaseReactiveTransport/mixedReactionCarbonateSystem_initialAggregate.xml new file mode 100644 index 00000000000..767b9387978 --- /dev/null +++ b/inputFiles/singlePhaseReactiveTransport/mixedReactionCarbonateSystem_initialAggregate.xml @@ -0,0 +1,133 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inputFiles/singlePhaseReactiveTransport/mixedReactionCarbonateSystem_initialPrimaryConc.xml b/inputFiles/singlePhaseReactiveTransport/mixedReactionCarbonateSystem_initialPrimaryConc.xml new file mode 100644 index 00000000000..9fe0e5abeae --- /dev/null +++ b/inputFiles/singlePhaseReactiveTransport/mixedReactionCarbonateSystem_initialPrimaryConc.xml @@ -0,0 +1,133 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inputFiles/singlePhaseReactiveTransport/mixedReactionCarbonateSystem_logConc.xml b/inputFiles/singlePhaseReactiveTransport/mixedReactionCarbonateSystem_logConc.xml new file mode 100644 index 00000000000..da291b97da0 --- /dev/null +++ b/inputFiles/singlePhaseReactiveTransport/mixedReactionCarbonateSystem_logConc.xml @@ -0,0 +1,62 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/inputFiles/singlePhaseReactiveTransport/singlePhaseReactiveTransport.ats b/inputFiles/singlePhaseReactiveTransport/singlePhaseReactiveTransport.ats new file mode 100644 index 00000000000..4f73e9e6fa3 --- /dev/null +++ b/inputFiles/singlePhaseReactiveTransport/singlePhaseReactiveTransport.ats @@ -0,0 +1,36 @@ +from geos.ats.test_builder import TestDeck, RestartcheckParameters, generate_geos_tests + +restartcheck_params = {} +restartcheck_params['atol'] = 1e-06 +restartcheck_params['rtol'] = 1e-05 + +decks = [ + TestDeck( + name="mixedReactionCarbonateSystemPorosityUpdateThermal_1D_smoke", + description= + '1D Carbonate mixed reaction with porosity update and thermal (1D, mixedReaction, thermal)', + partitions=((1, 1, 1), (2, 1, 1)), + restart_step=10, + check_step=20, + restartcheck_params=RestartcheckParameters(**restartcheck_params)), + + TestDeck( + name="mixedReactionCarbonateSystemPorosityUpdate_1D_smoke", + description= + '1D Carbonate mixed reaction with porosity update (1D, mixedReaction)', + partitions=((1, 1, 1), (2, 1, 1)), + restart_step=10, + check_step=20, + restartcheck_params=RestartcheckParameters(**restartcheck_params)), + + TestDeck( + name="mixedReactionCarbonateSystem_1D_smoke", + description= + '1D Carbonate mixed reaction (1D, mixedReaction)', + partitions=((1, 1, 1), (2, 1, 1)), + restart_step=10, + check_step=20, + restartcheck_params=RestartcheckParameters(**restartcheck_params)), +] + +generate_geos_tests(decks) diff --git a/scripts/test_submodule_updated.sh b/scripts/test_submodule_updated.sh index 604d95f2f80..f88b346dbb7 100755 --- a/scripts/test_submodule_updated.sh +++ b/scripts/test_submodule_updated.sh @@ -13,6 +13,7 @@ git submodule update --init src/cmake/blt git submodule update --init src/coreComponents/LvArray git submodule update --init src/coreComponents/constitutive/PVTPackage git submodule update --init src/coreComponents/fileIO/coupling/hdf5_interface +git submodule update --init src/coreComponents/constitutive/HPCReact # Initialize PR submodule hashes @@ -31,6 +32,7 @@ declare -Ar main_branches=( ["integratedTests"]="origin/develop" ["hdf5_interface"]="origin/master" ["PVTPackage"]="origin/develop" + ["HPCReact"]="origin/main" ) diff --git a/src/cmake/GeosxMacros.cmake b/src/cmake/GeosxMacros.cmake index b38789dfd59..65c17cdd8a9 100644 --- a/src/cmake/GeosxMacros.cmake +++ b/src/cmake/GeosxMacros.cmake @@ -47,6 +47,8 @@ macro( geosx_add_code_checks ) endforeach() endif() + message( "PROJECT_SOURCE_DIR=${PROJECT_SOURCE_DIR}" ) # debug + if ( ENABLE_UNCRUSTIFY ) blt_add_code_checks( PREFIX ${arg_PREFIX} SOURCES ${_sources} diff --git a/src/cmake/GeosxOptions.cmake b/src/cmake/GeosxOptions.cmake index fe3adabcc4b..1e504a6ed90 100644 --- a/src/cmake/GeosxOptions.cmake +++ b/src/cmake/GeosxOptions.cmake @@ -36,6 +36,8 @@ endif() option( ENABLE_PVTPackage "" ON ) +option( ENABLE_HPCREACT "" ON ) + option( ENABLE_UNCRUSTIFY "" ON ) option( ENABLE_XML_UPDATES "" ON ) diff --git a/src/coreComponents/CMakeLists.txt b/src/coreComponents/CMakeLists.txt index 4e7421f7988..b74e728c754 100644 --- a/src/coreComponents/CMakeLists.txt +++ b/src/coreComponents/CMakeLists.txt @@ -73,11 +73,11 @@ else() endif() geosx_add_code_checks( PREFIX coreComponents - EXCLUDES cmake constitutive/PVTPackage ) + EXCLUDES cmake constitutive/PVTPackage constitutive/HPCReact ) if( ENABLE_UNCRUSTIFY ) add_test( NAME testUncrustifyCheck - COMMAND sh -c "${CMAKE_MAKE_PROGRAM} coreComponents_uncrustify_check main_uncrustify_check 2>&1 >/dev/null" + COMMAND sh -c "${CMAKE_MAKE_PROGRAM} coreComponents_uncrustify_check main_uncrustify_check " WORKING_DIRECTORY ${CMAKE_BINARY_DIR} ) endif() diff --git a/src/coreComponents/constitutive/CMakeLists.txt b/src/coreComponents/constitutive/CMakeLists.txt index 96a11df9364..6af439fe23f 100644 --- a/src/coreComponents/constitutive/CMakeLists.txt +++ b/src/coreComponents/constitutive/CMakeLists.txt @@ -128,12 +128,8 @@ set( constitutive_headers fluid/multifluid/compositional/parameters/PressureTemperatureCoordinates.hpp fluid/multifluid/compositional/CompositionalMultiphaseFluid.hpp fluid/multifluid/compositional/CompositionalMultiphaseFluidUpdates.hpp - fluid/multifluid/reactive/ReactiveBrineFluid.hpp - fluid/multifluid/reactive/ReactiveMultiFluid.hpp - fluid/multifluid/reactive/ReactiveMultiFluidFields.hpp - fluid/multifluid/reactive/chemicalReactions/EquilibriumReactions.hpp - fluid/multifluid/reactive/chemicalReactions/KineticReactions.hpp - fluid/multifluid/reactive/chemicalReactions/ReactionsBase.hpp + fluid/reactivefluid/ReactiveFluidFields.hpp + fluid/reactivefluid/ReactiveFluidLayouts.hpp fluid/singlefluid/CompressibleSinglePhaseFluid.hpp fluid/singlefluid/ParticleFluid.hpp fluid/singlefluid/ParticleFluidBase.hpp @@ -196,6 +192,7 @@ set( constitutive_headers solid/PerfectlyPlastic.hpp solid/PorousSolid.hpp solid/PropertyConversions.hpp + solid/ReactiveSolid.hpp solid/SolidBase.hpp solid/SolidUtilities.hpp solid/SolidInternalEnergy.hpp @@ -211,6 +208,7 @@ set( constitutive_headers solid/porosity/PorosityBase.hpp solid/porosity/PressurePorosity.hpp solid/porosity/ProppantPorosity.hpp + solid/porosity/ReactivePorosity.hpp thermalConductivity/MultiPhaseConstantThermalConductivity.hpp thermalConductivity/MultiPhaseThermalConductivityBase.hpp thermalConductivity/MultiPhaseThermalConductivityFields.hpp @@ -288,11 +286,6 @@ set( constitutive_sources fluid/multifluid/compositional/parameters/PressureTemperatureCoordinates.cpp fluid/multifluid/compositional/CompositionalMultiphaseFluid.cpp fluid/multifluid/compositional/CompositionalMultiphaseFluidUpdates.cpp - fluid/multifluid/reactive/ReactiveBrineFluid.cpp - fluid/multifluid/reactive/ReactiveMultiFluid.cpp - fluid/multifluid/reactive/chemicalReactions/EquilibriumReactions.cpp - fluid/multifluid/reactive/chemicalReactions/KineticReactions.cpp - fluid/multifluid/reactive/chemicalReactions/ReactionsBase.cpp fluid/singlefluid/CompressibleSinglePhaseFluid.cpp fluid/singlefluid/ParticleFluid.cpp fluid/singlefluid/ParticleFluidBase.cpp @@ -342,6 +335,7 @@ set( constitutive_sources solid/PorousDamageSolid.cpp solid/PerfectlyPlastic.cpp solid/PorousSolid.cpp + solid/ReactiveSolid.cpp solid/SolidBase.cpp solid/SolidInternalEnergy.cpp solid/CeramicDamage.cpp @@ -349,6 +343,7 @@ set( constitutive_sources solid/porosity/PorosityBase.cpp solid/porosity/PressurePorosity.cpp solid/porosity/ProppantPorosity.cpp + solid/porosity/ReactivePorosity.cpp thermalConductivity/MultiPhaseConstantThermalConductivity.cpp thermalConductivity/MultiPhaseThermalConductivityBase.cpp thermalConductivity/MultiPhaseVolumeWeightedThermalConductivity.cpp @@ -373,6 +368,25 @@ if( ENABLE_PVTPackage ) list( APPEND dependencyList PVTPackage ) endif() +if (ENABLE_HPCREACT) + set( constitutive_headers + ${constitutive_headers} + fluid/reactivefluid/ReactiveFluidSelector.hpp + ) + + set( constitutive_headers + ${constitutive_headers} + fluid/reactivefluid/ReactiveSinglePhaseFluid.hpp + ) + + set( constitutive_sources + ${constitutive_sources} + fluid/reactivefluid/ReactiveSinglePhaseFluid.cpp + ) + add_subdirectory( HPCReact ) + list( APPEND dependencyList hpcReact ) +endif() + geos_decorate_link_dependencies( LIST decoratedDependencies DEPENDENCIES ${dependencyList} ) diff --git a/src/coreComponents/constitutive/ConstitutivePassThru.hpp b/src/coreComponents/constitutive/ConstitutivePassThru.hpp index fe4f5adaf1d..1c63ae20f7f 100644 --- a/src/coreComponents/constitutive/ConstitutivePassThru.hpp +++ b/src/coreComponents/constitutive/ConstitutivePassThru.hpp @@ -41,8 +41,10 @@ #include "solid/CompressibleSolid.hpp" #include "solid/ProppantSolid.hpp" #include "solid/CeramicDamage.hpp" +#include "solid/ReactiveSolid.hpp" #include "solid/porosity/PressurePorosity.hpp" #include "solid/porosity/ProppantPorosity.hpp" +#include "solid/porosity/ReactivePorosity.hpp" #include "permeability/ConstantPermeability.hpp" #include "permeability/CarmanKozenyPermeability.hpp" #include "permeability/ExponentialDecayPermeability.hpp" @@ -394,6 +396,34 @@ struct ConstitutivePassThru< CompressibleSolidBase > } }; +/** + * Specialization for the ReactiveSolid models. + */ +template<> +struct ConstitutivePassThru< ReactiveSolidBase > +{ + template< typename LAMBDA > + static void execute( ConstitutiveBase & constitutiveRelation, LAMBDA && lambda ) + { + ConstitutivePassThruHandler< ReactiveSolid< ReactivePorosity, ConstantPermeability >, + ReactiveSolid< ReactivePorosity, CarmanKozenyPermeability >, + ReactiveSolid< ReactivePorosity, PressurePermeability > + >::execute( constitutiveRelation, + std::forward< LAMBDA >( lambda ) ); + } + + template< typename LAMBDA > + static void execute( ConstitutiveBase const & constitutiveRelation, LAMBDA && lambda ) + { + ConstitutivePassThruHandler< ReactiveSolid< ReactivePorosity, ConstantPermeability >, + ReactiveSolid< ReactivePorosity, CarmanKozenyPermeability >, + ReactiveSolid< ReactivePorosity, PressurePermeability > + >::execute( constitutiveRelation, + std::forward< LAMBDA >( lambda ) ); + } +}; + + /** * Specialization for the ProppantModel. */ @@ -458,8 +488,11 @@ struct ConstitutivePassThru< CoupledSolidBase > PorousSolid< ElasticOrthotropic, CarmanKozenyPermeability >, PorousDamageSolid< DamageSpectral< ElasticIsotropic > >, PorousDamageSolid< DamageVolDev< ElasticIsotropic > >, - PorousDamageSolid< Damage< ElasticIsotropic > > >::execute( constitutiveRelation, - std::forward< LAMBDA >( lambda ) ); + PorousDamageSolid< Damage< ElasticIsotropic > >, + ReactiveSolid< ReactivePorosity, ConstantPermeability >, + ReactiveSolid< ReactivePorosity, CarmanKozenyPermeability >, + ReactiveSolid< ReactivePorosity, PressurePermeability > >::execute( constitutiveRelation, + std::forward< LAMBDA >( lambda ) ); } template< typename LAMBDA > @@ -496,8 +529,11 @@ struct ConstitutivePassThru< CoupledSolidBase > PorousSolid< ElasticOrthotropic, CarmanKozenyPermeability >, PorousDamageSolid< DamageSpectral< ElasticIsotropic > >, PorousDamageSolid< DamageVolDev< ElasticIsotropic > >, - PorousDamageSolid< Damage< ElasticIsotropic > > >::execute( constitutiveRelation, - std::forward< LAMBDA >( lambda ) ); + PorousDamageSolid< Damage< ElasticIsotropic > >, + ReactiveSolid< ReactivePorosity, ConstantPermeability >, + ReactiveSolid< ReactivePorosity, CarmanKozenyPermeability >, + ReactiveSolid< ReactivePorosity, PressurePermeability > >::execute( constitutiveRelation, + std::forward< LAMBDA >( lambda ) ); } }; diff --git a/src/coreComponents/constitutive/HPCReact b/src/coreComponents/constitutive/HPCReact new file mode 160000 index 00000000000..5268dc5c2a5 --- /dev/null +++ b/src/coreComponents/constitutive/HPCReact @@ -0,0 +1 @@ +Subproject commit 5268dc5c2a59e9dae23f435fba9aecf5a5c5de33 diff --git a/src/coreComponents/constitutive/fluid/multifluid/reactive/ReactiveBrineFluid.hpp b/src/coreComponents/constitutive/fluid/multifluid/reactive/ReactiveBrineFluid.hpp deleted file mode 100644 index f1d88712b00..00000000000 --- a/src/coreComponents/constitutive/fluid/multifluid/reactive/ReactiveBrineFluid.hpp +++ /dev/null @@ -1,311 +0,0 @@ -/* - * ------------------------------------------------------------------------------------------------------------ - * 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 ReactiveBrineFluid.hpp - */ - -#ifndef GEOS_CONSTITUTIVE_FLUID_REACTIVEBRINEFLUID_HPP_ -#define GEOS_CONSTITUTIVE_FLUID_REACTIVEBRINEFLUID_HPP_ - -#include "common/format/EnumStrings.hpp" -#include "constitutive/fluid/multifluid/reactive/ReactiveMultiFluid.hpp" -#include "constitutive/fluid/multifluid/MultiFluidUtils.hpp" -#include "constitutive/fluid/multifluid/CO2Brine/PhaseModel.hpp" -#include "constitutive/fluid/multifluid/CO2Brine/functions/BrineEnthalpy.hpp" -#include "constitutive/fluid/multifluid/CO2Brine/functions/NoOpPVTFunction.hpp" -#include "constitutive/fluid/multifluid/CO2Brine/functions/WaterDensity.hpp" -#include "constitutive/fluid/multifluid/CO2Brine/functions/PhillipsBrineViscosity.hpp" -#include "constitutive/fluid/multifluid/CO2Brine/functions/PureWaterProperties.hpp" -#include "common/Units.hpp" - - - -#include - -namespace geos -{ - -namespace constitutive -{ - -template< typename PHASE > -class ReactiveBrineFluid : public ReactiveMultiFluid -{ -public: - - using exec_policy = parallelDevicePolicy<>; - - ReactiveBrineFluid( string const & name, - Group * const parent ); - - virtual std::unique_ptr< ConstitutiveBase > - deliverClone( string const & name, - Group * const parent ) const override; - - static string catalogName(); - - virtual string getCatalogName() const override { return catalogName(); } - - virtual bool isThermal() const override final; - - /** - * @copydoc MultiFluidBase::checkTablesParameters( real64 pressure, real64 temperature ) - */ - void checkTablesParameters( real64 pressure, real64 temperature ) const override final; - - /** - * @brief Kernel wrapper class for ReactiveBrineFluid. - */ - class KernelWrapper final : public ReactiveMultiFluid::KernelWrapper - { -public: - GEOS_HOST_DEVICE - virtual void compute( real64 const pressure, - real64 const temperature, - arraySlice1d< real64 const, compflow::USD_COMP - 1 > const & composition, - PhaseProp::SliceType const phaseFraction, - PhaseProp::SliceType const phaseDensity, - PhaseProp::SliceType const phaseMassDensity, - PhaseProp::SliceType const phaseViscosity, - PhaseProp::SliceType const phaseEnthalpy, - PhaseProp::SliceType const phaseInternalEnergy, - PhaseComp::SliceType const phaseCompFraction, - FluidProp::SliceType const totalDensity ) const override; - - GEOS_HOST_DEVICE - virtual void update( localIndex const k, - localIndex const q, - real64 const pressure, - real64 const temperature, - arraySlice1d< real64 const, compflow::USD_COMP - 1 > const & composition ) const override; - - virtual void updateChemistry( localIndex const k, - localIndex const q, - real64 const pressure, - real64 const temperature, - arraySlice1d< real64 const, compflow::USD_COMP - 1 > const & composition ) const override; - -private: - - friend class ReactiveBrineFluid; - - KernelWrapper( PHASE const & phase, - arrayView1d< real64 const > componentMolarWeight, - bool const useMass, - bool const isThermal, - PhaseProp::ViewType phaseFraction, - PhaseProp::ViewType phaseDensity, - PhaseProp::ViewType phaseMassDensity, - PhaseProp::ViewType phaseViscosity, - PhaseProp::ViewType phaseEnthalpy, - PhaseProp::ViewType phaseInternalEnergy, - PhaseComp::ViewType phaseCompFraction, - FluidProp::ViewType totalDensity, - integer const numPrimarySpecies, - chemicalReactions::EquilibriumReactions const & equilibriumReactions, - chemicalReactions::KineticReactions const & kineticReactions, - arrayView2d< real64, compflow::USD_COMP > const & primarySpeciesConcentration, - arrayView2d< real64, compflow::USD_COMP > const & secondarySpeciesConcentration, - arrayView2d< real64, compflow::USD_COMP > const & primarySpeciesTotalConcentration, - arrayView2d< real64, compflow::USD_COMP > const & kineticReactionRates ); - - - /// Flag to specify whether the model is thermal or not - bool m_isThermal; - - /// Brine constitutive kernel wrappers - typename PHASE::KernelWrapper m_phase; - - }; - - virtual integer getWaterPhaseIndex() const override final; - - /** - * @brief Names of the submodels for input - */ - enum class SubModelInputNames : integer - { - DENSITY, ///< the keyword for the density model - VISCOSITY, ///< the keyword for the viscosity model - ENTHALPY ///< the keyword for the enthalpy model - }; - - /** - * @brief Create an update kernel wrapper. - * @return the wrapper - */ - KernelWrapper createKernelWrapper(); - - struct viewKeyStruct : ReactiveMultiFluid::viewKeyStruct - { - static constexpr char const * phasePVTParaFilesString() { return "phasePVTParaFiles"; } - static constexpr char const * writeCSVFlagString() { return "writeCSV"; } - }; - -protected: - - virtual void postInputInitialization() override; - -private: - - /** - * @brief Create a PVT Model and output them - */ - void createPVTModels(); - - /// Names of the files defining the viscosity and density models - path_array m_phasePVTParaFiles; - - /// Output csv file containing informations about PVT - integer m_writeCSV; - - /// Brine constitutive models - std::unique_ptr< PHASE > m_phase; - -}; - -// these aliases are useful in constitutive dispatch -using ReactiveBrine = - ReactiveBrineFluid< PhaseModel< PVTProps::WaterDensity, PVTProps::PhillipsBrineViscosity, PVTProps::NoOpPVTFunction > >; -using ReactiveBrineThermal = - ReactiveBrineFluid< PhaseModel< PVTProps::WaterDensity, PVTProps::PhillipsBrineViscosity, PVTProps::BrineEnthalpy > >; - -template< typename PHASE > -GEOS_HOST_DEVICE -inline void -ReactiveBrineFluid< PHASE >::KernelWrapper:: - compute( real64 const pressure, - real64 const temperature, - arraySlice1d< real64 const, compflow::USD_COMP - 1 > const & composition, - PhaseProp::SliceType const phaseFraction, - PhaseProp::SliceType const phaseDensity, - PhaseProp::SliceType const phaseMassDensity, - PhaseProp::SliceType const phaseViscosity, - PhaseProp::SliceType const phaseEnthalpy, - PhaseProp::SliceType const phaseInternalEnergy, - PhaseComp::SliceType const phaseCompFraction, - FluidProp::SliceType const totalDensity ) const -{ - integer constexpr numComp = chemicalReactions::ReactionsBase::maxNumPrimarySpecies; - - // 1. We perform a sort of single phase flash - stackArray1d< real64, numComp > compMoleFrac( composition.size() ); - - phaseFraction.value[0] = 1.0; // it's a single phase system - for( integer ic = 0; ic < composition.size(); ++ic ) - { - compMoleFrac[ic] = composition[ic]; - phaseCompFraction.value[0][ic] = composition[ic]; - } - - real64 const temperatureInCelsius = units::convertKToC( temperature ); - - // 2. Compute phase densities and phase viscosities - m_phase.density.compute( pressure, - temperatureInCelsius, - phaseCompFraction.value[0].toSliceConst(), phaseCompFraction.derivs[0].toSliceConst(), - phaseDensity.value[0], phaseDensity.derivs[0], - m_useMass ); - - m_phase.viscosity.compute( pressure, - temperatureInCelsius, - phaseCompFraction.value[0].toSliceConst(), phaseCompFraction.derivs[0].toSliceConst(), - phaseViscosity.value[0], phaseViscosity.derivs[0], - m_useMass ); - - - // for now, we have to compute the phase mass density here - m_phase.density.compute( pressure, - temperatureInCelsius, - phaseCompFraction.value[0].toSliceConst(), phaseCompFraction.derivs[0].toSliceConst(), - phaseMassDensity.value[0], phaseMassDensity.derivs[0], - true ); - - // 3. Compute enthalpy and internal energy - if( m_isThermal ) - { - m_phase.enthalpy.compute( pressure, - temperatureInCelsius, - phaseCompFraction.value[0].toSliceConst(), phaseCompFraction.derivs[0].toSliceConst(), - phaseEnthalpy.value[0], phaseEnthalpy.derivs[0], - m_useMass ); - - computeInternalEnergy( pressure, - phaseFraction, - phaseMassDensity, - phaseEnthalpy, - phaseInternalEnergy ); - } - - // 6. Compute total fluid mass/molar density and derivatives - computeTotalDensity( phaseFraction, - phaseDensity, - totalDensity ); -} - -template< typename PHASE > -GEOS_HOST_DEVICE inline void -ReactiveBrineFluid< PHASE >::KernelWrapper:: - update( localIndex const k, - localIndex const q, - real64 const pressure, - real64 const temperature, - arraySlice1d< geos::real64 const, compflow::USD_COMP - 1 > const & composition ) const -{ - compute( pressure, - temperature, - composition, - m_phaseFraction( k, q ), - m_phaseDensity( k, q ), - m_phaseMassDensity( k, q ), - m_phaseViscosity( k, q ), - m_phaseEnthalpy( k, q ), - m_phaseInternalEnergy( k, q ), - m_phaseCompFraction( k, q ), - m_totalDensity( k, q ) ); -} - -template< typename PHASE > -inline void -ReactiveBrineFluid< PHASE >::KernelWrapper:: - updateChemistry( localIndex const k, - localIndex const q, - real64 const pressure, - real64 const temperature, - arraySlice1d< geos::real64 const, compflow::USD_COMP - 1 > const & composition ) const - -{ - real64 const totalMolecularWeight = PVTProps::PureWaterProperties::MOLECULAR_WEIGHT; - - convertMoleFractionToMolarity( m_totalDensity( k, q ).value, - totalMolecularWeight, - composition, - m_primarySpeciesTotalConcentration[k] ); - - computeChemistry( pressure, - temperature, - m_primarySpeciesTotalConcentration[k], - m_primarySpeciesConcentration[k], - m_secondarySpeciesConcentration[k], - m_kineticReactionRates[k] ); -} - - -} // namespace constitutive - -} // namespace geos - -#endif //GEOS_CONSTITUTIVE_FLUID_REACTIVEBRINEFLUID_HPP_ diff --git a/src/coreComponents/constitutive/fluid/multifluid/reactive/ReactiveMultiFluid.cpp b/src/coreComponents/constitutive/fluid/multifluid/reactive/ReactiveMultiFluid.cpp deleted file mode 100644 index 3f59b5ac30b..00000000000 --- a/src/coreComponents/constitutive/fluid/multifluid/reactive/ReactiveMultiFluid.cpp +++ /dev/null @@ -1,98 +0,0 @@ -/* - * ------------------------------------------------------------------------------------------------------------ - * 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 ReactiveMultiFluid.cpp - */ -#include "ReactiveMultiFluid.hpp" -#include "ReactiveMultiFluidFields.hpp" - - -namespace geos -{ - -using namespace dataRepository; - -namespace constitutive -{ - -ReactiveMultiFluid:: - ReactiveMultiFluid( string const & name, Group * const parent ): - MultiFluidBase( name, parent ) -{ - // For now this is being hardcoded. We will see where this should come from. - m_numPrimarySpecies = 7; - m_numSecondarySpecies = 11; - m_numKineticReactions = 2; - - registerField< fields::reactivefluid::primarySpeciesConcentration >( &m_primarySpeciesConcentration ); - registerField< fields::reactivefluid::secondarySpeciesConcentration >( &m_secondarySpeciesConcentration ); - registerField< fields::reactivefluid::primarySpeciesTotalConcentration >( &m_primarySpeciesTotalConcentration ); - registerField< fields::reactivefluid::kineticReactionRates >( &m_kineticReactionRates ); -} - -bool ReactiveMultiFluid::isThermal() const -{ - return true; -} - -std::unique_ptr< ConstitutiveBase > ReactiveMultiFluid:: - deliverClone( string const & name, Group * const parent ) const -{ - std::unique_ptr< ConstitutiveBase > clone = MultiFluidBase::deliverClone( name, parent ); - - ReactiveMultiFluid & newConstitutiveRelation = dynamicCast< ReactiveMultiFluid & >( *clone ); - - newConstitutiveRelation.createChemicalReactions(); - - return clone; -} - -void ReactiveMultiFluid::postInputInitialization() -{ - MultiFluidBase::postInputInitialization(); - - GEOS_THROW_IF_NE_MSG( numFluidPhases(), 1, - GEOS_FMT( "{}: invalid number of phases", getFullName() ), - InputError ); - - createChemicalReactions(); -} - -void ReactiveMultiFluid::allocateConstitutiveData( Group & parent, - localIndex const numPts ) -{ - integer const numPrimarySpecies = this->numPrimarySpecies(); - integer const numSecondarySpecies = this->numSecondarySpecies(); - integer const numKineticReactions = this->numKineticReactions(); - - m_primarySpeciesConcentration.resize( 0, numPrimarySpecies ); - m_secondarySpeciesConcentration.resize( 0, numSecondarySpecies ); - m_primarySpeciesTotalConcentration.resize( 0, numPrimarySpecies ); - m_kineticReactionRates.resize( 0, numKineticReactions ); - - MultiFluidBase::allocateConstitutiveData( parent, numPts ); -} - -void ReactiveMultiFluid::createChemicalReactions() -{ - // instantiate reactions objects - m_equilibriumReactions = std::make_unique< chemicalReactions::EquilibriumReactions >( getName() + "_equilibriumReactions", m_numPrimarySpecies, m_numSecondarySpecies ); - m_kineticReactions = std::make_unique< chemicalReactions::KineticReactions >( getName() + "_kineticReactions", m_numPrimarySpecies, m_numSecondarySpecies, m_numKineticReactions ); -} - -} //namespace constitutive - -} //namespace geos diff --git a/src/coreComponents/constitutive/fluid/multifluid/reactive/ReactiveMultiFluid.hpp b/src/coreComponents/constitutive/fluid/multifluid/reactive/ReactiveMultiFluid.hpp deleted file mode 100644 index c4e2719aeed..00000000000 --- a/src/coreComponents/constitutive/fluid/multifluid/reactive/ReactiveMultiFluid.hpp +++ /dev/null @@ -1,246 +0,0 @@ -/* - * ------------------------------------------------------------------------------------------------------------ - * 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 ReactiveMultiFluid.hpp - */ - -#ifndef GEOS_CONSTITUTIVE_FLUID_MULTIFLUID_REACTIVE_REACTIVEMULTIFLUID_HPP_ -#define GEOS_CONSTITUTIVE_FLUID_MULTIFLUID_REACTIVE_REACTIVEMULTIFLUID_HPP_ - - -#include "common/format/EnumStrings.hpp" -#include "constitutive/fluid/multifluid/MultiFluidBase.hpp" -#include "constitutive/fluid/multifluid/reactive/chemicalReactions/EquilibriumReactions.hpp" -#include "constitutive/fluid/multifluid/reactive/chemicalReactions/KineticReactions.hpp" - -#include - -namespace geos -{ - -namespace constitutive -{ - -class ReactiveMultiFluid : public MultiFluidBase -{ -public: - - using exec_policy = serialPolicy; - - ReactiveMultiFluid( string const & name, - dataRepository::Group * const parent ); - - virtual std::unique_ptr< ConstitutiveBase > - deliverClone( string const & name, - dataRepository::Group * const parent ) const override; - - virtual void allocateConstitutiveData( dataRepository::Group & parent, - localIndex const numPts ) override; - - virtual bool isThermal() const override; - - arrayView2d< real64 const, compflow::USD_COMP > primarySpeciesConcentration() const - { return m_primarySpeciesConcentration; } - - arrayView2d< real64 const, compflow::USD_COMP > secondarySpeciesConcentration() const - { return m_secondarySpeciesConcentration; } - - arrayView2d< real64 const, compflow::USD_COMP > kineticReactionRates() const - { return m_kineticReactionRates; } - - integer numPrimarySpecies() const { return m_numPrimarySpecies; } - - integer numSecondarySpecies() const { return m_numSecondarySpecies; } - - integer numKineticReactions() const { return m_numKineticReactions; } - - /** - * @brief Kernel wrapper class for ReactiveMultiFluid. - */ - class KernelWrapper : public MultiFluidBase::KernelWrapper - { - -public: - - void computeChemistry( real64 const pressure, - real64 const temperature, - arraySlice1d< real64 const, compflow::USD_COMP - 1 > const & primarySpeciesTotalConcentration, - arraySlice1d< real64, compflow::USD_COMP - 1 > const & primarySpeciesConcentration, - arraySlice1d< real64, compflow::USD_COMP - 1 > const & secondarySpeciesConcentration, - arraySlice1d< real64, compflow::USD_COMP - 1 > const & kineticReactionRates ) const; - - virtual void updateChemistry( localIndex const k, - localIndex const q, - real64 const pressure, - real64 const temperature, - arraySlice1d< real64 const, compflow::USD_COMP - 1 > const & composition ) const = 0; - - /** - * @brief Construct a new Kernel Wrapper object - * - * @param componentMolarWeight - * @param useMass - * @param isThermal - * @param phaseFraction - * @param phaseDensity - * @param phaseMassDensity - * @param phaseViscosity - * @param phaseEnthalpy - * @param phaseInternalEnergy - * @param phaseCompFraction - * @param totalDensity - * @param numPrimarySpecies - * @param equilibriumReactions - * @param kineticReactions - * @param primarySpeciesConcentration - * @param secondarySpeciesConcentration - * @param primarySpeciesTotalConcentration - * @param kineticReactionRates - */ - KernelWrapper( arrayView1d< real64 const > componentMolarWeight, - bool const useMass, - PhaseProp::ViewType phaseFraction, - PhaseProp::ViewType phaseDensity, - PhaseProp::ViewType phaseMassDensity, - PhaseProp::ViewType phaseViscosity, - PhaseProp::ViewType phaseEnthalpy, - PhaseProp::ViewType phaseInternalEnergy, - PhaseComp::ViewType phaseCompFraction, - FluidProp::ViewType totalDensity, - integer const numPrimarySpecies, - chemicalReactions::EquilibriumReactions const & equilibriumReactions, - chemicalReactions::KineticReactions const & kineticReactions, - arrayView2d< real64, compflow::USD_COMP > const & primarySpeciesConcentration, - arrayView2d< real64, compflow::USD_COMP > const & secondarySpeciesConcentration, - arrayView2d< real64, compflow::USD_COMP > const & primarySpeciesTotalConcentration, - arrayView2d< real64, compflow::USD_COMP > const & kineticReactionRates ): - MultiFluidBase::KernelWrapper( std::move( componentMolarWeight ), - useMass, - std::move( phaseFraction ), - std::move( phaseDensity ), - std::move( phaseMassDensity ), - std::move( phaseViscosity ), - std::move( phaseEnthalpy ), - std::move( phaseInternalEnergy ), - std::move( phaseCompFraction ), - std::move( totalDensity ) ), - m_numPrimarySpecies( numPrimarySpecies ), - m_equilibriumReactions( equilibriumReactions.createKernelWrapper() ), - m_kineticReactions( kineticReactions.createKernelWrapper() ), - m_primarySpeciesConcentration( primarySpeciesConcentration ), - m_secondarySpeciesConcentration( secondarySpeciesConcentration ), - m_primarySpeciesTotalConcentration( primarySpeciesTotalConcentration ), - m_kineticReactionRates( kineticReactionRates ) - {} - -protected: - - void convertMoleFractionToMolarity( real64 const totalDensity, - real64 const totalMolecularWeight, - arraySlice1d< geos::real64 const, compflow::USD_COMP - 1 > const & composition, - arraySlice1d< geos::real64, compflow::USD_COMP - 1 > const & primarySpeciesTotalConcentration ) const; - - friend class ReactiveMultiFluid; - /// Reaction related terms - integer m_numPrimarySpecies; - - chemicalReactions::EquilibriumReactions::KernelWrapper m_equilibriumReactions; - - chemicalReactions::KineticReactions::KernelWrapper m_kineticReactions; - - arrayView2d< real64, compflow::USD_COMP > m_primarySpeciesConcentration; - - arrayView2d< real64, compflow::USD_COMP > m_secondarySpeciesConcentration; - - arrayView2d< real64, compflow::USD_COMP > m_primarySpeciesTotalConcentration; - - arrayView2d< real64, compflow::USD_COMP > m_kineticReactionRates; - }; - - struct viewKeyStruct : ConstitutiveBase::viewKeyStruct - {}; - -protected: - - virtual void postInputInitialization() override; - - void createChemicalReactions(); - - /// Reaction related terms - integer m_numPrimarySpecies; - - integer m_numSecondarySpecies; - - integer m_numKineticReactions; - - std::unique_ptr< chemicalReactions::EquilibriumReactions > m_equilibriumReactions; - - std::unique_ptr< chemicalReactions::KineticReactions > m_kineticReactions; - - array2d< real64, constitutive::multifluid::LAYOUT_FLUID > m_primarySpeciesConcentration; - - array2d< real64, constitutive::multifluid::LAYOUT_FLUID > m_secondarySpeciesConcentration; - - array2d< real64, constitutive::multifluid::LAYOUT_FLUID > m_primarySpeciesTotalConcentration; - - array2d< real64, constitutive::multifluid::LAYOUT_FLUID > m_kineticReactionRates; -}; - -inline void -ReactiveMultiFluid::KernelWrapper:: - computeChemistry( real64 const pressure, - real64 const temperature, - arraySlice1d< real64 const, compflow::USD_COMP - 1 > const & primarySpeciesTotalConcentration, - arraySlice1d< real64, compflow::USD_COMP - 1 > const & primarySpeciesConcentration, - arraySlice1d< real64, compflow::USD_COMP - 1 > const & secondarySpeciesConcentration, - arraySlice1d< real64, compflow::USD_COMP - 1 > const & kineticReactionRates ) const -{ - GEOS_UNUSED_VAR( pressure ); - - // 2. solve for equilibrium - m_equilibriumReactions.updateConcentrations( temperature, - primarySpeciesTotalConcentration, - primarySpeciesConcentration, - secondarySpeciesConcentration ); - - // 3. compute kinetic reaction rates - m_kineticReactions.computeReactionRates( temperature, - primarySpeciesConcentration, - secondarySpeciesConcentration, - kineticReactionRates ); -} - - -inline void -ReactiveMultiFluid::KernelWrapper:: - convertMoleFractionToMolarity( real64 const totalDensity, - real64 const totalMolecularWeight, - arraySlice1d< geos::real64 const, compflow::USD_COMP - 1 > const & composition, - arraySlice1d< geos::real64, compflow::USD_COMP - 1 > const & primarySpeciesTotalConcentration ) const -{ - // 1. Convert from mole fraction to molarity ( mol/L ) - real64 const conversionFactor = totalDensity / totalMolecularWeight * 1e-3; //conversion to L instead of cubic meters - for( int i=0; i < m_numPrimarySpecies; i++ ) - { - primarySpeciesTotalConcentration[i] = composition[i] * conversionFactor; - } -} - -} // namespace constitutive - -} // namespace geos - -#endif //GEOS_CONSTITUTIVE_FLUID_REACTIVEMULTIFLUID_HPP diff --git a/src/coreComponents/constitutive/fluid/multifluid/reactive/ReactiveMultiFluidFields.hpp b/src/coreComponents/constitutive/fluid/multifluid/reactive/ReactiveMultiFluidFields.hpp deleted file mode 100644 index d3a3a77f967..00000000000 --- a/src/coreComponents/constitutive/fluid/multifluid/reactive/ReactiveMultiFluidFields.hpp +++ /dev/null @@ -1,74 +0,0 @@ -/* - * ------------------------------------------------------------------------------------------------------------ - * 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 ReactiveMultiFluidFields.hpp - */ - -#ifndef GEOS_CONSTITUTIVE_FLUID_MULTIFLUID_REACTIVE_MULTIFLUIDFIELDS_HPP_ -#define GEOS_CONSTITUTIVE_FLUID_MULTIFLUID_REACTIVE_MULTIFLUIDFIELDS_HPP_ - -#include "constitutive/fluid/multifluid/Layouts.hpp" -#include "mesh/MeshFields.hpp" - -namespace geos -{ - -namespace fields -{ - -namespace reactivefluid -{ - -using array2dLayoutComp = array2d< real64, compflow::LAYOUT_COMP >; - -DECLARE_FIELD( primarySpeciesConcentration, - "primarySpeciesConcentration", - array2dLayoutComp, - 0, - LEVEL_0, - WRITE_AND_READ, - "primarySpeciesConcentration" ); - -DECLARE_FIELD( primarySpeciesTotalConcentration, - "primarySpeciesTotalConcentration", - array2dLayoutComp, - 0, - LEVEL_0, - WRITE_AND_READ, - "primarySpeciesTotalConcentration" ); - -DECLARE_FIELD( secondarySpeciesConcentration, - "secondarySpeciesConcentration", - array2dLayoutComp, - 0, - LEVEL_0, - WRITE_AND_READ, - "secondarySpeciesConcentration" ); - -DECLARE_FIELD( kineticReactionRates, - "kineticReactionRates", - array2dLayoutComp, - 0, - LEVEL_0, - WRITE_AND_READ, - "kineticReactionRates" ); -} - -} - -} - -#endif // GEOS_CONSTITUTIVE_FLUID_MULTIFLUIDFIELDS_HPP_ diff --git a/src/coreComponents/constitutive/fluid/multifluid/reactive/chemicalReactions/EquilibriumReactions.cpp b/src/coreComponents/constitutive/fluid/multifluid/reactive/chemicalReactions/EquilibriumReactions.cpp deleted file mode 100644 index 8ae3e0c8ee2..00000000000 --- a/src/coreComponents/constitutive/fluid/multifluid/reactive/chemicalReactions/EquilibriumReactions.cpp +++ /dev/null @@ -1,310 +0,0 @@ -/* - * ------------------------------------------------------------------------------------------------------------ - * 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 EquilibriumReactions.cpp - */ - -#include "EquilibriumReactions.hpp" - -#include "functions/FunctionManager.hpp" -#include "denseLinearAlgebra/interfaces/blaslapack/BlasLapackLA.hpp" - -namespace geos -{ - -using namespace stringutilities; - -namespace constitutive -{ - -namespace chemicalReactions -{ - -EquilibriumReactions::EquilibriumReactions( string const & name, integer const numPrimarySpecies, integer const numSecSpecies ): - ReactionsBase( name, numPrimarySpecies, numSecSpecies ) -{ - // Here we should either read the database or the input values. - - // Hardcoding values for now - - - - // Stochiometric Matrix - // First index: 0 = OH-, 1 = CO2, 2 = CO3-2, 3 = H2CO3, 4 = CaHCO3+, 5 = CaCO3, 6 = CaSO4, 7 = CaCl+, 8 = CaCl2, 9 = MgSO4, 10 = NaSO4- - // Second index: 0 = H+, 1 = HCO3-, 2 = Ca+2, 3 = SO4-2, 4 = Cl-, 5 = Mg+2, 6 = Na+1 - m_stoichMatrix.resize( m_numSecondarySpecies, m_numPrimarySpecies ); - m_stoichMatrix[0][0] = -1; - m_stoichMatrix[1][0] = 1; - m_stoichMatrix[1][1] = 1; - m_stoichMatrix[2][0] = -1; - m_stoichMatrix[2][1] = 1; - m_stoichMatrix[3][0] = 1; - m_stoichMatrix[3][1] = 1; - m_stoichMatrix[4][1] = 1; - m_stoichMatrix[4][2] = 1; - m_stoichMatrix[5][0] = -1; - m_stoichMatrix[5][1] = 1; - m_stoichMatrix[5][2] = 1; - m_stoichMatrix[6][2] = 1; - m_stoichMatrix[6][3] = 1; - m_stoichMatrix[7][2] = 1; - m_stoichMatrix[7][4] = 1; - m_stoichMatrix[8][2] = 1; - m_stoichMatrix[8][4] = 2; - m_stoichMatrix[9][5] = 1; - m_stoichMatrix[9][3] = 1; - m_stoichMatrix[10][6] = 1; - m_stoichMatrix[10][3] = 1; - - // Equilibrium Constant - m_log10EqConst.resize( m_numSecondarySpecies ); - m_log10EqConst[0] = 13.99; - m_log10EqConst[1] = -6.36; - m_log10EqConst[2] = 10.33; - m_log10EqConst[3] = -3.77; - m_log10EqConst[4] = -1.09; - m_log10EqConst[5] = 7.07; - m_log10EqConst[6] = -2.16; - m_log10EqConst[7] = 0.67; - m_log10EqConst[8] = 0.60; - m_log10EqConst[9] = -2.43; - m_log10EqConst[10] = -0.82; -} - -EquilibriumReactions::KernelWrapper EquilibriumReactions::createKernelWrapper() const -{ - return KernelWrapper( m_numPrimarySpecies, - m_numSecondarySpecies, - m_log10EqConst, - m_stoichMatrix, - m_chargePrimary, - m_chargeSec, - m_ionSizePrimary, - m_ionSizeSec, - m_DebyeHuckelA, - m_DebyeHuckelB, - m_WATEQBDot ); -} - -void EquilibriumReactions::KernelWrapper::assembleEquilibriumReactionSystem( real64 const temperature, - arraySlice1d< real64 const, compflow::USD_COMP - 1 > const & primarySpeciesTotalConcentration, - arraySlice1d< real64 const, compflow::USD_COMP - 1 > const & primarySpeciesConcentration, - arraySlice1d< real64, compflow::USD_COMP - 1 > const & secondarySpeciesConcentration, - arraySlice2d< real64 > const & matrix, - arraySlice1d< real64 > const & rhs ) const -{ - - stackArray1d< real64, ReactionsBase::maxNumPrimarySpecies > log10PrimaryActCoeff( m_numPrimarySpecies ); - stackArray1d< real64, ReactionsBase::maxNumSecondarySpecies > log10SecActCoeff( m_numSecondarySpecies ); - stackArray2d< real64, ReactionsBase::maxNumSecondarySpecies * ReactionsBase::maxNumPrimarySpecies > dLog10SecConc_dLog10PrimaryConc( m_numSecondarySpecies, m_numPrimarySpecies ); - stackArray1d< real64, ReactionsBase::maxNumPrimarySpecies > totalConcentration( m_numPrimarySpecies ); - stackArray2d< real64, ReactionsBase::maxNumPrimarySpecies * ReactionsBase::maxNumPrimarySpecies > dTotalConc_dLog10PrimaryConc( m_numPrimarySpecies, m_numPrimarySpecies ); - - real64 ionicStrength = 0.0; - stackArray1d< real64, ReactionsBase::maxNumPrimarySpecies > dIonicStrength_dPrimaryConcentration( m_numPrimarySpecies ); - stackArray1d< real64, ReactionsBase::maxNumPrimarySpecies > dLog10PrimaryActCoeff_dIonicStrength( m_numPrimarySpecies ); - stackArray1d< real64, ReactionsBase::maxNumSecondarySpecies > dLog10SecActCoeff_dIonicStrength( m_numSecondarySpecies ); - - /// activity coefficients - computeIonicStrength( primarySpeciesConcentration, - secondarySpeciesConcentration, - ionicStrength ); - - computeLog10ActCoefBDotModel( temperature, - ionicStrength, - log10PrimaryActCoeff, - dLog10PrimaryActCoeff_dIonicStrength, - log10SecActCoeff, - dLog10SecActCoeff_dIonicStrength ); - - computeSeondarySpeciesConcAndDerivative( temperature, - log10PrimaryActCoeff, - dLog10PrimaryActCoeff_dIonicStrength, - log10SecActCoeff, - dLog10SecActCoeff_dIonicStrength, - primarySpeciesConcentration, - secondarySpeciesConcentration, - dLog10SecConc_dLog10PrimaryConc ); - - computeTotalConcAndDerivative( temperature, - primarySpeciesConcentration, - secondarySpeciesConcentration, - dLog10SecConc_dLog10PrimaryConc, - totalConcentration, - dTotalConc_dLog10PrimaryConc ); - - for( int i=0; i const & primarySpeciesTotalConcentration, - arraySlice1d< real64, compflow::USD_COMP - 1 > const & primarySpeciesConcentration, - arraySlice1d< real64, compflow::USD_COMP - 1 > const & secondarySpeciesConcentration ) const - -{ - stackArray2d< real64, ReactionsBase::maxNumPrimarySpecies * ReactionsBase::maxNumPrimarySpecies > matrix( m_numPrimarySpecies, m_numPrimarySpecies ); - stackArray1d< real64, ReactionsBase::maxNumPrimarySpecies > rhs( m_numPrimarySpecies ); - stackArray1d< real64, ReactionsBase::maxNumPrimarySpecies > solution( m_numPrimarySpecies ); - - setInitialGuess( primarySpeciesTotalConcentration, primarySpeciesConcentration ); - - bool converged = false; - for( int iteration = 0; iteration < m_maxNumIterations; iteration++ ) - { - - for( int i = 0; i< m_numPrimarySpecies; i++ ) - { - rhs[i] = 0.0; - solution[i] = 0.0; - for( int j = 0; j< m_numPrimarySpecies; j++ ) - { - matrix( i, j ) = 0.0; - } - } - - assembleEquilibriumReactionSystem( temperature, - primarySpeciesTotalConcentration, - primarySpeciesConcentration, - secondarySpeciesConcentration, - matrix, - rhs ); - - real64 const residualNorm = BlasLapackLA::vectorNorm2( rhs.toSliceConst() ); - - if( residualNorm < m_newtonTol && iteration >= 1 ) - { - converged = true; - break; - } - - BlasLapackLA::solveLinearSystem( matrix, rhs, solution ); - - updatePrimarySpeciesConcentrations( solution, primarySpeciesConcentration ); - } - GEOS_ERROR_IF( !converged, "Equilibrium reactions did not converge." ); -} - -// function to compute the derivative of the concentration of secondary species with respect to the concentration of the primary species. -void EquilibriumReactions::KernelWrapper::computeSeondarySpeciesConcAndDerivative( real64 const temperature, - arraySlice1d< real64 const > const & log10PrimaryActCoeff, - arraySlice1d< real64 const > const & dLog10PrimaryActCoeff_dIonicStrength, - arraySlice1d< real64 const > const & log10SecActCoeff, - arraySlice1d< real64 const > const & dLog10SecActCoeff_dIonicStrength, - arraySlice1d< real64 const, compflow::USD_COMP - 1 > const & primarySpeciesConcentration, - arraySlice1d< real64, compflow::USD_COMP - 1 > const & secondarySpeciesConectration, - arraySlice2d< real64 > const & dLog10SecConc_dLog10PrimaryConc ) const -{ - GEOS_UNUSED_VAR( temperature ); - - // Compute d(concentration of dependent species)/d(concentration of basis species) - for( int iSec = 0; iSec < m_numSecondarySpecies; iSec++ ) - { - real64 log10SecConc = -m_log10EqConst[iSec] - log10SecActCoeff[iSec]; - - for( int jPri = 0; jPri < m_numPrimarySpecies; jPri++ ) - { - real64 const dIonicStrength_dPrimaryConc = log( 10 ) * 0.5 * m_chargePrimary[jPri] * m_chargePrimary[jPri]; - - log10SecConc += m_stoichMatrix[iSec][jPri] * ( log10( primarySpeciesConcentration[jPri] ) + log10PrimaryActCoeff[jPri] ); - dLog10SecConc_dLog10PrimaryConc[iSec][jPri] += m_stoichMatrix[iSec][jPri] - dLog10SecActCoeff_dIonicStrength[iSec] * primarySpeciesConcentration[jPri] * - dIonicStrength_dPrimaryConc; - for( int kDerivative = 0; kDerivative < m_numPrimarySpecies; kDerivative++ ) - { - // add contribution to the derivtive from all primary activity coefficients - dLog10SecConc_dLog10PrimaryConc[iSec][jPri] += m_stoichMatrix[iSec][kDerivative] * dLog10PrimaryActCoeff_dIonicStrength[kDerivative] * primarySpeciesConcentration[jPri] * - dIonicStrength_dPrimaryConc; - } - - } - secondarySpeciesConectration[iSec] = pow( 10, log10SecConc ); - } - -} - -void EquilibriumReactions::KernelWrapper::computeTotalConcAndDerivative( real64 const temperature, - arraySlice1d< real64 const, compflow::USD_COMP - 1 > const & primarySpeciesConcentration, - arraySlice1d< real64 const, compflow::USD_COMP - 1 > const & secondarySpeciesConectration, - arraySlice2d< real64 const > const & dLog10SecConc_dLog10PrimaryConc, - arraySlice1d< real64 > const & totalConc, - arraySlice2d< real64 > const & dTotalConc_dLog10PrimaryConc ) const - - -{ - GEOS_UNUSED_VAR( temperature ); - - // This function computes the total concentration and its derivative with respect to log10(basis species concentrations). - for( int iPri = 0; iPri < m_numPrimarySpecies; iPri++ ) - { - totalConc[iPri] = primarySpeciesConcentration[iPri]; - // d(total concentration)/d(log10(concentration)) - dTotalConc_dLog10PrimaryConc[iPri][iPri] = log( 10.0 ) * primarySpeciesConcentration[iPri]; - // contribution from all dependent species - for( int jSec = 0; jSec < m_numSecondarySpecies; jSec++ ) - { - totalConc[iPri] += m_stoichMatrix[jSec][iPri] * secondarySpeciesConectration[jSec]; - for( int kDerivative = 0; kDerivative < m_numPrimarySpecies; kDerivative++ ) - { - // add contribution to the derivtive from dependent species via the chain rule - dTotalConc_dLog10PrimaryConc[iPri][kDerivative] += m_stoichMatrix[jSec][iPri] * log( 10.0 ) * - secondarySpeciesConectration[jSec] * dLog10SecConc_dLog10PrimaryConc[jSec][kDerivative]; - } - } - } -} - -void EquilibriumReactions::KernelWrapper:: - updatePrimarySpeciesConcentrations( arraySlice1d< real64 const > const solution, - arraySlice1d< real64, compflow::USD_COMP - 1 > const & primarySpeciesConcentration ) const -{ - for( integer i = 0; i < m_numPrimarySpecies; i++ ) - { - primarySpeciesConcentration[i] *= pow( 10, solution[i] ); - } -} - -void EquilibriumReactions::KernelWrapper::setInitialGuess( arraySlice1d< real64 const, compflow::USD_COMP - 1 > const & primarySpeciesTotalConcentration, - arraySlice1d< real64, compflow::USD_COMP - 1 > const & primarySpeciesConcentration ) const -{ - for( integer i = 0; i < m_numPrimarySpecies; i++ ) - { - primarySpeciesConcentration[i] = primarySpeciesTotalConcentration[i]; - } - real64 const hPlusConcentration = 2*primarySpeciesConcentration[2]-2*primarySpeciesConcentration[3]-primarySpeciesConcentration[4]+2*primarySpeciesConcentration[5]+primarySpeciesConcentration[6]; - if( hPlusConcentration < 0 ) - { - primarySpeciesConcentration[0] = -hPlusConcentration; - } - else - { - primarySpeciesConcentration[0] = 1e-7; - } - -} - - -} // end namespace chemicalReactions - -} // namespace constitutive - -} // end namespace geos diff --git a/src/coreComponents/constitutive/fluid/multifluid/reactive/chemicalReactions/EquilibriumReactions.hpp b/src/coreComponents/constitutive/fluid/multifluid/reactive/chemicalReactions/EquilibriumReactions.hpp deleted file mode 100644 index e3908d4df7d..00000000000 --- a/src/coreComponents/constitutive/fluid/multifluid/reactive/chemicalReactions/EquilibriumReactions.hpp +++ /dev/null @@ -1,132 +0,0 @@ -/* - * ------------------------------------------------------------------------------------------------------------ - * 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 EquilibriumReactions.hpp - */ - -#ifndef GEOS_CONSTITUTIVE_FLUID_MULTIFLUID_REACTIVE_CHEMICALREACTIONS_EQUILIBRIUMREACTIONS_HPP_ -#define GEOS_CONSTITUTIVE_FLUID_MULTIFLUID_REACTIVE_CHEMICALREACTIONS_EQUILIBRIUMREACTIONS_HPP_ - -#include "ReactionsBase.hpp" - -#include "constitutive/fluid/multifluid/Layouts.hpp" -#include "constitutive/fluid/multifluid/MultiFluidConstants.hpp" - -namespace geos -{ - -namespace constitutive -{ - -namespace chemicalReactions -{ - -class EquilibriumReactions : public ReactionsBase -{ -public: - - EquilibriumReactions( string const & name, integer const numPrimarySpecies, integer const numSecSpecies ); - - class KernelWrapper final : public ReactionsBase::KernelWrapper - { -public: - - KernelWrapper( integer const numPrimarySpecies, - integer const numSecondarySpecies, - arrayView1d< real64 > const & log10EqConst, - arrayView2d< real64 > const & stoichMatrix, - arrayView1d< integer > const & chargePrimary, - arrayView1d< integer > const & chargeSec, - arrayView1d< real64 > const & ionSizePrimary, - arrayView1d< real64 > const & ionSizeSec, - real64 const DebyeHuckelA, - real64 const DebyeHuckelB, - real64 const WATEQBDot ): - ReactionsBase::KernelWrapper( numPrimarySpecies, - numSecondarySpecies, - log10EqConst, - stoichMatrix, - chargePrimary, - chargeSec, - ionSizePrimary, - ionSizeSec, - DebyeHuckelA, - DebyeHuckelB, - WATEQBDot ) - {} - - /** - * @brief Construct a new update Concentrations object - * - * @param temperature - * @param totalConc - * @param dLog10PrimaryConc_dTotalConc - */ - void updateConcentrations( real64 const temperature, - arraySlice1d< real64 const, compflow::USD_COMP - 1 > const & primarySpeciesTotalConcentration, - arraySlice1d< real64, compflow::USD_COMP - 1 > const & primarySpeciesContentration, - arraySlice1d< real64, compflow::USD_COMP - 1 > const & secondarySpeciesConcentration ) const; -private: - - void assembleEquilibriumReactionSystem( real64 const temperature, - arraySlice1d< real64 const, compflow::USD_COMP - 1 > const & primarySpeciesTotalConcentration, - arraySlice1d< real64 const, compflow::USD_COMP - 1 > const & primarySpeciesConcentration, - arraySlice1d< real64, compflow::USD_COMP - 1 > const & secondarySpeciesConcentration, - arraySlice2d< real64 > const & matrix, - arraySlice1d< real64 > const & rhs ) const; - - void computeSeondarySpeciesConcAndDerivative( real64 const temperature, - arraySlice1d< real64 const > const & log10PrimaryActCoeff, - arraySlice1d< real64 const > const & dLog10PrimaryActCoeff_dIonicStrength, - arraySlice1d< real64 const > const & log10SecActCoeff, - arraySlice1d< real64 const > const & dLog10SecActCoeff_dIonicStrength, - arraySlice1d< real64 const, compflow::USD_COMP - 1 > const & primarySpeciesConcentration, - arraySlice1d< real64, compflow::USD_COMP - 1 > const & secondarySpeciesConcentration, - arraySlice2d< real64 > const & dLog10SecConc_dLog10PrimaryConc ) const; - - void computeTotalConcAndDerivative( real64 const temperature, - arraySlice1d< real64 const, compflow::USD_COMP - 1 > const & primarySpeciesConcentration, - arraySlice1d< real64 const, compflow::USD_COMP - 1 > const & secondarySpeciesConcentration, - arraySlice2d< real64 const > const & dLog10SecConc_dLog10PrimaryConc, - arraySlice1d< real64 > const & totalConc, - arraySlice2d< real64 > const & dTotalConc_dLog10PrimaryConc ) const; - - void updatePrimarySpeciesConcentrations( arraySlice1d< real64 const > const solution, - arraySlice1d< real64, compflow::USD_COMP - 1 > const & primarySpeciesConcentration ) const; - - void setInitialGuess( arraySlice1d< real64 const, compflow::USD_COMP - 1 > const & primarySpeciesTotalConcentration, - arraySlice1d< real64, compflow::USD_COMP - 1 > const & primarySpeciesConcentration ) const; - - static constexpr integer m_maxNumIterations = MultiFluidConstants::maxNewtonIterations; - static constexpr real64 m_newtonTol = 1e-6; - }; - -/** - * @brief Create an update kernel wrapper. - * @return the wrapper - */ - KernelWrapper createKernelWrapper() const; - -}; - - -} // end namespace chemicalReactions - -} // end namespace constitutive - -} // end namespace geos - -#endif //GEOS_CONSTITUTIVE_FLUID_CHEMICALREACTIONS_REQUILIBRIUMREACTIONS_HPP_ diff --git a/src/coreComponents/constitutive/fluid/multifluid/reactive/chemicalReactions/KineticReactions.cpp b/src/coreComponents/constitutive/fluid/multifluid/reactive/chemicalReactions/KineticReactions.cpp deleted file mode 100644 index 5009c9fbeae..00000000000 --- a/src/coreComponents/constitutive/fluid/multifluid/reactive/chemicalReactions/KineticReactions.cpp +++ /dev/null @@ -1,205 +0,0 @@ -/* - * ------------------------------------------------------------------------------------------------------------ - * 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 KineticReactions.cpp - */ - -#include "constitutive/fluid/multifluid/reactive/chemicalReactions/KineticReactions.hpp" -#include "functions/FunctionManager.hpp" -#include "common/Units.hpp" - -namespace geos -{ - -using namespace stringutilities; - -namespace constitutive -{ - -namespace chemicalReactions -{ - -KineticReactions::KineticReactions( string const & name, integer const numPrimarySpecies, integer const numSecSpecies, integer const numKineticReactions ): - ReactionsBase( name, numPrimarySpecies, numSecSpecies ), - m_numKineticReactions( numKineticReactions ) -{ - - // Stochiometric Matrix for the kinetic reactions (in terms of primary species only) - // First index: 0 = Ca(OH)2 dissolution, 1 = CaCO3 dissolution - // Second index: 0 = H+, 1 = HCO3-, 2 = Ca+2, 3 = SO4-2, 4 = Cl-, 5 = Mg+2, 6 = Na+1 - m_stoichMatrix.resize( m_numKineticReactions, m_numPrimarySpecies ); - m_stoichMatrix[0][0] = -2; - m_stoichMatrix[0][2] = 1; - m_stoichMatrix[1][0] = -1; - m_stoichMatrix[1][1] = 1; - m_stoichMatrix[1][2] = 1; - - // Equilibrium Constant - m_log10EqConst.resize( m_numKineticReactions ); - m_log10EqConst[0] = 20.19; - m_log10EqConst[1] = 1.32; - - // Rate Constant - // have to check the values as the functional form of the rate equation here differs from the implementation in GEOS - m_reactionRateConstant.resize( m_numKineticReactions ); - m_reactionRateConstant[0] = 9.95e-1; - m_reactionRateConstant[1] = 9.95e-3; - // Here we should either read the database or the input values. - - m_specificSurfaceArea = 1.0; -} - -KineticReactions::KernelWrapper KineticReactions::createKernelWrapper() const -{ - return KernelWrapper( m_numPrimarySpecies, - m_numSecondarySpecies, - m_numKineticReactions, - m_log10EqConst, - m_stoichMatrix, - m_chargePrimary, - m_chargeSec, - m_ionSizePrimary, - m_ionSizeSec, - m_DebyeHuckelA, - m_DebyeHuckelB, - m_WATEQBDot, - m_reactionRateConstant, - m_specificSurfaceArea ); -} - -// function to the reaction rate. Includes impact of temperature, concentration, surface area, volume fraction and porosity -void KineticReactions::KernelWrapper::computeReactionRates( real64 const & temperature, - arraySlice1d< real64 const, compflow::USD_COMP - 1 > const & primarySpeciesConcentration, - arraySlice1d< real64 const, compflow::USD_COMP - 1 > const & secondarySpeciesConcentration, - arraySlice1d< real64, compflow::USD_COMP - 1 > const & reactionRates ) const -{ - /// 1. Create local vectors - stackArray1d< real64, ReactionsBase::maxNumPrimarySpecies > log10PrimaryActCoeff( m_numPrimarySpecies ); - stackArray1d< real64, ReactionsBase::maxNumSecondarySpecies > log10SecActCoeff( m_numSecondarySpecies ); - - real64 ionicStrength = 0.0; - stackArray1d< real64, ReactionsBase::maxNumPrimarySpecies > dIonicStrength_dPrimaryConcentration( m_numPrimarySpecies ); - stackArray1d< real64, ReactionsBase::maxNumPrimarySpecies > dLog10PrimaryActCoeff_dIonicStrength( m_numPrimarySpecies ); - stackArray1d< real64, ReactionsBase::maxNumSecondarySpecies > dLog10SecActCoeff_dIonicStrength( m_numSecondarySpecies ); - - /// 2. Compute activity coefficients - computeIonicStrength( primarySpeciesConcentration, - secondarySpeciesConcentration, - ionicStrength ); - - computeLog10ActCoefBDotModel( temperature, - ionicStrength, - log10PrimaryActCoeff, - dLog10PrimaryActCoeff_dIonicStrength, - log10SecActCoeff, - dLog10SecActCoeff_dIonicStrength ); - - - /// 3. Compute reaction rates - for( int iRxn = 0; iRxn < m_numKineticReactions; iRxn++ ) - { - real64 saturationIndex = -m_log10EqConst[iRxn]; - - for( int iPri = 0; iPri < m_numPrimarySpecies; ++iPri ) - { - saturationIndex += m_stoichMatrix[iRxn][iPri] * log10( primarySpeciesConcentration[iPri] ); - saturationIndex += m_stoichMatrix[iRxn][iPri] * log10PrimaryActCoeff[iPri]; - } - - reactionRates[iRxn] = m_specificSurfaceArea * (1.0 - pow( 10, saturationIndex ) ) * m_reactionRateConstant[iRxn]; - } -} - -} // end namespace chemicalReactions - -} // namespace constitutive - -} // end namespace geos - -/* - for( localIndex ir = 0; ir < NReaction; ++ir ) - { - - for( localIndex ic = 0; ic < kineticReaction.stochs.size(); ++ic ) - { - dSIndex[id] = kineticReaction.stochs[ic]; - - } - - - for( localIndex ic = 0; ic < NBasis; ++ic ) - { - - dKineticReactionRate_dConc[ir][ic] = S * kineticReaction.rateConst * rateTemp * pow( 10.0, SIndex ) * log( 10.0 ) * dSIndex[ic]; - - } - - } - - - for( localIndex ir = 0; ir < NReaction; ++ir ) - { - - for( localIndex i = 0; i < (kineticReaction.stochs).size(); ++i ) - { - - localIndex ic = basisSpeciesIndices[i]; - - kineticSpeciesReactionRate[ic] += -(kineticReaction.stochs)[i] * kineticReactionRate[ir]; - - for( localIndex id = 0; id < NBasis; ++id ) - { - - dKineticSpeciesReactionRate_dConc[ic][id] += -(kineticReaction.stochs)[i] * dKineticReactionRate_dConc[ir][id]; - - } - - } - - } - - } - // arraySlice1d< real64 const > const & concentration, - // arraySlice1d< real64 const > const & surfaceArea0, - // arraySlice1d< real64 const > const & volumeFraction0, - // arraySlice1d< real64 const > const & volumeFraction, - // real64 const & porosity0, - // real64 const & porosity, - // arraySlice1d< real64 > const & kineticReactionRate ) - // { - // for( localIndex ir = 0; ir < kineticReactionArray.size(); ++ir ) - // { - // const KineticReaction & kineticReaction = kineticReactionArray[ir]; - // const array1d< localIndex > & basisSpeciesIndices = kineticReaction.basisSpeciesIndices; - // // calculate saturation index - // real64 SIndex = -kineticReaction.logK; - // for( localIndex ic = 0; ic < kineticReaction.stochs.size(); ++ic ) - // { - // SIndex += kineticReaction.stochs[ic] * concentration[basisSpeciesIndices[ic] ]; // Check that the input "concentration" is - // // actually ln(activity - // coefficient*concentration) - // } - // // surface area is assumed to scale with the volume fraction. Check whether this is the volume fraction of the mineral - // // dissolving/precipitating. Not sure why porosity is included. - // real64 S = surfaceArea0[ir] * pow( volumeFraction[ir] / volumeFraction0[ir], 2.0/3.0 ) * pow( porosity / porosity0, 2.0/3.0 ); - // // computing the rate at the correct temperature. Looks like EQ36 database has it at 298.15 K - // real64 rateTemp = exp( -kineticReaction.E / RConst * (1.0 / units::convertCToK( temperature ) - 1.0 / 298.15)); - // real64 SS = (pow( 10.0, SIndex ) - 1.0); - // kineticReactionRate[ir] = S * kineticReaction.rateConst * rateTemp * SS; - //} - - - */ diff --git a/src/coreComponents/constitutive/fluid/multifluid/reactive/chemicalReactions/KineticReactions.hpp b/src/coreComponents/constitutive/fluid/multifluid/reactive/chemicalReactions/KineticReactions.hpp deleted file mode 100644 index 7c2ff5bf64a..00000000000 --- a/src/coreComponents/constitutive/fluid/multifluid/reactive/chemicalReactions/KineticReactions.hpp +++ /dev/null @@ -1,124 +0,0 @@ -/* - * ------------------------------------------------------------------------------------------------------------ - * 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 EquilibriumReaction.hpp - */ - -#ifndef GEOS_CONSTITUTIVE_FLUID_MULTIFLUID_REACTIVE_CHEMICALREACTIONS_REACTIONBASE_HPP_ -#define GEOS_CONSTITUTIVE_FLUID_MULTIFLUID_REACTIVE_CHEMICALREACTIONS_REACTIONBASE_HPP_ - -#include "ReactionsBase.hpp" - -#include "constitutive/fluid/multifluid/Layouts.hpp" -#include "common/PhysicsConstants.hpp" - -namespace geos -{ - -namespace constitutive -{ - -namespace chemicalReactions -{ - -class KineticReactions : public ReactionsBase -{ -public: - - KineticReactions( string const & name, integer const numPrimarySpecies, integer const numSecSpecies, integer const numKineticReactions ); - - class KernelWrapper final : public ReactionsBase::KernelWrapper - { -public: - - static constexpr real64 RConst = constants::gasConstant; - - KernelWrapper( integer const numPrimarySpecies, - integer const numSecondarySpecies, - integer const numKineticReactions, - arrayView1d< real64 > const & log10EqConst, - arrayView2d< real64 > const & stoichMatrix, - arrayView1d< integer > const & chargePrimary, - arrayView1d< integer > const & chargeSec, - arrayView1d< real64 > const & ionSizePrimary, - arrayView1d< real64 > const & ionSizeSec, - real64 const DebyeHuckelA, - real64 const DebyeHuckelB, - real64 const WATEQBDot, - arrayView1d< real64 > const & reactionRateConstant, - real64 const specificSurfaceArea ): - ReactionsBase::KernelWrapper( numPrimarySpecies, - numSecondarySpecies, - log10EqConst, - stoichMatrix, - chargePrimary, - chargeSec, - ionSizePrimary, - ionSizeSec, - DebyeHuckelA, - DebyeHuckelB, - WATEQBDot ), - m_reactionRateConstant( reactionRateConstant ), - m_numKineticReactions( numKineticReactions ), - m_specificSurfaceArea( specificSurfaceArea ) - {} - - /** - * @brief Compute kinetic reaction rates. - * - * @param temperature - * @param primarySpeciesConcentration concentration of the primary species - * @param log10PrimaryActCoeff - * @param specificSurfaceArea the surface area available per unit volume - * @param reactionRates - */ - void computeReactionRates( real64 const & temperature, - arraySlice1d< real64 const, compflow::USD_COMP - 1 > const & primarySpeciesConcentration, - arraySlice1d< real64 const, compflow::USD_COMP - 1 > const & secondarySpeciesConcentration, - arraySlice1d< real64, compflow::USD_COMP - 1 > const & reactionRates ) const; - -private: - - arrayView1d< real64 > m_reactionRateConstant; - - integer m_numKineticReactions; - - real64 m_specificSurfaceArea; - - }; - - /** - * @brief Create an update kernel wrapper. - * @return the wrapper - */ - KernelWrapper createKernelWrapper() const; - -private: - - array1d< real64 > m_reactionRateConstant; - - integer m_numKineticReactions; - - real64 m_specificSurfaceArea; -}; - -} // end namespace chemicalReactions - -} // end namespace constitutive - -} // end namespace geos - -#endif //GEOS_CONSTITUTIVE_FLUID_CHEMICALREACTIONS_REACTIONBASE_HPP_ diff --git a/src/coreComponents/constitutive/fluid/multifluid/reactive/chemicalReactions/ReactionsBase.cpp b/src/coreComponents/constitutive/fluid/multifluid/reactive/chemicalReactions/ReactionsBase.cpp deleted file mode 100644 index df579b3587e..00000000000 --- a/src/coreComponents/constitutive/fluid/multifluid/reactive/chemicalReactions/ReactionsBase.cpp +++ /dev/null @@ -1,152 +0,0 @@ -/* - * ------------------------------------------------------------------------------------------------------------ - * 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 EquilibriumReaction.cpp - */ - -#include "constitutive/fluid/multifluid/reactive/chemicalReactions/ReactionsBase.hpp" - -#include "functions/FunctionManager.hpp" - -namespace geos -{ - -using namespace stringutilities; - -namespace constitutive -{ - -namespace chemicalReactions -{ - -ReactionsBase::ReactionsBase( string const & name, integer const numPrimarySpecies, integer const numSecSpecies ): - m_name( name ), - m_numPrimarySpecies( numPrimarySpecies ), - m_numSecondarySpecies( numSecSpecies ) -{ - // Activity coefficient related constants - m_DebyeHuckelA = 0.5465; - m_DebyeHuckelB = 0.3346; - m_WATEQBDot = 0.0438; - - m_ionSizePrimary.resize( m_numPrimarySpecies ); - m_ionSizePrimary[0] = 9.00; - m_ionSizePrimary[1] = 4.00; - m_ionSizePrimary[2] = 6.00; - m_ionSizePrimary[3] = 4.00; - m_ionSizePrimary[4] = 3.00; - m_ionSizePrimary[5] = 8.00; - m_ionSizePrimary[6] = 4.00; - - m_ionSizeSec.resize( m_numSecondarySpecies ); - m_ionSizeSec[0] = 3.50; - m_ionSizeSec[1] = 3.00; - m_ionSizeSec[2] = 4.50; - m_ionSizeSec[3] = 3.00; - m_ionSizeSec[4] = 4.00; - m_ionSizeSec[5] = 3.00; - m_ionSizeSec[6] = 3.00; - m_ionSizeSec[7] = 4.00; - m_ionSizeSec[8] = 3.00; - m_ionSizeSec[9] = 3.00; - m_ionSizeSec[10] = 4.00; - - m_chargePrimary.resize( m_numPrimarySpecies ); - m_chargePrimary[0] = 1; - m_chargePrimary[1] = -1; - m_chargePrimary[2] = 2; - m_chargePrimary[3] = -2; - m_chargePrimary[4] = -1; - m_chargePrimary[5] = 2; - m_chargePrimary[6] = 1; - - m_chargeSec.resize( m_numSecondarySpecies ); - m_chargeSec[0] = -1; - m_chargeSec[1] = 0; - m_chargeSec[2] = -2; - m_chargeSec[3] = 0; - m_chargeSec[4] = 1; - m_chargeSec[5] = 0; - m_chargeSec[6] = 0; - m_chargeSec[7] = 1; - m_chargeSec[8] = 0; - m_chargeSec[9] = 0; - m_chargeSec[10] = -1; - -} - -void ReactionsBase::KernelWrapper::computeLog10ActCoefBDotModel( real64 const temperature, - real64 const ionicStrength, - arraySlice1d< real64 > const & log10PrimaryActCoeff, - arraySlice1d< real64 > const & dLog10PrimaryActCoeff_dIonicStrength, - arraySlice1d< real64 > const & log10SecActCoeff, - arraySlice1d< real64 > const & dLog10SecActCoeff_dIonicStrength ) const -{ - // Compute log10(ActivityCoefficient) for basis and dependent species along with their - // derivatives with respect to Ionic strength using the B-Dot Model - // which is the same as the Extended Debye-Huckel model in GEOS. - // localIndex const NBasis = m_numPrimarySpecies; - // localIndex const NDependent = m_numSecondarySpecies; - - GEOS_UNUSED_VAR( temperature ); - - for( localIndex i = 0; i < m_numPrimarySpecies; ++i ) - { - log10PrimaryActCoeff[i] = m_WATEQBDot * ionicStrength - m_DebyeHuckelA * m_chargePrimary[i] * m_chargePrimary[i] * sqrt( ionicStrength ) / - (1.0 + m_ionSizePrimary[i] * m_DebyeHuckelB * sqrt( ionicStrength )); - dLog10PrimaryActCoeff_dIonicStrength[i] = m_WATEQBDot - m_DebyeHuckelA * m_chargePrimary[i] * m_chargePrimary[i] * - (0.5 / sqrt( ionicStrength ) / (1.0 + m_ionSizePrimary[i] * m_DebyeHuckelB * sqrt( ionicStrength )) - 0.5 * m_ionSizePrimary[i] * m_DebyeHuckelB / - (1.0 + m_ionSizePrimary[i] * m_DebyeHuckelB * sqrt( ionicStrength )) / - (1.0 + m_ionSizePrimary[i] * m_DebyeHuckelB * sqrt( ionicStrength ))); -// log10PrimaryActCoeff[i] = 0; -// dLog10PrimaryActCoeff_dIonicStrength[i] = 0; - } - for( localIndex i = 0; i < m_numSecondarySpecies; ++i ) - { - log10SecActCoeff[i] = m_WATEQBDot * ionicStrength - m_DebyeHuckelA * m_chargeSec[i] * m_chargeSec[i] * sqrt( ionicStrength ) / - (1.0 + m_ionSizeSec[i] * m_DebyeHuckelB * sqrt( ionicStrength )); - dLog10SecActCoeff_dIonicStrength[i] = m_WATEQBDot - m_DebyeHuckelA * m_chargeSec[i] * m_chargeSec[i] * - (0.5 / sqrt( ionicStrength ) / (1.0 + m_ionSizeSec[i] * m_DebyeHuckelB * sqrt( ionicStrength )) - 0.5 * m_ionSizeSec[i] * m_DebyeHuckelB / - (1.0 + m_ionSizeSec[i] * m_DebyeHuckelB * sqrt( ionicStrength )) / - (1.0 + m_ionSizeSec[i] * m_DebyeHuckelB * sqrt( ionicStrength ))); -// log10SecActCoeff[i] = 0; -// dLog10SecActCoeff_dIonicStrength[i] = 0; - } -} - -void ReactionsBase::KernelWrapper::computeIonicStrength( arraySlice1d< real64 const, compflow::USD_COMP - 1 > const & primarySpeciesConcentration, - arraySlice1d< real64 const, compflow::USD_COMP - 1 > const & secondarySpeciesConcentration, - real64 & ionicStrength ) const -{ - //get ionic strength - ionicStrength = 0.0; - // Primary species - for( localIndex i = 0; i < m_numPrimarySpecies; ++i ) - { - ionicStrength += 0.5 * m_chargePrimary[i] * m_chargePrimary[i] * primarySpeciesConcentration[i]; - } - // Secondary species - for( int j = 0; j < m_numSecondarySpecies; ++j ) - { - ionicStrength += 0.5 * m_chargeSec[j] * m_chargeSec[j] * secondarySpeciesConcentration[j]; - } -} - -} // end namespace chemicalReactions - -} // namespace constitutive - -} // end namespace geos diff --git a/src/coreComponents/constitutive/fluid/multifluid/reactive/chemicalReactions/ReactionsBase.hpp b/src/coreComponents/constitutive/fluid/multifluid/reactive/chemicalReactions/ReactionsBase.hpp deleted file mode 100644 index 8b3e01b335f..00000000000 --- a/src/coreComponents/constitutive/fluid/multifluid/reactive/chemicalReactions/ReactionsBase.hpp +++ /dev/null @@ -1,170 +0,0 @@ -/* - * ------------------------------------------------------------------------------------------------------------ - * 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 ReactionsBase.hpp - */ - -#ifndef GEOS_CONSTITUTIVE_FLUID_MULTIFLUID_REACTIVE_CHEMICALREACTIONS_REACTIONSBASE_HPP_ -#define GEOS_CONSTITUTIVE_FLUID_MULTIFLUID_REACTIVE_CHEMICALREACTIONS_REACTIONSBASE_HPP_ - -#include "dataRepository/ObjectCatalog.hpp" - -namespace geos -{ - -namespace constitutive -{ - -namespace chemicalReactions -{ - -class ReactionsBase -{ -public: - - ReactionsBase( string const & name, integer const numPrimarySpecies, integer const numSecSpecies ); - - virtual ~ReactionsBase() = default; - - constexpr static integer maxNumPrimarySpecies = 12; - constexpr static integer maxNumSecondarySpecies = 15; - - string const & reactionName() const { return m_name; } - -protected: - /// Name the solubility model - string m_name; - - integer m_numPrimarySpecies; - - integer m_numSecondarySpecies; - - /// Array storing the name of the components - string_array m_componentNames; - - /// Array storing the component molar weights - array1d< real64 > m_componentMolarWeight; - - array1d< real64 > m_log10EqConst; - - array2d< real64 > m_stoichMatrix; - - array1d< integer > m_chargePrimary; - array1d< integer > m_chargeSec; - - array1d< real64 > m_ionSizePrimary; - array1d< real64 > m_ionSizeSec; - - real64 m_DebyeHuckelA; - real64 m_DebyeHuckelB; - real64 m_WATEQBDot; - - class KernelWrapper - { -public: - - /** - * @brief Construct a new Kernel Wrapper object - * - * @param log10EqConst - * @param stoichMatrix - * @param chargePrimary - * @param chargeSec - * @param m_ionSizePrimary - * @param ionSizeSec - * @param DebyeHuckelA - * @param DebyeHuckelB - * @param WATEQBDot - */ - KernelWrapper( integer const numPrimarySpecies, - integer const numSecondarySpecies, - arrayView1d< real64 > const & log10EqConst, - arrayView2d< real64 > const & stoichMatrix, - arrayView1d< integer > const & chargePrimary, - arrayView1d< integer > const & chargeSec, - arrayView1d< real64 > const & ionSizePrimary, - arrayView1d< real64 > const & ionSizeSec, - real64 const DebyeHuckelA, - real64 const DebyeHuckelB, - real64 const WATEQBDot ): - m_numPrimarySpecies( numPrimarySpecies ), - m_numSecondarySpecies( numSecondarySpecies ), - m_log10EqConst( log10EqConst ), - m_stoichMatrix( stoichMatrix ), - m_chargePrimary( chargePrimary ), - m_chargeSec( chargeSec ), - m_ionSizePrimary( ionSizePrimary ), - m_ionSizeSec( ionSizeSec ), - m_DebyeHuckelA( DebyeHuckelA ), - m_DebyeHuckelB( DebyeHuckelB ), - m_WATEQBDot( WATEQBDot ) - {} - -protected: - - /** - * @brief - * - * @param temperature - * @param ionicStrength - * @param log10PrimaryActCoeff - * @param dLog10PrimaryActCoeff_dIonicStrength - * @param log10SecActCoeff - * @param dLog10SecActCoeff_dIonicStrength - */ - void computeLog10ActCoefBDotModel( real64 const temperature, - real64 const ionicStrength, - arraySlice1d< real64 > const & log10PrimaryActCoeff, - arraySlice1d< real64 > const & dLog10PrimaryActCoeff_dIonicStrength, - arraySlice1d< real64 > const & log10SecActCoeff, - arraySlice1d< real64 > const & dLog10SecActCoeff_dIonicStrength ) const; - /** - * @brief - * - * @return - */ - void computeIonicStrength( arraySlice1d< real64 const, compflow::USD_COMP - 1 > const & primarySpeciesConcentration, - arraySlice1d< real64 const, compflow::USD_COMP - 1 > const & secondarySpeciesConcentration, - real64 & ionicStrength ) const; - - - /// Hard coding the example case - eventually would have to be changed such that it is read from an input file - integer m_numPrimarySpecies; // Currently not including H2O and O2gas - integer m_numSecondarySpecies; - - arrayView1d< real64 > m_log10EqConst; - arrayView2d< real64 > m_stoichMatrix; - - arrayView1d< integer > m_chargePrimary; - arrayView1d< integer > m_chargeSec; - - arrayView1d< real64 > m_ionSizePrimary; - arrayView1d< real64 > m_ionSizeSec; - - real64 m_DebyeHuckelA; - real64 m_DebyeHuckelB; - real64 m_WATEQBDot; - }; - -}; - -} // end namespace chemicalReactions - -} // end namespace constitutive - -} // end namespace geos - -#endif //GEOS_CONSTITUTIVE_FLUID_CHEMICALREACTIONS_REACTIONSBASE_HPP_ diff --git a/src/coreComponents/constitutive/fluid/reactivefluid/ReactiveFluidFields.hpp b/src/coreComponents/constitutive/fluid/reactivefluid/ReactiveFluidFields.hpp new file mode 100644 index 00000000000..54e1c9a1fc2 --- /dev/null +++ b/src/coreComponents/constitutive/fluid/reactivefluid/ReactiveFluidFields.hpp @@ -0,0 +1,123 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * 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 ReactiveFluidFields.hpp + */ + +#ifndef GEOS_CONSTITUTIVE_FLUID_REACTIVEFLUID_REACTIVEFLUIDFIELDS_HPP_ +#define GEOS_CONSTITUTIVE_FLUID_REACTIVEFLUID_REACTIVEFLUIDFIELDS_HPP_ + +#include "constitutive/fluid/reactivefluid/ReactiveFluidLayouts.hpp" +#include "mesh/MeshFields.hpp" + +namespace geos +{ + +namespace fields +{ + +namespace reactivefluid +{ + +using array3dLayoutSpecies = array3d< real64, constitutive::reactivefluid::LAYOUT_SPECIES >; +using array4dLayoutSpecies_dC = array4d< real64, constitutive::reactivefluid::LAYOUT_SPECIES_DC >; + +DECLARE_FIELD( initialPrimarySpeciesConcentration, + "initialPrimarySpeciesConcentration", + array3dLayoutSpecies, + 1e-16, + LEVEL_0, + WRITE_AND_READ, + "initialPrimarySpeciesConcentration" ); + +DECLARE_FIELD( primarySpeciesAggregateConcentration, + "primarySpeciesAggregateConcentration", + array3dLayoutSpecies, + 1e-16, + LEVEL_0, + WRITE_AND_READ, + "primarySpeciesAggregateConcentration" ); + +DECLARE_FIELD( primarySpeciesAggregateConcentration_n, + "primarySpeciesAggregateConcentration_n", + array3dLayoutSpecies, + 1e-16, + LEVEL_0, + WRITE_AND_READ, + "primarySpeciesAggregateConcentration at the previous timestep" ); + +DECLARE_FIELD( primarySpeciesMobileAggregateConcentration, + "primarySpeciesMobileAggregateConcentration", + array3dLayoutSpecies, + 1e-16, + LEVEL_0, + WRITE_AND_READ, + "primarySpeciesMobileAggregateConcentration" ); + +DECLARE_FIELD( dPrimarySpeciesAggregateConcentration_dLogPrimarySpeciesConcentrations, + "dPrimarySpeciesAggregateConcentration_dLogPrimarySpeciesConcentrations", + array4dLayoutSpecies_dC, + 0, + LEVEL_0, + WRITE_AND_READ, + "Deivatives of primarySpeciesAggregateConcentration w.r.t log primary species concentration" ); + +DECLARE_FIELD( dPrimarySpeciesMobileAggregateConcentration_dLogPrimarySpeciesConcentrations, + "dPrimarySpeciesMobileAggregateConcentration_dLogPrimarySpeciesConcentrations", + array4dLayoutSpecies_dC, + 0, + LEVEL_0, + WRITE_AND_READ, + "Deivatives of primarySpeciesMobileAggregateConcentration w.r.t log primary species concentration" ); + +DECLARE_FIELD( secondarySpeciesConcentration, + "secondarySpeciesConcentration", + array3dLayoutSpecies, + 1e-16, + NOPLOT, + WRITE_AND_READ, + "secondarySpeciesConcentration" ); + +DECLARE_FIELD( kineticReactionRates, + "kineticReactionRates", + array3dLayoutSpecies, + 0, + NOPLOT, + WRITE_AND_READ, + "kineticReactionRates" ); + +DECLARE_FIELD( aggregateSpeciesRates, + "aggregateSpeciesRates", + array3dLayoutSpecies, + 0, + LEVEL_0, + WRITE_AND_READ, + "aggregateSpeciesRates" ); + +DECLARE_FIELD( dAggregateSpeciesRates_dLogPrimarySpeciesConcentrations, + "dAggregateSpeciesRates_dLogPrimarySpeciesConcentrations", + array4dLayoutSpecies_dC, + 0, + LEVEL_0, + WRITE_AND_READ, + "Deivatives of aggregate concentration rates w.r.t log primary species concentration" ); +} + +} + +} + +#endif // GEOS_CONSTITUTIVE_FLUID_REACTIVEFLUID_REACTIVEFLUIDFIELDS_HPP_ diff --git a/src/coreComponents/constitutive/fluid/reactivefluid/ReactiveFluidLayouts.hpp b/src/coreComponents/constitutive/fluid/reactivefluid/ReactiveFluidLayouts.hpp new file mode 100644 index 00000000000..5949d33e9ab --- /dev/null +++ b/src/coreComponents/constitutive/fluid/reactivefluid/ReactiveFluidLayouts.hpp @@ -0,0 +1,114 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * SPDX-License-Identifier: LGPL-2.1-only + * + * Copyright (c) 2016-2024 Lawrence Livermore National Security LLC + * Copyright (c) 2018-2024 Total, S.A + * 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 Layouts.hpp + */ + +#ifndef GEOS_CONSTITUTIVE_FLUID_REACTIVEFLUID_REACTIVEFLUIDLAYOUTS_HPP +#define GEOS_CONSTITUTIVE_FLUID_REACTIVEFLUID_REACTIVEFLUIDLAYOUTS_HPP + +#include "common/DataTypes.hpp" +#include "common/GeosxConfig.hpp" + +#include "LvArray/src/typeManipulation.hpp" +#include "RAJA/RAJA.hpp" + +namespace geos +{ +namespace constitutive +{ + +namespace reactivefluid +{ +struct DerivativeOffset +{ + /// index of derivative wrt pressure + static integer constexpr dP = 0; + /// index of derivative wrt temperature + static integer constexpr dT = 1; + /// index of first derivative wrt compositions + static integer constexpr dC = 2; +}; + +/// indices of pressure, temperature, and composition derivatives +template< integer NC, integer IS_THERMAL > +struct DerivativeOffsetC {}; + +template< integer NC > +struct DerivativeOffsetC< NC, 1 > +{ + /// index of derivative wrt pressure + static integer constexpr dP = 0; + /// index of derivative wrt temperature + static integer constexpr dT = dP + 1; + /// index of first derivative wrt compositions + static integer constexpr dC = dP+2; + /// number of derivatives + static integer constexpr nDer = NC + 2; +}; +template< integer NC > +struct DerivativeOffsetC< NC, 0 > +{ + /// index of derivative wrt pressure + static integer constexpr dP = 0; + /// index of first derivative wrt compositions + static integer constexpr dC = dP+1; + /// number of derivatives + static integer constexpr nDer = NC + 1; +}; + + #if defined( GEOS_USE_DEVICE ) + +/// Constitutive model species variable array array layout +using LAYOUT_SPECIES = RAJA::PERM_JKI; +/// Constitutive model species derivative of species variable array layout +using LAYOUT_SPECIES_DC = RAJA::PERM_JKLI; + +/// Constitutive model fluid property array layout +using LAYOUT_FLUID = RAJA::PERM_JI; +/// Constitutive model fluid property species derivative array layout +using LAYOUT_FLUID_DC = RAJA::PERM_JKI; + + #else + +/// Constitutive model species variable array array layout +using LAYOUT_SPECIES = RAJA::PERM_IJK; +/// Constitutive model species derivative of species variable array layout +using LAYOUT_SPECIES_DC = RAJA::PERM_IJKL; + +/// Constitutive model fluid property array layout +using LAYOUT_FLUID = RAJA::PERM_IJ; +/// Constitutive model fluid property species derivative array layout +using LAYOUT_FLUID_DC = RAJA::PERM_IJK; + + #endif + + +/// Constitutive model phase composition unit stride dimension +static constexpr int USD_SPECIES = LvArray::typeManipulation::getStrideOneDimension( LAYOUT_SPECIES{} ); +/// Constitutive model phase composition compositional derivative unit stride dimension +static constexpr int USD_SPECIES_DC = LvArray::typeManipulation::getStrideOneDimension( LAYOUT_SPECIES_DC{} ); + +/// Constitutive model fluid property unit stride dimension +static constexpr int USD_FLUID = LvArray::typeManipulation::getStrideOneDimension( LAYOUT_FLUID{} ); +/// Constitutive model fluid property compositional derivative unit stride dimension +static constexpr int USD_FLUID_DC = LvArray::typeManipulation::getStrideOneDimension( LAYOUT_FLUID_DC{} ); + +} // namespace reactivefluid +} // namespace constitutive +} // namespace geos + +#endif //GEOS_CONSTITUTIVE_FLUID_REACTIVEFLUID_REACTIVEFLUIDLAYOUTS_HPP diff --git a/src/coreComponents/constitutive/fluid/multifluid/reactive/ReactiveFluidSelector.hpp b/src/coreComponents/constitutive/fluid/reactivefluid/ReactiveFluidSelector.hpp similarity index 55% rename from src/coreComponents/constitutive/fluid/multifluid/reactive/ReactiveFluidSelector.hpp rename to src/coreComponents/constitutive/fluid/reactivefluid/ReactiveFluidSelector.hpp index 03439e9629b..8a3ef4b4602 100644 --- a/src/coreComponents/constitutive/fluid/multifluid/reactive/ReactiveFluidSelector.hpp +++ b/src/coreComponents/constitutive/fluid/reactivefluid/ReactiveFluidSelector.hpp @@ -16,13 +16,11 @@ /** * @file ReactiveFluidSelector.hpp */ -#ifndef GEOS_CONSTITUTIVE_FLUID_MULTIFLUID_REACTIVE_REACTIVEFLUIDSELECTOR_HPP_ -#define GEOS_CONSTITUTIVE_FLUID_MULTIFLUID_REACTIVE_REACTIVEFLUIDSELECTOR_HPP_ +#ifndef GEOS_CONSTITUTIVE_FLUID_REACTIVEFLUID_REACTIVEFLUIDSELECTOR_HPP_ +#define GEOS_CONSTITUTIVE_FLUID_REACTIVEFLUID_REACTIVEFLUIDSELECTOR_HPP_ #include "constitutive/ConstitutivePassThruHandler.hpp" -#include "constitutive/fluid/multifluid/reactive/ReactiveBrineFluid.hpp" - -#include "common/GeosxConfig.hpp" +#include "constitutive/fluid/reactivefluid/ReactiveSinglePhaseFluid.hpp" namespace geos { @@ -31,23 +29,23 @@ namespace constitutive { template< typename LAMBDA > -void constitutiveUpdatePassThru( ReactiveMultiFluid const & fluid, +void constitutiveUpdatePassThru( SingleFluidBase const & fluid, LAMBDA && lambda ) { - ConstitutivePassThruHandler< ReactiveBrine, - ReactiveBrineThermal >::execute( fluid, std::forward< LAMBDA >( lambda ) ); + ConstitutivePassThruHandler< reactivefluid::ReactiveCompressibleSinglePhaseFluid, + reactivefluid::ReactiveThermalCompressibleSinglePhaseFluid >::execute( fluid, std::forward< LAMBDA >( lambda ) ); } template< typename LAMBDA > -void constitutiveUpdatePassThru( ReactiveMultiFluid & fluid, +void constitutiveUpdatePassThru( SingleFluidBase & fluid, LAMBDA && lambda ) { - ConstitutivePassThruHandler< ReactiveBrine, - ReactiveBrineThermal >::execute( fluid, std::forward< LAMBDA >( lambda ) ); + ConstitutivePassThruHandler< reactivefluid::ReactiveCompressibleSinglePhaseFluid, + reactivefluid::ReactiveThermalCompressibleSinglePhaseFluid >::execute( fluid, std::forward< LAMBDA >( lambda ) ); } } // namespace constitutive } // namespace geos -#endif //GEOS_CONSTITUTIVE_FLUID_REACTIVEFLUIDSELECTOR_HPP_ +#endif //GEOS_CONSTITUTIVE_FLUID_REACTIVEFLUID_REACTIVEFLUIDSELECTOR_HPP_ diff --git a/src/coreComponents/constitutive/fluid/reactivefluid/ReactiveSinglePhaseFluid.cpp b/src/coreComponents/constitutive/fluid/reactivefluid/ReactiveSinglePhaseFluid.cpp new file mode 100644 index 00000000000..69b7b750934 --- /dev/null +++ b/src/coreComponents/constitutive/fluid/reactivefluid/ReactiveSinglePhaseFluid.cpp @@ -0,0 +1,168 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * 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 ReactiveSinglePhaseFluid.cpp + */ +#include "ReactiveSinglePhaseFluid.hpp" +#include "ReactiveFluidFields.hpp" + +namespace geos +{ + +using namespace dataRepository; + +namespace constitutive +{ + +namespace reactivefluid +{ + +using namespace hpcReact::bulkGeneric; + +template< typename BASE > +ReactiveSinglePhaseFluid< BASE >:: +ReactiveSinglePhaseFluid( string const & name, Group * const parent ): + BASE( name, parent ) +{ + + this->registerWrapper( viewKeyStruct::chemicalSystemNameString(), &m_chemicalSystemType ). + setInputFlag( InputFlags::REQUIRED ). + setDescription( "Chemical System type. Available options are: " + "``" + EnumStrings< ChemicalSystemType >::concat( "|" ) + "``" ); + + this->template registerField< fields::reactivefluid::initialPrimarySpeciesConcentration >( &m_initialPrimarySpeciesConcentration ); + this->template registerField< fields::reactivefluid::secondarySpeciesConcentration >( &m_secondarySpeciesConcentration ); + this->template registerField< fields::reactivefluid::primarySpeciesAggregateConcentration >( &m_primarySpeciesAggregateConcentration ); + this->template registerField< fields::reactivefluid::primarySpeciesAggregateConcentration_n >( &m_primarySpeciesAggregateConcentration_n ); + this->template registerField< fields::reactivefluid::primarySpeciesMobileAggregateConcentration >( &m_primarySpeciesMobileAggregateConcentration ); + this->template registerField< fields::reactivefluid::dPrimarySpeciesAggregateConcentration_dLogPrimarySpeciesConcentrations >( + &m_dPrimarySpeciesAggregateConcentration_dLogPrimarySpeciesConcentrations ); + this->template registerField< fields::reactivefluid::dPrimarySpeciesMobileAggregateConcentration_dLogPrimarySpeciesConcentrations >( + &m_dPrimarySpeciesMobileAggregateConcentration_dLogPrimarySpeciesConcentrations ); + this->template registerField< fields::reactivefluid::kineticReactionRates >( &m_kineticReactionRates ); + this->template registerField< fields::reactivefluid::aggregateSpeciesRates >( &m_aggregateSpeciesRates ); + this->template registerField< fields::reactivefluid::dAggregateSpeciesRates_dLogPrimarySpeciesConcentrations >( &m_dAggregateSpeciesRates_dLogPrimarySpeciesConcentrations ); +} + +template< typename BASE > +std::unique_ptr< ConstitutiveBase > ReactiveSinglePhaseFluid< BASE >:: +deliverClone( string const & name, Group * const parent ) const +{ + std::unique_ptr< ConstitutiveBase > clone = BASE::deliverClone( name, parent ); + + ReactiveSinglePhaseFluid & newConstitutiveRelation = dynamicCast< ReactiveSinglePhaseFluid & >( *clone ); + + newConstitutiveRelation.m_chemicalSystemType = m_chemicalSystemType; + newConstitutiveRelation.m_numPrimarySpecies = m_numPrimarySpecies; + newConstitutiveRelation.m_numSecondarySpecies = m_numSecondarySpecies; + newConstitutiveRelation.m_numKineticReactions = m_numKineticReactions; + + return clone; +} + +template< typename BASE > +void ReactiveSinglePhaseFluid< BASE >::postInputInitialization() +{ + BASE::postInputInitialization(); + + switch( m_chemicalSystemType ) + { + case ChemicalSystemType::ultramafic: + m_numPrimarySpecies = 9; + m_numSecondarySpecies = 16; + m_numKineticReactions = 5; + break; + + case ChemicalSystemType::carbonate: + m_numPrimarySpecies = 7; + m_numSecondarySpecies = 10; + m_numKineticReactions = 1; + break; + + case ChemicalSystemType::carbonateAllEquilibrium: + m_numPrimarySpecies = 7; + m_numSecondarySpecies = 11; + m_numKineticReactions = 0; + break; + + case ChemicalSystemType::chainSerialAllKinetic: + m_numPrimarySpecies = 3; + m_numSecondarySpecies = 0; + m_numKineticReactions = 3; + break; + + case ChemicalSystemType::momasMedium: + m_numPrimarySpecies = 5; + m_numSecondarySpecies = 9; + m_numKineticReactions = 1; + break; + + default: + m_numPrimarySpecies = 5; + m_numSecondarySpecies = 7; + m_numKineticReactions = 0; + break; + } +} + +template< typename BASE > +void ReactiveSinglePhaseFluid< BASE >::saveConvergedState() const +{ + BASE::saveConvergedState(); + + m_primarySpeciesAggregateConcentration_n.setValues< parallelDevicePolicy<> >( m_primarySpeciesAggregateConcentration.toViewConst() ); +} + +template< typename BASE > +void ReactiveSinglePhaseFluid< BASE >::resizeFields( localIndex const size, localIndex const numPts ) +{ + integer const numPrimarySpecies = this->numPrimarySpecies(); + integer const numSecondarySpecies = this->numSecondarySpecies(); + integer const numKineticReactions = this->numKineticReactions(); + + m_initialPrimarySpeciesConcentration.resize( size, numPts, numPrimarySpecies ); + m_secondarySpeciesConcentration.resize( size, numPts, numSecondarySpecies ); + m_primarySpeciesAggregateConcentration.resize( size, numPts, numPrimarySpecies ); + m_primarySpeciesAggregateConcentration_n.resize( size, numPts, numPrimarySpecies ); + m_primarySpeciesMobileAggregateConcentration.resize( size, numPts, numPrimarySpecies ); + m_dPrimarySpeciesAggregateConcentration_dLogPrimarySpeciesConcentrations.resize( size, numPts, numPrimarySpecies, numPrimarySpecies ); + m_dPrimarySpeciesMobileAggregateConcentration_dLogPrimarySpeciesConcentrations.resize( size, numPts, numPrimarySpecies, numPrimarySpecies ); + m_kineticReactionRates.resize( size, numPts, numKineticReactions ); + m_aggregateSpeciesRates.resize( size, numPts, numPrimarySpecies ); + m_dAggregateSpeciesRates_dLogPrimarySpeciesConcentrations.resize( size, numPts, numPrimarySpecies, numPrimarySpecies ); +} + +template< typename BASE > +void ReactiveSinglePhaseFluid< BASE >::allocateConstitutiveData( dataRepository::Group & parent, + localIndex const numConstitutivePointsPerParentIndex ) +{ + BASE::allocateConstitutiveData( parent, numConstitutivePointsPerParentIndex ); + resizeFields( parent.size(), numConstitutivePointsPerParentIndex ); +} + +template class ReactiveSinglePhaseFluid< CompressibleSinglePhaseFluid >; + +REGISTER_CATALOG_ENTRY( ConstitutiveBase, ReactiveCompressibleSinglePhaseFluid, string const &, Group * const ) + +template class ReactiveSinglePhaseFluid< ThermalCompressibleSinglePhaseFluid >; + +REGISTER_CATALOG_ENTRY( ConstitutiveBase, ReactiveThermalCompressibleSinglePhaseFluid, string const &, Group * const ) + +} // namespace reactivefluid + +} // namespace constitutive + +} // namespace geos diff --git a/src/coreComponents/constitutive/fluid/reactivefluid/ReactiveSinglePhaseFluid.hpp b/src/coreComponents/constitutive/fluid/reactivefluid/ReactiveSinglePhaseFluid.hpp new file mode 100644 index 00000000000..6346d030c4f --- /dev/null +++ b/src/coreComponents/constitutive/fluid/reactivefluid/ReactiveSinglePhaseFluid.hpp @@ -0,0 +1,529 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * 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 ReactiveSinglePhaseFluid.hpp + */ + +#ifndef GEOS_CONSTITUTIVE_FLUID_REACTIVEFLUID_REACTIVESINGLEPHASEFLUID_HPP_ +#define GEOS_CONSTITUTIVE_FLUID_REACTIVEFLUID_REACTIVESINGLEPHASEFLUID_HPP_ + +#include "common/format/EnumStrings.hpp" + +#include "constitutive/ConstitutiveBase.hpp" +#include "constitutive/fluid/reactivefluid/ReactiveFluidLayouts.hpp" +#include "constitutive/fluid/singlefluid/CompressibleSinglePhaseFluid.hpp" +#include "constitutive/fluid/singlefluid/ThermalCompressibleSinglePhaseFluid.hpp" + +#include "constitutive/HPCReact/src/reactions/geochemistry/GeochemicalSystems.hpp" +#include "constitutive/HPCReact/src/reactions/exampleSystems/BulkGeneric.hpp" +#include "constitutive/HPCReact/src/reactions/exampleSystems/ChainGeneric.hpp" +#include "constitutive/HPCReact/src/reactions/exampleSystems/MoMasBenchmark.hpp" +#include "constitutive/HPCReact/src/reactions/reactionsSystems/EquilibriumReactions.hpp" +#include "constitutive/HPCReact/src/reactions/reactionsSystems/MixedEquilibriumKineticReactions.hpp" +#include "constitutive/HPCReact/src/reactions/massActions/MassActions.hpp" +#include + +namespace geos +{ + +namespace constitutive +{ + +namespace reactivefluid +{ + +using namespace hpcReact::reactionsSystems; + +enum class ChemicalSystemType : integer +{ + carbonate, + carbonateAllEquilibrium, + ultramafic, + momasEasy, + momasMedium, + chainSerialAllKinetic +}; + +template< typename BASE > +class ReactiveSinglePhaseFluid : public BASE +{ +public: + + ReactiveSinglePhaseFluid( string const & name, + dataRepository::Group * const parent ); + + virtual std::unique_ptr< ConstitutiveBase > + deliverClone( string const & name, + dataRepository::Group * const parent ) const override; + + static string catalogName() { return string( "Reactive" ) + BASE::catalogName(); } + virtual string getCatalogName() const override { return catalogName(); } + + virtual void saveConvergedState() const override; + + virtual void allocateConstitutiveData( dataRepository::Group & parent, + localIndex const numConstitutivePointsPerParentIndex ) override; + + static constexpr integer MAX_NUM_SPECIES = 20; + static constexpr integer MAX_NUM_KINETIC_REACTIONS = 10; + + arrayView3d< real64 const, reactivefluid::USD_SPECIES > primarySpeciesAggregateConcentration() const + { return m_primarySpeciesAggregateConcentration; } + + arrayView3d< real64 const, reactivefluid::USD_SPECIES > primarySpeciesAggregateConcentration_n() const + { return m_primarySpeciesAggregateConcentration_n; } + + arrayView3d< real64 const, reactivefluid::USD_SPECIES > primarySpeciesMobileAggregateConcentration() const + { return m_primarySpeciesMobileAggregateConcentration; } + + arrayView4d< real64 const, reactivefluid::USD_SPECIES_DC > dPrimarySpeciesAggregateConcentration_dLogPrimarySpeciesConcentrations() const + { return m_dPrimarySpeciesAggregateConcentration_dLogPrimarySpeciesConcentrations; } + + arrayView4d< real64 const, reactivefluid::USD_SPECIES_DC > dPrimarySpeciesMobileAggregateConcentration_dLogPrimarySpeciesConcentrations() const + { return m_dPrimarySpeciesMobileAggregateConcentration_dLogPrimarySpeciesConcentrations; } + + arrayView3d< real64 const, reactivefluid::USD_SPECIES > secondarySpeciesConcentration() const + { return m_secondarySpeciesConcentration; } + + arrayView3d< real64 const, reactivefluid::USD_SPECIES > aggregateSpeciesRates() const + { return m_aggregateSpeciesRates; } + + arrayView3d< real64 const, reactivefluid::USD_SPECIES > kineticReactionRates() const + { return m_kineticReactionRates; } + + arrayView4d< real64 const, reactivefluid::USD_SPECIES_DC > dAggregateSpeciesRates_dLogPrimarySpeciesConcentrations() const + { return m_dAggregateSpeciesRates_dLogPrimarySpeciesConcentrations; } + + integer numPrimarySpecies() const { return m_numPrimarySpecies; } + + integer numSecondarySpecies() const { return m_numSecondarySpecies; } + + integer numKineticReactions() const { return m_numKineticReactions; } + + /** + * @brief Kernel wrapper class for ReactiveSinglePhaseFluid. + */ + template< typename REACTION_PARAMS_TYPE > + class ReactionKernelWrapper + { + +public: + + ReactionKernelWrapper( arrayView3d< real64, reactivefluid::USD_SPECIES > const & primarySpeciesAggregateConcentration, + arrayView3d< real64, reactivefluid::USD_SPECIES > const & primarySpeciesMobileAggregateConcentration, + arrayView4d< real64, reactivefluid::USD_SPECIES_DC > const & dPrimarySpeciesAggregateConcentration_dLogPrimarySpeciesConcentrations, + arrayView4d< real64, reactivefluid::USD_SPECIES_DC > const & dPrimarySpeciesMobileAggregateConcentration_dLogPrimarySpeciesConcentrations, + arrayView3d< real64 const, reactivefluid::USD_SPECIES > const & initialPrimarySpeciesConcentration, + arrayView3d< real64, reactivefluid::USD_SPECIES > const & secondarySpeciesConcentration, + arrayView3d< real64, reactivefluid::USD_SPECIES > const & kineticReactionRates, + arrayView3d< real64, reactivefluid::USD_SPECIES > const & aggregateSpeciesRates, + arrayView4d< real64, reactivefluid::USD_SPECIES_DC > const & dAggregateSpeciesRates_dLogPrimarySpeciesConcentrations, + integer const numPrimarySpecies, + integer const numSecondarySpecies, + integer const numKineticReactions, + REACTION_PARAMS_TYPE params ): + m_numPrimarySpecies( numPrimarySpecies ), + m_numSecondarySpecies( numSecondarySpecies ), + m_numKineticReactions( numKineticReactions ), + m_primarySpeciesAggregateConcentration( primarySpeciesAggregateConcentration ), + m_primarySpeciesMobileAggregateConcentration( primarySpeciesMobileAggregateConcentration ), + m_dPrimarySpeciesAggregateConcentration_dLogPrimarySpeciesConcentrations( dPrimarySpeciesAggregateConcentration_dLogPrimarySpeciesConcentrations ), + m_dPrimarySpeciesMobileAggregateConcentration_dLogPrimarySpeciesConcentrations( dPrimarySpeciesMobileAggregateConcentration_dLogPrimarySpeciesConcentrations ), + m_initialPrimarySpeciesConcentration( initialPrimarySpeciesConcentration ), + m_secondarySpeciesConcentration( secondarySpeciesConcentration ), + m_kineticReactionRates( kineticReactionRates ), + m_aggregateSpeciesRates( aggregateSpeciesRates ), + m_dAggregateSpeciesRates_dLogPrimarySpeciesConcentrations( dAggregateSpeciesRates_dLogPrimarySpeciesConcentrations ), + m_params( params ) + {} + + using EquilibriumReactionsType = hpcReact::reactionsSystems::EquilibriumReactions< real64, integer, localIndex >; + + /** + * @brief Get number of elements in this wrapper. + * @return number of elements + */ + GEOS_HOST_DEVICE + localIndex numElems() const { return m_secondarySpeciesConcentration.size( 0 ); } + + GEOS_HOST_DEVICE + void updateEquilibriumReaction( localIndex const k, + real64 const pressure, + real64 const temperature, + arraySlice1d< real64, compflow::USD_COMP - 1 > const & logPrimarySpeciesConcentration ) const; + + GEOS_HOST_DEVICE + void enforceEquilibrium( real64 const pressure, + real64 const temperature, + arraySlice1d< real64 const, reactivefluid::USD_SPECIES - 2 > const & targetPrimarySpeciesAggregateConcentration, + arraySlice1d< real64 const, reactivefluid::USD_SPECIES - 2 > const & initialPrimarySpeciesConcentration, + arraySlice1d< real64, compflow::USD_COMP - 1 > const & logPrimarySpeciesConcentration, + arraySlice1d< real64 > const & logSecondarySpeciesConcentration ) const; + + GEOS_HOST_DEVICE + void updateMixedReactionSystem( localIndex const k, + real64 const pressure, + real64 const temperature, + arraySlice1d< real64 const, compflow::USD_COMP - 1 > const & logPrimarySpeciesConcentration, + arraySlice1d< real64 const, compflow::USD_COMP - 1 > const & surfaceArea ) const; + + GEOS_HOST_DEVICE + void computeAggregateConcentrationsAndRates( real64 const pressure, + real64 const temperature, + arraySlice1d< real64 const, compflow::USD_COMP - 1 > const & logPrimarySpeciesConcentration, + arraySlice1d< real64 const, compflow::USD_COMP - 1 > const & surfaceArea, + arraySlice1d< real64 > const & logSecondarySpeciesConcentration, + arraySlice1d< real64, reactivefluid::USD_SPECIES - 2 > const & primarySpeciesAggregateConcentration, + arraySlice1d< real64, reactivefluid::USD_SPECIES - 2 > const & primarySpeciesMobileAggregateConcentration, + arraySlice2d< real64, reactivefluid::USD_SPECIES_DC - 2 > const & dPrimarySpeciesAggregateConcentration_dLogPrimarySpeciesConcentrations, + arraySlice2d< real64, reactivefluid::USD_SPECIES_DC - 2 > const & dPrimarySpeciesMobileAggregateConcentration_dLogPrimarySpeciesConcentrations, + arraySlice1d< real64, reactivefluid::USD_SPECIES - 2 > const & reactionRates, + arraySlice2d< real64 > const & dReactionRates_dLogPrimarySpeciesConcentrations, + arraySlice1d< real64, reactivefluid::USD_SPECIES - 2 > const & aggregateSpeciesRates, + arraySlice2d< real64, reactivefluid::USD_SPECIES_DC - 2 > const & dAggregateSpeciesRates_dLogPrimarySpeciesConcentrations ) const; + +protected: + + integer m_numPrimarySpecies; + + integer m_numSecondarySpecies; + + integer m_numKineticReactions; + + arrayView3d< real64, reactivefluid::USD_SPECIES > m_primarySpeciesAggregateConcentration; + + arrayView3d< real64, reactivefluid::USD_SPECIES > m_primarySpeciesMobileAggregateConcentration; + + arrayView4d< real64, reactivefluid::USD_SPECIES_DC > m_dPrimarySpeciesAggregateConcentration_dLogPrimarySpeciesConcentrations; + + arrayView4d< real64, reactivefluid::USD_SPECIES_DC > m_dPrimarySpeciesMobileAggregateConcentration_dLogPrimarySpeciesConcentrations; + + arrayView3d< real64 const, reactivefluid::USD_SPECIES > const m_initialPrimarySpeciesConcentration; + + arrayView3d< real64, reactivefluid::USD_SPECIES > m_secondarySpeciesConcentration; + + arrayView3d< real64, reactivefluid::USD_SPECIES > m_kineticReactionRates; + + arrayView3d< real64, reactivefluid::USD_SPECIES > m_aggregateSpeciesRates; + + arrayView4d< real64, reactivefluid::USD_SPECIES_DC > m_dAggregateSpeciesRates_dLogPrimarySpeciesConcentrations; + + REACTION_PARAMS_TYPE m_params; + }; + + std::variant< + typename ReactiveSinglePhaseFluid< BASE >::template ReactionKernelWrapper< hpcReact::geochemistry::ultramaficSystemType >, + typename ReactiveSinglePhaseFluid< BASE >::template ReactionKernelWrapper< hpcReact::geochemistry::carbonateSystemType >, + typename ReactiveSinglePhaseFluid< BASE >::template ReactionKernelWrapper< hpcReact::geochemistry::carbonateSystemAllEquilibriumType >, + typename ReactiveSinglePhaseFluid< BASE >::template ReactionKernelWrapper< hpcReact::ChainGeneric::serialAllKineticType >, + typename ReactiveSinglePhaseFluid< BASE >::template ReactionKernelWrapper< hpcReact::MoMasBenchmark::mediumCaseType >, + typename ReactiveSinglePhaseFluid< BASE >::template ReactionKernelWrapper< hpcReact::MoMasBenchmark::easyCaseType > > + createReactionKernelWrapper() const + { + using namespace hpcReact::geochemistry; + using namespace hpcReact::MoMasBenchmark; + using namespace hpcReact::bulkGeneric; + using namespace hpcReact::ChainGeneric; + switch( m_chemicalSystemType ) + { + case ChemicalSystemType::ultramafic: + return ReactionKernelWrapper< ultramaficSystemType >( m_primarySpeciesAggregateConcentration, + m_primarySpeciesMobileAggregateConcentration, + m_dPrimarySpeciesAggregateConcentration_dLogPrimarySpeciesConcentrations, + m_dPrimarySpeciesMobileAggregateConcentration_dLogPrimarySpeciesConcentrations, + m_initialPrimarySpeciesConcentration, + m_secondarySpeciesConcentration, + m_kineticReactionRates, + m_aggregateSpeciesRates, + m_dAggregateSpeciesRates_dLogPrimarySpeciesConcentrations, + m_numPrimarySpecies, + m_numSecondarySpecies, + m_numKineticReactions, + ultramaficSystem ); + + case ChemicalSystemType::carbonate: + return ReactionKernelWrapper< carbonateSystemType >( m_primarySpeciesAggregateConcentration, + m_primarySpeciesMobileAggregateConcentration, + m_dPrimarySpeciesAggregateConcentration_dLogPrimarySpeciesConcentrations, + m_dPrimarySpeciesMobileAggregateConcentration_dLogPrimarySpeciesConcentrations, + m_initialPrimarySpeciesConcentration, + m_secondarySpeciesConcentration, + m_kineticReactionRates, + m_aggregateSpeciesRates, + m_dAggregateSpeciesRates_dLogPrimarySpeciesConcentrations, + m_numPrimarySpecies, + m_numSecondarySpecies, + m_numKineticReactions, + carbonateSystem ); + case ChemicalSystemType::carbonateAllEquilibrium: + return ReactionKernelWrapper< carbonateSystemAllEquilibriumType >( m_primarySpeciesAggregateConcentration, + m_primarySpeciesMobileAggregateConcentration, + m_dPrimarySpeciesAggregateConcentration_dLogPrimarySpeciesConcentrations, + m_dPrimarySpeciesMobileAggregateConcentration_dLogPrimarySpeciesConcentrations, + m_initialPrimarySpeciesConcentration, + m_secondarySpeciesConcentration, + m_kineticReactionRates, + m_aggregateSpeciesRates, + m_dAggregateSpeciesRates_dLogPrimarySpeciesConcentrations, + m_numPrimarySpecies, + m_numSecondarySpecies, + m_numKineticReactions, + carbonateSystemAllEquilibrium ); + case ChemicalSystemType::chainSerialAllKinetic: + return ReactionKernelWrapper< serialAllKineticType >( m_primarySpeciesAggregateConcentration, + m_primarySpeciesMobileAggregateConcentration, + m_dPrimarySpeciesAggregateConcentration_dLogPrimarySpeciesConcentrations, + m_dPrimarySpeciesMobileAggregateConcentration_dLogPrimarySpeciesConcentrations, + m_initialPrimarySpeciesConcentration, + m_secondarySpeciesConcentration, + m_kineticReactionRates, + m_aggregateSpeciesRates, + m_dAggregateSpeciesRates_dLogPrimarySpeciesConcentrations, + m_numPrimarySpecies, + m_numSecondarySpecies, + m_numKineticReactions, + serialAllKineticParams ); + case ChemicalSystemType::momasMedium: + return ReactionKernelWrapper< mediumCaseType >( m_primarySpeciesAggregateConcentration, + m_primarySpeciesMobileAggregateConcentration, + m_dPrimarySpeciesAggregateConcentration_dLogPrimarySpeciesConcentrations, + m_dPrimarySpeciesMobileAggregateConcentration_dLogPrimarySpeciesConcentrations, + m_initialPrimarySpeciesConcentration, + m_secondarySpeciesConcentration, + m_kineticReactionRates, + m_aggregateSpeciesRates, + m_dAggregateSpeciesRates_dLogPrimarySpeciesConcentrations, + m_numPrimarySpecies, + m_numSecondarySpecies, + m_numKineticReactions, + mediumCaseParams ); + default: + return ReactionKernelWrapper< easyCaseType >( m_primarySpeciesAggregateConcentration, + m_primarySpeciesMobileAggregateConcentration, + m_dPrimarySpeciesAggregateConcentration_dLogPrimarySpeciesConcentrations, + m_dPrimarySpeciesMobileAggregateConcentration_dLogPrimarySpeciesConcentrations, + m_initialPrimarySpeciesConcentration, + m_secondarySpeciesConcentration, + m_kineticReactionRates, + m_aggregateSpeciesRates, + m_dAggregateSpeciesRates_dLogPrimarySpeciesConcentrations, + m_numPrimarySpecies, + m_numSecondarySpecies, + m_numKineticReactions, + easyCaseParams ); + } + } + + struct viewKeyStruct : ConstitutiveBase::viewKeyStruct + { + static constexpr char const * chemicalSystemNameString() { return "chemicalSystemType"; } + }; + +protected: + + virtual void postInputInitialization() override; + + virtual void resizeFields( localIndex const size, localIndex const numPts ); + + integer m_numPrimarySpecies; + + integer m_numSecondarySpecies; + + integer m_numKineticReactions; + + array3d< real64, constitutive::reactivefluid::LAYOUT_SPECIES > m_initialPrimarySpeciesConcentration; + + array3d< real64, constitutive::reactivefluid::LAYOUT_SPECIES > m_secondarySpeciesConcentration; + + array3d< real64, constitutive::reactivefluid::LAYOUT_SPECIES > m_primarySpeciesAggregateConcentration; + + array3d< real64, constitutive::reactivefluid::LAYOUT_SPECIES > m_primarySpeciesAggregateConcentration_n; + + array3d< real64, constitutive::reactivefluid::LAYOUT_SPECIES > m_primarySpeciesMobileAggregateConcentration; + + array4d< real64, constitutive::reactivefluid::LAYOUT_SPECIES_DC > m_dPrimarySpeciesAggregateConcentration_dLogPrimarySpeciesConcentrations; + + array4d< real64, constitutive::reactivefluid::LAYOUT_SPECIES_DC > m_dPrimarySpeciesMobileAggregateConcentration_dLogPrimarySpeciesConcentrations; + + array3d< real64, constitutive::reactivefluid::LAYOUT_SPECIES > m_kineticReactionRates; + + array3d< real64, constitutive::reactivefluid::LAYOUT_SPECIES > m_aggregateSpeciesRates; + + array4d< real64, constitutive::reactivefluid::LAYOUT_SPECIES_DC > m_dAggregateSpeciesRates_dLogPrimarySpeciesConcentrations; + + ChemicalSystemType m_chemicalSystemType; +}; + +// these aliases are useful in constitutive dispatch +using ReactiveCompressibleSinglePhaseFluid = ReactiveSinglePhaseFluid< CompressibleSinglePhaseFluid >; + +using ReactiveThermalCompressibleSinglePhaseFluid = ReactiveSinglePhaseFluid< ThermalCompressibleSinglePhaseFluid >; + +template< typename BASE > +template< typename REACTION_PARAMS_TYPE > +GEOS_HOST_DEVICE +inline void +ReactiveSinglePhaseFluid< BASE >::ReactionKernelWrapper< REACTION_PARAMS_TYPE >:: +updateEquilibriumReaction( localIndex const k, + real64 const pressure, + real64 const temperature, + arraySlice1d< real64, compflow::USD_COMP - 1 > const & logPrimarySpeciesConcentration ) const +{ + integer const numSecondarySpecies = m_numSecondarySpecies; + + if( numSecondarySpecies > 0 ) + { + stackArray1d< real64, MAX_NUM_SPECIES > logSecondarySpeciesConcentration( numSecondarySpecies ); + + enforceEquilibrium( pressure, temperature, m_primarySpeciesAggregateConcentration[k][0], m_initialPrimarySpeciesConcentration[k][0], logPrimarySpeciesConcentration, + logSecondarySpeciesConcentration.toSlice() ); + + for( integer i=0; i < numSecondarySpecies; ++i ) + { + m_secondarySpeciesConcentration[k][0][i] = LvArray::math::exp( logSecondarySpeciesConcentration[i] ); + } + } + else + { + GEOS_UNUSED_VAR( k, pressure, temperature, logPrimarySpeciesConcentration ); + } + +} + +template< typename BASE > +template< typename REACTION_PARAMS_TYPE > +GEOS_HOST_DEVICE +inline void +ReactiveSinglePhaseFluid< BASE >::ReactionKernelWrapper< REACTION_PARAMS_TYPE >:: +enforceEquilibrium( real64 const pressure, + real64 const temperature, + arraySlice1d< real64 const, reactivefluid::USD_SPECIES - 2 > const & targetPrimarySpeciesAggregateConcentration, + arraySlice1d< real64 const, reactivefluid::USD_SPECIES - 2 > const & initialPrimarySpeciesConcentration, + arraySlice1d< real64, compflow::USD_COMP - 1 > const & logPrimarySpeciesConcentration, + arraySlice1d< real64 > const & logSecondarySpeciesConcentration ) const +{ + GEOS_UNUSED_VAR( pressure ); + + integer const numPrimarySpecies = m_numPrimarySpecies; + + stackArray1d< real64, MAX_NUM_SPECIES > logPrimarySpeciesConcentration0( numPrimarySpecies ); + stackArray1d< real64, MAX_NUM_SPECIES > targetPrimarySpeciesAggregateConc( numPrimarySpecies ); + + for( integer i=0; i < numPrimarySpecies; ++i ) + { + targetPrimarySpeciesAggregateConc[i] = targetPrimarySpeciesAggregateConcentration[i]; + logPrimarySpeciesConcentration0[i] = LvArray::math::log( initialPrimarySpeciesConcentration[i] ); + } + + // 1. We enforce equilibrium + EquilibriumReactionsType::enforceEquilibrium_Aggregate( temperature, m_params, targetPrimarySpeciesAggregateConc, logPrimarySpeciesConcentration0, logPrimarySpeciesConcentration ); + + // 2. We calculate the secondary species concentration + hpcReact::massActions::calculateLogSecondarySpeciesConcentration< real64, + localIndex, + localIndex >( m_params, logPrimarySpeciesConcentration, logSecondarySpeciesConcentration ); +} + +template< typename BASE > +template< typename REACTION_PARAMS_TYPE > +GEOS_HOST_DEVICE +inline void +ReactiveSinglePhaseFluid< BASE >::ReactionKernelWrapper< REACTION_PARAMS_TYPE >:: +updateMixedReactionSystem( localIndex const k, + real64 const pressure, + real64 const temperature, + arraySlice1d< real64 const, compflow::USD_COMP - 1 > const & logPrimarySpeciesConcentration, + arraySlice1d< real64 const, compflow::USD_COMP - 1 > const & surfaceArea ) const +{ + integer const numPrimarySpecies = m_numPrimarySpecies; + integer const numSecondarySpecies = m_numSecondarySpecies; + integer const numKineticReactions = m_numKineticReactions; + + stackArray1d< real64, MAX_NUM_SPECIES > logSecondarySpeciesConcentration( numSecondarySpecies ); + stackArray2d< real64, MAX_NUM_KINETIC_REACTIONS * MAX_NUM_SPECIES > dReactionRates_dLogPrimarySpeciesConcentrations( numKineticReactions, numPrimarySpecies ); + + computeAggregateConcentrationsAndRates( pressure, + temperature, + logPrimarySpeciesConcentration, + surfaceArea, + logSecondarySpeciesConcentration.toSlice(), + m_primarySpeciesAggregateConcentration[k][0], + m_primarySpeciesMobileAggregateConcentration[k][0], + m_dPrimarySpeciesAggregateConcentration_dLogPrimarySpeciesConcentrations[k][0], + m_dPrimarySpeciesMobileAggregateConcentration_dLogPrimarySpeciesConcentrations[k][0], + m_kineticReactionRates[k][0], + dReactionRates_dLogPrimarySpeciesConcentrations.toSlice(), + m_aggregateSpeciesRates[k][0], + m_dAggregateSpeciesRates_dLogPrimarySpeciesConcentrations[k][0] ); + + for( integer i=0; i < numSecondarySpecies; ++i ) + { + m_secondarySpeciesConcentration[k][0][i] = LvArray::math::exp( logSecondarySpeciesConcentration[i] ); + } +} + +template< typename BASE > +template< typename REACTION_PARAMS_TYPE > +GEOS_HOST_DEVICE +inline void +ReactiveSinglePhaseFluid< BASE >::ReactionKernelWrapper< REACTION_PARAMS_TYPE >:: +computeAggregateConcentrationsAndRates( real64 const pressure, + real64 const temperature, + arraySlice1d< real64 const, compflow::USD_COMP - 1 > const & logPrimarySpeciesConcentration, + arraySlice1d< real64 const, compflow::USD_COMP - 1 > const & surfaceArea, + arraySlice1d< real64 > const & logSecondarySpeciesConcentration, + arraySlice1d< real64, reactivefluid::USD_SPECIES - 2 > const & primarySpeciesAggregateConcentration, + arraySlice1d< real64, reactivefluid::USD_SPECIES - 2 > const & primarySpeciesMobileAggregateConcentration, + arraySlice2d< real64, reactivefluid::USD_SPECIES_DC - 2 > const & dPrimarySpeciesAggregateConcentration_dLogPrimarySpeciesConcentrations, + arraySlice2d< real64, reactivefluid::USD_SPECIES_DC - 2 > const & dPrimarySpeciesMobileAggregateConcentration_dLogPrimarySpeciesConcentrations, + arraySlice1d< real64, reactivefluid::USD_SPECIES - 2 > const & reactionRates, + arraySlice2d< real64 > const & dReactionRates_dLogPrimarySpeciesConcentrations, + arraySlice1d< real64, reactivefluid::USD_SPECIES - 2 > const & aggregateSpeciesRates, + arraySlice2d< real64, reactivefluid::USD_SPECIES_DC - 2 > const & dAggregateSpeciesRates_dLogPrimarySpeciesConcentrations ) const +{ + GEOS_UNUSED_VAR( pressure ); + + MixedEquilibriumKineticReactions< real64, localIndex, localIndex, true >:: + updateMixedSystem( temperature, + m_params, + logPrimarySpeciesConcentration, + surfaceArea, + logSecondarySpeciesConcentration, + primarySpeciesAggregateConcentration, + primarySpeciesMobileAggregateConcentration, + dPrimarySpeciesAggregateConcentration_dLogPrimarySpeciesConcentrations, + dPrimarySpeciesMobileAggregateConcentration_dLogPrimarySpeciesConcentrations, + reactionRates, + dReactionRates_dLogPrimarySpeciesConcentrations, + aggregateSpeciesRates, + dAggregateSpeciesRates_dLogPrimarySpeciesConcentrations ); +} + +ENUM_STRINGS( ChemicalSystemType, + "carbonate", + "carbonateAllEquilibrium", + "ultramafic", + "momasEasy", + "momasMedium", + "chainSerialAllKinetic" ); + +} // namespace reactivefluid + +} // namespace constitutive + +} // namespace geos + +#endif // GEOS_CONSTITUTIVE_FLUID_REACTIVEFLUID_REACTIVESINGLEPHASEFLUID_HPP_ diff --git a/src/coreComponents/constitutive/fluid/singlefluid/CompressibleSinglePhaseFluid.hpp b/src/coreComponents/constitutive/fluid/singlefluid/CompressibleSinglePhaseFluid.hpp index 09faa09ab49..e177053795c 100644 --- a/src/coreComponents/constitutive/fluid/singlefluid/CompressibleSinglePhaseFluid.hpp +++ b/src/coreComponents/constitutive/fluid/singlefluid/CompressibleSinglePhaseFluid.hpp @@ -130,6 +130,21 @@ class CompressibleSinglePhaseUpdate : public SingleFluidBaseUpdate m_dViscosity[k][q][DerivOffset::dP] ); } + GEOS_HOST_DEVICE + GEOS_FORCE_INLINE + virtual void update( localIndex const k, + localIndex const q, + real64 const pressure, + real64 const GEOS_UNUSED_PARAM( temperature ), + arraySlice1d< real64 const, compflow::USD_COMP - 1 > const & GEOS_UNUSED_PARAM( logPrimaryConcentration ) ) const override + { + compute( pressure, + m_density[k][q], + m_dDensity[k][q][DerivOffset::dP], + m_viscosity[k][q], + m_dViscosity[k][q][DerivOffset::dP] ); + } + private: /// Relationship between the fluid density and pressure diff --git a/src/coreComponents/constitutive/fluid/singlefluid/SingleFluidBase.hpp b/src/coreComponents/constitutive/fluid/singlefluid/SingleFluidBase.hpp index 57baebcd702..87e5866e79d 100644 --- a/src/coreComponents/constitutive/fluid/singlefluid/SingleFluidBase.hpp +++ b/src/coreComponents/constitutive/fluid/singlefluid/SingleFluidBase.hpp @@ -20,6 +20,7 @@ #ifndef GEOS_CONSTITUTIVE_FLUID_SINGLEFLUID_SINGLEFLUIDBASE_HPP #define GEOS_CONSTITUTIVE_FLUID_SINGLEFLUID_SINGLEFLUIDBASE_HPP +#include "common/DataLayouts.hpp" #include "constitutive/ConstitutiveBase.hpp" #include "constitutive/fluid/singlefluid/SingleFluidLayouts.hpp" #include "constitutive/fluid/singlefluid/SingleFluidUtils.hpp" @@ -240,6 +241,21 @@ class SingleFluidBaseUpdate real64 const pressure, real64 const temperature ) const = 0; + /** + * @brief Update fluid state at a single point. + * @param[in] k element index + * @param[in] q gauss point index + * @param[in] pressure the target pressure value + * @param[in] temperature the target temperature value + * @param[in] logPrimaryConcentration the target logPrimaryConc value + */ + GEOS_HOST_DEVICE + virtual void update( localIndex const k, + localIndex const q, + real64 const pressure, + real64 const temperature, + arraySlice1d< real64 const, compflow::USD_COMP - 1 > const & logPrimaryConcentration ) const = 0; + }; //END_SPHINX_INCLUDE_02 diff --git a/src/coreComponents/constitutive/fluid/singlefluid/ThermalCompressibleSinglePhaseFluid.hpp b/src/coreComponents/constitutive/fluid/singlefluid/ThermalCompressibleSinglePhaseFluid.hpp index 574164785b7..2f110c295ef 100644 --- a/src/coreComponents/constitutive/fluid/singlefluid/ThermalCompressibleSinglePhaseFluid.hpp +++ b/src/coreComponents/constitutive/fluid/singlefluid/ThermalCompressibleSinglePhaseFluid.hpp @@ -165,6 +165,30 @@ class ThermalCompressibleSinglePhaseUpdate : public SingleFluidBaseUpdate m_dEnthalpy[k][q][DerivOffset::dT] ); } + GEOS_HOST_DEVICE + GEOS_FORCE_INLINE + virtual void update( localIndex const k, + localIndex const q, + real64 const pressure, + real64 const temperature, + arraySlice1d< real64 const, compflow::USD_COMP - 1 > const & GEOS_UNUSED_PARAM( logPrimaryConcentration ) ) const override + { + compute( pressure, + temperature, + m_density[k][q], + m_dDensity[k][q][DerivOffset::dP], + m_dDensity[k][q][DerivOffset::dT], + m_viscosity[k][q], + m_dViscosity[k][q][DerivOffset::dP], + m_dViscosity[k][q][DerivOffset::dT], + m_internalEnergy[k][q], + m_dInternalEnergy[k][q][DerivOffset::dP], + m_dInternalEnergy[k][q][DerivOffset::dT], + m_enthalpy[k][q], + m_dEnthalpy[k][q][DerivOffset::dP], + m_dEnthalpy[k][q][DerivOffset::dT] ); + } + private: /// Fluid internal energy and derivatives diff --git a/src/coreComponents/constitutive/solid/ReactiveSolid.cpp b/src/coreComponents/constitutive/solid/ReactiveSolid.cpp new file mode 100644 index 00000000000..eb015de05f4 --- /dev/null +++ b/src/coreComponents/constitutive/solid/ReactiveSolid.cpp @@ -0,0 +1,55 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * 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 ReactiveSolid.cpp + */ + +#include "ReactiveSolid.hpp" +#include "porosity/ReactivePorosity.hpp" +#include "constitutive/permeability/ConstantPermeability.hpp" +#include "constitutive/permeability/CarmanKozenyPermeability.hpp" +#include "constitutive/permeability/PressurePermeability.hpp" + +namespace geos +{ + +using namespace dataRepository; + +namespace constitutive +{ + +template< typename PORO_TYPE, + typename PERM_TYPE > +ReactiveSolid< PORO_TYPE, PERM_TYPE >::ReactiveSolid( string const & name, Group * const parent ): + CoupledSolid< NullModel, PORO_TYPE, PERM_TYPE >( name, parent ) +{} + +template< typename PORO_TYPE, + typename PERM_TYPE > +ReactiveSolid< PORO_TYPE, PERM_TYPE >::~ReactiveSolid() = default; + +// Register all ReactiveSolid model types. +typedef ReactiveSolid< ReactivePorosity, ConstantPermeability > ReactiveRockConstant; +typedef ReactiveSolid< ReactivePorosity, CarmanKozenyPermeability > ReactiveRockCK; +typedef ReactiveSolid< ReactivePorosity, PressurePermeability > ReactiveRockPressurePerm; + +REGISTER_CATALOG_ENTRY( ConstitutiveBase, ReactiveRockConstant, string const &, Group * const ) +REGISTER_CATALOG_ENTRY( ConstitutiveBase, ReactiveRockCK, string const &, Group * const ) +REGISTER_CATALOG_ENTRY( ConstitutiveBase, ReactiveRockPressurePerm, string const &, Group * const ) + +} +} /* namespace geos */ diff --git a/src/coreComponents/constitutive/solid/ReactiveSolid.hpp b/src/coreComponents/constitutive/solid/ReactiveSolid.hpp new file mode 100644 index 00000000000..5e06af85ef3 --- /dev/null +++ b/src/coreComponents/constitutive/solid/ReactiveSolid.hpp @@ -0,0 +1,183 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * 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 ReactiveSolid.hpp + */ + +#ifndef GEOS_CONSTITUTIVE_SOLID_REACTIVESOLID_HPP_ +#define GEOS_CONSTITUTIVE_SOLID_REACTIVESOLID_HPP_ + +#include "constitutive/solid/CoupledSolid.hpp" +#include "constitutive/solid/porosity/ReactivePorosity.hpp" +#include "constitutive/NullModel.hpp" + +#include "constitutive/fluid/reactivefluid/ReactiveFluidLayouts.hpp" + +namespace geos +{ +namespace constitutive +{ + +/** + * @brief Provides kernel-callable constitutive update routines + * + * + * @tparam PORO_TYPE type of the porosity model + * @tparam PERM_TYPE type of the permeability model + */ +template< typename PORO_TYPE, + typename PERM_TYPE > +class ReactiveSolidUpdates : public CoupledSolidUpdates< NullModel, PORO_TYPE, PERM_TYPE > +{ +public: + + /** + * @brief Constructor + */ + ReactiveSolidUpdates( NullModel const & solidModel, + PORO_TYPE const & porosityModel, + PERM_TYPE const & permModel ): + CoupledSolidUpdates< NullModel, PORO_TYPE, PERM_TYPE >( solidModel, porosityModel, permModel ) + {} + + GEOS_HOST_DEVICE + void updateStateFromPressureAndReactions( localIndex const k, + localIndex const q, + real64 const & pressure, + arraySlice1d< real64 const, compflow::USD_COMP - 1 > const & kineticReactionMolarIncrements ) const + { + m_porosityUpdate.updateFromReactions( k, q, kineticReactionMolarIncrements ); + real64 const porosity = m_porosityUpdate.getPorosity( k, q ); + m_permUpdate.updateFromPressureAndPorosity( k, q, pressure, porosity ); + } + + GEOS_HOST_DEVICE + void updateSurfaceArea( localIndex const k, + localIndex const q, + arraySlice1d< real64 const, compflow::USD_COMP - 1 > const & initialSurfaceArea, + arraySlice1d< real64, compflow::USD_COMP - 1 > const & surfaceArea ) const + { + real64 const porosity = m_porosityUpdate.getPorosity( k, q ); + real64 const initialPorosity = m_porosityUpdate.getInitialPorosity( k, q ); + + for( integer r=0; r < initialSurfaceArea.size(); ++r ) + { + real64 const volumeFraction_r = m_porosityUpdate.getVolumeFractionForMineral( k, q, r ); + real64 const initialVolumeFraction_r = m_porosityUpdate.getInitialVolumeFractionForMineral( k, q, r ); + surfaceArea[r] = initialSurfaceArea[r] * pow( volumeFraction_r / initialVolumeFraction_r, 2.0/3.0 ) + * pow( porosity / initialPorosity, 2.0/3.0 ); + } + } + +private: + using CoupledSolidUpdates< NullModel, ReactivePorosity, PERM_TYPE >::m_solidUpdate; + using CoupledSolidUpdates< NullModel, ReactivePorosity, PERM_TYPE >::m_porosityUpdate; + using CoupledSolidUpdates< NullModel, ReactivePorosity, PERM_TYPE >::m_permUpdate; + +}; + + +/** + * @brief ReactiveSolidBase class used for dispatch of all Reactive solids. + */ +class ReactiveSolidBase +{}; + + +/** + * @brief Class to represent a porous material for flow simulations. + * It is used as an interface to access all constitutive models relative to the properties of a porous material + * for flow only simulations. + * + * @tparam PORO_TYPE type of porosity model + * @tparam PERM_TYPE type of the permeability model + */ + +template< typename PORO_TYPE, + typename PERM_TYPE > +class ReactiveSolid : public CoupledSolid< NullModel, PORO_TYPE, PERM_TYPE > +{ +public: + + + /// Alias for ElasticIsotropicUpdates + using KernelWrapper = ReactiveSolidUpdates< PORO_TYPE, PERM_TYPE >; + + /** + * @brief Constructor + * @param name Object name + * @param parent Object's parent group + */ + ReactiveSolid( string const & name, dataRepository::Group * const parent ); + + /// Destructor + virtual ~ReactiveSolid() override; + + /** + * @brief Catalog name + * @return Static catalog string + */ + static string catalogName() { return string( "ReactiveSolid" ) + PERM_TYPE::catalogName(); } + + /** + * @brief Get catalog name + * @return Catalog name string + */ + virtual string getCatalogName() const override { return catalogName(); } + + /* + * @brief get the volume fractions. + * return a constant arrayView3d to the new volume fractions + */ + arrayView3d< real64 const > const getVolumeFractions() const + { + return getPorosityModel().getVolumeFractions(); + } + + /* + * @brief get the initial volume fractions. + * return a constant arrayView1d to the initial volume fractions + */ + arrayView1d< real64 const > const getInitialVolumeFractions() const + { + return getPorosityModel().getInitialVolumeFractions(); + } + + + /** + * @brief Create a instantiation of the ReactiveSolidUpdates class + * that refers to the data in this. + * @return An instantiation of ReactiveSolidUpdates. + */ + KernelWrapper createKernelUpdates() const + { + + return ReactiveSolidUpdates< PORO_TYPE, PERM_TYPE >( getSolidModel(), + getPorosityModel(), + getPermModel() ); + } +private: + using CoupledSolid< NullModel, PORO_TYPE, PERM_TYPE >::getSolidModel; + using CoupledSolid< NullModel, PORO_TYPE, PERM_TYPE >::getPorosityModel; + using CoupledSolid< NullModel, PORO_TYPE, PERM_TYPE >::getPermModel; + +}; + +} +} /* namespace geos */ + +#endif /* GEOS_CONSTITUTIVE_SOLID_REACTIVESOLID_HPP_ */ diff --git a/src/coreComponents/constitutive/solid/porosity/ReactivePorosity.cpp b/src/coreComponents/constitutive/solid/porosity/ReactivePorosity.cpp new file mode 100644 index 00000000000..7865764fd7c --- /dev/null +++ b/src/coreComponents/constitutive/solid/porosity/ReactivePorosity.cpp @@ -0,0 +1,144 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * 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 ReactivePorosity.cpp + */ + +#include "ReactivePorosity.hpp" + +namespace geos +{ + +using namespace dataRepository; + +namespace constitutive +{ + +ReactivePorosity::ReactivePorosity( string const & name, Group * const parent ): + PorosityBase( name, parent ) +{ + registerWrapper( viewKeyStruct::defaultInitialVolumeFractionsString(), &m_defaultInitialVolumeFractions ). + setInputFlag( InputFlags::REQUIRED ). + setDescription( "Default initial volume fractions" ); + + registerWrapper( viewKeyStruct::initialVolumeFractionsString(), &m_initialVolumeFractions ). + setApplyDefaultValue( 0.0 ). + setPlotLevel( PlotLevel::LEVEL_0 ). + setDescription( "Initial volume fractions" ); + + registerWrapper( viewKeyStruct::volumeFractionsString(), &m_volumeFractions ). + setApplyDefaultValue( 0.0 ). + setPlotLevel( PlotLevel::LEVEL_0 ). + setDescription( "Current volume fractions" ); + + registerWrapper( viewKeyStruct::volumeFractions_nString(), &m_volumeFractions_n ). + setApplyDefaultValue( 0.0 ). + setPlotLevel( PlotLevel::LEVEL_0 ). + setDescription( "Volume fractions at last time step" ); + + registerWrapper( viewKeyStruct::molarWeightsString(), &m_molarWeights ). + setInputFlag( InputFlags::REQUIRED ). + setDescription( "Mineral molar weights" ); + + registerWrapper( viewKeyStruct::mineralDensitiesString(), &m_mineralDensities ). + setInputFlag( InputFlags::REQUIRED ). + setDescription( "Mineral densities" ); +} + +std::unique_ptr< ConstitutiveBase > ReactivePorosity::deliverClone( string const & name, Group * const parent ) const +{ + std::unique_ptr< ConstitutiveBase > clone = ConstitutiveBase::deliverClone( name, parent ); + + ReactivePorosity & newConstitutiveRelation = dynamicCast< ReactivePorosity & >( *clone ); + + newConstitutiveRelation.m_numKineticReactions = m_numKineticReactions; + + return clone; +} + +void ReactivePorosity::postInputInitialization() +{ + PorosityBase::postInputInitialization(); + + GEOS_THROW_IF_NE_MSG( m_molarWeights.size(), m_defaultInitialVolumeFractions.size(), + GEOS_FMT( "{}: mismatch in number of components in porosity model attribute '{}' and '{}", + getFullName(), viewKeyStruct::molarWeightsString(), viewKeyStruct::initialVolumeFractionsString() ), + InputError ); + + GEOS_THROW_IF_NE_MSG( m_mineralDensities.size(), m_defaultInitialVolumeFractions.size(), + GEOS_FMT( "{}: mismatch in number of components in porosity model attribute '{}' and '{}", + getFullName(), viewKeyStruct::mineralDensitiesString(), viewKeyStruct::initialVolumeFractionsString() ), + InputError ); + + m_numKineticReactions = m_defaultInitialVolumeFractions.size(); +} + +void ReactivePorosity::allocateConstitutiveData( dataRepository::Group & parent, + localIndex const numConstitutivePointsPerParentIndex ) +{ + PorosityBase::allocateConstitutiveData( parent, numConstitutivePointsPerParentIndex ); + + resizeFields( parent.size(), numConstitutivePointsPerParentIndex ); + +} + +void ReactivePorosity::resizeFields( localIndex const size, localIndex const numPts ) +{ + integer const numKineticReactions = this->numKineticReactions(); + + m_initialVolumeFractions.resize( size, numPts, numKineticReactions ); + m_volumeFractions.resize( size, numPts, numKineticReactions ); + m_volumeFractions_n.resize( size, numPts, numKineticReactions ); +} + +void ReactivePorosity::saveConvergedState() const +{ + PorosityBase::saveConvergedState(); + + m_volumeFractions_n.setValues< parallelDevicePolicy<> >( m_volumeFractions.toViewConst() ); +} + +void ReactivePorosity::initializeState() const +{ + integer const numKineticReactions = this->numKineticReactions(); + + for( localIndex ei = 0; ei < m_newPorosity.size( 0 ); ++ei ) + { + for( localIndex q = 0; q < m_newPorosity.size( 1 ); ++q ) + { + m_newPorosity[ei][q] = m_referencePorosity[ei]; + } + } + + PorosityBase::initializeState(); + + for( localIndex ei = 0; ei < m_volumeFractions.size( 0 ); ++ei ) + { + for( localIndex q = 0; q < m_volumeFractions.size( 1 ); ++q ) + { + for( integer r = 0; r < numKineticReactions; ++r ) + { + m_volumeFractions[ei][q][r] = m_defaultInitialVolumeFractions[r]; + m_initialVolumeFractions[ei][q][r] = m_defaultInitialVolumeFractions[r]; + m_volumeFractions_n[ei][q][r] = m_defaultInitialVolumeFractions[r]; + } + } + } +} + +REGISTER_CATALOG_ENTRY( ConstitutiveBase, ReactivePorosity, string const &, Group * const ) +} +} /* namespace geos */ diff --git a/src/coreComponents/constitutive/solid/porosity/ReactivePorosity.hpp b/src/coreComponents/constitutive/solid/porosity/ReactivePorosity.hpp new file mode 100644 index 00000000000..54bc69db6a7 --- /dev/null +++ b/src/coreComponents/constitutive/solid/porosity/ReactivePorosity.hpp @@ -0,0 +1,219 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * 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 ReactivePorosity.hpp + */ + +#ifndef GEOS_CONSTITUTIVE_POROSITY_REACTIVEPOROSITY_HPP_ +#define GEOS_CONSTITUTIVE_POROSITY_REACTIVEPOROSITY_HPP_ + +#include "PorosityBase.hpp" + +#include "constitutive/fluid/reactivefluid/ReactiveFluidLayouts.hpp" + +namespace geos +{ +namespace constitutive +{ + +class ReactivePorosityUpdates : public PorosityBaseUpdates +{ +public: + + ReactivePorosityUpdates( arrayView2d< real64 > const & newPorosity, + arrayView2d< real64 const > const & porosity_n, + arrayView2d< real64 > const & dPorosity_dPressure, + arrayView2d< real64 > const & dPorosity_dTemperature, + arrayView2d< real64 const > const & initialPorosity, + arrayView1d< real64 const > const & referencePorosity, + arrayView3d< real64, reactivefluid::USD_SPECIES > const & volumeFractions, + arrayView3d< real64 const, reactivefluid::USD_SPECIES > const & initialVolumeFractions, + arrayView3d< real64 const, reactivefluid::USD_SPECIES > const & volumeFractions_n, + integer const numKineticReactions, + arrayView1d< real64 const > const & molarWeights, + arrayView1d< real64 const > const & mineralDensities ): + PorosityBaseUpdates( newPorosity, + porosity_n, + dPorosity_dPressure, + dPorosity_dTemperature, + initialPorosity, + referencePorosity ), + m_volumeFractions( volumeFractions ), + m_initialVolumeFractions( initialVolumeFractions ), + m_volumeFractions_n( volumeFractions_n ), + m_numKineticReactions( numKineticReactions ), + m_molarWeights( molarWeights ), + m_mineralDensities( mineralDensities ) + {} + + GEOS_HOST_DEVICE + void computePorosity( arraySlice1d< real64 const, compflow::USD_COMP - 1 > const & kineticReactionMolarIncrements, + arraySlice1d< real64, reactivefluid::USD_SPECIES - 2 > const & volumeFractions, + arraySlice1d< real64 const, reactivefluid::USD_SPECIES - 2 > const & volumeFractions_n, + real64 & porosity, + real64 const & porosity_n, + integer const & numKineticReactions, + arrayView1d< real64 const > const & molarWeights, + arrayView1d< real64 const > const & mineralDensities ) const + { + real64 porosityIncrement = 0.0; + + for( integer r=0; r < numKineticReactions; ++r ) + { + real64 const volumeFractionIncrement = -kineticReactionMolarIncrements[r] * molarWeights[r]/mineralDensities[r]; + volumeFractions[r] = volumeFractions_n[r] + volumeFractionIncrement; + + porosityIncrement -= volumeFractionIncrement; + } + + porosity = porosity_n + porosityIncrement; + + if( porosity < 0 ) + { + porosity = 0; + } + else if( porosity > 1.0 ) + { + porosity = 1.0; + } + + } + + GEOS_HOST_DEVICE + void updateFromReactions( localIndex const k, + localIndex const q, + arraySlice1d< real64 const, compflow::USD_COMP - 1 > const & kineticReactionMolarIncrements ) const + { + computePorosity( kineticReactionMolarIncrements, + m_volumeFractions[k][q], + m_volumeFractions_n[k][q], + m_newPorosity[k][q], + m_porosity_n[k][q], + m_numKineticReactions, + m_molarWeights, + m_mineralDensities ); + } + + GEOS_HOST_DEVICE + inline + real64 getVolumeFractionForMineral( localIndex const k, + localIndex const q, + localIndex const r ) const + { + return m_volumeFractions[k][q][r]; + } + + GEOS_HOST_DEVICE + inline + real64 getInitialVolumeFractionForMineral( localIndex const k, + localIndex const q, + localIndex const r ) const + { + return m_initialVolumeFractions[k][q][r]; + } + +protected: + + arrayView3d< real64, reactivefluid::USD_SPECIES > m_volumeFractions; + arrayView3d< real64 const, reactivefluid::USD_SPECIES > m_initialVolumeFractions; + arrayView3d< real64 const, reactivefluid::USD_SPECIES > const m_volumeFractions_n; + + integer const m_numKineticReactions; + arrayView1d< real64 const > const m_molarWeights; + arrayView1d< real64 const > const m_mineralDensities; +}; + + +class ReactivePorosity : public PorosityBase +{ +public: + ReactivePorosity( string const & name, Group * const parent ); + + virtual std::unique_ptr< ConstitutiveBase > + deliverClone( string const & name, + dataRepository::Group * const parent ) const override; + + virtual void allocateConstitutiveData( dataRepository::Group & parent, + localIndex const numConstitutivePointsPerParentIndex ) override; + + static string catalogName() { return "ReactivePorosity"; } + + virtual string getCatalogName() const override { return catalogName(); } + + virtual void saveConvergedState() const override; + + integer numKineticReactions() const { return m_numKineticReactions; } + + virtual void initializeState() const override; + + struct viewKeyStruct : public PorosityBase::viewKeyStruct + { + static constexpr char const * defaultInitialVolumeFractionsString() { return "defaultInitialVolumeFractions"; } + static constexpr char const * initialVolumeFractionsString() { return "initialVolumeFractions"; } + static constexpr char const * volumeFractionsString() { return "volumeFractions"; } + static constexpr char const * volumeFractions_nString() { return "volumeFractions_n"; } + static constexpr char const * molarWeightsString() { return "molarWeights"; } + static constexpr char const * mineralDensitiesString() { return "mineralDensities"; } + } viewKeys; + + + using KernelWrapper = ReactivePorosityUpdates; + + /** + * @brief Create an update kernel wrapper. + * @return the wrapper + */ + KernelWrapper createKernelUpdates() const + { + return KernelWrapper( m_newPorosity, + m_porosity_n, + m_dPorosity_dPressure, + m_dPorosity_dTemperature, + m_initialPorosity, + m_referencePorosity, + m_volumeFractions, + m_initialVolumeFractions, + m_volumeFractions_n, + m_numKineticReactions, + m_molarWeights, + m_mineralDensities ); + } + + +private: + virtual void postInputInitialization() override; + + virtual void resizeFields( localIndex const size, localIndex const numPts ); + + array1d< real64 > m_defaultInitialVolumeFractions; + + array3d< real64, constitutive::reactivefluid::LAYOUT_SPECIES > m_volumeFractions; + array3d< real64, constitutive::reactivefluid::LAYOUT_SPECIES > m_initialVolumeFractions; + array3d< real64, constitutive::reactivefluid::LAYOUT_SPECIES > m_volumeFractions_n; + + integer m_numKineticReactions; + array1d< real64 > m_molarWeights; + array1d< real64 > m_mineralDensities; + +}; + + +}/* namespace constitutive */ + +} /* namespace geos */ + + +#endif //GEOS_CONSTITUTIVE_POROSITY_REACTIVEPOROSITY_HPP_ diff --git a/src/coreComponents/constitutiveDrivers/CMakeLists.txt b/src/coreComponents/constitutiveDrivers/CMakeLists.txt index de519ea2fdb..0e464c6d7f0 100644 --- a/src/coreComponents/constitutiveDrivers/CMakeLists.txt +++ b/src/coreComponents/constitutiveDrivers/CMakeLists.txt @@ -25,7 +25,6 @@ set( constitutiveDrivers_headers fluid/multiFluid/LogLevelsInfo.hpp fluid/multiFluid/PVTDriver.hpp fluid/multiFluid/PVTDriverRunTest.hpp - fluid/multiFluid/reactive/ReactiveFluidDriver.hpp relativePermeability/RelpermDriver.hpp relativePermeability/RelpermDriverRunTest.hpp solid/TriaxialDriver.hpp @@ -47,7 +46,6 @@ set( constitutiveDrivers_sources fluid/multiFluid/compositional/PVTDriverRunTestCompositionalTwoPhaseConstantViscosity.cpp fluid/multiFluid/compositional/PVTDriverRunTestCompositionalTwoPhasePhillipsBrine.cpp fluid/multiFluid/compositional/PVTDriverRunTestCompositionalTwoPhaseLohrenzBrayClarkViscosity.cpp - fluid/multiFluid/reactive/ReactiveFluidDriver.cpp relativePermeability/RelpermDriver.cpp relativePermeability/RelpermDriverBrooksCoreyBakerRunTest.cpp relativePermeability/RelpermDriverBrooksCoreyStone2RunTest.cpp diff --git a/src/coreComponents/integrationTests/constitutiveTests/CMakeLists.txt b/src/coreComponents/integrationTests/constitutiveTests/CMakeLists.txt index 6a9957547f7..bbb94f47a59 100644 --- a/src/coreComponents/integrationTests/constitutiveTests/CMakeLists.txt +++ b/src/coreComponents/integrationTests/constitutiveTests/CMakeLists.txt @@ -22,9 +22,6 @@ set( gtest_pvt_xmls testPVT_ThreePhaseCompositional.xml ) -set( gtest_reactivefluid_xmls - testReactiveFluid.xml ) - set( tplDependencyList ${parallelDeps} gtest ) set( dependencyList mainInterface ) @@ -67,16 +64,3 @@ foreach(test ${gtest_pvt_xmls}) geos_add_test( NAME ${test_name} COMMAND testPVT -i ${CMAKE_CURRENT_LIST_DIR}/${test} ) endforeach() - - -# Add reactivefluiddriver xml based tests -blt_add_executable( NAME testReactiveFluid - SOURCES testReactiveFluid.cpp - OUTPUT_DIR ${TEST_OUTPUT_DIRECTORY} - DEPENDS_ON ${dependencyList} "-lz" ${tplDependencyList} ) - -foreach(test ${gtest_reactivefluid_xmls}) - get_filename_component( test_name ${test} NAME_WE ) - geos_add_test( NAME ${test_name} - COMMAND testReactiveFluid -i ${CMAKE_CURRENT_LIST_DIR}/${test} ) -endforeach() diff --git a/src/coreComponents/integrationTests/fluidFlowTests/CMakeLists.txt b/src/coreComponents/integrationTests/fluidFlowTests/CMakeLists.txt index ce446028ce0..951cac7c502 100644 --- a/src/coreComponents/integrationTests/fluidFlowTests/CMakeLists.txt +++ b/src/coreComponents/integrationTests/fluidFlowTests/CMakeLists.txt @@ -4,7 +4,8 @@ set( gtest_geosx_tests testThermalSinglePhaseFlow.cpp testTransmissibility.cpp testImmiscibleMultiphaseFlow.cpp - testSinglePhaseMFDPolyhedral.cpp ) + testSinglePhaseMFDPolyhedral.cpp + testSinglePhaseReactiveTransport.cpp ) if( ENABLE_PVTPackage ) list( APPEND gtest_geosx_tests diff --git a/src/coreComponents/integrationTests/fluidFlowTests/testSinglePhaseReactiveTransport.cpp b/src/coreComponents/integrationTests/fluidFlowTests/testSinglePhaseReactiveTransport.cpp new file mode 100644 index 00000000000..c4b50dfab30 --- /dev/null +++ b/src/coreComponents/integrationTests/fluidFlowTests/testSinglePhaseReactiveTransport.cpp @@ -0,0 +1,582 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * SPDX-License-Identifier: LGPL-2.1-only + * + * Copyright (c) 2016-2024 Lawrence Livermore National Security LLC + * Copyright (c) 2018-2024 TotalEnergies + * Copyright (c) 2018-2024 The Board of Trustees of the Leland Stanford Junior University + * Copyright (c) 2023-2024 Chevron + * Copyright (c) 2019- GEOS/GEOSX Contributors + * All rights reserved + * + * See top level LICENSE, COPYRIGHT, CONTRIBUTORS, NOTICE, and ACKNOWLEDGEMENTS files for details. + * ------------------------------------------------------------------------------------------------------------ + */ + +#include "constitutive/fluid/singlefluid/SingleFluidBase.hpp" +#include "finiteVolume/FiniteVolumeManager.hpp" +#include "finiteVolume/FluxApproximationBase.hpp" +#include "mainInterface/initialization.hpp" +#include "mainInterface/GeosxState.hpp" +#include "physicsSolvers/PhysicsSolverBase.hpp" +#include "physicsSolvers/PhysicsSolverManager.hpp" +#include "physicsSolvers/fluidFlow/SinglePhaseBaseFields.hpp" +#include "physicsSolvers/fluidFlow/SinglePhaseReactiveTransportFields.hpp" +#include "physicsSolvers/fluidFlow/SinglePhaseReactiveTransport.hpp" +#include "physicsSolvers/fluidFlow/FlowSolverBaseFields.hpp" +#include "integrationTests/fluidFlowTests/testSinglePhaseReactiveTransportUtils.hpp" + +using namespace geos; +using namespace geos::dataRepository; +using namespace geos::constitutive; +using namespace geos::constitutive::reactivefluid; +using namespace geos::testing; + +CommandLineOptions g_commandLineOptions; + +// Sphinx start after input XML + +char const * xmlInputCarbonate = + R"xml( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + )xml"; +// Sphinx end before input XML + +template< typename LAMBDA > +void testNumericalJacobian( SinglePhaseReactiveTransport & solver, + DomainPartition & domain, + real64 const perturbParameter, + real64 const relTol, + LAMBDA assembleFunction ) +{ + CRSMatrix< real64, globalIndex > const & jacobian = solver.getLocalMatrix(); + array1d< real64 > residual( jacobian.numRows() ); + + // assemble the analytical residual + solver.resetStateToBeginningOfStep( domain ); + + residual.zero(); + jacobian.zero(); + + assembleFunction( jacobian.toViewConstSizes(), residual.toView() ); + residual.move( hostMemorySpace, false ); + + // copy the analytical residual + array1d< real64 > residualOrig( residual ); + + // create the numerical jacobian + jacobian.move( hostMemorySpace ); + CRSMatrix< real64, globalIndex > jacobianFD( jacobian ); + jacobianFD.zero(); + + // fill jacobian FD + fillCellCenteredNumericalJacobian( solver, + domain, + false, + perturbParameter, + residual.toView(), + residualOrig.toView(), + jacobian.toView(), + jacobianFD.toView(), + assembleFunction ); + + // assemble the analytical jacobian + solver.resetStateToBeginningOfStep( domain ); + + residual.zero(); + jacobian.zero(); + assembleFunction( jacobian.toViewConstSizes(), residual.toView() ); + + compareLocalMatrices( jacobian.toViewConst(), jacobianFD.toViewConst(), relTol, 1e-6 ); +} + +class SinglePhaseReactiveTransportTest : public ::testing::Test +{ +public: + + SinglePhaseReactiveTransportTest(): + state( std::make_unique< CommandLineOptions >( g_commandLineOptions ) ) + {} + +protected: + + void SetUp() override + { + + setupProblemFromXML( state.getProblemManager(), xmlInputCarbonate ); + solver = &state.getProblemManager().getPhysicsSolverManager().getGroup< SinglePhaseReactiveTransport >( "SinglePhaseReactiveFlow" ); + + DomainPartition & domain = state.getProblemManager().getDomainPartition(); + + solver->setupSystem( domain, + solver->getDofManager(), + solver->getLocalMatrix(), + solver->getSystemRhs(), + solver->getSystemSolution() ); + + solver->implicitStepSetup( time, dt, domain ); + } + + static real64 constexpr time = 0.0; + static real64 constexpr dt = 1.0; + static real64 constexpr eps = std::numeric_limits< real64 >::epsilon(); + + GeosxState state; + SinglePhaseReactiveTransport * solver; +}; + +real64 constexpr SinglePhaseReactiveTransportTest::time; +real64 constexpr SinglePhaseReactiveTransportTest::dt; +real64 constexpr SinglePhaseReactiveTransportTest::eps; + +TEST_F( SinglePhaseReactiveTransportTest, jacobianNumericalCheck_flux ) +{ + real64 const perturb = std::sqrt( eps ); + real64 const tol = 1e-10; // 1% error margin + + DomainPartition & domain = state.getProblemManager().getDomainPartition(); + + testNumericalJacobian( *solver, domain, perturb, tol, + [&] ( CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) + { + // The first input parameter denotes t_n, which is unused. Just input something here. + solver->assembleFluxTerms( dt, domain, solver->getDofManager(), localMatrix, localRhs ); + } ); +} + +TEST_F( SinglePhaseReactiveTransportTest, jacobianNumericalCheck_accumulationBalance ) +{ + real64 const perturb = std::sqrt( eps ); + real64 const tol = 1e-6; // 1% error margin + + DomainPartition & domain = state.getProblemManager().getDomainPartition(); + + testNumericalJacobian( *solver, domain, perturb, tol, + [&] ( CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) + { + solver->assembleAccumulationTermsInMassBalanceAndSpeciesAmountEqs( dt, domain, solver->getDofManager(), localMatrix, localRhs ); + } ); +} + +int main( int argc, char * * argv ) +{ + ::testing::InitGoogleTest( &argc, argv ); + g_commandLineOptions = *geos::basicSetup( argc, argv ); + int const result = RUN_ALL_TESTS(); + geos::basicCleanup(); + return result; +} diff --git a/src/coreComponents/integrationTests/fluidFlowTests/testSinglePhaseReactiveTransportUtils.hpp b/src/coreComponents/integrationTests/fluidFlowTests/testSinglePhaseReactiveTransportUtils.hpp new file mode 100644 index 00000000000..2cd58eeb87d --- /dev/null +++ b/src/coreComponents/integrationTests/fluidFlowTests/testSinglePhaseReactiveTransportUtils.hpp @@ -0,0 +1,227 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * 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. + * ------------------------------------------------------------------------------------------------------------ + */ + +#ifndef GEOS_TESTSINGLEPHASEREACTIVETRANSPORTUTILS_HPP +#define GEOS_TESTSINGLEPHASEREACTIVETRANSPORTUTILS_HPP + +#include "codingUtilities/UnitTestUtilities.hpp" +#include "constitutive/ConstitutiveManager.hpp" +#include "constitutive/fluid/singlefluid/SingleFluidBase.hpp" +#include "constitutive/fluid/singlefluid/SingleFluidLayouts.hpp" +#include "mesh/MeshManager.hpp" +#include "mainInterface/ProblemManager.hpp" +#include "physicsSolvers/fluidFlow/SinglePhaseBase.hpp" +#include "physicsSolvers/fluidFlow/SinglePhaseBaseFields.hpp" +#include "physicsSolvers/fluidFlow/SinglePhaseReactiveTransportFields.hpp" +#include "physicsSolvers/fluidFlow/SinglePhaseReactiveTransport.hpp" +#include "physicsSolvers/fluidFlow/SinglePhaseFVM.hpp" +#include "physicsSolvers/fluidFlow/FlowSolverBaseFields.hpp" +#include "testFlowUtils.hpp" + +namespace geos +{ + +namespace testing +{ + +void fillNumericalJacobian( arrayView1d< real64 const > const & residual, + arrayView1d< real64 const > const & residualOrig, + globalIndex const dofIndex, + real64 const eps, + CRSMatrixView< real64, globalIndex const > const & jacobian ) +{ + forAll< parallelDevicePolicy<> >( residual.size(), [=] GEOS_HOST_DEVICE ( localIndex const row ) + { + real64 const dRdX = ( residual[row] - residualOrig[row] ) / eps; + if( fabs( dRdX ) > 0.0 ) + { + jacobian.addToRow< parallelDeviceAtomic >( row, &dofIndex, &dRdX, 1 ); + } + } ); +} + +void setupProblemFromXML( ProblemManager & problemManager, char const * const xmlInput ) +{ + xmlWrapper::xmlDocument xmlDocument; + xmlWrapper::xmlResult xmlResult = xmlDocument.loadString( xmlInput ); + if( !xmlResult ) + { + GEOS_LOG_RANK_0( "XML parsed with errors!" ); + GEOS_LOG_RANK_0( "Error description: " << xmlResult.description()); + GEOS_LOG_RANK_0( "Error offset: " << xmlResult.offset ); + } + + int mpiSize = MpiWrapper::commSize( MPI_COMM_GEOS ); + + dataRepository::Group & commandLine = + problemManager.getGroup< dataRepository::Group >( problemManager.groupKeys.commandLine ); + + commandLine.registerWrapper< integer >( problemManager.viewKeys.xPartitionsOverride.key() ). + setApplyDefaultValue( mpiSize ); + + xmlWrapper::xmlNode xmlProblemNode = xmlDocument.getChild( dataRepository::keys::ProblemManager ); + problemManager.processInputFileRecursive( xmlDocument, xmlProblemNode ); + + DomainPartition & domain = problemManager.getDomainPartition(); + + constitutive::ConstitutiveManager & constitutiveManager = domain.getConstitutiveManager(); + xmlWrapper::xmlNode topLevelNode = xmlProblemNode.child( constitutiveManager.getName().c_str()); + constitutiveManager.processInputFileRecursive( xmlDocument, topLevelNode ); + + MeshManager & meshManager = problemManager.getGroup< MeshManager >( problemManager.groupKeys.meshManager ); + meshManager.generateMeshLevels( domain ); + + ElementRegionManager & elementManager = domain.getMeshBody( 0 ).getBaseDiscretization().getElemManager(); + topLevelNode = xmlProblemNode.child( elementManager.getName().c_str()); + elementManager.processInputFileRecursive( xmlDocument, topLevelNode ); + + problemManager.problemSetup(); + problemManager.applyInitialConditions(); +} + +template< typename SINGLE_REACTIVE_TRANSPORT_SOLVER, typename LAMBDA > +void fillCellCenteredNumericalJacobian( SINGLE_REACTIVE_TRANSPORT_SOLVER & solver, + DomainPartition & domain, + bool const isThermal, + real64 const perturbParameter, + arrayView1d< real64 > residual, + arrayView1d< real64 > residualOrig, + CRSMatrixView< real64, globalIndex > jacobian, + CRSMatrixView< real64, globalIndex > jacobianFD, + LAMBDA assembleFunction ) +{ + integer const numSpecies = solver.numPrimarySpecies(); + + DofManager const & dofManager = solver.getDofManager(); + string const elemDofKey = dofManager.getKey( SINGLE_REACTIVE_TRANSPORT_SOLVER::viewKeyStruct::elemDofFieldString() ); + + solver.forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &, + MeshLevel & mesh, + string_array const & regionNames ) + { + mesh.getElemManager().forElementSubRegions( regionNames, + [&]( localIndex const, + ElementSubRegionBase & subRegion ) + { + arrayView1d< integer const > const & elemGhostRank = subRegion.ghostRank(); + arrayView1d< globalIndex const > const & elemDofNumber = + subRegion.getReference< array1d< globalIndex > >( elemDofKey ); + + arrayView1d< real64 > const pres = + subRegion.getField< fields::flow::pressure >(); + arrayView2d< real64, compflow::USD_COMP > const logPrimaryConc = + subRegion.getField< fields::flow::logPrimarySpeciesConcentration >(); + + for( localIndex ei = 0; ei < subRegion.size(); ++ei ) + { + if( elemGhostRank[ei] >= 0 ) + { + continue; + } + + solver.resetStateToBeginningOfStep( domain ); + + // Step 1: compute numerical derivatives wrt pressure + + { + pres.move( hostMemorySpace, true ); // to get the correct pres after reset + real64 const dP = perturbParameter * ( pres[ei] + perturbParameter ); + pres[ei] += dP; +#if defined(GEOS_USE_CUDA) + pres.move( parallelDeviceMemorySpace, false ); +#endif + + solver.updateState( domain ); + + residual.zero(); + jacobian.zero(); + assembleFunction( jacobian.toViewConstSizes(), residual.toView() ); + + fillNumericalJacobian( residual.toViewConst(), + residualOrig.toViewConst(), + elemDofNumber[ei], + dP, + jacobianFD.toViewConstSizes() ); + } + + // Step 2: compute numerical derivatives wrt temperature + + if( isThermal ) + { + arrayView1d< real64 > const temp = + subRegion.getField< fields::flow::temperature >(); + temp.move( hostMemorySpace, false ); + + solver.resetStateToBeginningOfStep( domain ); + + temp.move( hostMemorySpace, true ); + real64 const dT = perturbParameter * ( temp[ei] + perturbParameter ); + temp[ei] += dT; +#if defined(GEOS_USE_CUDA) + temp.move( parallelDeviceMemorySpace, false ); +#endif + + // here, we make sure that rock internal energy is updated + // in other words, a call to updateFluidState would not work + solver.updateState( domain ); + + residual.zero(); + jacobian.zero(); + assembleFunction( jacobian.toViewConstSizes(), residual.toView() ); + + fillNumericalJacobian( residual.toViewConst(), + residualOrig.toViewConst(), + elemDofNumber[ei] + 1, + dT, + jacobianFD.toViewConstSizes() ); + } + + // Step 3: compute numerical derivatives wrt log primary species concentration + for( integer is = 0; is < numSpecies; ++is ) + { + solver.resetStateToBeginningOfStep( domain ); + + logPrimaryConc.move( hostMemorySpace, true ); // to get the correct logPrimaryConc after reset + real64 const dConc = perturbParameter * ( logPrimaryConc[ei][is] + perturbParameter ); + logPrimaryConc[ei][is] += dConc; +#if defined(GEOS_USE_CUDA) + logPrimaryConc.move( parallelDeviceMemorySpace, false ); +#endif + solver.updateState( domain ); + + residual.zero(); + jacobian.zero(); + assembleFunction( jacobian.toViewConstSizes(), residual.toView() ); + + integer const speciesIndexShift = (isThermal)? 2:1; + + fillNumericalJacobian( residual.toViewConst(), + residualOrig.toViewConst(), + elemDofNumber[ei] + speciesIndexShift + is, + dConc, + jacobianFD.toViewConstSizes() ); + } + + } + } ); + } ); +} + + +} // namespace testing + +} // namespace geos + +#endif //GEOS_TESTSINGLEPHASEREACTIVETRANSPORTUTILS_HPP diff --git a/src/coreComponents/physicsSolvers/fluidFlow/CMakeLists.txt b/src/coreComponents/physicsSolvers/fluidFlow/CMakeLists.txt index 31a867a8e75..6eb55e9775a 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/CMakeLists.txt +++ b/src/coreComponents/physicsSolvers/fluidFlow/CMakeLists.txt @@ -65,6 +65,18 @@ set( fluidFlowSolvers_headers kernels/singlePhase/ThermalFluxComputeKernel.hpp kernels/singlePhase/proppant/ProppantBaseKernels.hpp kernels/singlePhase/proppant/ProppantFluxKernels.hpp + kernels/singlePhase/reactive/AccumulationKernels.hpp + kernels/singlePhase/reactive/DirichletFluxComputeKernel.hpp + kernels/singlePhase/reactive/FluidUpdateKernel.hpp + kernels/singlePhase/reactive/FluxComputeKernel.hpp + kernels/singlePhase/reactive/KernelLaunchSelectors.hpp + kernels/singlePhase/reactive/ReactionUpdateKernel.hpp + kernels/singlePhase/reactive/ResidualNormKernel.hpp + kernels/singlePhase/reactive/SourceFluxComputeKernel.hpp + kernels/singlePhase/reactive/ThermalAccumulationKernels.hpp + kernels/singlePhase/reactive/ThermalDirichletFluxComputeKernel.hpp + kernels/singlePhase/reactive/ThermalFluxComputeKernel.hpp + kernels/singlePhase/reactive/ThermalSourceFluxComputeKernel.hpp kernels/compositional/AccumulationKernel.hpp kernels/compositional/AquiferBCKernel.hpp kernels/compositional/PPUPhaseFlux.hpp @@ -140,6 +152,7 @@ set( fluidFlowSolvers_sources SinglePhaseFVM.cpp SinglePhaseHybridFVM.cpp SinglePhaseProppantBase.cpp + SinglePhaseReactiveTransport.cpp SourceFluxStatistics.cpp StencilDataCollection.cpp kernels/singlePhase/proppant/ProppantFluxKernels.cpp diff --git a/src/coreComponents/physicsSolvers/fluidFlow/FlowSolverBase.hpp b/src/coreComponents/physicsSolvers/fluidFlow/FlowSolverBase.hpp index e67bae98e2e..8b68a5fad08 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/FlowSolverBase.hpp +++ b/src/coreComponents/physicsSolvers/fluidFlow/FlowSolverBase.hpp @@ -105,7 +105,7 @@ class FlowSolverBase : public PhysicsSolverBase void enableJumpStabilization() { m_isJumpStabilized = true; } - void updatePorosityAndPermeability( CellElementSubRegion & subRegion ) const; + virtual void updatePorosityAndPermeability( CellElementSubRegion & subRegion ) const; virtual void updatePorosityAndPermeability( SurfaceElementSubRegion & subRegion ) const; diff --git a/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseBase.cpp b/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseBase.cpp index 6fca54281ae..9b06fff0cf3 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseBase.cpp +++ b/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseBase.cpp @@ -162,11 +162,11 @@ void SinglePhaseBase::validateConstitutiveModels( DomainPartition & domain ) con constitutiveUpdatePassThru( fluid, [&] ( auto & castedFluid ) { string const fluidModelName = castedFluid.getCatalogName(); - GEOS_THROW_IF( m_isThermal && (fluidModelName != "ThermalCompressibleSinglePhaseFluid"), + GEOS_THROW_IF( m_isThermal && ((fluidModelName != "ThermalCompressibleSinglePhaseFluid") && (fluidModelName != "ReactiveThermalCompressibleSinglePhaseFluid")), GEOS_FMT( "SingleFluidBase {}: the thermal option is enabled in the solver, but the fluid model {} is not for thermal fluid", getDataContext(), fluid.getDataContext() ), InputError, getDataContext(), fluid.getDataContext() ); - GEOS_THROW_IF( !m_isThermal && (fluidModelName == "ThermalCompressibleSinglePhaseFluid"), + GEOS_THROW_IF( !m_isThermal && ((fluidModelName == "ThermalCompressibleSinglePhaseFluid") || (fluidModelName == "ReactiveThermalCompressibleSinglePhaseFluid")), GEOS_FMT( "SingleFluidBase {}: the fluid model is for thermal fluid {}, but the solver option is incompatible with the fluid model", getDataContext(), fluid.getDataContext() ), InputError, getDataContext(), fluid.getDataContext() ); diff --git a/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseBase.hpp b/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseBase.hpp index a9e0af8e961..87ff12f39f7 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseBase.hpp +++ b/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseBase.hpp @@ -223,7 +223,7 @@ class SinglePhaseBase : public FlowSolverBase * @param localMatrix local system matrix * @param localRhs local system right-hand side vector */ - void + virtual void applyDirichletBC( real64 const time_n, real64 const dt, DomainPartition & domain, @@ -240,7 +240,7 @@ class SinglePhaseBase : public FlowSolverBase * @param localMatrix local system matrix * @param localRhs local system right-hand side vector */ - void + virtual void applySourceFluxBC( real64 const time_n, real64 const dt, DomainPartition & domain, @@ -266,7 +266,7 @@ class SinglePhaseBase : public FlowSolverBase arrayView1d< real64 > const & localRhs ) const = 0; virtual void - updateState ( DomainPartition & domain ) override final; + updateState ( DomainPartition & domain ) override; /** * @brief Function to update all constitutive state and dependent variables diff --git a/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseReactiveTransport.cpp b/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseReactiveTransport.cpp new file mode 100644 index 00000000000..5d543342aaa --- /dev/null +++ b/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseReactiveTransport.cpp @@ -0,0 +1,1601 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * 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 SinglePhaseReactiveTransport.cpp + */ + +#include "SinglePhaseReactiveTransport.hpp" + +#include "constitutive/ConstitutiveManager.hpp" +#include "constitutive/ConstitutivePassThru.hpp" +#include "constitutive/diffusion/DiffusionFields.hpp" +#include "constitutive/diffusion/DiffusionSelector.hpp" +#include "constitutive/fluid/reactivefluid/ReactiveFluidLayouts.hpp" +#include "constitutive/fluid/reactivefluid/ReactiveSinglePhaseFluid.hpp" +#include "constitutive/fluid/reactivefluid/ReactiveSinglePhaseFluid.cpp" +#include "constitutive/fluid/reactivefluid/ReactiveFluidSelector.hpp" +#include "constitutive/solid/CoupledSolidBase.hpp" +#include "constitutive/solid/ReactiveSolid.hpp" +#include "fieldSpecification/FieldSpecificationManager.hpp" +#include "fieldSpecification/SourceFluxBoundaryCondition.hpp" +#include "physicsSolvers/fluidFlow/SourceFluxStatistics.hpp" +#include "physicsSolvers/fluidFlow/kernels/singlePhase/reactive/KernelLaunchSelectors.hpp" +#include "finiteVolume/FluxApproximationBase.hpp" +#include "mesh/DomainPartition.hpp" +#include "physicsSolvers/LogLevelsInfo.hpp" + +/** + * @namespace the geos namespace that encapsulates the majority of the code + */ +namespace geos +{ + +using namespace dataRepository; +using namespace constitutive; + +template< typename POROUSWRAPPER_TYPE > +void updatePorosityAndPermeabilityFromPressureAndReactions( POROUSWRAPPER_TYPE porousWrapper, + ElementSubRegionBase & subRegion, + arrayView1d< real64 const > const & pressure, + arrayView2d< real64 const, compflow::USD_COMP > const & kineticReactionMolarIncrements ) +{ + forAll< parallelDevicePolicy<> >( subRegion.size(), [=] GEOS_DEVICE ( localIndex const k ) + { + for( localIndex q = 0; q < porousWrapper.numGauss(); ++q ) + { + porousWrapper.updateStateFromPressureAndReactions( k, q, + pressure[k], + kineticReactionMolarIncrements[k] ); + } + } ); +} + +template< typename POROUSWRAPPER_TYPE > +void updateSurfaceAreaFromReactions( POROUSWRAPPER_TYPE porousWrapper, + ElementSubRegionBase & subRegion, + arrayView2d< real64 const, compflow::USD_COMP > const & initialSurfaceArea, + arrayView2d< real64, compflow::USD_COMP > const & surfaceArea ) +{ + forAll< parallelDevicePolicy<> >( subRegion.size(), [=] GEOS_DEVICE ( localIndex const k ) + { + for( localIndex q = 0; q < porousWrapper.numGauss(); ++q ) + { + porousWrapper.updateSurfaceArea( k, q, + initialSurfaceArea[k], + surfaceArea[k] ); + } + } ); +} + +SinglePhaseReactiveTransport::SinglePhaseReactiveTransport( const string & name, + Group * const parent ): + SinglePhaseBase( name, parent ), + m_numPrimarySpecies( 0 ), + m_numKineticReactions( 0 ), + m_hasDiffusion( 0 ), + m_isUpdateReactivePorosity( 0 ), + m_isUpdateSurfaceArea( 0 ) +{ + // To add modeling parameters we want to add here + + this->registerWrapper( viewKeyStruct::isUpdateReactivePorosityString(), &m_isUpdateReactivePorosity ). + setApplyDefaultValue( 0 ). + setInputFlag( InputFlags::OPTIONAL ). + setDescription( "Flag indicating whether use the reactive porosity or not" ); + + this->registerWrapper( viewKeyStruct::isUpdateSurfaceAreaString(), &m_isUpdateSurfaceArea ). + setApplyDefaultValue( 0 ). + setInputFlag( InputFlags::OPTIONAL ). + setDescription( "Flag indicating whether to update the surface area or not" ); + + this->registerWrapper( viewKeyStruct::immobilePrimarySpeciesIndicesString(), &m_immobilePrimarySpeciesIndices ). + setApplyDefaultValue( { } ). + setInputFlag( InputFlags::OPTIONAL ). + setDescription( "Array to store the indices of immobile species. Default is {}, which indicates no immobile species." ); + + addLogLevel< logInfo::BoundaryConditions >(); +} + +void SinglePhaseReactiveTransport::registerDataOnMesh( Group & meshBodies ) +{ + using namespace fields::flow; + + SinglePhaseBase::registerDataOnMesh( meshBodies ); + + DomainPartition const & domain = this->getGroupByPath< DomainPartition >( "/Problem/domain" ); + ConstitutiveManager const & cm = domain.getConstitutiveManager(); + + // 0. Find a reactive fluid model name (at this point, models are already attached to subregions) + forDiscretizationOnMeshTargets( meshBodies, [&]( string const &, + MeshLevel & mesh, + string_array const & regionNames ) + { + mesh.getElemManager().forElementSubRegions( regionNames, + [&]( localIndex const, + ElementSubRegionBase & subRegion ) + { + if( m_reactiveFluidModelName.empty() ) + { + m_reactiveFluidModelName = m_isThermal? getConstitutiveName< reactivefluid::ReactiveThermalCompressibleSinglePhaseFluid >( subRegion ): + getConstitutiveName< reactivefluid::ReactiveCompressibleSinglePhaseFluid >( subRegion ); + } + + // If at least one region has a diffusion model, consider it enabled for all + string const diffusionName = getConstitutiveName< DiffusionBase >( subRegion ); + if( !diffusionName.empty() ) + { + m_hasDiffusion = true; + } + } ); + } ); + + // 1. Set key dimensions of the problem + // Check needed to avoid errors when running in schema generation mode. + if( !m_reactiveFluidModelName.empty() ) + { + if( m_isThermal ) + { + reactivefluid::ReactiveThermalCompressibleSinglePhaseFluid const & reactiveFluid = cm.getConstitutiveRelation< reactivefluid::ReactiveThermalCompressibleSinglePhaseFluid >( + m_reactiveFluidModelName ); + m_numPrimarySpecies = reactiveFluid.numPrimarySpecies(); + m_numKineticReactions = reactiveFluid.numKineticReactions(); + } + else + { + reactivefluid::ReactiveCompressibleSinglePhaseFluid const & reactiveFluid = cm.getConstitutiveRelation< reactivefluid::ReactiveCompressibleSinglePhaseFluid >( m_reactiveFluidModelName ); + m_numPrimarySpecies = reactiveFluid.numPrimarySpecies(); + m_numKineticReactions = reactiveFluid.numKineticReactions(); + } + } + + // n_c components + one pressure ( + one temperature if needed ) + m_numDofPerCell = m_isThermal ? m_numPrimarySpecies + 2 : m_numPrimarySpecies + 1; + + // 2. Register and resize all fields as necessary + forDiscretizationOnMeshTargets( meshBodies, [&]( string const &, + MeshLevel & mesh, + string_array const & regionNames ) + { + ElementRegionManager & elemManager = mesh.getElemManager(); + + elemManager.forElementSubRegions< ElementSubRegionBase >( regionNames, + [&]( localIndex const, + ElementSubRegionBase & subRegion ) + { + if( m_hasDiffusion ) + { + subRegion.registerWrapper< string >( viewKeyStruct::diffusionNamesString() ). + setPlotLevel( PlotLevel::NOPLOT ). + setRestartFlags( RestartFlags::NO_WRITE ). + setSizedFromParent( 0 ). + setDescription( "Name of the diffusion constitutive model to use" ); + + string & diffusionName = subRegion.getReference< string >( viewKeyStruct::diffusionNamesString() ); + diffusionName = getConstitutiveName< DiffusionBase >( subRegion ); + GEOS_THROW_IF( diffusionName.empty(), + GEOS_FMT( "Diffusion model not found on subregion {}", subRegion.getName() ), + InputError ); + } + + subRegion.registerField< logPrimarySpeciesConcentration >( getName() ). + reference().resizeDimension< 1 >( m_numPrimarySpecies ); + + subRegion.registerField< logPrimarySpeciesConcentration_n >( getName() ). + reference().resizeDimension< 1 >( m_numPrimarySpecies ); + + subRegion.registerField< primarySpeciesAggregateMole >( getName() ). + reference().resizeDimension< 1 >( m_numPrimarySpecies ); + + subRegion.registerField< primarySpeciesAggregateMole_n >( getName() ). + reference().resizeDimension< 1 >( m_numPrimarySpecies ); + + subRegion.registerField< bcLogPrimarySpeciesConcentration >( getName() ). + reference().resizeDimension< 1 >( m_numPrimarySpecies ); + + subRegion.registerField< kineticReactionMolarIncrements >( getName() ). + reference().resizeDimension< 1 >( m_numKineticReactions ); + + subRegion.registerField< surfaceArea >( getName() ). + reference().resizeDimension< 1 >( m_numKineticReactions ); + + subRegion.registerField< initialSurfaceArea >( getName() ). + reference().resizeDimension< 1 >( m_numKineticReactions ); + } ); + } ); +} + +void SinglePhaseReactiveTransport::validateConstitutiveModels( DomainPartition & domain ) const +{ + GEOS_MARK_FUNCTION; + + SinglePhaseBase::validateConstitutiveModels( domain ); + + forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &, + MeshLevel & mesh, + string_array const & regionNames ) + { + mesh.getElemManager().forElementSubRegions( regionNames, [&]( localIndex const, + ElementSubRegionBase & subRegion ) + { + string const & porosityModelName = getConstitutiveName< PorosityBase >( subRegion ); + + PorosityBase const & porosity = getConstitutiveModel< PorosityBase >( subRegion, porosityModelName ); + + GEOS_THROW_IF( m_isUpdateReactivePorosity && (porosity.getCatalogName() != "ReactivePorosity"), + GEOS_FMT( "SinglePhaseReactiveTransport {}: the reaction porosity update option is enabled in the solver, but the porosity model {} is not for reactive porosity", + getDataContext(), porosity.getDataContext() ), + InputError ); + + if( m_isUpdateReactivePorosity ) + { + ReactivePorosity const & reactivePorosity = getConstitutiveModel< ReactivePorosity >( subRegion, porosityModelName ); + + GEOS_THROW_IF_NE_MSG( reactivePorosity.numKineticReactions(), m_numKineticReactions, + GEOS_FMT( "Mismatch in number of kinetic reactions, check the number of components input in porosity model {}", + reactivePorosity.getDataContext() ), + InputError ); + } + } ); + } ); +} + +void SinglePhaseReactiveTransport::setupDofs( DomainPartition const & domain, + DofManager & dofManager ) const +{ + // add a field for the cell-centered degrees of freedom + dofManager.addField( viewKeyStruct::elemDofFieldString(), + FieldLocation::Elem, + m_numDofPerCell, + getMeshTargets() ); + + NumericalMethodsManager const & numericalMethodManager = domain.getNumericalMethodManager(); + FiniteVolumeManager const & fvManager = numericalMethodManager.getFiniteVolumeManager(); + FluxApproximationBase const & fluxApprox = fvManager.getFluxApproximation( m_discretizationName ); + + dofManager.addCoupling( viewKeyStruct::elemDofFieldString(), fluxApprox ); +} + +void SinglePhaseReactiveTransport::resetStateToBeginningOfStep( DomainPartition & domain ) +{ + forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &, + MeshLevel & mesh, + string_array const & regionNames ) + { + mesh.getElemManager().forElementSubRegions< CellElementSubRegion, SurfaceElementSubRegion >( regionNames, [&]( localIndex const, + auto & subRegion ) + { + arrayView2d< real64, compflow::USD_COMP > const logPrimarySpeciesConc = subRegion.template getField< fields::flow::logPrimarySpeciesConcentration >(); + arrayView2d< real64 const, compflow::USD_COMP > const logPrimarySpeciesConc_n = subRegion.template getField< fields::flow::logPrimarySpeciesConcentration_n >(); + logPrimarySpeciesConc.setValues< parallelDevicePolicy<> >( logPrimarySpeciesConc_n ); + } ); + } ); + + SinglePhaseBase::resetStateToBeginningOfStep( domain ); + + forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &, + MeshLevel & mesh, + string_array const & regionNames ) + { + mesh.getElemManager().forElementSubRegions< CellElementSubRegion, SurfaceElementSubRegion >( regionNames, [&]( localIndex const, + auto & subRegion ) + { + updateMixedReactionSystem( subRegion ); + updateSpeciesAmount( subRegion ); + } ); + } ); +} + +void SinglePhaseReactiveTransport::implicitStepSetup( real64 const & time_n, + real64 const & dt, + DomainPartition & domain ) +{ + GEOS_MARK_FUNCTION; + + SinglePhaseBase::implicitStepSetup( time_n, dt, domain ); + + forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &, + MeshLevel & mesh, + string_array const & regionNames ) + { + mesh.getElemManager().forElementSubRegions< CellElementSubRegion, SurfaceElementSubRegion >( regionNames, [&]( localIndex const, + auto & subRegion ) + { + updateMixedReactionSystem( subRegion ); + updateSpeciesAmount( subRegion ); + } ); + } ); +} + +void SinglePhaseReactiveTransport::implicitStepComplete( real64 const & time, + real64 const & dt, + DomainPartition & domain ) +{ + GEOS_MARK_FUNCTION; + + SinglePhaseBase::implicitStepComplete( time, dt, domain ); + + // Update molar increments of kinetic reactions for porosity update + forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &, + MeshLevel & mesh, + string_array const & regionNames ) + { + mesh.getElemManager().forElementSubRegions( regionNames, [&]( localIndex const, + ElementSubRegionBase & subRegion ) + { + updateKineticReactionMolarIncrements( dt, subRegion ); + } ); + } ); + + if( m_hasDiffusion ) + { + forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &, + MeshLevel & mesh, + string_array const & regionNames ) + { + mesh.getElemManager().forElementSubRegions( regionNames, + [&]( localIndex const, + ElementSubRegionBase & subRegion ) + { + string const & diffusionName = subRegion.getReference< string >( viewKeyStruct::diffusionNamesString() ); + DiffusionBase const & diffusionMaterial = getConstitutiveModel< DiffusionBase >( subRegion, diffusionName ); + arrayView1d< real64 const > const temperature = subRegion.template getField< fields::flow::temperature >(); + diffusionMaterial.saveConvergedTemperatureState( temperature ); + } ); + } ); + } +} + +void SinglePhaseReactiveTransport::assembleSystem( real64 const GEOS_UNUSED_PARAM( time_n ), + real64 const dt, + DomainPartition & domain, + DofManager const & dofManager, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) +{ + GEOS_MARK_FUNCTION; + + assembleAccumulationTermsInMassBalanceAndSpeciesAmountEqs( dt, + domain, + dofManager, + localMatrix, + localRhs ); + assembleFluxTerms( dt, + domain, + dofManager, + localMatrix, + localRhs ); +} + +void SinglePhaseReactiveTransport::assembleAccumulationTermsInMassBalanceAndSpeciesAmountEqs( real64 const dt, + DomainPartition & domain, + DofManager const & dofManager, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) const +{ + GEOS_MARK_FUNCTION; + + forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &, + MeshLevel & mesh, + string_array const & regionNames ) + { + mesh.getElemManager().forElementSubRegions( regionNames, + [&]( localIndex const, + ElementSubRegionBase const & subRegion ) + { + geos::constitutive::CoupledSolidBase const & solid = + getConstitutiveModel< geos::constitutive::CoupledSolidBase >( subRegion, subRegion.template getReference< string >( viewKeyStruct::solidNamesString() ) ); + + string const dofKey = dofManager.getKey( viewKeyStruct::elemDofFieldString() ); + + if( m_isThermal ) + { + reactivefluid::ReactiveThermalCompressibleSinglePhaseFluid const & fluid = + getConstitutiveModel< reactivefluid::ReactiveThermalCompressibleSinglePhaseFluid >( subRegion, subRegion.template getReference< string >( viewKeyStruct::fluidNamesString() ) ); + + thermalSinglePhaseReactiveBaseKernels:: + AccumulationKernelFactory:: + createAndLaunch< parallelDevicePolicy<> >( m_numPrimarySpecies, + dt, + dofManager.rankOffset(), + dofKey, + subRegion, + fluid, + solid, + localMatrix, + localRhs ); + } + else + { + reactivefluid::ReactiveCompressibleSinglePhaseFluid const & fluid = + getConstitutiveModel< reactivefluid::ReactiveCompressibleSinglePhaseFluid >( subRegion, subRegion.template getReference< string >( viewKeyStruct::fluidNamesString() ) ); + + singlePhaseReactiveBaseKernels:: + AccumulationKernelFactory:: + createAndLaunch< parallelDevicePolicy<> >( m_numPrimarySpecies, + dt, + dofManager.rankOffset(), + dofKey, + subRegion, + fluid, + solid, + localMatrix, + localRhs ); + } + } ); + } ); +} + +void SinglePhaseReactiveTransport::assembleFluxTerms( real64 const dt, + DomainPartition const & domain, + DofManager const & dofManager, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) +{ + array1d< integer > mobilePrimarySpeciesFlags; + mobilePrimarySpeciesFlags.resize( m_numPrimarySpecies ); + + for( integer i=0; i 0 ) + { + for( integer i = 0; i < m_immobilePrimarySpeciesIndices.size(); ++i ) + { + localIndex const immobileSpeciesIndex = m_immobilePrimarySpeciesIndices[i]; + mobilePrimarySpeciesFlags[immobileSpeciesIndex] = 0; + } + } + + forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &, + MeshLevel const & mesh, + string_array const & ) + { + NumericalMethodsManager const & numericalMethodManager = domain.getNumericalMethodManager(); + FiniteVolumeManager const & fvManager = numericalMethodManager.getFiniteVolumeManager(); + FluxApproximationBase const & fluxApprox = fvManager.getFluxApproximation( m_discretizationName ); + + string const & dofKey = dofManager.getKey( viewKeyStruct::elemDofFieldString() ); + + fluxApprox.forAllStencils( mesh, [&] ( auto & stencil ) + { + typename TYPEOFREF( stencil ) ::KernelWrapper stencilWrapper = stencil.createKernelWrapper(); + + if( m_isThermal ) + { + thermalSinglePhaseReactiveFVMKernels:: + FluxComputeKernelFactory::createAndLaunch< parallelDevicePolicy<> >( m_numPrimarySpecies, + m_hasDiffusion, + mobilePrimarySpeciesFlags.toViewConst(), + dofManager.rankOffset(), + dofKey, + getName(), + mesh.getElemManager(), + stencilWrapper, + dt, + localMatrix.toViewConstSizes(), + localRhs.toView() ); + } + else + { + singlePhaseReactiveFVMKernels:: + FluxComputeKernelFactory::createAndLaunch< parallelDevicePolicy<> >( m_numPrimarySpecies, + m_hasDiffusion, + mobilePrimarySpeciesFlags.toViewConst(), + dofManager.rankOffset(), + dofKey, + getName(), + mesh.getElemManager(), + stencilWrapper, + dt, + localMatrix.toViewConstSizes(), + localRhs.toView() ); + } + } ); + } ); +} + +void SinglePhaseReactiveTransport::updateState( DomainPartition & domain ) +{ + GEOS_MARK_FUNCTION; + + SinglePhaseBase::updateState( domain ); + + forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &, + MeshLevel & mesh, + string_array const & regionNames ) + { + mesh.getElemManager().forElementSubRegions< CellElementSubRegion, SurfaceElementSubRegion >( regionNames, [&]( localIndex const, + auto & subRegion ) + { + updateMixedReactionSystem( subRegion ); + + updateSpeciesAmount( subRegion ); + } ); + } ); +} + +void SinglePhaseReactiveTransport::updateSpeciesAmount( ElementSubRegionBase & subRegion ) const +{ + GEOS_MARK_FUNCTION; + + arrayView2d< real64, compflow::USD_COMP > const primarySpeciesAggregateMole = subRegion.getField< fields::flow::primarySpeciesAggregateMole >(); + arrayView2d< real64, compflow::USD_COMP > const primarySpeciesAggregateMole_n = subRegion.getField< fields::flow::primarySpeciesAggregateMole_n >(); + + CoupledSolidBase const & porousSolid = + getConstitutiveModel< CoupledSolidBase >( subRegion, subRegion.template getReference< string >( viewKeyStruct::solidNamesString() ) ); + arrayView2d< real64 const > const porosity = porousSolid.getPorosity(); + arrayView2d< real64 const > const porosity_n = porousSolid.getPorosity_n(); + + arrayView1d< real64 const > const volume = subRegion.getElementVolume(); + arrayView1d< real64 > const deltaVolume = subRegion.getField< fields::flow::deltaVolume >(); + + integer const numPrimarySpecies = m_numPrimarySpecies; + + if( m_isThermal ) + { + reactivefluid::ReactiveThermalCompressibleSinglePhaseFluid & fluid = + getConstitutiveModel< reactivefluid::ReactiveThermalCompressibleSinglePhaseFluid >( subRegion, subRegion.getReference< string >( viewKeyStruct::fluidNamesString() ) ); + arrayView3d< real64 const, reactivefluid::USD_SPECIES > const primarySpeciesAggregateConcentration = fluid.primarySpeciesAggregateConcentration(); + arrayView3d< real64 const, reactivefluid::USD_SPECIES > const primarySpeciesAggregateConcentration_n = fluid.primarySpeciesAggregateConcentration_n(); + + forAll< parallelDevicePolicy<> >( subRegion.size(), [=] GEOS_HOST_DEVICE ( localIndex const ei ) + { + for( integer is = 0; is < numPrimarySpecies; ++is ) + { + primarySpeciesAggregateMole[ei][is] = porosity[ei][0] * ( volume[ei] + deltaVolume[ei] ) * primarySpeciesAggregateConcentration[ei][0][is]; + + if( isZero( primarySpeciesAggregateMole_n[ei][is] ) ) + primarySpeciesAggregateMole_n[ei][is] = porosity_n[ei][0] * volume[ei] * primarySpeciesAggregateConcentration_n[ei][0][is]; + } + } ); + } + else + { + reactivefluid::ReactiveCompressibleSinglePhaseFluid & fluid = + getConstitutiveModel< reactivefluid::ReactiveCompressibleSinglePhaseFluid >( subRegion, subRegion.getReference< string >( viewKeyStruct::fluidNamesString() ) ); + arrayView3d< real64 const, reactivefluid::USD_SPECIES > const primarySpeciesAggregateConcentration = fluid.primarySpeciesAggregateConcentration(); + arrayView3d< real64 const, reactivefluid::USD_SPECIES > const primarySpeciesAggregateConcentration_n = fluid.primarySpeciesAggregateConcentration_n(); + + forAll< parallelDevicePolicy<> >( subRegion.size(), [=] GEOS_HOST_DEVICE ( localIndex const ei ) + { + for( integer is = 0; is < numPrimarySpecies; ++is ) + { + primarySpeciesAggregateMole[ei][is] = porosity[ei][0] * ( volume[ei] + deltaVolume[ei] ) * primarySpeciesAggregateConcentration[ei][0][is]; + + if( isZero( primarySpeciesAggregateMole_n[ei][is] ) ) + primarySpeciesAggregateMole_n[ei][is] = porosity_n[ei][0] * volume[ei] * primarySpeciesAggregateConcentration_n[ei][0][is]; + } + } ); + } +} + +void SinglePhaseReactiveTransport::updateKineticReactionMolarIncrements( real64 const dt, + ElementSubRegionBase & subRegion ) const +{ + GEOS_MARK_FUNCTION; + + arrayView2d< real64, compflow::USD_COMP > const kineticReactionMolarIncrements = subRegion.getField< fields::flow::kineticReactionMolarIncrements >(); + + integer const numKineticReactions = m_numKineticReactions; + + if( m_isThermal ) + { + reactivefluid::ReactiveThermalCompressibleSinglePhaseFluid & fluid = + getConstitutiveModel< reactivefluid::ReactiveThermalCompressibleSinglePhaseFluid >( subRegion, subRegion.getReference< string >( viewKeyStruct::fluidNamesString() ) ); + arrayView3d< real64 const, reactivefluid::USD_SPECIES > const kineticReactionRates = fluid.kineticReactionRates(); + + forAll< parallelDevicePolicy<> >( subRegion.size(), [=] GEOS_HOST_DEVICE ( localIndex const ei ) + { + for( integer r = 0; r < numKineticReactions; ++r ) + { + kineticReactionMolarIncrements[ei][r] = dt* kineticReactionRates[ei][0][r]; + } + } ); + } + else + { + reactivefluid::ReactiveCompressibleSinglePhaseFluid & fluid = + getConstitutiveModel< reactivefluid::ReactiveCompressibleSinglePhaseFluid >( subRegion, subRegion.getReference< string >( viewKeyStruct::fluidNamesString() ) ); + arrayView3d< real64 const, reactivefluid::USD_SPECIES > const kineticReactionRates = fluid.kineticReactionRates(); + + forAll< parallelDevicePolicy<> >( subRegion.size(), [=] GEOS_HOST_DEVICE ( localIndex const ei ) + { + for( integer r = 0; r < numKineticReactions; ++r ) + { + kineticReactionMolarIncrements[ei][r] = dt* kineticReactionRates[ei][0][r]; + } + } ); + } +} + +void SinglePhaseReactiveTransport::updateFluidModel( ObjectManagerBase & dataGroup ) const +{ + GEOS_MARK_FUNCTION; + + arrayView1d< real64 const > const pres = dataGroup.getField< fields::flow::pressure >(); + arrayView1d< real64 const > const temp = dataGroup.getField< fields::flow::temperature >(); + arrayView2d< real64 const, compflow::USD_COMP > const logPrimaryConc = dataGroup.getField< fields::flow::logPrimarySpeciesConcentration >(); + + if( m_isThermal ) + { + reactivefluid::ReactiveThermalCompressibleSinglePhaseFluid & fluid = + getConstitutiveModel< reactivefluid::ReactiveThermalCompressibleSinglePhaseFluid >( dataGroup, dataGroup.getReference< string >( viewKeyStruct::fluidNamesString() ) ); + + constitutive::constitutiveUpdatePassThru( fluid, [&]( auto & castedFluid ) + { + typename TYPEOFREF( castedFluid ) ::KernelWrapper fluidWrapper = castedFluid.createKernelWrapper(); + singlePhaseReactiveBaseKernels::FluidUpdateKernel::launch( fluidWrapper, pres, temp, logPrimaryConc ); + } ); + } + else + { + reactivefluid::ReactiveCompressibleSinglePhaseFluid & fluid = + getConstitutiveModel< reactivefluid::ReactiveCompressibleSinglePhaseFluid >( dataGroup, dataGroup.getReference< string >( viewKeyStruct::fluidNamesString() ) ); + + constitutive::constitutiveUpdatePassThru( fluid, [&]( auto & castedFluid ) + { + typename TYPEOFREF( castedFluid ) ::KernelWrapper fluidWrapper = castedFluid.createKernelWrapper(); + singlePhaseReactiveBaseKernels::FluidUpdateKernel::launch( fluidWrapper, pres, temp, logPrimaryConc ); + } ); + } +} + +void SinglePhaseReactiveTransport::updatePorosityAndPermeability( CellElementSubRegion & subRegion ) const +{ + GEOS_MARK_FUNCTION; + + if( m_isUpdateReactivePorosity ) + { + arrayView1d< real64 const > const & pressure = subRegion.getField< fields::flow::pressure >(); + arrayView2d< real64 const, compflow::USD_COMP > const kineticReactionMolarIncrements = subRegion.getField< fields::flow::kineticReactionMolarIncrements >(); + + string const & solidName = subRegion.getReference< string >( viewKeyStruct::solidNamesString() ); + CoupledSolidBase & porousSolid = subRegion.template getConstitutiveModel< CoupledSolidBase >( solidName ); + + constitutive::ConstitutivePassThru< ReactiveSolidBase >::execute( porousSolid, [=, &subRegion] ( auto & castedPorousSolid ) + { + typename TYPEOFREF( castedPorousSolid ) ::KernelWrapper porousWrapper = castedPorousSolid.createKernelUpdates(); + updatePorosityAndPermeabilityFromPressureAndReactions( porousWrapper, subRegion, pressure, kineticReactionMolarIncrements ); + } ); + } + else + { + FlowSolverBase::updatePorosityAndPermeability( subRegion ); + } +} + +void SinglePhaseReactiveTransport::updateMixedReactionSystem( ElementSubRegionBase & subRegion ) const +{ + GEOS_MARK_FUNCTION; + + updateSurfaceArea( subRegion ); + + arrayView1d< real64 const > const pres = subRegion.getField< fields::flow::pressure >(); + arrayView1d< real64 const > const temp = subRegion.getField< fields::flow::temperature >(); + arrayView2d< real64 const, compflow::USD_COMP > const logPrimaryConc = subRegion.getField< fields::flow::logPrimarySpeciesConcentration >(); + arrayView2d< real64 const, compflow::USD_COMP > const surfaceArea = subRegion.getField< fields::flow::surfaceArea >(); + + if( m_isThermal ) + { + reactivefluid::ReactiveThermalCompressibleSinglePhaseFluid & fluid = + getConstitutiveModel< reactivefluid::ReactiveThermalCompressibleSinglePhaseFluid >( subRegion, subRegion.getReference< string >( viewKeyStruct::fluidNamesString() ) ); + + constitutive::constitutiveUpdatePassThru( fluid, [&]( auto & castedFluid ) + { + singlePhaseReactiveBaseKernels::MixedSystemReactionUpdateKernel::launch( castedFluid, pres, temp, logPrimaryConc, surfaceArea ); + } ); + } + else + { + reactivefluid::ReactiveCompressibleSinglePhaseFluid & fluid = + getConstitutiveModel< reactivefluid::ReactiveCompressibleSinglePhaseFluid >( subRegion, subRegion.getReference< string >( viewKeyStruct::fluidNamesString() ) ); + + constitutive::constitutiveUpdatePassThru( fluid, [&]( auto & castedFluid ) + { + singlePhaseReactiveBaseKernels::MixedSystemReactionUpdateKernel::launch( castedFluid, pres, temp, logPrimaryConc, surfaceArea ); + } ); + } +} + +void SinglePhaseReactiveTransport::updateSurfaceArea( ElementSubRegionBase & subRegion ) const +{ + GEOS_MARK_FUNCTION; + + arrayView2d< real64 const, compflow::USD_COMP > const initialSurfaceArea = subRegion.getField< fields::flow::initialSurfaceArea >(); + arrayView2d< real64, compflow::USD_COMP > const surfaceArea = subRegion.getField< fields::flow::surfaceArea >(); + + if( m_isUpdateReactivePorosity && m_isUpdateSurfaceArea ) + { + string const & solidName = subRegion.getReference< string >( viewKeyStruct::solidNamesString() ); + CoupledSolidBase & porousSolid = subRegion.template getConstitutiveModel< CoupledSolidBase >( solidName ); + + constitutive::ConstitutivePassThru< ReactiveSolidBase >::execute( porousSolid, [=, &subRegion] ( auto & castedPorousSolid ) + { + typename TYPEOFREF( castedPorousSolid ) ::KernelWrapper porousWrapper = castedPorousSolid.createKernelUpdates(); + updateSurfaceAreaFromReactions( porousWrapper, subRegion, initialSurfaceArea, surfaceArea ); + } ); + } + else + { + integer const numKineticReactions = m_numKineticReactions; + + forAll< parallelDevicePolicy<> >( subRegion.size(), [=] GEOS_HOST_DEVICE ( localIndex const ei ) + { + for( integer ir = 0; ir < numKineticReactions; ++ir ) + { + surfaceArea[ei][ir] = initialSurfaceArea[ei][ir]; + } + } ); + } +} + +void SinglePhaseReactiveTransport::initializeFluidState( MeshLevel & mesh, string_array const & regionNames ) +{ + mesh.getElemManager().forElementSubRegions< CellElementSubRegion, SurfaceElementSubRegion >( regionNames, [&]( localIndex const, + auto & subRegion ) + { + string const & fluidName = subRegion.template getReference< string >( viewKeyStruct::fluidNamesString() ); + updateFluidState( subRegion ); + + // 2. save the initial density (for use in the single-phase poromechanics solver to compute the deltaBodyForce) + if( m_isThermal ) + { + reactivefluid::ReactiveThermalCompressibleSinglePhaseFluid const & fluid = + getConstitutiveModel< reactivefluid::ReactiveThermalCompressibleSinglePhaseFluid >( subRegion, fluidName ); + + fluid.initializeState(); + } + else + { + reactivefluid::ReactiveCompressibleSinglePhaseFluid const & fluid = + getConstitutiveModel< reactivefluid::ReactiveCompressibleSinglePhaseFluid >( subRegion, fluidName ); + + fluid.initializeState(); + } + + initializeEquilibriumReaction( subRegion ); + + updateMixedReactionSystem( subRegion ); + + SinglePhaseBase::updateMass( subRegion ); + updateSpeciesAmount( subRegion ); + + saveConvergedState( subRegion ); + + // If the diffusion is supported, initialize the model + if( m_hasDiffusion ) + { + string const & diffusionName = subRegion.template getReference< string >( viewKeyStruct::diffusionNamesString() ); + DiffusionBase const & diffusionMaterial = getConstitutiveModel< DiffusionBase >( subRegion, diffusionName ); + arrayView1d< real64 const > const temperature = subRegion.template getField< fields::flow::temperature >(); + diffusionMaterial.initializeTemperatureState( temperature ); + } + } ); +} + +void SinglePhaseReactiveTransport::initializeEquilibriumReaction( ElementSubRegionBase & subRegion ) const +{ + GEOS_MARK_FUNCTION; + + arrayView1d< real64 const > const pres = subRegion.getField< fields::flow::pressure >(); + arrayView1d< real64 const > const temp = subRegion.getField< fields::flow::temperature >(); + arrayView2d< real64, compflow::USD_COMP > const logPrimaryConc = subRegion.getField< fields::flow::logPrimarySpeciesConcentration >(); + + if( m_isThermal ) + { + reactivefluid::ReactiveThermalCompressibleSinglePhaseFluid & fluid = + getConstitutiveModel< reactivefluid::ReactiveThermalCompressibleSinglePhaseFluid >( subRegion, subRegion.getReference< string >( viewKeyStruct::fluidNamesString() ) ); + + constitutive::constitutiveUpdatePassThru( fluid, [&]( auto & castedFluid ) + { + singlePhaseReactiveBaseKernels::EquilibriumReactionUpdateKernel::launch( castedFluid, pres, temp, logPrimaryConc ); + } ); + + fluid.saveConvergedState(); + } + else + { + reactivefluid::ReactiveCompressibleSinglePhaseFluid & fluid = + getConstitutiveModel< reactivefluid::ReactiveCompressibleSinglePhaseFluid >( subRegion, subRegion.getReference< string >( viewKeyStruct::fluidNamesString() ) ); + + constitutive::constitutiveUpdatePassThru( fluid, [&]( auto & castedFluid ) + { + singlePhaseReactiveBaseKernels::EquilibriumReactionUpdateKernel::launch( castedFluid, pres, temp, logPrimaryConc ); + } ); + + fluid.saveConvergedState(); + } +} + +void SinglePhaseReactiveTransport::initializePostInitialConditionsPreSubGroups() +{ + GEOS_MARK_FUNCTION; + + FlowSolverBase::initializePostInitialConditionsPreSubGroups(); + + DomainPartition & domain = this->getGroupByPath< DomainPartition >( "/Problem/domain" ); + + forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &, + MeshLevel & mesh, + string_array const & regionNames ) + { + FieldIdentifiers fieldsToBeSync; + fieldsToBeSync.addElementFields( { fields::flow::logPrimarySpeciesConcentration::key() }, + regionNames ); + + CommunicationTools::getInstance().synchronizeFields( fieldsToBeSync, mesh, domain.getNeighbors(), false ); + } ); + + FlowSolverBase::initializeState( domain ); +} + +namespace +{ +char const bcLogMessage[] = + "SinglePhaseReactiveTransport {}: at time {}s, " + "the <{}> boundary condition '{}' is applied to the element set '{}' in subRegion '{}'. " + "\nThe scale of this boundary condition is {} and multiplies the value of the provided function (if any). " + "\nThe total number of target elements (including ghost elements) is {}. " + "\nNote that if this number is equal to zero for all subRegions, the boundary condition will not be applied on this element set."; +} + +void SinglePhaseReactiveTransport::applyBoundaryConditions( real64 const time_n, + real64 const dt, + DomainPartition & domain, + DofManager const & dofManager, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) +{ + GEOS_MARK_FUNCTION; + + // apply Dirichlet boundary conditions. + applyDirichletBC( time_n, dt, domain, dofManager, localMatrix.toViewConstSizes(), localRhs.toView() ); + + // apply flux boundary conditions + applySourceFluxBC( time_n, dt, domain, dofManager, localMatrix.toViewConstSizes(), localRhs.toView() ); + + // apply face Dirichlet boundary conditions. + applyFaceDirichletBC( time_n, dt, dofManager, domain, localMatrix, localRhs ); +} + +void SinglePhaseReactiveTransport::applySourceFluxBC( real64 const time_n, + real64 const dt, + DomainPartition & domain, + DofManager const & dofManager, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) const +{ + GEOS_MARK_FUNCTION; + + FieldSpecificationManager & fsManager = FieldSpecificationManager::getInstance(); + + string const dofKey = dofManager.getKey( viewKeyStruct::elemDofFieldString() ); + + // Step 1: count individual source flux boundary conditions + + stdMap< string, localIndex > bcNameToBcId; + localIndex bcCounter = 0; + + fsManager.forSubGroups< SourceFluxBoundaryCondition >( [&] ( SourceFluxBoundaryCondition const & bc ) + { + // collect all the bc names to idx + bcNameToBcId[bc.getName()] = bcCounter; + bcCounter++; + } ); + + if( bcCounter == 0 ) + { + return; + } + + // Step 2: count the set size for each source flux (each source flux may have multiple target sets) + + array1d< globalIndex > bcAllSetsSize( bcNameToBcId.size() ); + + computeSourceFluxSizeScalingFactor( time_n, + dt, + domain, + bcNameToBcId, + bcAllSetsSize.toView() ); + + // Step 3: we are ready to impose the boundary condition, normalized by the set size + + forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &, + MeshLevel & mesh, + string_array const & ) + { + fsManager.apply< ElementSubRegionBase, + SourceFluxBoundaryCondition >( time_n + dt, + mesh, + SourceFluxBoundaryCondition::catalogName(), + [&]( SourceFluxBoundaryCondition const & fs, + string const & setName, + SortedArrayView< localIndex const > const & targetSet, + ElementSubRegionBase & subRegion, + string const & ) + { + if( m_nonlinearSolverParameters.m_numNewtonIterations == 0 ) + { + globalIndex const numTargetElems = MpiWrapper::sum< globalIndex >( targetSet.size() ); + GEOS_LOG_LEVEL_RANK_0_ON_GROUP( logInfo::BoundaryConditions, + GEOS_FMT( bcLogMessage, + getName(), time_n+dt, fs.getCatalogName(), fs.getName(), + setName, subRegion.getName(), fs.getScale(), numTargetElems ), + fs ); + } + + if( targetSet.size() == 0 ) + { + return; + } + if( !subRegion.hasWrapper( dofKey ) ) + { + GEOS_LOG_LEVEL_BY_RANK_ON_GROUP( logInfo::BoundaryConditions, + GEOS_FMT( "{}: trying to apply {}, but its targetSet named '{}' intersects with non-simulated region named '{}'.", + getDataContext(), SourceFluxBoundaryCondition::catalogName(), setName, subRegion.getName() ), + fs ); + return; + } + + arrayView1d< globalIndex const > const dofNumber = subRegion.getReference< array1d< globalIndex > >( dofKey ); + arrayView1d< integer const > const ghostRank = subRegion.ghostRank(); + + // Step 3.1: get the values of the source boundary condition that need to be added to the rhs + + array1d< globalIndex > dofArray( targetSet.size() ); + array1d< real64 > rhsContributionArray( targetSet.size() ); + arrayView1d< real64 > rhsContributionArrayView = rhsContributionArray.toView(); + localIndex const rankOffset = dofManager.rankOffset(); + + RAJA::ReduceSum< parallelDeviceReduce, real64 > massProd( 0.0 ); + + // note that the dofArray will not be used after this step (simpler to use dofNumber instead) + fs.computeRhsContribution< FieldSpecificationAdd, + parallelDevicePolicy<> >( targetSet.toViewConst(), + time_n + dt, + dt, + subRegion, + dofNumber, + rankOffset, + localMatrix, + dofArray.toView(), + rhsContributionArrayView, + [] GEOS_HOST_DEVICE ( localIndex const ) + { + return 0.0; + } ); + + // Step 3.2: we are ready to add the right-hand side contributions, taking into account our equation layout + + // get the normalizer + real64 const sizeScalingFactor = bcAllSetsSize[bcNameToBcId.at( fs.getName())]; + + if( m_isThermal ) + { + reactivefluid::ReactiveThermalCompressibleSinglePhaseFluid const & fluid = + getConstitutiveModel< reactivefluid::ReactiveThermalCompressibleSinglePhaseFluid >( subRegion, subRegion.template getReference< string >( viewKeyStruct::fluidNamesString() ) ); + + thermalSinglePhaseReactiveBaseKernels:: + SourceFluxComputeKernelFactory:: + createAndLaunch< parallelDevicePolicy<> >( m_numPrimarySpecies, + rankOffset, + dofNumber, + ghostRank, + targetSet, + rhsContributionArrayView, + sizeScalingFactor, + fluid, + localMatrix, + localRhs, + massProd ); + } + else + { + reactivefluid::ReactiveCompressibleSinglePhaseFluid const & fluid = + getConstitutiveModel< reactivefluid::ReactiveCompressibleSinglePhaseFluid >( subRegion, subRegion.template getReference< string >( viewKeyStruct::fluidNamesString() ) ); + + singlePhaseReactiveBaseKernels:: + SourceFluxComputeKernelFactory:: + createAndLaunch< parallelDevicePolicy<> >( m_numPrimarySpecies, + rankOffset, + dofNumber, + ghostRank, + targetSet, + rhsContributionArrayView, + sizeScalingFactor, + fluid, + localMatrix, + localRhs, + massProd ); + } + + SourceFluxStatsAggregator::forAllFluxStatWrappers( subRegion, fs.getName(), + [&]( SourceFluxStatsAggregator::WrappedStats & wrapper ) + { + // set the new sub-region statistics for this timestep + array1d< real64 > massProdArr{ 1 }; + massProdArr[0] = massProd.get(); + wrapper.gatherTimeStepStats( time_n, dt, massProdArr.toViewConst(), targetSet.size() ); + } ); + } ); + } ); +} + +void SinglePhaseReactiveTransport::applyDirichletBC( real64 const time_n, + real64 const dt, + DomainPartition & domain, + DofManager const & dofManager, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) const +{ + FieldSpecificationManager & fsManager = FieldSpecificationManager::getInstance(); + + forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &, + MeshLevel & mesh, + string_array const & ) + { + // 1. Apply pressure Dirichlet BCs, store in a separate field + applyFieldValue< ElementSubRegionBase >( time_n, dt, mesh, bcLogMessage, + fields::flow::pressure::key(), fields::flow::bcPressure::key() ); + // 2. Apply primary species BC (log promary species concentration) + applyFieldValue< ElementSubRegionBase >( time_n, dt, mesh, bcLogMessage, + fields::flow::logPrimarySpeciesConcentration::key(), fields::flow::bcLogPrimarySpeciesConcentration::key() ); + // 3. Apply temperature Dirichlet BCs, store in a separate field + if( m_isThermal ) + { + applyFieldValue< ElementSubRegionBase >( time_n, dt, mesh, bcLogMessage, + fields::flow::temperature::key(), fields::flow::bcTemperature::key() ); + } + + globalIndex const rankOffset = dofManager.rankOffset(); + string const dofKey = dofManager.getKey( viewKeyStruct::elemDofFieldString() ); + + // 4. Apply pressure to the system + fsManager.apply< ElementSubRegionBase >( time_n + dt, + mesh, + fields::flow::pressure::key(), + [&] ( FieldSpecificationBase const &, + string const &, + SortedArrayView< localIndex const > const & targetSet, + ElementSubRegionBase & subRegion, + string const & ) + { + arrayView1d< integer const > const ghostRank = subRegion.ghostRank(); + arrayView1d< globalIndex const > const dofNumber = + subRegion.getReference< array1d< globalIndex > >( dofKey ); + + arrayView1d< real64 const > const bcPres = + subRegion.getReference< array1d< real64 > >( fields::flow::bcPressure::key() ); + arrayView1d< real64 const > const pres = + subRegion.getReference< array1d< real64 > >( fields::flow::pressure::key() ); + + forAll< parallelDevicePolicy<> >( targetSet.size(), [=] GEOS_HOST_DEVICE ( localIndex const a ) + { + localIndex const ei = targetSet[a]; + if( ghostRank[ei] >= 0 ) + { + return; + } + + globalIndex const dofIndex = dofNumber[ei]; + localIndex const localRow = dofIndex - rankOffset; + real64 rhsValue; + + // 4.1. Apply pressure value to the matrix/rhs + FieldSpecificationEqual::SpecifyFieldValue( dofIndex, + rankOffset, + localMatrix, + rhsValue, + bcPres[ei], + pres[ei] ); + localRhs[localRow] = rhsValue; + } ); + } ); + + // 5. Apply log primary species concentration to the system + fsManager.apply< ElementSubRegionBase >( time_n + dt, + mesh, + fields::flow::logPrimarySpeciesConcentration::key(), + [&] ( FieldSpecificationBase const &, + string const &, + SortedArrayView< localIndex const > const & targetSet, + ElementSubRegionBase & subRegion, + string const & ) + { + arrayView1d< integer const > const ghostRank = subRegion.ghostRank(); + arrayView1d< globalIndex const > const dofNumber = + subRegion.getReference< array1d< globalIndex > >( dofKey ); + + arrayView2d< real64 const, compflow::USD_COMP > const bcLogPrimaryConc = + subRegion.getReference< array2d< real64, compflow::LAYOUT_COMP > >( fields::flow::bcLogPrimarySpeciesConcentration::key() ); + arrayView2d< real64 const, compflow::USD_COMP > const logPrimaryConc = + subRegion.getReference< array2d< real64, compflow::LAYOUT_COMP > >( fields::flow::logPrimarySpeciesConcentration::key() ); + + integer const numPrimarySpecies = m_numPrimarySpecies; + integer const isThermal = m_isThermal; + + forAll< parallelDevicePolicy<> >( targetSet.size(), [=] GEOS_HOST_DEVICE ( localIndex const a ) + { + localIndex const ei = targetSet[a]; + if( ghostRank[ei] >= 0 ) + { + return; + } + + globalIndex const dofIndex = dofNumber[ei]; + localIndex const localRow = dofIndex - rankOffset; + real64 rhsValue; + + integer const speciesDofBeginIndex = isThermal? 2:1; + + // 5.1. For each component, apply target global density value + for( integer is = 0; is < numPrimarySpecies; ++is ) + { + FieldSpecificationEqual::SpecifyFieldValue( dofIndex + is + speciesDofBeginIndex, + rankOffset, + localMatrix, + rhsValue, + bcLogPrimaryConc[ei][is], + logPrimaryConc[ei][is] ); + localRhs[localRow + is + speciesDofBeginIndex] = rhsValue; + } + } ); + } ); + + // 6. Apply temperature to the system + if( m_isThermal ) + { + fsManager.apply< ElementSubRegionBase >( time_n + dt, + mesh, + fields::flow::temperature::key(), + [&] ( FieldSpecificationBase const &, + string const &, + SortedArrayView< localIndex const > const & targetSet, + ElementSubRegionBase & subRegion, + string const & ) + { + arrayView1d< integer const > const ghostRank = + subRegion.getReference< array1d< integer > >( ObjectManagerBase::viewKeyStruct::ghostRankString() ); + arrayView1d< globalIndex const > const dofNumber = + subRegion.getReference< array1d< globalIndex > >( dofKey ); + arrayView1d< real64 const > const bcTemp = + subRegion.getReference< array1d< real64 > >( fields::flow::bcTemperature::key() ); + arrayView1d< real64 const > const temp = + subRegion.getReference< array1d< real64 > >( fields::flow::temperature::key() ); + + forAll< parallelDevicePolicy<> >( targetSet.size(), [=] GEOS_HOST_DEVICE ( localIndex const a ) + { + localIndex const ei = targetSet[a]; + if( ghostRank[ei] >= 0 ) + { + return; + } + + globalIndex const dofIndex = dofNumber[ei]; + localIndex const localRow = dofIndex - rankOffset; + real64 rhsValue; + + // 4.2. Apply temperature value to the matrix/rhs + FieldSpecificationEqual::SpecifyFieldValue( dofIndex + 1, + rankOffset, + localMatrix, + rhsValue, + bcTemp[ei], + temp[ei] ); + localRhs[localRow + 1] = rhsValue; + } ); + } ); + } + } ); +} + +real64 SinglePhaseReactiveTransport::calculateResidualNorm( real64 const & GEOS_UNUSED_PARAM( time_n ), + real64 const & GEOS_UNUSED_PARAM( dt ), + DomainPartition const & domain, + DofManager const & dofManager, + arrayView1d< real64 const > const & localRhs ) +{ + GEOS_MARK_FUNCTION; + + integer constexpr numNorm = 3; // total mass balance, energy balance, and species mass balance + array1d< real64 > localResidualNorm; + array1d< real64 > localResidualNormalizer; + localResidualNorm.resize( numNorm ); + localResidualNormalizer.resize( numNorm ); + + physicsSolverBaseKernels::NormType const normType = SinglePhaseBase::getNonlinearSolverParameters().normType(); + + globalIndex const rankOffset = dofManager.rankOffset(); + string const dofKey = dofManager.getKey( SinglePhaseBase::viewKeyStruct::elemDofFieldString() ); + + this->forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &, + MeshLevel const & mesh, + string_array const & regionNames ) + { + mesh.getElemManager().forElementSubRegions( regionNames, + [&]( localIndex const, + ElementSubRegionBase const & subRegion ) + { + real64 subRegionResidualNorm[numNorm]{}; + real64 subRegionResidualNormalizer[numNorm]{}; + + // step 1: compute the norm in the subRegion + + if( m_isThermal ) + { + singlePhaseReactiveBaseKernels:: + ResidualNormKernelFactory:: + createAndLaunch< parallelDevicePolicy<> >( normType, + m_numPrimarySpecies, + rankOffset, + dofKey, + localRhs, + subRegion, + m_nonlinearSolverParameters.m_minNormalizer, + subRegionResidualNorm, + subRegionResidualNormalizer ); + } + else + { + real64 subRegionFlowResidualNorm[2]{}; + real64 subRegionFlowResidualNormalizer[2]{}; + + singlePhaseReactiveBaseKernels:: + ResidualNormKernelFactory:: + createAndLaunch< parallelDevicePolicy<> >( normType, + m_numPrimarySpecies, + rankOffset, + dofKey, + localRhs, + subRegion, + m_nonlinearSolverParameters.m_minNormalizer, + subRegionFlowResidualNorm, + subRegionFlowResidualNormalizer ); + subRegionResidualNorm[0] = subRegionFlowResidualNorm[0]; + subRegionResidualNorm[1] = subRegionFlowResidualNorm[1]; + subRegionResidualNormalizer[0] = subRegionFlowResidualNormalizer[0]; + subRegionResidualNormalizer[1] = subRegionFlowResidualNormalizer[1]; + } + + // step 2: first reduction across meshBodies/regions/subRegions + + if( normType == physicsSolverBaseKernels::NormType::Linf ) + { + physicsSolverBaseKernels::LinfResidualNormHelper:: + updateLocalNorm< numNorm >( subRegionResidualNorm, localResidualNorm ); + } + else + { + physicsSolverBaseKernels::L2ResidualNormHelper:: + updateLocalNorm< numNorm >( subRegionResidualNorm, subRegionResidualNormalizer, localResidualNorm, localResidualNormalizer ); + } + } ); + } ); + + // step 3: second reduction across MPI ranks + + real64 residualNorm = 0.0; + array1d< real64 > globalResidualNorm; + globalResidualNorm.resize( numNorm ); + if( m_isThermal ) + { + if( normType == physicsSolverBaseKernels::NormType::Linf ) + { + physicsSolverBaseKernels::LinfResidualNormHelper:: + computeGlobalNorm( localResidualNorm, globalResidualNorm ); + } + else + { + physicsSolverBaseKernels::L2ResidualNormHelper:: + computeGlobalNorm( localResidualNorm, localResidualNormalizer, globalResidualNorm ); + } + residualNorm = sqrt( globalResidualNorm[0] * globalResidualNorm[0] + globalResidualNorm[1] * globalResidualNorm[1] + globalResidualNorm[2] * globalResidualNorm[2] ); + + GEOS_LOG_LEVEL_RANK_0_NLR( logInfo::Convergence, GEOS_FMT( " ( RtotalMass RspeciesAmount ) = ( {:4.2e} {:4.2e} ) ( Renergy ) = ( {:4.2e} )", + globalResidualNorm[0], globalResidualNorm[2], globalResidualNorm[1] )); + } + else + { + if( normType == physicsSolverBaseKernels::NormType::Linf ) + { + physicsSolverBaseKernels::LinfResidualNormHelper:: + computeGlobalNorm( localResidualNorm, globalResidualNorm ); + } + else + { + physicsSolverBaseKernels::L2ResidualNormHelper:: + computeGlobalNorm( localResidualNorm, localResidualNormalizer, globalResidualNorm ); + } + residualNorm = sqrt( globalResidualNorm[0] * globalResidualNorm[0] + globalResidualNorm[1] * globalResidualNorm[1] ); + + GEOS_LOG_LEVEL_RANK_0_NLR( logInfo::Convergence, GEOS_FMT( " ( RtotalMass RspeciesAmount ) = ( {:4.2e} {:4.2e} )", + globalResidualNorm[0], globalResidualNorm[1] ) ); + } + return residualNorm; +} + +void SinglePhaseReactiveTransport::applySystemSolution( DofManager const & dofManager, + arrayView1d< real64 const > const & localSolution, + real64 const scalingFactor, + real64 const dt, + DomainPartition & domain ) +{ + GEOS_UNUSED_VAR( dt ); + + if( m_isThermal ) + { + DofManager::CompMask pressureMask( m_numDofPerCell, 0, 1 ); + DofManager::CompMask temperatureMask( m_numDofPerCell, 1, 2 ); + DofManager::CompMask speciesMask( m_numDofPerCell, 2, m_numPrimarySpecies+2 ); + + dofManager.addVectorToField( localSolution, + viewKeyStruct::elemDofFieldString(), + fields::flow::pressure::key(), + scalingFactor, + pressureMask ); + + dofManager.addVectorToField( localSolution, + viewKeyStruct::elemDofFieldString(), + fields::flow::temperature::key(), + scalingFactor, + temperatureMask ); + + dofManager.addVectorToField( localSolution, + viewKeyStruct::elemDofFieldString(), + fields::flow::logPrimarySpeciesConcentration::key(), + scalingFactor, + speciesMask ); + } + else + { + DofManager::CompMask pressureMask( m_numDofPerCell, 0, 1 ); + DofManager::CompMask speciesMask( m_numDofPerCell, 1, m_numPrimarySpecies+1 ); + + dofManager.addVectorToField( localSolution, + viewKeyStruct::elemDofFieldString(), + fields::flow::pressure::key(), + scalingFactor, + pressureMask ); + + dofManager.addVectorToField( localSolution, + viewKeyStruct::elemDofFieldString(), + fields::flow::logPrimarySpeciesConcentration::key(), + scalingFactor, + speciesMask ); + } + + this->forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &, + MeshLevel & mesh, + string_array const & regionNames ) + { + std::vector< string > fields{ fields::flow::pressure::key() }; + + if( m_isThermal ) + { + fields.emplace_back( fields::flow::temperature::key() ); + } + + fields.emplace_back( fields::flow::logPrimarySpeciesConcentration::key() ); + + FieldIdentifiers fieldsToBeSync; + fieldsToBeSync.addElementFields( fields, regionNames ); + + CommunicationTools::getInstance().synchronizeFields( fieldsToBeSync, mesh, domain.getNeighbors(), true ); + } ); +} + +void SinglePhaseReactiveTransport::saveConvergedState( ElementSubRegionBase & subRegion ) const +{ + SinglePhaseBase::saveConvergedState( subRegion ); + + arrayView2d< real64 const, compflow::USD_COMP > const logPrimaryConc = subRegion.template getField< fields::flow::logPrimarySpeciesConcentration >(); + arrayView2d< real64, compflow::USD_COMP > const logPrimarySpeciesConc_n = subRegion.template getField< fields::flow::logPrimarySpeciesConcentration_n >(); + logPrimarySpeciesConc_n.setValues< parallelDevicePolicy<> >( logPrimaryConc ); + + arrayView2d< real64 const, compflow::USD_COMP > const primarySpeciesAggregateMole = subRegion.template getField< fields::flow::primarySpeciesAggregateMole >(); + arrayView2d< real64, compflow::USD_COMP > const primarySpeciesAggregateMole_n = subRegion.template getField< fields::flow::primarySpeciesAggregateMole_n >(); + primarySpeciesAggregateMole_n.setValues< parallelDevicePolicy<> >( primarySpeciesAggregateMole ); +} + +namespace +{ +char const faceBcLogMessage[] = + "SinglePhaseReactiveTransport {}: at time {}s, " + "the <{}> boundary condition '{}' is applied to the face set '{}' in '{}'. " + "\nThe scale of this boundary condition is {} and multiplies the value of the provided function (if any). " + "\nThe total number of target faces (including ghost faces) is {}." + "\nNote that if this number is equal to zero, the boundary condition will not be applied on this face set."; +} + +void SinglePhaseReactiveTransport::applyFaceDirichletBC( real64 const time_n, + real64 const dt, + DofManager const & dofManager, + DomainPartition & domain, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) +{ + GEOS_MARK_FUNCTION; + + array1d< integer > mobilePrimarySpeciesFlags; + mobilePrimarySpeciesFlags.resize( m_numPrimarySpecies ); + + for( integer i=0; i 0 ) + { + for( integer i = 0; i < m_immobilePrimarySpeciesIndices.size(); ++i ) + { + localIndex const immobileSpeciesIndex = m_immobilePrimarySpeciesIndices[i]; + mobilePrimarySpeciesFlags[immobileSpeciesIndex] = 0; + } + } + + FieldSpecificationManager & fsManager = FieldSpecificationManager::getInstance(); + + NumericalMethodsManager const & numericalMethodManager = domain.getNumericalMethodManager(); + FiniteVolumeManager const & fvManager = numericalMethodManager.getFiniteVolumeManager(); + FluxApproximationBase const & fluxApprox = fvManager.getFluxApproximation( m_discretizationName ); + + string const & dofKey = dofManager.getKey( SinglePhaseBase::viewKeyStruct::elemDofFieldString() ); + + this->forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &, + MeshLevel & mesh, + string_array const & ) + { + FaceManager & faceManager = mesh.getFaceManager(); + ElementRegionManager & elemManager = mesh.getElemManager(); + + if( m_isThermal ) + { + // Take BCs defined for "pressure" field and apply values to "facePressure" + applyFieldValue< FaceManager >( time_n, dt, mesh, faceBcLogMessage, + fields::flow::pressure::key(), fields::flow::facePressure::key() ); + + // Take BCs defined for "temperature" field and apply values to "faceTemperature" + applyFieldValue< FaceManager >( time_n, dt, mesh, faceBcLogMessage, + fields::flow::temperature::key(), fields::flow::faceTemperature::key() ); + + // Then launch the face Dirichlet kernel + fsManager.apply< FaceManager >( time_n + dt, + mesh, + fields::flow::pressure::key(), // we have required that pressure is always present + [&] ( FieldSpecificationBase const &, + string const & setName, + SortedArrayView< localIndex const > const &, + FaceManager &, + string const & ) + { + BoundaryStencil const & stencil = fluxApprox.getStencil< BoundaryStencil >( mesh, setName ); + if( stencil.size() == 0 ) + { + return; + } + + // TODO: same issue as in the single-phase case + // currently we just use model from the first cell in this stencil + // since it's not clear how to create fluid kernel wrappers for arbitrary models. + // Can we just use cell properties for an approximate flux computation? + // Then we can forget about capturing the fluid model. + localIndex const er = stencil.getElementRegionIndices()( 0, 0 ); + localIndex const esr = stencil.getElementSubRegionIndices()( 0, 0 ); + ElementSubRegionBase & subRegion = elemManager.getRegion( er ).getSubRegion( esr ); + string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString() ); + reactivefluid::ReactiveThermalCompressibleSinglePhaseFluid & reactiveFluid = subRegion.getConstitutiveModel< reactivefluid::ReactiveThermalCompressibleSinglePhaseFluid >( fluidName ); + + BoundaryStencilWrapper const stencilWrapper = stencil.createKernelWrapper(); + + thermalSinglePhaseReactiveFVMKernels:: + DirichletFluxComputeKernelFactory:: + createAndLaunch< parallelDevicePolicy<> >( m_numPrimarySpecies, + mobilePrimarySpeciesFlags, + dofManager.rankOffset(), + dofKey, + this->getName(), + faceManager, + elemManager, + stencilWrapper, + reactiveFluid, + dt, + localMatrix, + localRhs ); + } ); + } + else + { + // Take BCs defined for "pressure" field and apply values to "facePressure" + applyFieldValue< FaceManager >( time_n, dt, mesh, faceBcLogMessage, + fields::flow::pressure::key(), fields::flow::facePressure::key() ); + + // Then launch the face Dirichlet kernel + fsManager.apply< FaceManager >( time_n + dt, + mesh, + fields::flow::pressure::key(), // we have required that pressure is always present + [&] ( FieldSpecificationBase const &, + string const & setName, + SortedArrayView< localIndex const > const &, + FaceManager &, + string const & ) + { + BoundaryStencil const & stencil = fluxApprox.getStencil< BoundaryStencil >( mesh, setName ); + if( stencil.size() == 0 ) + { + return; + } + + // TODO: same issue as in the single-phase case + // currently we just use model from the first cell in this stencil + // since it's not clear how to create fluid kernel wrappers for arbitrary models. + // Can we just use cell properties for an approximate flux computation? + // Then we can forget about capturing the fluid model. + localIndex const er = stencil.getElementRegionIndices()( 0, 0 ); + localIndex const esr = stencil.getElementSubRegionIndices()( 0, 0 ); + ElementSubRegionBase & subRegion = elemManager.getRegion( er ).getSubRegion( esr ); + string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString() ); + reactivefluid::ReactiveCompressibleSinglePhaseFluid & reactiveFluid = subRegion.getConstitutiveModel< reactivefluid::ReactiveCompressibleSinglePhaseFluid >( fluidName ); + + BoundaryStencilWrapper const stencilWrapper = stencil.createKernelWrapper(); + + singlePhaseReactiveFVMKernels:: + DirichletFluxComputeKernelFactory:: + createAndLaunch< parallelDevicePolicy<> >( m_numPrimarySpecies, + mobilePrimarySpeciesFlags, + dofManager.rankOffset(), + dofKey, + this->getName(), + faceManager, + elemManager, + stencilWrapper, + reactiveFluid, + dt, + localMatrix, + localRhs ); + } ); + } + } ); +} + +void SinglePhaseReactiveTransport::assembleEDFMFluxTerms( real64 const GEOS_UNUSED_PARAM( time_n ), + real64 const GEOS_UNUSED_PARAM( dt ), + DomainPartition const & GEOS_UNUSED_PARAM( domain ), + DofManager const & GEOS_UNUSED_PARAM( dofManager ), + CRSMatrixView< real64, globalIndex const > const & GEOS_UNUSED_PARAM( localMatrix ), + arrayView1d< real64 > const & GEOS_UNUSED_PARAM( localRhs ), + string const & GEOS_UNUSED_PARAM( jumpDofKey ) ) +{} + +void SinglePhaseReactiveTransport::applyAquiferBC( real64 const GEOS_UNUSED_PARAM( time ), + real64 const GEOS_UNUSED_PARAM( dt ), + DomainPartition & GEOS_UNUSED_PARAM( domain ), + DofManager const & GEOS_UNUSED_PARAM( dofManager ), + CRSMatrixView< real64, globalIndex const > const & GEOS_UNUSED_PARAM( localMatrix ), + arrayView1d< real64 > const & GEOS_UNUSED_PARAM( localRhs ) ) const +{} + +void SinglePhaseReactiveTransport::assembleStabilizedFluxTerms( real64 const GEOS_UNUSED_PARAM( dt ), + DomainPartition const & GEOS_UNUSED_PARAM( domain ), + DofManager const & GEOS_UNUSED_PARAM( dofManager ), + CRSMatrixView< real64, globalIndex const > const & GEOS_UNUSED_PARAM( localMatrix ), + arrayView1d< real64 > const & GEOS_UNUSED_PARAM( localRhs ) ) +{} + +REGISTER_CATALOG_ENTRY( PhysicsSolverBase, SinglePhaseReactiveTransport, string const &, Group * const ) + +} /* namespace geos */ diff --git a/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseReactiveTransport.hpp b/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseReactiveTransport.hpp new file mode 100644 index 00000000000..e8c45a7260a --- /dev/null +++ b/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseReactiveTransport.hpp @@ -0,0 +1,373 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * 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 SinglePhaseReactiveTransport.hpp + */ + +#ifndef GEOS_PHYSICSSOLVERS_FLUIDFLOW_SINGLEPHASEREACTIVETRANSPORT_HPP_ +#define GEOS_PHYSICSSOLVERS_FLUIDFLOW_SINGLEPHASEREACTIVETRANSPORT_HPP_ + +#include "fieldSpecification/FieldSpecificationManager.hpp" +#include "physicsSolvers/fluidFlow/SinglePhaseBase.hpp" +#include "physicsSolvers/fluidFlow/SinglePhaseFVM.hpp" +#include "physicsSolvers/fluidFlow/SinglePhaseReactiveTransportFields.hpp" +#include "physicsSolvers/fluidFlow/kernels/singlePhase/reactive/AccumulationKernels.hpp" +#include "physicsSolvers/fluidFlow/kernels/singlePhase/reactive/DirichletFluxComputeKernel.hpp" +#include "physicsSolvers/fluidFlow/kernels/singlePhase/reactive/FluidUpdateKernel.hpp" +#include "physicsSolvers/fluidFlow/kernels/singlePhase/reactive/FluxComputeKernel.hpp" +#include "physicsSolvers/fluidFlow/kernels/singlePhase/reactive/ResidualNormKernel.hpp" +#include "physicsSolvers/fluidFlow/kernels/singlePhase/reactive/ReactionUpdateKernel.hpp" +#include "physicsSolvers/fluidFlow/kernels/singlePhase/reactive/SourceFluxComputeKernel.hpp" +#include "physicsSolvers/fluidFlow/kernels/singlePhase/reactive/ThermalAccumulationKernels.hpp" +#include "physicsSolvers/fluidFlow/kernels/singlePhase/reactive/ThermalDirichletFluxComputeKernel.hpp" +#include "physicsSolvers/fluidFlow/kernels/singlePhase/reactive/ThermalFluxComputeKernel.hpp" +#include "physicsSolvers/fluidFlow/kernels/singlePhase/reactive/ThermalSourceFluxComputeKernel.hpp" +#include "constitutive/fluid/reactivefluid/ReactiveSinglePhaseFluid.hpp" + + +namespace geos +{ + +/** + * @class SinglePhaseReactiveTransport + * + * A solver for single phase reactive transport + */ +class SinglePhaseReactiveTransport : public SinglePhaseBase +{ + +public: + + /** + * @brief main constructor for Group Objects + * @param name the name of this instantiation of Group in the repository + * @param parent the parent group of this instantiation of Group + */ + SinglePhaseReactiveTransport( const string & name, + dataRepository::Group * const parent ); + + SinglePhaseReactiveTransport() = delete; + + /// deleted copy constructor + SinglePhaseReactiveTransport( SinglePhaseReactiveTransport const & ) = delete; + + /// default move constructor + SinglePhaseReactiveTransport( SinglePhaseReactiveTransport && ) = default; + + /// deleted assignment operator + SinglePhaseReactiveTransport & operator=( SinglePhaseReactiveTransport const & ) = delete; + + /// deleted move operator + SinglePhaseReactiveTransport & operator=( SinglePhaseReactiveTransport && ) = delete; + + /** + * @brief default destructor + */ + virtual ~SinglePhaseReactiveTransport() override = default; + + /** + * @brief name of the node manager in the object catalog + * @return string that contains the catalog name to generate a new NodeManager object through the object catalog. + */ + static string catalogName() + { + return "SinglePhaseReactiveTransport"; + } + + /** + * @copydoc PhysicsSolverBase::getCatalogName() + */ + string getCatalogName() const override { return catalogName(); } + + virtual void registerDataOnMesh( dataRepository::Group & meshBodies ) override; + + virtual void + setupDofs( DomainPartition const & domain, + DofManager & dofManager ) const override; + + virtual void + implicitStepSetup( real64 const & time_n, + real64 const & dt, + DomainPartition & domain ) override final; + + virtual void + assembleSystem( real64 const time_n, + real64 const dt, + DomainPartition & domain, + DofManager const & dofManager, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) override; + + virtual void + applyBoundaryConditions( real64 const time_n, + real64 const dt, + DomainPartition & domain, + DofManager const & dofManager, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) override; + + virtual real64 + calculateResidualNorm( real64 const & time_n, + real64 const & dt, + DomainPartition const & domain, + DofManager const & dofManager, + arrayView1d< real64 const > const & localRhs ) override; + + virtual void + applySystemSolution( DofManager const & dofManager, + arrayView1d< real64 const > const & localSolution, + real64 const scalingFactor, + real64 const dt, + DomainPartition & domain ) override; + + virtual void + resetStateToBeginningOfStep( DomainPartition & domain ) override; + + virtual void + implicitStepComplete( real64 const & time, + real64 const & dt, + DomainPartition & domain ) override final; + + virtual void saveConvergedState( ElementSubRegionBase & subRegion ) const override final; + + virtual void + updateState ( DomainPartition & domain ) override final; + + void updateMixedReactionSystem( ElementSubRegionBase & subRegion ) const; + + void updateSurfaceArea( ElementSubRegionBase & subRegion ) const; + + void updateSpeciesAmount( ElementSubRegionBase & subRegion ) const; + + void updateKineticReactionMolarIncrements( real64 const dt, + ElementSubRegionBase & subRegion ) const; + + virtual void updateFluidModel( ObjectManagerBase & dataGroup ) const override; + + virtual void updatePorosityAndPermeability( CellElementSubRegion & subRegion ) const override; + + virtual void initializePostInitialConditionsPreSubGroups() override; + + virtual void initializeFluidState( MeshLevel & mesh, string_array const & regionNames ) override; + + void initializeEquilibriumReaction( ElementSubRegionBase & subRegion ) const; + + /** + * @brief Checks constitutive models for consistency + * @param[in] domain the domain partition + */ + virtual void validateConstitutiveModels( DomainPartition & domain ) const override final; + + /** + * @brief Getter for the number of fluid components (species) + * @return the number of components + */ + integer numPrimarySpecies() const { return m_numPrimarySpecies; } + + /** + * @brief assembles the accumulation terms in total mass balance and primary species amount equation for all cells + * @param dt time step + * @param domain the physical domain object + * @param dofManager degree-of-freedom manager associated with the linear system + * @param localMatrix the system matrix + * @param localRhs the system right-hand side vector + */ + void assembleAccumulationTermsInMassBalanceAndSpeciesAmountEqs( real64 const dt, + DomainPartition & domain, + DofManager const & dofManager, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) const; + + /** + * @brief assembles the flux terms for all cells + * @param dt time step + * @param domain the physical domain object + * @param dofManager degree-of-freedom manager associated with the linear system + * @param matrix the system matrix + * @param rhs the system right-hand side vector + */ + virtual void + assembleFluxTerms( real64 const dt, + DomainPartition const & domain, + DofManager const & dofManager, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) override; + + /** + * @brief Apply source flux boundary conditions to the system + * @param time current time + * @param dt time step + * @param dofManager degree-of-freedom manager associated with the linear system + * @param domain the domain + * @param localMatrix local system matrix + * @param localRhs local system right-hand side vector + */ + virtual void + applySourceFluxBC( real64 const time_n, + real64 const dt, + DomainPartition & domain, + DofManager const & dofManager, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) const override; + + /** + * @brief Function to perform the Application of Dirichlet type BC's + * @param time current time + * @param dt time step + * @param dofManager degree-of-freedom manager associated with the linear system + * @param domain the domain + * @param localMatrix local system matrix + * @param localRhs local system right-hand side vector + */ + virtual void + applyDirichletBC( real64 const time_n, + real64 const dt, + DomainPartition & domain, + DofManager const & dofManager, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) const override; + + /** + * @brief Utility function that encapsulates the call to FieldSpecificationBase::applyFieldValue in BC application + * @param[in] time_n the time at the beginning of the step + * @param[in] dt the time step + * @param[in] mesh the mesh level object + * @param[in] logMessage the log message issued by the solver if the bc is called + * @param[in] fieldKey the key of the field specified in the xml file + * @param[in] boundaryFieldKey the key of the boundary field + */ + template< typename OBJECT_TYPE > + void applyFieldValue( real64 const & time_n, + real64 const & dt, + MeshLevel & mesh, + char const logMessage[], + string const fieldKey, + string const boundaryFieldKey ) const; + + virtual void + applyAquiferBC( real64 const time, + real64 const dt, + DomainPartition & domain, + DofManager const & dofManager, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) const override; + + virtual void + assembleEDFMFluxTerms( real64 const time_n, + real64 const dt, + DomainPartition const & domain, + DofManager const & dofManager, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs, + string const & jumpDofKey ) override final; + + virtual void + assembleStabilizedFluxTerms( real64 const dt, + DomainPartition const & domain, + DofManager const & dofManager, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) override; + + struct viewKeyStruct : SinglePhaseBase::viewKeyStruct + { + static constexpr char const * diffusionNamesString() { return "diffusionNames"; } + static constexpr char const * isUpdateReactivePorosityString() { return "isUpdateReactivePorosity"; } + static constexpr char const * isUpdateSurfaceAreaString() { return "isUpdateSurfaceArea"; } + static constexpr char const * immobilePrimarySpeciesIndicesString() { return "immobilePrimarySpeciesIndices"; } + }; + +protected: + + /// the number of primary species in the fluid + integer m_numPrimarySpecies; + + /// the number of kinetic reactions + integer m_numKineticReactions; + + /// name of the reactive fluid constitutive model + string m_reactiveFluidModelName; + + /// flag to determine whether or not to apply diffusion + integer m_hasDiffusion; + + /// flag to determine whether or not to use the reactive porosity + integer m_isUpdateReactivePorosity; + + /// flag to determine whether or not to update the surface area + integer m_isUpdateSurfaceArea; + + /// array to store the indices of immobile primary species + array1d< integer > m_immobilePrimarySpeciesIndices; + +private: + + /** + * @brief Function to perform the application of Dirichlet BCs on faces + * @param time_n current time + * @param dt time step + * @param faceSet degree-of-freedom manager associated with the linear system + * @param domain the domain + * @param matrix the system matrix + * @param rhs the system right-hand side vector + */ + void applyFaceDirichletBC( real64 const time_n, + real64 const dt, + DofManager const & faceSet, + DomainPartition & domain, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ); + +}; + +template< typename OBJECT_TYPE > +void SinglePhaseReactiveTransport::applyFieldValue( real64 const & time_n, + real64 const & dt, + MeshLevel & mesh, + char const logMessage[], + string const fieldKey, + string const boundaryFieldKey ) const +{ + FieldSpecificationManager & fsManager = FieldSpecificationManager::getInstance(); + + fsManager.apply< OBJECT_TYPE >( time_n + dt, + mesh, + fieldKey, + [&]( FieldSpecificationBase const & fs, + string const & setName, + SortedArrayView< localIndex const > const & lset, + OBJECT_TYPE & targetGroup, + string const & ) + { + if( fs.getLogLevel() >= 1 && m_nonlinearSolverParameters.m_numNewtonIterations == 0 ) + { + globalIndex const numTargetElems = MpiWrapper::sum< globalIndex >( lset.size() ); + GEOS_LOG_RANK_0( GEOS_FMT( logMessage, + getName(), time_n+dt, fs.getCatalogName(), fs.getName(), + setName, targetGroup.getName(), fs.getScale(), numTargetElems ) ); + } + + // Specify the bc value of the field + fs.applyFieldValue< FieldSpecificationEqual, + parallelDevicePolicy<> >( lset, + time_n + dt, + targetGroup, + boundaryFieldKey ); + } ); +} + +} /* namespace geos */ + +#endif //GEOS_PHYSICSSOLVERS_FLUIDFLOW_SINGLEPHASEREACTIVETRANSPORT_HPP_ diff --git a/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseReactiveTransportFields.hpp b/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseReactiveTransportFields.hpp new file mode 100644 index 00000000000..28c72f5c170 --- /dev/null +++ b/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseReactiveTransportFields.hpp @@ -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 SinglePhaseReactiveTransportFields.hpp + */ + +#ifndef GEOS_PHYSICSSOLVERS_FLUIDFLOW_SINGLEPHASEREACTIVETRANSPORTFIELDS_HPP_ +#define GEOS_PHYSICSSOLVERS_FLUIDFLOW_SINGLEPHASEREACTIVETRANSPORTFIELDS_HPP_ + +#include "mesh/MeshFields.hpp" + +namespace geos +{ +/** + * A scope for field traits. + */ +namespace fields +{ + +namespace flow +{ +using array2dLayoutComp = array2d< real64, compflow::LAYOUT_COMP >; +using array2dLayoutFluid_dC = array2d< real64, compflow::LAYOUT_FLUID_DC >; + +DECLARE_FIELD( logPrimarySpeciesConcentration, + "logPrimarySpeciesConcentration", + array2dLayoutComp, + 0, + LEVEL_0, + WRITE_AND_READ, + "Natural log of primary species concentration (molarity)" ); + +DECLARE_FIELD( logPrimarySpeciesConcentration_n, + "logPrimarySpeciesConcentration_n", + array2dLayoutComp, + 0, + LEVEL_0, + WRITE_AND_READ, + "Natural log of primary species concentration (molarity) at the previous converged time step" ); + +DECLARE_FIELD( bcLogPrimarySpeciesConcentration, + "bcLogPrimarySpeciesConcentration", + array2dLayoutComp, + 0, + LEVEL_0, + WRITE_AND_READ, + "Boundary condition for natural log of primary species concentration (molarity)" ); + +DECLARE_FIELD( primarySpeciesAggregateMole, + "primarySpeciesAggregateMole", + array2dLayoutComp, + 0, + LEVEL_0, + WRITE_AND_READ, + "Aggregate amount of primary species in mole" ); + +DECLARE_FIELD( primarySpeciesAggregateMole_n, + "primarySpeciesAggregateMole_n", + array2dLayoutComp, + 0, + LEVEL_0, + WRITE_AND_READ, + "Aggregate amount of primary species in mole at the previous converged time step" ); + +DECLARE_FIELD( kineticReactionMolarIncrements, + "kineticReactionMolarIncrements", + array2dLayoutComp, + 0, + NOPLOT, + WRITE_AND_READ, + "Molar increment in the current timestep for kinetic reactions" ); + +DECLARE_FIELD( dMobility_dLogPrimaryConc, + "dMobility_dLogPrimaryConc", + array2dLayoutFluid_dC, + 0, + NOPLOT, + NO_WRITE, + "Derivative of fluid mobility with respect to log of primary species concentration" ); + +DECLARE_FIELD( surfaceArea, + "surfaceArea", + array2dLayoutComp, + 0, + NOPLOT, + WRITE_AND_READ, + "Surface area for surface reactions." ); + +DECLARE_FIELD( initialSurfaceArea, + "initialSurfaceArea", + array2dLayoutComp, + 0, + NOPLOT, + WRITE_AND_READ, + "Initial surface area for surface reactions." ); + +} + +} + +} + +#endif // GEOS_PHYSICSSOLVERS_FLUIDFLOW_SINGLEPHASEREACTIVETRANSPORTFIELDS_HPP_ diff --git a/src/coreComponents/physicsSolvers/fluidFlow/kernels/singlePhase/AccumulationKernels.hpp b/src/coreComponents/physicsSolvers/fluidFlow/kernels/singlePhase/AccumulationKernels.hpp index b321eab6a44..13393664683 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/kernels/singlePhase/AccumulationKernels.hpp +++ b/src/coreComponents/physicsSolvers/fluidFlow/kernels/singlePhase/AccumulationKernels.hpp @@ -57,8 +57,8 @@ class AccumulationKernel /// Note: Derivative lineup only supports dP & dT, not component terms - static constexpr integer isThermal = NUM_DOF-1; - using DerivOffset = constitutive::singlefluid::DerivativeOffsetC< isThermal >; + // static constexpr integer isThermal = NUM_DOF-1; + using DerivOffset = constitutive::singlefluid::DerivativeOffsetC< 0 >; // Why not just input 0 /** * @brief Constructor diff --git a/src/coreComponents/physicsSolvers/fluidFlow/kernels/singlePhase/DirichletFluxComputeKernel.hpp b/src/coreComponents/physicsSolvers/fluidFlow/kernels/singlePhase/DirichletFluxComputeKernel.hpp index 15c9ecc7a48..000c4d0b254 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/kernels/singlePhase/DirichletFluxComputeKernel.hpp +++ b/src/coreComponents/physicsSolvers/fluidFlow/kernels/singlePhase/DirichletFluxComputeKernel.hpp @@ -27,6 +27,7 @@ #include "finiteVolume/BoundaryStencil.hpp" #include "physicsSolvers/fluidFlow/FlowSolverBaseFields.hpp" #include "physicsSolvers/fluidFlow/StencilAccessors.hpp" +#include "physicsSolvers/fluidFlow/kernels/singlePhase/FluxComputeKernel.hpp" #include "physicsSolvers/fluidFlow/kernels/singlePhase/MobilityKernel.hpp" #include "codingUtilities/Utilities.hpp" @@ -44,8 +45,8 @@ namespace singlePhaseFVMKernels * @brief Define the interface for the assembly kernel in charge of Dirichlet face flux terms */ template< integer NUM_EQN, integer NUM_DOF, typename FLUIDWRAPPER > -class DirichletFluxComputeKernel : public FluxComputeKernel< NUM_EQN, NUM_DOF, - BoundaryStencilWrapper > +class DirichletFluxComputeKernel : public singlePhaseFVMKernels::FluxComputeKernel< NUM_EQN, NUM_DOF, + BoundaryStencilWrapper > { public: using Deriv = constitutive::singlefluid::DerivativeOffset; @@ -223,10 +224,14 @@ class DirichletFluxComputeKernel : public FluxComputeKernel< NUM_EQN, NUM_DOF, real64 const mobility_up = mobility[k_up]; real64 const dMobility_dP_up = dMobility_dP[k_up]; + // Upwind density + real64 const dens_up = ( potDif >= 0 ) ? m_dens[er][esr][ei][0] : faceDens; + real64 const dDens_dP_up = ( potDif >= 0 ) ? m_dDens[er][esr][ei][0][Deriv::dP] : 0.0; + // call the lambda in the phase loop to allow the reuse of the fluxes and their derivatives // possible use: assemble the derivatives wrt temperature, and the flux term of the energy equation for this phase - compFluxKernelOp( er, esr, ei, kf, f, dF_dP, mobility_up, dMobility_dP_up ); + compFluxKernelOp( er, esr, ei, kf, f, dF_dP, mobility_up, dMobility_dP_up, dens_up, dDens_dP_up ); // *** end of upwinding diff --git a/src/coreComponents/physicsSolvers/fluidFlow/kernels/singlePhase/ThermalAccumulationKernels.hpp b/src/coreComponents/physicsSolvers/fluidFlow/kernels/singlePhase/ThermalAccumulationKernels.hpp index e2e92b14792..c470b9790a1 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/kernels/singlePhase/ThermalAccumulationKernels.hpp +++ b/src/coreComponents/physicsSolvers/fluidFlow/kernels/singlePhase/ThermalAccumulationKernels.hpp @@ -51,8 +51,8 @@ class AccumulationKernel : public singlePhaseBaseKernels::AccumulationKernel< SU using Base::m_dMass; /// Note: Derivative lineup only supports dP & dT, not component terms - static constexpr integer isThermal = NUM_DOF-1; - using DerivOffset = constitutive::singlefluid::DerivativeOffsetC< isThermal >; + // static constexpr integer isThermal = NUM_DOF-1; + using DerivOffset = constitutive::singlefluid::DerivativeOffsetC< 1 >; /** * @brief Constructor * @param[in] rankOffset the offset of my MPI rank diff --git a/src/coreComponents/physicsSolvers/fluidFlow/kernels/singlePhase/ThermalDirichletFluxComputeKernel.hpp b/src/coreComponents/physicsSolvers/fluidFlow/kernels/singlePhase/ThermalDirichletFluxComputeKernel.hpp index bcd1b1bc880..b8e863d9ac0 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/kernels/singlePhase/ThermalDirichletFluxComputeKernel.hpp +++ b/src/coreComponents/physicsSolvers/fluidFlow/kernels/singlePhase/ThermalDirichletFluxComputeKernel.hpp @@ -200,8 +200,11 @@ class DirichletFluxComputeKernel : public singlePhaseFVMKernels::DirichletFluxCo real64 const & f, real64 const & dF_dP, real64 const & mobility_up, - real64 const & dMobility_dP_up ) + real64 const & dMobility_dP_up, + real64 const & dens_up, + real64 const & dDens_dP_up ) { + GEOS_UNUSED_VAR( dens_up, dDens_dP_up ); // Compute the derivatives of the density wrt temperature diff --git a/src/coreComponents/physicsSolvers/fluidFlow/kernels/singlePhase/reactive/AccumulationKernels.hpp b/src/coreComponents/physicsSolvers/fluidFlow/kernels/singlePhase/reactive/AccumulationKernels.hpp new file mode 100644 index 00000000000..28c3b1cc181 --- /dev/null +++ b/src/coreComponents/physicsSolvers/fluidFlow/kernels/singlePhase/reactive/AccumulationKernels.hpp @@ -0,0 +1,330 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * 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 AccumulationKernels.hpp + */ + +#ifndef GEOS_PHYSICSSOLVERS_FLUIDFLOW_SINGLEPHASEREACTIVE_ACCUMULATIONKERNELS_HPP +#define GEOS_PHYSICSSOLVERS_FLUIDFLOW_SINGLEPHASEREACTIVE_ACCUMULATIONKERNELS_HPP + +#include "common/DataLayouts.hpp" +#include "common/DataTypes.hpp" +#include "constitutive/fluid/reactivefluid/ReactiveSinglePhaseFluid.hpp" +#include "constitutive/fluid/reactivefluid/ReactiveFluidLayouts.hpp" +#include "constitutive/solid/CoupledSolidBase.hpp" +#include "physicsSolvers/fluidFlow/kernels/singlePhase/AccumulationKernels.hpp" +#include "physicsSolvers/fluidFlow/kernels/singlePhase/reactive/KernelLaunchSelectors.hpp" + +namespace geos +{ + +namespace singlePhaseReactiveBaseKernels +{ + +/******************************** AccumulationKernel ********************************/ + +/** + * @class AccumulationKernel + * @brief Define the interface for the assembly kernel in charge of accumulation + */ +template< typename SUBREGION_TYPE, integer NUM_DOF, integer NUM_SPECIES, typename BASE_FLUID_TYPE > +class AccumulationKernel : public singlePhaseBaseKernels::AccumulationKernel< SUBREGION_TYPE, NUM_DOF > +{ + +public: + + using Base = singlePhaseBaseKernels::AccumulationKernel< SUBREGION_TYPE, NUM_DOF >; + using Base::numDof; + using Base::numEqn; + using Base::m_rankOffset; + using Base::m_dofNumber; + using Base::m_elemGhostRank; + using Base::m_localMatrix; + using Base::m_localRhs; + using Base::m_dMass; + + /// Note: Derivative lineup only supports dP & dT, not component terms + using DerivOffset = constitutive::singlefluid::DerivativeOffsetC< 0 >; + + /// Compile time value for the number of primary species + static constexpr integer numSpecies = NUM_SPECIES; + + /** + * @brief Constructor + * @param[in] rankOffset the offset of my MPI rank + * @param[in] dofKey the string key to retrieve the degress of freedom numbers + * @param[in] subRegion the element subregion + * @param[in] fluid the fluid model + * @param[in] solid the solid model + * @param[inout] localMatrix the local CRS matrix + * @param[inout] localRhs the local right-hand side vector + */ + AccumulationKernel( globalIndex const rankOffset, + string const dofKey, + SUBREGION_TYPE const & subRegion, + constitutive::reactivefluid::ReactiveSinglePhaseFluid< BASE_FLUID_TYPE > const & fluid, + constitutive::CoupledSolidBase const & solid, + real64 const & dt, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) + : Base( rankOffset, dofKey, subRegion, localMatrix, localRhs ), + m_dt( dt ), + m_volume( subRegion.getElementVolume() ), + m_deltaVolume( subRegion.template getField< fields::flow::deltaVolume >() ), + m_porosity( solid.getPorosity() ), + m_dPoro_dPres( solid.getDporosity_dPressure() ), + // m_dDensity_dLogPrimaryConc( fluid.dDensity_dLogPrimaryConc() ), + // m_dPoro_dLogPrimaryConc( solid.getDporosity_dLogPrimaryConc() ), + m_primarySpeciesAggregateConcentration( fluid.primarySpeciesAggregateConcentration() ), + // m_dPrimarySpeciesAggregateConcentration_dPres( fluid.dPrimarySpeciesAggregateConcentration_dPres() ), + m_dPrimarySpeciesAggregateConcentration_dLogPrimarySpeciesConcentrations( fluid.dPrimarySpeciesAggregateConcentration_dLogPrimarySpeciesConcentrations() ), + m_primarySpeciesAggregateKineticRate( fluid.aggregateSpeciesRates() ), + // m_dPrimarySpeciesAggregateKineticRate_dPres( fluid.dPrimarySpeciesAggregateKineticRate_dPres() ), + m_dPrimarySpeciesAggregateKineticRate_dLogPrimaryConc( fluid.dAggregateSpeciesRates_dLogPrimarySpeciesConcentrations() ), + m_primarySpeciesAggregateMole_n( subRegion.template getField< fields::flow::primarySpeciesAggregateMole_n >() ) + {} + + /** + * @struct StackVariables + * @brief Kernel variables (dof numbers, jacobian and residual) located on the stack + */ + struct StackVariables : public Base::StackVariables + { +public: + + GEOS_HOST_DEVICE + StackVariables() + : Base::StackVariables() + {} + + using Base::StackVariables::localRow; + using Base::StackVariables::dofIndices; + using Base::StackVariables::localResidual; + using Base::StackVariables::localJacobian; + + // Pore volume information + + /// Pore volume at time n+1 + real64 poreVolume = 0.0; + + /// Derivative of pore volume with respect to pressure + real64 dPoreVolume_dPres = 0.0; + + /// Derivative of pore volume with respect to each primary species concentration + real64 dPoreVolume_dLogPrimaryConc[numSpecies]{}; + + }; + + /** + * @brief Performs the setup phase for the kernel. + * @param[in] ei the element index + * @param[in] stack the stack variables + */ + GEOS_HOST_DEVICE + void setup( localIndex const ei, + StackVariables & stack ) const + { + // initialize the pore volume + stack.poreVolume = ( m_volume[ei] + m_deltaVolume[ei] ) * m_porosity[ei][0]; + stack.dPoreVolume_dPres = ( m_volume[ei] + m_deltaVolume[ei] ) * m_dPoro_dPres[ei][0]; + + Base::setup( ei, stack ); + + // // is - index of the primary species + // for( integer is = 0; is < numSpecies; ++is ) + // { + // stack.dPoreVolume_dLogPrimaryConc[is] = ( m_volume[ei] + m_deltaVolume[ei] ) * m_dPoro_dLogPrimaryConc[ei][is] + // } + } + + /** + * @brief Compute the local accumulation contributions to the residual and Jacobian + * @param[in] ei the element index + * @param[inout] stack the stack variables + * @param[in] kernelOp the function used to customize the kernel + */ + GEOS_HOST_DEVICE + void computeAccumulation( localIndex const ei, + StackVariables & stack ) const + { + // Residual[is] += (primarySpeciesAggregateConcentration[is] * stack.poreVolume - primarySpeciesAggregateMole_n[is]) + // - dt * m_volume * primarySpeciesKineticRate[is] // To Check: what's the unit of the kinetic rate + + Base::computeAccumulation( ei, stack ); + + // Step 1: assemble the derivatives of the total mass equation w.r.t log of primary species concentration + // for( integer is = 0; is < numSpecies; ++is ) + // { + // stack.localJacobian[0][is+numDof-numSpecies] = stack.poreVolume * m_dDensity_dLogPrimaryConc[ei][is] + + // stack.dPoreVolume_dLogPrimaryConc[is] * m_density[ei][0]; + // } + + arraySlice2d< real64 const, + constitutive::reactivefluid::USD_SPECIES_DC - + 2 > dPrimarySpeciesAggregateConcentration_dLogPrimarySpeciesConcentrations = m_dPrimarySpeciesAggregateConcentration_dLogPrimarySpeciesConcentrations[ei][0]; + arraySlice2d< real64 const, constitutive::reactivefluid::USD_SPECIES_DC - 2 > dPrimarySpeciesAggregateKineticRate_dLogPrimaryConc = m_dPrimarySpeciesAggregateKineticRate_dLogPrimaryConc[ei][0]; + + for( integer is = 0; is < numSpecies; ++is ) + { + // Step 2: assemble the accumulation term of the species mass balance equation + // Step 2.1: residual + // Primary species mole amount in pore volume + stack.localResidual[is+numEqn-numSpecies] -= m_primarySpeciesAggregateMole_n[ei][is]; + stack.localResidual[is+numEqn-numSpecies] += m_primarySpeciesAggregateConcentration[ei][0][is] * stack.poreVolume; + + // Reaction term + stack.localResidual[is+numEqn-numSpecies] -= m_dt * ( m_volume[ei] + m_deltaVolume[ei] ) * m_primarySpeciesAggregateKineticRate[ei][0][is]; + + // Step 2.1: jacobian + // Drivative of primary species amount in pore volume wrt pressure + stack.localJacobian[is+numEqn-numSpecies][0] += stack.dPoreVolume_dPres * m_primarySpeciesAggregateConcentration[ei][0][is] + /* + stack.poreVolume * m_dTotalPrimarySpeciesConcentration_dPres[ei][is] */; + // // Derivative of reaction term wrt pressure + // stack.localJacobian[is+numEqn-numSpecies][0] -= m_dt * ( m_volume[ei] + m_deltaVolume[ei] ) * + // m_dPrimarySpeciesTotalKineticRate_dPres[is]; + + // Derivative wrt log of primary species concentration + for( integer js = 0; js < numSpecies; ++js ) + { + stack.localJacobian[is+numEqn-numSpecies][js+numDof-numSpecies] = /* stack.dPoreVolume_dLogPrimaryConc[js] * + m_primarySpeciesAggregateConcentration[ei][0][is] + + */stack.poreVolume * dPrimarySpeciesAggregateConcentration_dLogPrimarySpeciesConcentrations[is][js]; // To + // check + // if + // the + // permutation + // is + // consistent + + stack.localJacobian[is+numEqn-numSpecies][js+numDof-numSpecies] -= m_dt * ( m_volume[ei] + m_deltaVolume[ei] ) * dPrimarySpeciesAggregateKineticRate_dLogPrimaryConc[is][js]; + } + } + } + + /** + * @brief Performs the complete phase for the kernel. + * @param[in] ei the element index + * @param[inout] stack the stack variables + */ + GEOS_HOST_DEVICE + void complete( localIndex const ei, + StackVariables & stack ) const + { + // Step 1: assemble the total mass balance equation + // - the total mass balance equations (i = 0) + Base::complete( ei, stack ); + + // Step 2: assemble the primary species mole amount balance equation + // - the species mole amount balance equations (i = numEqn-numSpecies to i = numEqn-1) + integer const beginRowSpecies = numEqn-numSpecies; + for( integer i = 0; i < numSpecies; ++i ) + { + m_localRhs[stack.localRow + beginRowSpecies + i] += stack.localResidual[beginRowSpecies+i]; + m_localMatrix.template addToRow< serialAtomic >( stack.localRow + beginRowSpecies + i, + stack.dofIndices, + stack.localJacobian[beginRowSpecies + i], + numDof ); + } + } + +protected: + + /// Time step size + real64 const m_dt; + + /// View on the element volumes + arrayView1d< real64 const > const m_volume; + arrayView1d< real64 const > const m_deltaVolume; + + /// Views on the porosity + arrayView2d< real64 const > const m_porosity; + arrayView2d< real64 const > const m_dPoro_dPres; + + // // View on the derivatives of fluid density wrt log of primary species concentration + // arrayView2d< real64 const, compflow::USD_COMP > m_dDensity_dLogPrimaryConc; + + // // View on the derivatives of porosity wrt log of primary species concentration + // arrayView2d< real64 const, compflow::USD_COMP > m_dPoro_dLogPrimaryConc; + + // View on the total concentration of ions that contain the primary species + arrayView3d< real64 const, constitutive::reactivefluid::USD_SPECIES > m_primarySpeciesAggregateConcentration; + + // // View on the derivatives of aggregate concentration for the primary species wrt pressure + // arrayView2d< real64 const, compflow::USD_COMP > m_dPrimarySpeciesAggregateConcentration_dPres; + + // View on the derivatives of total ion concentration for the primary species wrt log of primary species concentration + arrayView4d< real64 const, constitutive::reactivefluid::USD_SPECIES_DC > m_dPrimarySpeciesAggregateConcentration_dLogPrimarySpeciesConcentrations; + + // View on the aggregate kinetic rate of primary species from all reactions + arrayView3d< real64 const, constitutive::reactivefluid::USD_SPECIES > m_primarySpeciesAggregateKineticRate; + + // // View on the derivatives of aggregate kinetic rate of primary species wrt pressure + // arrayView2d< real64 const, compflow::USD_COMP > m_dPrimarySpeciesAggregateKineticRate_dPres; + + // View on the derivatives of aggregate kinetic rate of primary species wrt log of primary species concentration + arrayView4d< real64 const, constitutive::reactivefluid::USD_SPECIES_DC > m_dPrimarySpeciesAggregateKineticRate_dLogPrimaryConc; + + // View on primary species mole amount from previous time step + arrayView2d< real64 const, compflow::USD_COMP > m_primarySpeciesAggregateMole_n; +}; + +/** + * @class AccumulationKernelFactory + */ +class AccumulationKernelFactory +{ +public: + + /** + * @brief Create a new kernel and launch + * @tparam POLICY the policy used in the RAJA kernel + * @param[in] numSpecies the number of primary species + * @param[in] dt time step + * @param[in] rankOffset the offset of my MPI rank + * @param[in] dofKey the string key to retrieve the degress of freedom numbers + * @param[in] subRegion the element subregion + * @param[in] fluid the fluid model + * @param[in] solid the solid model + * @param[inout] localMatrix the local CRS matrix + * @param[inout] localRhs the local right-hand side vector + */ + template< typename POLICY, typename SUBREGION_TYPE, typename BASE_FLUID_TYPE > + static void + createAndLaunch( integer const numSpecies, + real64 const dt, + globalIndex const rankOffset, + string const dofKey, + SUBREGION_TYPE const & subRegion, + constitutive::reactivefluid::ReactiveSinglePhaseFluid< BASE_FLUID_TYPE > const & fluid, + constitutive::CoupledSolidBase const & solid, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) + { + internal::kernelLaunchSelectorCompSwitch( numSpecies, [&] ( auto NS ) + { + integer constexpr NUM_SPECIES = NS(); + integer constexpr NUM_DOF = 1+NS(); + AccumulationKernel< SUBREGION_TYPE, NUM_DOF, NUM_SPECIES, BASE_FLUID_TYPE > kernel( rankOffset, dofKey, subRegion, fluid, solid, dt, localMatrix, localRhs ); + AccumulationKernel< SUBREGION_TYPE, NUM_DOF, NUM_SPECIES, BASE_FLUID_TYPE >::template launch< POLICY >( subRegion.size(), kernel ); + } ); + } +}; + +} // namespace singlePhaseReactiveBaseKernels + +} // namespace geos + +#endif //GEOS_PHYSICSSOLVERS_FLUIDFLOW_SINGLEPHASEREACTIVE_ACCUMULATIONKERNELS_HPP diff --git a/src/coreComponents/physicsSolvers/fluidFlow/kernels/singlePhase/reactive/DirichletFluxComputeKernel.hpp b/src/coreComponents/physicsSolvers/fluidFlow/kernels/singlePhase/reactive/DirichletFluxComputeKernel.hpp new file mode 100644 index 00000000000..80ff9bce9ba --- /dev/null +++ b/src/coreComponents/physicsSolvers/fluidFlow/kernels/singlePhase/reactive/DirichletFluxComputeKernel.hpp @@ -0,0 +1,362 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * 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 DirichletFluxComputeKernel.hpp + */ + +#ifndef GEOS_PHYSICSSOLVERS_FLUIDFLOW_SINGLEPHASEREACTIVE_DIRICHLETFLUXCOMPUTEKERNEL_HPP +#define GEOS_PHYSICSSOLVERS_FLUIDFLOW_SINGLEPHASEREACTIVE_DIRICHLETFLUXCOMPUTEKERNEL_HPP + +#include "constitutive/fluid/reactivefluid/ReactiveSinglePhaseFluid.hpp" +#include "constitutive/fluid/reactivefluid/ReactiveFluidFields.hpp" +#include "constitutive/fluid/reactivefluid/ReactiveFluidSelector.hpp" +#include "physicsSolvers/fluidFlow/kernels/singlePhase/DirichletFluxComputeKernel.hpp" + +namespace geos +{ + +namespace singlePhaseReactiveFVMKernels +{ + +/******************************** DirichletFluxComputeKernel ********************************/ + +/** + * @class DirichletFluxComputeKernel + * @tparam NUM_SPECIES number of fluid primary species + * @tparam NUM_EQN number of equations + * @tparam NUM_DOF number of degrees of freedom + * @tparam FLUIDWRAPPER the type of the fluid wrapper + * @tparam BASE_FLUID_TYPE the type of the base model for the reactive fluid model + * @brief Define the interface for the assembly kernel in charge of Dirichlet face flux terms + */ +template< integer NUM_SPECIES, integer NUM_EQN, integer NUM_DOF, typename FLUIDWRAPPER, typename BASE_FLUID_TYPE > +class DirichletFluxComputeKernel : public singlePhaseFVMKernels::DirichletFluxComputeKernel< NUM_EQN, NUM_DOF, + FLUIDWRAPPER > +{ +public: + /// Compile time value for the number of primary species + static constexpr integer numSpecies = NUM_SPECIES; + + /** + * @brief The type for element-based data. Consists entirely of ArrayView's. + * + * Can be converted from ElementRegionManager::ElementViewConstAccessor + * by calling .toView() or .toViewConst() on an accessor instance + */ + template< typename VIEWTYPE > + using ElementViewConst = ElementRegionManager::ElementViewConst< VIEWTYPE >; + + using AbstractBase = singlePhaseFVMKernels::FluxComputeKernelBase; + using DofNumberAccessor = AbstractBase::DofNumberAccessor; + using SinglePhaseFlowAccessors = AbstractBase::SinglePhaseFlowAccessors; + using SinglePhaseFluidAccessors = AbstractBase::SinglePhaseFluidAccessors; + using PermeabilityAccessors = AbstractBase::PermeabilityAccessors; + + using AbstractBase::m_dt; + using AbstractBase::m_rankOffset; + using AbstractBase::m_dofNumber; + using AbstractBase::m_dens; + using AbstractBase::m_dDens; + + using Base = singlePhaseFVMKernels::DirichletFluxComputeKernel< NUM_EQN, NUM_DOF, + FLUIDWRAPPER >; + using Base::numDof; + using Base::numEqn; + using Base::m_stencilWrapper; + using Base::m_seri; + using Base::m_sesri; + using Base::m_sei; + + using Base::m_facePres; + using Base::m_fluidWrapper; + + using ReactiveSinglePhaseFlowAccessors = + StencilAccessors< fields::flow::logPrimarySpeciesConcentration, + fields::flow::dMobility_dLogPrimaryConc >; + + using ReactiveSinglePhaseFluidAccessors = + StencilMaterialAccessors< constitutive::reactivefluid::ReactiveSinglePhaseFluid< BASE_FLUID_TYPE >, + fields::reactivefluid::primarySpeciesMobileAggregateConcentration, + fields::reactivefluid::dPrimarySpeciesMobileAggregateConcentration_dLogPrimarySpeciesConcentrations >; + + /** + * @brief Constructor for the kernel interface + * @param[in] rankOffset the offset of my MPI rank + * @param[in] faceManager the face manager + * @param[in] stencilWrapper reference to the stencil wrapper + * @param[in] fluidWrapper reference to the fluid wrapper + * @param[in] dofNumberAccessor + * @param[in] singlePhaseFlowAccessors + * @param[in] singlePhaseFluidAccessors + * @param[in] permeabilityAccessors + * @param[in] dt time step size + * @param[inout] localMatrix the local CRS matrix + * @param[inout] localRhs the local right-hand side vector + */ + DirichletFluxComputeKernel( globalIndex const rankOffset, + FaceManager const & faceManager, + BoundaryStencilWrapper const & stencilWrapper, + FLUIDWRAPPER const & fluidWrapper, + DofNumberAccessor const & dofNumberAccessor, + SinglePhaseFlowAccessors const & singlePhaseFlowAccessors, + ReactiveSinglePhaseFlowAccessors const & reactiveSinglePhaseFlowAccessors, + SinglePhaseFluidAccessors const & singlePhaseFluidAccessors, + ReactiveSinglePhaseFluidAccessors const & reactiveSinglePhaseFluidAccessors, + PermeabilityAccessors const & permeabilityAccessors, + arrayView1d< integer const > const & mobilePrimarySpeciesFlags, + real64 const & dt, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) + : Base( rankOffset, + faceManager, + stencilWrapper, + fluidWrapper, + dofNumberAccessor, + singlePhaseFlowAccessors, + singlePhaseFluidAccessors, + permeabilityAccessors, + dt, + localMatrix, + localRhs ), + m_dMob_dLogPrimaryConc( reactiveSinglePhaseFlowAccessors.get( fields::flow::dMobility_dLogPrimaryConc {} ) ), + m_primarySpeciesMobileAggregateConc( reactiveSinglePhaseFluidAccessors.get( fields::reactivefluid::primarySpeciesMobileAggregateConcentration {} ) ), + m_dPrimarySpeciesMobileAggregateConc_dLogPrimaryConc( reactiveSinglePhaseFluidAccessors.get( + fields::reactivefluid::dPrimarySpeciesMobileAggregateConcentration_dLogPrimarySpeciesConcentrations {} ) ), + m_mobilePrimarySpeciesFlags( mobilePrimarySpeciesFlags ) + {} + + /** + * @struct StackVariables + * @brief Kernel variables (dof numbers, jacobian and residual) located on the stack + */ + struct StackVariables : public Base::StackVariables + { +public: + + /** + * @brief Constructor for the stack variables + * @param[in] size size of the stencil for this connection + * @param[in] numElems number of elements for this connection + */ + GEOS_HOST_DEVICE + StackVariables( localIndex const size, localIndex numElems ) + : Base::StackVariables( size, numElems ) + {} + + using Base::StackVariables::transmissibility; + using Base::StackVariables::dofColIndices; + using Base::StackVariables::localFlux; + using Base::StackVariables::localFluxJacobian; + + }; + + /** + * @brief Compute the local Dirichlet face flux contributions to the residual and Jacobian + * @param[in] iconn the connection index + * @param[inout] stack the stack variables + * @param[in] compFluxKernelOp the function used to customize the computation of the component fluxes + */ + template< typename FUNC = NoOpFunc > + GEOS_HOST_DEVICE + void computeFlux( localIndex const iconn, + StackVariables & stack, + FUNC && compFluxKernelOp = NoOpFunc{} ) const + { + Base::computeFlux( iconn, stack, [&] ( localIndex const seri, + localIndex const sesri, + localIndex const sei, + localIndex const kf, + real64 const & fluxVal, + real64 const & dFlux_dP, + real64 const & mobility_up, + real64 const & dMobility_dP_up, + real64 const & dens_up, + real64 const & dDens_dP_up ) + { + GEOS_UNUSED_VAR( kf ); + + real64 speciesFlux[numSpecies]{}; + real64 dSpeciesFlux_dP[numSpecies]{}; + real64 dSpeciesFlux_dLogConc[numSpecies][numSpecies]{}; + + for( integer is = 0; is < numSpecies; ++is ) + { + real64 const aggregateConc_i = m_primarySpeciesMobileAggregateConc[seri][sesri][sei][0][is]; + speciesFlux[is] = aggregateConc_i / dens_up * fluxVal * mobility_up; + + dSpeciesFlux_dP[is] = aggregateConc_i / dens_up * dFlux_dP * mobility_up + + aggregateConc_i / dens_up * fluxVal * dMobility_dP_up + - aggregateConc_i * fluxVal * mobility_up * dDens_dP_up / (dens_up * dens_up); + + for( integer js = 0; js < numSpecies; ++js ) + { + real64 const dAggregateConc_i_dLogConc_j = m_dPrimarySpeciesMobileAggregateConc_dLogPrimaryConc[seri][sesri][sei][0][is][js]; + dSpeciesFlux_dLogConc[is][js] += dAggregateConc_i_dLogConc_j / dens_up * fluxVal * mobility_up; + } + } + + /// populate local flux vector and derivatives + for( integer is = 0; is < numSpecies; ++is ) + { + stack.localFlux[numEqn - numSpecies + is] = m_dt * speciesFlux[is] * m_mobilePrimarySpeciesFlags[is]; + stack.localFluxJacobian[numEqn - numSpecies + is][0] = m_dt * dSpeciesFlux_dP[is] * m_mobilePrimarySpeciesFlags[is]; + + for( integer js = 0; js < numSpecies; ++js ) + { + stack.localFluxJacobian[numEqn - numSpecies + is][numDof - numSpecies + js] += m_dt * dSpeciesFlux_dLogConc[is][js] * m_mobilePrimarySpeciesFlags[is]; + } + } + + compFluxKernelOp( seri, sesri, sei, kf, fluxVal, dFlux_dP, mobility_up, dMobility_dP_up ); + } ); + } + + /** + * @brief Performs the complete phase for the kernel. + * @param[in] iconn the connection index + * @param[inout] stack the stack variables + */ + template< typename FUNC = NoOpFunc > + GEOS_HOST_DEVICE + void complete( localIndex const iconn, + StackVariables & stack, + FUNC && assemblyKernelOp = NoOpFunc{} ) const + { + Base::complete( iconn, stack, [&] ( localIndex const localRow ) + { + for( integer is = 0; is < numSpecies; ++is ) + { + RAJA::atomicAdd( parallelDeviceAtomic{}, &AbstractBase::m_localRhs[localRow + numEqn - numSpecies + is], + stack.localFlux[numEqn - numSpecies + is] ); + + AbstractBase::m_localMatrix.addToRowBinarySearchUnsorted< parallelDeviceAtomic > + ( localRow + numEqn - numSpecies + is, + stack.dofColIndices, + stack.localFluxJacobian[numEqn - numSpecies + is], + numDof ); + } + + assemblyKernelOp( localRow ); + } ); + } + +protected: + + /// Views on derivatives of fluid mobilities + ElementViewConst< arrayView2d< real64 const, compflow::USD_FLUID_DC > > const m_dMob_dLogPrimaryConc; + + /// Views on primary species aggregate concentration + ElementViewConst< arrayView3d< real64 const, constitutive::reactivefluid::USD_SPECIES > > const m_primarySpeciesMobileAggregateConc; + + /// Views on the derivative of primary species mobile aggregate concentration wrt log of primary concentration + ElementViewConst< arrayView4d< real64 const, constitutive::reactivefluid::USD_SPECIES_DC > > const m_dPrimarySpeciesMobileAggregateConc_dLogPrimaryConc; + + /// Array of flags to indicate mobile primary species + arrayView1d< integer const > const m_mobilePrimarySpeciesFlags; + +}; + + +/** + * @class DirichletFluxComputeKernelFactory + */ +class DirichletFluxComputeKernelFactory +{ +public: + + /** + * @brief Create a new kernel and launch + * @tparam POLICY the policy used in the RAJA kernel + * @param[in] numSpecies the number of primary species + * @param[in] mobilePrimarySpeciesFlags the array of flags to indicate mobile primary species + * @param[in] rankOffset the offset of my MPI rank + * @param[in] dofKey string to get the element degrees of freedom numbers + * @param[in] solverName name of the solver (to name accessors) + * @param[in] faceManager reference to the face manager + * @param[in] elemManager reference to the element region manager + * @param[in] stencilWrapper reference to the boundary stencil wrapper + * @param[in] reactiveFluid the single phase reactive fluid constitutive model + * @param[in] dt time step size + * @param[inout] localMatrix the local CRS matrix + * @param[inout] localRhs the local right-hand side vector + */ + template< typename POLICY > + static void + createAndLaunch( integer const numSpecies, + arrayView1d< integer const > const mobilePrimarySpeciesFlags, + globalIndex const rankOffset, + string const & dofKey, + string const & solverName, + FaceManager const & faceManager, + ElementRegionManager const & elemManager, + BoundaryStencilWrapper const & stencilWrapper, + constitutive::reactivefluid::ReactiveCompressibleSinglePhaseFluid & reactiveFluid, + real64 const & dt, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) + { + constitutiveUpdatePassThru( reactiveFluid, [&]( auto & fluid ) + { + using FluidType = TYPEOFREF( fluid ); + typename FluidType::KernelWrapper fluidWrapper = fluid.createKernelWrapper(); + + singlePhaseReactiveBaseKernels::internal::kernelLaunchSelectorCompSwitch( numSpecies, [&]( auto NS ) + { + integer constexpr NUM_SPECIES = NS(); + integer constexpr NUM_DOF = 1+NS(); + integer constexpr NUM_EQN = 1+NS(); + + using kernelType = DirichletFluxComputeKernel< NUM_SPECIES, NUM_EQN, NUM_DOF, typename FluidType::KernelWrapper, + constitutive::CompressibleSinglePhaseFluid >; + + ElementRegionManager::ElementViewAccessor< arrayView1d< globalIndex const > > dofNumberAccessor = + elemManager.constructArrayViewAccessor< globalIndex, 1 >( dofKey ); + + dofNumberAccessor.setName( solverName + "/accessors/" + dofKey ); + + typename kernelType::SinglePhaseFlowAccessors singlePhaseFlowAccessors( elemManager, solverName ); + typename kernelType::ReactiveSinglePhaseFlowAccessors reactiveFlowAccessors( elemManager, solverName ); + typename kernelType::SinglePhaseFluidAccessors singlePhaseFluidAccessors( elemManager, solverName ); + typename kernelType::ReactiveSinglePhaseFluidAccessors reactiveFluidAccessors( elemManager, solverName ); + typename kernelType::PermeabilityAccessors permeabilityAccessors( elemManager, solverName ); + + kernelType kernel( rankOffset, + faceManager, + stencilWrapper, + fluidWrapper, + dofNumberAccessor, + singlePhaseFlowAccessors, + reactiveFlowAccessors, + singlePhaseFluidAccessors, + reactiveFluidAccessors, + permeabilityAccessors, + mobilePrimarySpeciesFlags, + dt, + localMatrix, + localRhs ); + + kernelType::template launch< POLICY >( stencilWrapper.size(), kernel ); + } ); + } ); + } + +}; + +} // namespace singlePhaseReactiveFVMKernels + +} // namespace geos + +#endif //GEOS_PHYSICSSOLVERS_FLUIDFLOW_SINGLEPHASEREACTIVE_DIRICHLETFLUXCOMPUTEKERNEL_HPP diff --git a/src/coreComponents/physicsSolvers/fluidFlow/kernels/singlePhase/reactive/FluidUpdateKernel.hpp b/src/coreComponents/physicsSolvers/fluidFlow/kernels/singlePhase/reactive/FluidUpdateKernel.hpp new file mode 100644 index 00000000000..3e9e79b6a4d --- /dev/null +++ b/src/coreComponents/physicsSolvers/fluidFlow/kernels/singlePhase/reactive/FluidUpdateKernel.hpp @@ -0,0 +1,56 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * 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 FluidUpdateKernel.hpp + */ + +#ifndef GEOS_PHYSICSSOLVERS_FLUIDFLOW_SINGLEPHASE_REACTIVE_FLUIDUPDATEKERNEL_HPP +#define GEOS_PHYSICSSOLVERS_FLUIDFLOW_SINGLEPHASE_REACTIVE_FLUIDUPDATEKERNEL_HPP + +#include "common/DataTypes.hpp" +#include "common/GEOS_RAJA_Interface.hpp" + +namespace geos +{ + +namespace singlePhaseReactiveBaseKernels +{ + +/******************************** FluidUpdateKernel ********************************/ + +struct FluidUpdateKernel +{ + template< typename FLUID_WRAPPER > + static void launch( FLUID_WRAPPER const & fluidWrapper, + arrayView1d< real64 const > const & pres, + arrayView1d< real64 const > const & temp, + arrayView2d< real64 const, compflow::USD_COMP > const logPrimaryConc ) + { + forAll< parallelDevicePolicy<> >( fluidWrapper.numElems(), [=] GEOS_HOST_DEVICE ( localIndex const k ) + { + for( localIndex q = 0; q < fluidWrapper.numGauss(); ++q ) + { + fluidWrapper.update( k, q, pres[k], temp[k], logPrimaryConc[k] ); + } + } ); + } +}; + +} // namespace singlePhaseReactiveBaseKernels + +} // namespace geos + +#endif //GEOS_PHYSICSSOLVERS_FLUIDFLOW_SINGLEPHASE_REACTIVE_FLUIDUPDATEKERNEL_HPP diff --git a/src/coreComponents/physicsSolvers/fluidFlow/kernels/singlePhase/reactive/FluxComputeKernel.hpp b/src/coreComponents/physicsSolvers/fluidFlow/kernels/singlePhase/reactive/FluxComputeKernel.hpp new file mode 100644 index 00000000000..a408c29aa0d --- /dev/null +++ b/src/coreComponents/physicsSolvers/fluidFlow/kernels/singlePhase/reactive/FluxComputeKernel.hpp @@ -0,0 +1,574 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * 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 FluxComputeKernel.hpp + */ + +#ifndef GEOS_PHYSICSSOLVERS_FLUIDFLOW_SINGLEPHASE_REACTIVE_FLUXCOMPUTEKERNEL_HPP +#define GEOS_PHYSICSSOLVERS_FLUIDFLOW_SINGLEPHASE_REACTIVE_FLUXCOMPUTEKERNEL_HPP + +#include "constitutive/diffusion/DiffusionFields.hpp" +#include "constitutive/diffusion/DiffusionBase.hpp" +#include "constitutive/solid/porosity/PorosityBase.hpp" +#include "constitutive/solid/porosity/PorosityFields.hpp" +#include "constitutive/fluid/reactivefluid/ReactiveSinglePhaseFluid.hpp" +#include "constitutive/fluid/reactivefluid/ReactiveFluidFields.hpp" +#include "physicsSolvers/fluidFlow/kernels/singlePhase/FluxComputeKernel.hpp" +#include "physicsSolvers/fluidFlow/kernels/singlePhase/reactive/KernelLaunchSelectors.hpp" + + +namespace geos +{ + +namespace singlePhaseReactiveFVMKernels +{ + +/** + * @class FluxComputeKernel + * @tparam NUM_SPECIES number of fluid primary species + * @tparam NUM_EQN number of equations + * @tparam NUM_DOF number of degrees of freedom + * @tparam STENCILWRAPPER the type of the stencil wrapper + * @tparam BASE_FLUID_TYPE the type of the base model for the reactive fluid model + * @brief Define the interface for the assembly kernel in charge of flux terms + */ +template< integer NUM_SPECIES, integer NUM_EQN, integer NUM_DOF, typename STENCILWRAPPER, typename BASE_FLUID_TYPE > +class FluxComputeKernel : public singlePhaseFVMKernels::FluxComputeKernel< NUM_EQN, NUM_DOF, STENCILWRAPPER > +{ +public: + + /// Compile time value for the number of primary species + static constexpr integer numSpecies = NUM_SPECIES; + + /// Number of flux support points (hard-coded for TFPA) + static constexpr integer numFluxSupportPoints = 2; + + /** + * @brief The type for element-based data. Consists entirely of ArrayView's. + * + * Can be converted from ElementRegionManager::ElementViewConstAccessor + * by calling .toView() or .toViewConst() on an accessor instance + */ + template< typename VIEWTYPE > + using ElementViewConst = ElementRegionManager::ElementViewConst< VIEWTYPE >; + + using AbstractBase = singlePhaseFVMKernels::FluxComputeKernelBase; + using DofNumberAccessor = AbstractBase::DofNumberAccessor; + using SinglePhaseFlowAccessors = AbstractBase::SinglePhaseFlowAccessors; + using SinglePhaseFluidAccessors = AbstractBase::SinglePhaseFluidAccessors; + using PermeabilityAccessors = AbstractBase::PermeabilityAccessors; + + using AbstractBase::m_dt; + using AbstractBase::m_rankOffset; + using AbstractBase::m_dofNumber; + using AbstractBase::m_gravCoef; + using AbstractBase::m_mob; + using AbstractBase::m_dens; + using AbstractBase::m_dDens; + + using Base = singlePhaseFVMKernels::FluxComputeKernel< NUM_EQN, NUM_DOF, STENCILWRAPPER >; + using Base::numDof; + using Base::numEqn; + using Base::maxNumElems; + using Base::maxNumConns; + using Base::maxStencilSize; + using Base::m_stencilWrapper; + using Base::m_seri; + using Base::m_sesri; + using Base::m_sei; + + using ReactiveSinglePhaseFlowAccessors = + StencilAccessors< fields::flow::logPrimarySpeciesConcentration, + fields::flow::dMobility_dLogPrimaryConc >; + + using ReactiveSinglePhaseFluidAccessors = + StencilMaterialAccessors< constitutive::reactivefluid::ReactiveSinglePhaseFluid< BASE_FLUID_TYPE >, + fields::reactivefluid::primarySpeciesMobileAggregateConcentration, + fields::reactivefluid::dPrimarySpeciesMobileAggregateConcentration_dLogPrimarySpeciesConcentrations >; + + using DiffusionAccessors = + StencilMaterialAccessors< constitutive::DiffusionBase, + fields::diffusion::diffusivity, + fields::diffusion::dDiffusivity_dTemperature >; + + using PorosityAccessors = + StencilMaterialAccessors< constitutive::PorosityBase, + fields::porosity::referencePorosity >; + + /** + * @brief Constructor for the kernel interface + * @param[in] rankOffset the offset of my MPI rank + * @param[in] stencilWrapper reference to the stencil wrapper + * @param[in] dofNumberAccessor + * @param[in] singlePhaseFlowAccessors + * @param[in] reactiveSinglePhaseFlowAccessors + * @param[in] singlePhaseFluidAccessors + * @param[in] reactiveSinglePhaseFluidAccessors + * @param[in] permeabilityAccessors + * @param[in] diffusionAccessors + * @param[in] porosityAccessors + * @param[in] hasDiffusion the flag to turn on diffusion calculation + * @param[in] mobilePrimarySpeciesFlags the array of flags to indicate mobile primary species + * @param[in] dt time step size + * @param[inout] localMatrix the local CRS matrix + * @param[inout] localRhs the local right-hand side vector + */ + FluxComputeKernel( globalIndex const rankOffset, + STENCILWRAPPER const & stencilWrapper, + DofNumberAccessor const & dofNumberAccessor, + SinglePhaseFlowAccessors const & singlePhaseFlowAccessors, + ReactiveSinglePhaseFlowAccessors const & reactiveSinglePhaseFlowAccessors, + SinglePhaseFluidAccessors const & singlePhaseFluidAccessors, + ReactiveSinglePhaseFluidAccessors const & reactiveSinglePhaseFluidAccessors, + PermeabilityAccessors const & permeabilityAccessors, + DiffusionAccessors const & diffusionAccessors, + PorosityAccessors const & porosityAccessors, + integer const & hasDiffusion, + arrayView1d< integer const > const & mobilePrimarySpeciesFlags, + real64 const & dt, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) + : Base( rankOffset, + stencilWrapper, + dofNumberAccessor, + singlePhaseFlowAccessors, + singlePhaseFluidAccessors, + permeabilityAccessors, + dt, + localMatrix, + localRhs ), + m_logPrimarySpeciesConc( reactiveSinglePhaseFlowAccessors.get( fields::flow::logPrimarySpeciesConcentration {} ) ), + m_dMob_dLogPrimaryConc( reactiveSinglePhaseFlowAccessors.get( fields::flow::dMobility_dLogPrimaryConc {} ) ), + m_primarySpeciesMobileAggregateConc( reactiveSinglePhaseFluidAccessors.get( fields::reactivefluid::primarySpeciesMobileAggregateConcentration {} ) ), + m_dPrimarySpeciesMobileAggregateConc_dLogPrimaryConc( reactiveSinglePhaseFluidAccessors.get( + fields::reactivefluid::dPrimarySpeciesMobileAggregateConcentration_dLogPrimarySpeciesConcentrations {} ) ), + m_diffusivity( diffusionAccessors.get( fields::diffusion::diffusivity {} ) ), + m_dDiffusivity_dTemp( diffusionAccessors.get( fields::diffusion::dDiffusivity_dTemperature {} ) ), + m_referencePorosity( porosityAccessors.get( fields::porosity::referencePorosity {} ) ), + m_hasDiffusion( hasDiffusion ), + m_mobilePrimarySpeciesFlags( mobilePrimarySpeciesFlags ) + {} + + /** + * @struct StackVariables + * @brief Kernel variables (dof numbers, jacobian and residual) located on the stack + */ + struct StackVariables : public Base::StackVariables + { +public: + + /** + * @brief Constructor for the stack variables + * @param[in] size size of the stencil for this connection + * @param[in] numElems number of elements for this connection + */ + GEOS_HOST_DEVICE + StackVariables( localIndex const size, localIndex numElems ) + : Base::StackVariables( size, numElems ) + {} + + using Base::StackVariables::stencilSize; + using Base::StackVariables::numFluxElems; + using Base::StackVariables::transmissibility; + using Base::StackVariables::dTrans_dPres; + using Base::StackVariables::dofColIndices; + using Base::StackVariables::localFlux; + using Base::StackVariables::localFluxJacobian; + + /// Diffusion transmissibility + real64 diffusionTransmissibility[maxNumConns][numFluxSupportPoints]{}; + /// Derivatives of diffusion transmissibility with respect to temperature + real64 dDiffusionTrans_dT[maxNumConns][numFluxSupportPoints]{}; + + }; + + /** + * @brief Compute the local flux contributions to the residual and Jacobian + * @tparam FUNC the type of the function that can be used to customize the computation of the flux + * @param[in] iconn the connection index + * @param[inout] stack the stack variables + * @param[in] NoOpFunc the function used to customize the computation of the flux + */ + template< typename FUNC = NoOpFunc > + GEOS_HOST_DEVICE + void computeFlux( localIndex const iconn, + StackVariables & stack, + FUNC && kernelOp = NoOpFunc{} ) const + { + using DerivOffset = constitutive::singlefluid::DerivativeOffsetC< 1 >; + // *********************************************** + // First, we call the base computeFlux to compute: + // 1) massFlux and its derivatives, + // 2) speciesFlux and its derivatives + Base::computeFlux( iconn, stack, [&] ( localIndex const (&k)[2], + localIndex const (&seri)[2], + localIndex const (&sesri)[2], + localIndex const (&sei)[2], + localIndex const connectionIndex, + real64 const alpha, + real64 const mobility, + real64 const & potGrad, + real64 const & fluxVal, + real64 const (&dFlux_dP)[2] ) + { + // Step 1: compute the derivatives of the fluid density, potential difference, + // and the massFlux wrt log of primary species concentration (to complete) + real64 dFlux_dLogConc[numFluxSupportPoints][numSpecies]{}; + + GEOS_UNUSED_VAR( dFlux_dLogConc ); // Todo: to add the massFlux derivatives wrt speciesConc + + // Step 2: compute the speciesFlux + real64 speciesFlux[numSpecies]{}; + real64 dSpeciesFlux_dP[numFluxSupportPoints][numSpecies]{}; + real64 dSpeciesFlux_dLogConc[numFluxSupportPoints][numSpecies][numSpecies]{}; + // real64 dSpeciesFlux_dTrans[numSpecies]{}; + + // choose upstream cell + localIndex const k_up = (potGrad >= 0) ? 0 : 1; + + localIndex const er_up = seri[k_up]; + localIndex const esr_up = sesri[k_up]; + localIndex const ei_up = sei[k_up]; + + real64 const fluidDens_up = m_dens[er_up][esr_up][ei_up][0]; + real64 const dDens_dPres = m_dDens[er_up][esr_up][ei_up][0][DerivOffset::dP]; + + // compute species fluxes and derivatives using upstream cell concentration + for( integer is = 0; is < numSpecies; ++is ) + { + real64 const aggregateConc_i = m_primarySpeciesMobileAggregateConc[er_up][esr_up][ei_up][0][is]; + speciesFlux[is] = aggregateConc_i / fluidDens_up * fluxVal; + + for( integer ke = 0; ke < numFluxSupportPoints; ++ke ) + { + dSpeciesFlux_dP[ke][is] += aggregateConc_i / fluidDens_up * dFlux_dP[ke]; + } + + dSpeciesFlux_dP[k_up][is] += -aggregateConc_i * fluxVal * dDens_dPres / (fluidDens_up * fluidDens_up); + + for( integer js = 0; js < numSpecies; ++js ) + { + real64 const dAggregateConc_i_dLogConc_j = m_dPrimarySpeciesMobileAggregateConc_dLogPrimaryConc[er_up][esr_up][ei_up][0][is][js]; + dSpeciesFlux_dLogConc[k_up][is][js] += dAggregateConc_i_dLogConc_j / fluidDens_up * fluxVal; + } + } + + /// populate local flux vector and derivatives + for( integer is = 0; is < numSpecies; ++is ) + { + integer const eqIndex0 = k[0] * numEqn + numEqn - numSpecies + is; + integer const eqIndex1 = k[1] * numEqn + numEqn - numSpecies + is; + + stack.localFlux[eqIndex0] += m_dt * speciesFlux[is] * m_mobilePrimarySpeciesFlags[is]; + stack.localFlux[eqIndex1] -= m_dt * speciesFlux[is] * m_mobilePrimarySpeciesFlags[is]; + + for( integer ke = 0; ke < numFluxSupportPoints; ++ke ) + { + localIndex const localDofIndexPres = k[ke] * numDof; + stack.localFluxJacobian[eqIndex0][localDofIndexPres] += m_dt * dSpeciesFlux_dP[ke][is] * m_mobilePrimarySpeciesFlags[is]; + stack.localFluxJacobian[eqIndex1][localDofIndexPres] -= m_dt * dSpeciesFlux_dP[ke][is] * m_mobilePrimarySpeciesFlags[is]; + + for( integer js = 0; js < numSpecies; ++js ) + { + localIndex const localDofIndexSpecies = localDofIndexPres + js + numDof - numSpecies; + stack.localFluxJacobian[eqIndex0][localDofIndexSpecies] += m_dt * dSpeciesFlux_dLogConc[ke][is][js] * m_mobilePrimarySpeciesFlags[is]; + stack.localFluxJacobian[eqIndex1][localDofIndexSpecies] -= m_dt * dSpeciesFlux_dLogConc[ke][is][js] * m_mobilePrimarySpeciesFlags[is]; + } + } + } + + // Customize the kernel with this lambda + kernelOp( k, seri, sesri, sei, connectionIndex, alpha, mobility, potGrad, fluxVal, dFlux_dP, fluidDens_up ); + } ); + } + + /** + * @brief Compute the local diffusion contributions to the residual and Jacobian + * @tparam FUNC the type of the function that can be used to customize the computation of the flux + * @param[in] iconn the connection index + * @param[inout] stack the stack variables + * @param[in] NoOpFunc the function used to customize the computation of the flux + */ + template< typename FUNC = NoOpFunc > + GEOS_HOST_DEVICE + void computeDiffusion( localIndex const iconn, + StackVariables & stack, + FUNC && kernelOp = NoOpFunc{} ) const + { + if( m_hasDiffusion ) + { + // ***************************************************** + // Computation of the diffusion term in the species flux + + // Step 1: compute the diffusion transmissibilities at this face + m_stencilWrapper.computeWeights( iconn, + m_diffusivity, + m_dDiffusivity_dTemp, + stack.diffusionTransmissibility, + stack.dDiffusionTrans_dT ); + + localIndex k[numFluxSupportPoints]{}; + localIndex connectionIndex = 0; + + for( k[0] = 0; k[0] < stack.numFluxElems; ++k[0] ) + { + for( k[1] = k[0] + 1; k[1] < stack.numFluxElems; ++k[1] ) + { + localIndex const seri[numFluxSupportPoints] = {m_seri( iconn, k[0] ), m_seri( iconn, k[1] )}; + localIndex const sesri[numFluxSupportPoints] = {m_sesri( iconn, k[0] ), m_sesri( iconn, k[1] )}; + localIndex const sei[numFluxSupportPoints] = {m_sei( iconn, k[0] ), m_sei( iconn, k[1] )}; + + // clear working arrays + real64 diffusionFlux[numSpecies]{}; + real64 speciesGrad[numSpecies]{}; + // real64 dDiffusionFlux_dP[numFluxSupportPoints][numSpecies]{}; // Turn on if diffusionFlux is pressure-dependent + real64 dDiffusionFlux_dLogConc[numFluxSupportPoints][numSpecies][numSpecies]{}; + + real64 const diffusionTrans[numFluxSupportPoints] = { stack.diffusionTransmissibility[connectionIndex][0], + stack.diffusionTransmissibility[connectionIndex][1] }; + + //***** calculation of flux ***** + // loop over primary species + for( integer is = 0; is < numSpecies; ++is ) + { + // real64 dSpeciesGrad_i_dP[numFluxSupportPoints]{}; // Turn on if speciesGrad is pressure-dependent + real64 dSpeciesGrad_i_dLogConc[numFluxSupportPoints][numSpecies]{}; + + // Step 2: compute species gradient + for( integer ke = 0; ke < numFluxSupportPoints; ++ke ) + { + localIndex const er = seri[ke]; + localIndex const esr = sesri[ke]; + localIndex const ei = sei[ke]; + + real64 const aggregateConc_i = m_primarySpeciesMobileAggregateConc[er][esr][ei][0][is]; + + speciesGrad[is] += diffusionTrans[ke] * aggregateConc_i; + + for( integer js = 0; js < numSpecies; ++js ) + { + real64 const dAggregateConc_i_dLogConc_j = m_dPrimarySpeciesMobileAggregateConc_dLogPrimaryConc[er][esr][ei][0][is][js]; + + dSpeciesGrad_i_dLogConc[ke][js] += diffusionTrans[ke] * dAggregateConc_i_dLogConc_j; + } + } + + // choose upstream cell for species upwinding + localIndex const k_up = (speciesGrad[is] >= 0) ? 0 : 1; + + localIndex const er_up = seri[k_up]; + localIndex const esr_up = sesri[k_up]; + localIndex const ei_up = sei[k_up]; + + // computation of the upwinded species flux + diffusionFlux[is] += m_referencePorosity[er_up][esr_up][ei_up] * speciesGrad[is]; + + // add contributions of the derivatives of component fractions wrt pressure/component fractions + for( integer ke = 0; ke < numFluxSupportPoints; ke++ ) + { + for( integer js = 0; js < numSpecies; ++js ) + { + dDiffusionFlux_dLogConc[ke][is][js] += m_referencePorosity[er_up][esr_up][ei_up] * dSpeciesGrad_i_dLogConc[ke][js]; + } + } + + // Add the local diffusion flux contribution to the residual and Jacobian + integer const eqIndex0 = k[0] * numEqn + numEqn - numSpecies + is; + integer const eqIndex1 = k[1] * numEqn + numEqn - numSpecies + is; + + stack.localFlux[eqIndex0] += m_dt * diffusionFlux[is] * m_mobilePrimarySpeciesFlags[is]; + stack.localFlux[eqIndex1] -= m_dt * diffusionFlux[is] * m_mobilePrimarySpeciesFlags[is]; + + for( integer ke = 0; ke < numFluxSupportPoints; ++ke ) + { + localIndex const localDofIndexPres = k[ke] * numDof; + // stack.localFluxJacobian[eqIndex0][localDofIndexPres] += m_dt * dDiffusionFlux_dP[ke][is] * m_mobilePrimarySpeciesFlags[is]; + // stack.localFluxJacobian[eqIndex1][localDofIndexPres] -= m_dt * dDiffusionFlux_dP[ke][is] * m_mobilePrimarySpeciesFlags[is]; + + for( integer js = 0; js < numSpecies; ++js ) + { + localIndex const localDofIndexSpecies = localDofIndexPres + js + numDof - numSpecies; + stack.localFluxJacobian[eqIndex0][localDofIndexSpecies] += m_dt * dDiffusionFlux_dLogConc[ke][is][js] * m_mobilePrimarySpeciesFlags[is]; + stack.localFluxJacobian[eqIndex1][localDofIndexSpecies] -= m_dt * dDiffusionFlux_dLogConc[ke][is][js] * m_mobilePrimarySpeciesFlags[is]; + } + } + + // Customize the kernel with this lambda + kernelOp( is, k, seri, sesri, sei, connectionIndex, k_up ); + } // loop over primary species + connectionIndex++; + } + } + } + } + + /** + * @brief Performs the complete phase for the kernel. + * @param[in] iconn the connection index + * @param[inout] stack the stack variables + */ + template< typename FUNC = NoOpFunc > + GEOS_HOST_DEVICE + void complete( localIndex const iconn, + StackVariables & stack, + FUNC && kernelOp = NoOpFunc{} ) const + { + // Call Base::complete to assemble the total mass balance equation + // In the lambda, add contribution to residual and jacobian into the species amount balance equation + Base::complete( iconn, stack, [&] ( integer const i, + localIndex const localRow ) + { + // The no. of fluxes is equal to the no. of equations in m_localRhs and m_localMatrix + for( integer is = 0; is < numSpecies; ++is ) + { + RAJA::atomicAdd( parallelDeviceAtomic{}, &AbstractBase::m_localRhs[localRow + numEqn - numSpecies + is], + stack.localFlux[i * numEqn + numEqn - numSpecies + is] ); + AbstractBase::m_localMatrix.addToRowBinarySearchUnsorted< parallelDeviceAtomic > + ( localRow + numEqn - numSpecies + is, + stack.dofColIndices.data(), + stack.localFluxJacobian[i * numEqn + numEqn - numSpecies + is].dataIfContiguous(), + stack.stencilSize * numDof ); + } + + // call the lambda to assemble additional terms, such as thermal terms + kernelOp( i, localRow ); + } ); + } + + /** + * @brief Performs the kernel launch + * @tparam POLICY the policy used in the RAJA kernels + * @tparam KERNEL_TYPE the kernel type + * @param[in] numConnections the number of connections + * @param[inout] kernelComponent the kernel component providing access to setup/compute/complete functions and stack variables + */ + template< typename POLICY, typename KERNEL_TYPE > + static void + launch( localIndex const numConnections, + KERNEL_TYPE const & kernelComponent ) + { + GEOS_MARK_FUNCTION; + + forAll< POLICY >( numConnections, [=] GEOS_HOST_DEVICE ( localIndex const iconn ) + { + typename KERNEL_TYPE::StackVariables stack( kernelComponent.stencilSize( iconn ), + kernelComponent.numPointsInFlux( iconn ) ); + + kernelComponent.setup( iconn, stack ); + kernelComponent.computeFlux( iconn, stack ); + kernelComponent.computeDiffusion( iconn, stack ); + kernelComponent.complete( iconn, stack ); + } ); + } + +protected: + + /// Views on log of primary species concentration + ElementViewConst< arrayView2d< real64 const, compflow::USD_COMP > > const m_logPrimarySpeciesConc; + + /// Views on derivatives of fluid mobilities + ElementViewConst< arrayView2d< real64 const, compflow::USD_FLUID_DC > > const m_dMob_dLogPrimaryConc; + + /// Views on primary species aggregate concentration + ElementViewConst< arrayView3d< real64 const, constitutive::reactivefluid::USD_SPECIES > > const m_primarySpeciesMobileAggregateConc; + + /// Views on the derivative of primary species mobile aggregate concentration wrt log of primary concentration + ElementViewConst< arrayView4d< real64 const, constitutive::reactivefluid::USD_SPECIES_DC > > const m_dPrimarySpeciesMobileAggregateConc_dLogPrimaryConc; + + /// Views on diffusivity + ElementViewConst< arrayView3d< real64 const > > const m_diffusivity; + ElementViewConst< arrayView3d< real64 const > > const m_dDiffusivity_dTemp; + + /// View on the reference porosity + ElementViewConst< arrayView1d< real64 const > > const m_referencePorosity; + + /// Flag of adding the diffusion term + integer const m_hasDiffusion; + + /// Array of flags to indicate mobile primary species + arrayView1d< integer const > const m_mobilePrimarySpeciesFlags; +}; + +/** + * @class FluxComputeKernelFactory + */ +class FluxComputeKernelFactory +{ +public: + + /** + * @brief Create a new kernel and launch + * @tparam POLICY the policy used in the RAJA kernel + * @tparam STENCILWRAPPER the type of the stencil wrapper + * @param[in] numSpecies the number of primary species + * @param[in] hasDiffusion the flag of adding diffusion term + * @param[in] mobilePrimarySpeciesFlags the array of flags to indicate mobile primary species + * @param[in] rankOffset the offset of my MPI rank + * @param[in] dofKey string to get the element degrees of freedom numbers + * @param[in] solverName name of the solver (to name accessors) + * @param[in] elemManager reference to the element region manager + * @param[in] stencilWrapper reference to the stencil wrapper + * @param[in] dt time step size + * @param[inout] localMatrix the local CRS matrix + * @param[inout] localRhs the local right-hand side vector + */ + template< typename POLICY, typename STENCILWRAPPER > + static void + createAndLaunch( integer const numSpecies, + integer const hasDiffusion, + arrayView1d< integer const > const mobilePrimarySpeciesFlags, + globalIndex const rankOffset, + string const & dofKey, + string const & solverName, + ElementRegionManager const & elemManager, + STENCILWRAPPER const & stencilWrapper, + real64 const & dt, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) + { + singlePhaseReactiveBaseKernels::internal::kernelLaunchSelectorCompSwitch( numSpecies, [&]( auto NS ) + { + integer constexpr NUM_SPECIES = NS(); + integer constexpr NUM_DOF = 1+NS(); + integer constexpr NUM_EQN = 1+NS(); + + ElementRegionManager::ElementViewAccessor< arrayView1d< globalIndex const > > dofNumberAccessor = + elemManager.constructArrayViewAccessor< globalIndex, 1 >( dofKey ); + dofNumberAccessor.setName( solverName + "/accessors/" + dofKey ); + + using KernelType = FluxComputeKernel< NUM_SPECIES, NUM_EQN, NUM_DOF, STENCILWRAPPER, constitutive::CompressibleSinglePhaseFluid >; + typename KernelType::SinglePhaseFlowAccessors flowAccessors( elemManager, solverName ); + typename KernelType::ReactiveSinglePhaseFlowAccessors reactiveFlowAccessors( elemManager, solverName ); + typename KernelType::SinglePhaseFluidAccessors fluidAccessors( elemManager, solverName ); + typename KernelType::ReactiveSinglePhaseFluidAccessors reactiveFluidAccessors( elemManager, solverName ); + typename KernelType::PermeabilityAccessors permAccessors( elemManager, solverName ); + typename KernelType::DiffusionAccessors diffusionAccessors( elemManager, solverName ); + typename KernelType::PorosityAccessors porosityAccessors( elemManager, solverName ); + + KernelType kernel( rankOffset, stencilWrapper, dofNumberAccessor, + flowAccessors, reactiveFlowAccessors, fluidAccessors, reactiveFluidAccessors, + permAccessors, diffusionAccessors, porosityAccessors, hasDiffusion, mobilePrimarySpeciesFlags, + dt, localMatrix, localRhs ); + KernelType::template launch< POLICY >( stencilWrapper.size(), kernel ); + } ); + } +}; + +} // namespace singlePhaseReactiveFVMKernels + +} // namespace geos + +#endif //GEOS_PHYSICSSOLVERS_FLUIDFLOW_SINGLEPHASE_REACTIVE_FLUXCOMPUTEKERNEL_HPP diff --git a/src/coreComponents/physicsSolvers/fluidFlow/kernels/singlePhase/reactive/KernelLaunchSelectors.hpp b/src/coreComponents/physicsSolvers/fluidFlow/kernels/singlePhase/reactive/KernelLaunchSelectors.hpp new file mode 100644 index 00000000000..8964b9b6ef0 --- /dev/null +++ b/src/coreComponents/physicsSolvers/fluidFlow/kernels/singlePhase/reactive/KernelLaunchSelectors.hpp @@ -0,0 +1,77 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * 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 KernelLaunchSelector.hpp + */ + +#ifndef GEOS_PHYSICSSOLVERS_FLUIDFLOW_SINGLEPHASEREACTIVE_KERNELLAUNCHSELECTOR_HPP +#define GEOS_PHYSICSSOLVERS_FLUIDFLOW_SINGLEPHASEREACTIVE_KERNELLAUNCHSELECTOR_HPP + +#include "physicsSolvers/KernelLaunchSelectors.hpp" +#include "codingUtilities/Utilities.hpp" +#include "common/DataLayouts.hpp" +#include "common/DataTypes.hpp" +#include "common/GEOS_RAJA_Interface.hpp" + +namespace geos +{ + +namespace singlePhaseReactiveBaseKernels +{ + +/******************************** Kernel launch machinery ********************************/ + +namespace internal +{ + +template< typename T, typename LAMBDA > +void kernelLaunchSelectorCompSwitch( T value, LAMBDA && lambda ) +{ + static_assert( std::is_integral< T >::value, "kernelLaunchSelectorCompSwitch: type should be integral" ); + + switch( value ) + { + case 1: + { lambda( std::integral_constant< T, 1 >() ); return; } + case 2: + { lambda( std::integral_constant< T, 2 >() ); return; } + case 3: + { lambda( std::integral_constant< T, 3 >() ); return; } + case 4: + { lambda( std::integral_constant< T, 4 >() ); return; } + case 5: + { lambda( std::integral_constant< T, 5 >() ); return; } + case 6: + { lambda( std::integral_constant< T, 6 >() ); return; } + case 7: + { lambda( std::integral_constant< T, 7 >() ); return; } + case 8: + { lambda( std::integral_constant< T, 8 >() ); return; } + case 9: + { lambda( std::integral_constant< T, 9 >() ); return; } + default: + { GEOS_ERROR( "Unsupported number of primary species: " << value ); } + } +} + +} // namespace internal + +} // namespace singlePhaseReactiveBaseKernels + +} // namespace geos + + +#endif //GEOS_PHYSICSSOLVERS_FLUIDFLOW_SINGLEPHASEREACTIVE_KERNELLAUNCHSELECTOR_HPP diff --git a/src/coreComponents/physicsSolvers/fluidFlow/kernels/singlePhase/reactive/ReactionUpdateKernel.hpp b/src/coreComponents/physicsSolvers/fluidFlow/kernels/singlePhase/reactive/ReactionUpdateKernel.hpp new file mode 100644 index 00000000000..79496d888fe --- /dev/null +++ b/src/coreComponents/physicsSolvers/fluidFlow/kernels/singlePhase/reactive/ReactionUpdateKernel.hpp @@ -0,0 +1,100 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * 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 ReactionUpdateKernel.hpp + */ + +#ifndef GEOS_PHYSICSSOLVERS_FLUIDFLOW_SINGLEPHASE_REACTIVE_REACTIONUPDATEKERNEL_HPP +#define GEOS_PHYSICSSOLVERS_FLUIDFLOW_SINGLEPHASE_REACTIVE_REACTIONUPDATEKERNEL_HPP + +#include "common/DataTypes.hpp" +#include "common/GEOS_RAJA_Interface.hpp" + +#include "constitutive/fluid/reactivefluid/ReactiveFluidLayouts.hpp" + +namespace geos +{ + +namespace singlePhaseReactiveBaseKernels +{ + +/******************************** EquilibriumReactionUpdateKernel ********************************/ + +struct EquilibriumReactionUpdateKernel +{ + + template< typename REACTION_WRAPPER_TYPE > + static void helper( REACTION_WRAPPER_TYPE const & reactionWrapper, + arrayView1d< real64 const > const & pres, + arrayView1d< real64 const > const & temp, + arrayView2d< real64, compflow::USD_COMP > const logPrimaryConc ) + { + forAll< parallelDevicePolicy<> >( reactionWrapper.numElems(), [=] GEOS_HOST_DEVICE ( localIndex const k ) + { + reactionWrapper.updateEquilibriumReaction( k, pres[k], temp[k], logPrimaryConc[k] ); + } ); + } + + template< typename REACTIVE_FLUID > + static void launch( REACTIVE_FLUID const & fluid, + arrayView1d< real64 const > const & pres, + arrayView1d< real64 const > const & temp, + arrayView2d< real64, compflow::USD_COMP > const logPrimaryConc ) + { + std::visit( [&]( auto const & reactionWrapper ) + { + helper( reactionWrapper, pres, temp, logPrimaryConc ); + }, fluid.createReactionKernelWrapper()); + } +}; + +/******************************** MixedSystemReactionUpdateKernel ********************************/ + +struct MixedSystemReactionUpdateKernel +{ + + template< typename REACTION_WRAPPER_TYPE > + static void helper( REACTION_WRAPPER_TYPE const & reactionWrapper, + arrayView1d< real64 const > const & pres, + arrayView1d< real64 const > const & temp, + arrayView2d< real64 const, compflow::USD_COMP > const logPrimaryConc, + arrayView2d< real64 const, compflow::USD_COMP > const surfaceArea ) + { + forAll< parallelDevicePolicy<> >( reactionWrapper.numElems(), [=] GEOS_HOST_DEVICE ( localIndex const k ) + { + reactionWrapper.updateMixedReactionSystem( k, pres[k], temp[k], logPrimaryConc[k], surfaceArea[k] ); + } ); + } + + template< typename REACTIVE_FLUID > + static void launch( REACTIVE_FLUID const & fluid, + arrayView1d< real64 const > const & pres, + arrayView1d< real64 const > const & temp, + arrayView2d< real64 const, compflow::USD_COMP > const logPrimaryConc, + arrayView2d< real64 const, compflow::USD_COMP > const surfaceArea ) + { + std::visit( [&]( auto const & reactionWrapper ) + { + helper( reactionWrapper, pres, temp, logPrimaryConc, surfaceArea ); + }, fluid.createReactionKernelWrapper()); + } +}; + +} // namespace singlePhaseReactiveBaseKernels + +} // namespace geos + +#endif //GEOS_PHYSICSSOLVERS_FLUIDFLOW_SINGLEPHASE_REACTIVE_FLUIDUPDATEKERNEL_HPP diff --git a/src/coreComponents/physicsSolvers/fluidFlow/kernels/singlePhase/reactive/ResidualNormKernel.hpp b/src/coreComponents/physicsSolvers/fluidFlow/kernels/singlePhase/reactive/ResidualNormKernel.hpp new file mode 100644 index 00000000000..c9fc35acce1 --- /dev/null +++ b/src/coreComponents/physicsSolvers/fluidFlow/kernels/singlePhase/reactive/ResidualNormKernel.hpp @@ -0,0 +1,331 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * 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 ResidualNormKernel.hpp + */ + +#ifndef GEOS_PHYSICSSOLVERS_FLUIDFLOW_SINGLEPHASE_REACTIVE_RESIDUALNORMKERNEL_HPP +#define GEOS_PHYSICSSOLVERS_FLUIDFLOW_SINGLEPHASE_REACTIVE_RESIDUALNORMKERNEL_HPP + +#include "physicsSolvers/PhysicsSolverBaseKernels.hpp" +#include "physicsSolvers/fluidFlow/SinglePhaseReactiveTransportFields.hpp" + +namespace geos +{ + +namespace singlePhaseReactiveBaseKernels +{ + +/******************************** ResidualNormKernel ********************************/ + +/** + * @class IsothermalResidualNormKernel + */ +class IsothermalResidualNormKernel : public physicsSolverBaseKernels::ResidualNormKernelBase< 2 > +{ +public: + + using Base = physicsSolverBaseKernels::ResidualNormKernelBase< 2 >; + using Base::m_minNormalizer; + using Base::m_rankOffset; + using Base::m_localResidual; + using Base::m_dofNumber; + + IsothermalResidualNormKernel( globalIndex const rankOffset, + arrayView1d< real64 const > const & localResidual, + arrayView1d< globalIndex const > const & dofNumber, + arrayView1d< localIndex const > const & ghostRank, + integer const numPrimarySpecies, + ElementSubRegionBase const & subRegion, + real64 const minNormalizer ) + : Base( rankOffset, + localResidual, + dofNumber, + ghostRank, + minNormalizer ), + m_numPrimarySpecies( numPrimarySpecies ), + m_mass_n( subRegion.template getField< fields::flow::mass_n >() ), + m_primarySpeciesAggregateMole_n( subRegion.getField< fields::flow::primarySpeciesAggregateMole_n >() ) + {} + + GEOS_HOST_DEVICE + virtual void computeLinf( localIndex const ei, + LinfStackVariables & stack ) const override + { + real64 const totalMassNormalizer = LvArray::math::max( m_minNormalizer, m_mass_n[ei] ); + + // step 1: total mass residuals + real64 const valMass = LvArray::math::abs( m_localResidual[stack.localRow] ) / totalMassNormalizer; + if( valMass > stack.localValue[0] ) + { + stack.localValue[0] = valMass; + } + + // step 2: species amount residuals + for( integer idof = 0; idof < m_numPrimarySpecies; ++idof ) + { + real64 const speciesAmountNormalizer = LvArray::math::max( m_minNormalizer, m_primarySpeciesAggregateMole_n[ei][idof] ); + real64 const valAmount = LvArray::math::abs( m_localResidual[stack.localRow + idof + 1] ) / speciesAmountNormalizer; + if( valAmount > stack.localValue[1] ) + { + stack.localValue[1] = valAmount; + } + } + } + + GEOS_HOST_DEVICE + virtual void computeL2( localIndex const ei, + L2StackVariables & stack ) const override + { + real64 const totalMassNormalizer = LvArray::math::max( m_minNormalizer, m_mass_n[ei] ); + + // step 1: total mass residuals + stack.localValue[0] += m_localResidual[stack.localRow] * m_localResidual[stack.localRow]; + stack.localNormalizer[0] += totalMassNormalizer; + + // step 2: species amount residuals + for( integer idof = 0; idof < m_numPrimarySpecies; ++idof ) + { + real64 const speciesAmountNormalizer = LvArray::math::max( m_minNormalizer, m_primarySpeciesAggregateMole_n[ei][idof] ); + + stack.localValue[1] += m_localResidual[stack.localRow + idof + 1] * m_localResidual[stack.localRow + idof + 1]; + stack.localNormalizer[1] += speciesAmountNormalizer; + } + } + + +protected: + + /// Number of primary species + integer const m_numPrimarySpecies; + + /// View on mass at the previous converged time step + arrayView1d< real64 const > const m_mass_n; + + // View on primary species aggregate amount (moles) from previous time step + arrayView2d< real64 const, compflow::USD_COMP > m_primarySpeciesAggregateMole_n; + +}; + +/** + * @class ThermalResidualNormKernel + */ +class ThermalResidualNormKernel : public physicsSolverBaseKernels::ResidualNormKernelBase< 3 > +{ +public: + + using Base = physicsSolverBaseKernels::ResidualNormKernelBase< 3 >; + using Base::m_minNormalizer; + using Base::m_rankOffset; + using Base::m_localResidual; + using Base::m_dofNumber; + + ThermalResidualNormKernel( globalIndex const rankOffset, + arrayView1d< real64 const > const & localResidual, + arrayView1d< globalIndex const > const & dofNumber, + arrayView1d< localIndex const > const & ghostRank, + integer const numPrimarySpecies, + ElementSubRegionBase const & subRegion, + real64 const minNormalizer ) + : Base( rankOffset, + localResidual, + dofNumber, + ghostRank, + minNormalizer ), + m_numPrimarySpecies( numPrimarySpecies ), + m_mass_n( subRegion.template getField< fields::flow::mass_n >() ), + m_primarySpeciesAggregateMole_n( subRegion.getField< fields::flow::primarySpeciesAggregateMole_n >() ), + m_energy_n( subRegion.template getField< fields::flow::energy_n >() ) + {} + + GEOS_HOST_DEVICE + void computeMassEnergyNormalizers( localIndex const ei, + real64 & totalMassNormalizer, + real64 & energyNormalizer ) const + { + totalMassNormalizer = LvArray::math::max( m_minNormalizer, m_mass_n[ei] ); + energyNormalizer = LvArray::math::max( m_minNormalizer, LvArray::math::abs( m_energy_n[ei] ) ); // energy can be negative + } + + GEOS_HOST_DEVICE + virtual void computeLinf( localIndex const ei, + LinfStackVariables & stack ) const override + { + real64 totalMassNormalizer = 0.0, energyNormalizer = 0.0; + computeMassEnergyNormalizers( ei, totalMassNormalizer, energyNormalizer ); + + // step 1: mass residual + + real64 const valMass = LvArray::math::abs( m_localResidual[stack.localRow] ) / totalMassNormalizer; + if( valMass > stack.localValue[0] ) + { + stack.localValue[0] = valMass; + } + + // step 2: energy residual + real64 const valEnergy = LvArray::math::abs( m_localResidual[stack.localRow + 1] ) / energyNormalizer; + if( valEnergy > stack.localValue[1] ) + { + stack.localValue[1] = valEnergy; + } + + // step 3: species amount residuals + for( integer idof = 0; idof < m_numPrimarySpecies; ++idof ) + { + real64 const speciesAmountNormalizer = LvArray::math::max( m_minNormalizer, m_primarySpeciesAggregateMole_n[ei][idof] ); + real64 const valAmount = LvArray::math::abs( m_localResidual[stack.localRow + idof + 2] ) / speciesAmountNormalizer; + if( valAmount > stack.localValue[2] ) + { + stack.localValue[2] = valAmount; + } + } + } + + GEOS_HOST_DEVICE + virtual void computeL2( localIndex const ei, + L2StackVariables & stack ) const override + { + real64 totalMassNormalizer = 0.0, energyNormalizer = 0.0; + computeMassEnergyNormalizers( ei, totalMassNormalizer, energyNormalizer ); + + // step 1: mass residual + + stack.localValue[0] += m_localResidual[stack.localRow] * m_localResidual[stack.localRow]; + stack.localNormalizer[0] += totalMassNormalizer; + + // step 2: energy residual + + stack.localValue[1] += m_localResidual[stack.localRow + 1] * m_localResidual[stack.localRow + 1]; + stack.localNormalizer[1] += energyNormalizer; + + // step 3: species amount residuals + for( integer idof = 0; idof < m_numPrimarySpecies; ++idof ) + { + real64 const speciesAmountNormalizer = LvArray::math::max( m_minNormalizer, m_primarySpeciesAggregateMole_n[ei][idof] ); + + stack.localValue[2] += m_localResidual[stack.localRow + idof + 2] * m_localResidual[stack.localRow + idof + 2]; + stack.localNormalizer[2] += speciesAmountNormalizer; + } + } + + +protected: + + /// Number of primary species + integer const m_numPrimarySpecies; + + /// View on mass at the previous converged time step + arrayView1d< real64 const > const m_mass_n; + + // View on primary species aggregate amount (moles) from previous time step + arrayView2d< real64 const, compflow::USD_COMP > m_primarySpeciesAggregateMole_n; + + /// View on energy at the previous converged time step + arrayView1d< real64 const > const m_energy_n; + +}; + +/** + * @class ResidualNormKernelFactory + */ +class ResidualNormKernelFactory +{ +public: + + /** + * @brief Create a new kernel and launch + * @tparam POLICY the policy used in the RAJA kernel + * @param[in] normType the type of norm used (Linf or L2) + * @param[in] numPrimarySpecies the number of primary species + * @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 + * @param[in] subRegion the element subregion + * @param[out] residualNorm the residual norm on the subRegion + * @param[out] residualNormalizer the residual normalizer on the subRegion + */ + template< typename POLICY > + static void + createAndLaunch( physicsSolverBaseKernels::NormType const normType, + integer const numPrimarySpecies, + globalIndex const rankOffset, + string const dofKey, + arrayView1d< real64 const > const & localResidual, + ElementSubRegionBase const & subRegion, + real64 const minNormalizer, + real64 (& residualNorm)[2], + real64 (& residualNormalizer)[2] ) + { + arrayView1d< globalIndex const > const dofNumber = subRegion.getReference< array1d< globalIndex > >( dofKey ); + arrayView1d< integer const > const ghostRank = subRegion.ghostRank(); + + IsothermalResidualNormKernel kernel( rankOffset, localResidual, dofNumber, ghostRank, numPrimarySpecies, subRegion, minNormalizer ); + if( normType == physicsSolverBaseKernels::NormType::Linf ) + { + IsothermalResidualNormKernel::launchLinf< POLICY >( subRegion.size(), kernel, residualNorm ); + } + else // L2 norm + { + IsothermalResidualNormKernel::launchL2< POLICY >( subRegion.size(), kernel, residualNorm, residualNormalizer ); + } + } + + /** + * @brief Create a new kernel and launch (thermal version) + * @tparam POLICY the policy used in the RAJA kernel + * @param[in] normType the type of norm used (Linf or L2) + * @param[in] numPrimarySpecies the number of primary species + * @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 + * @param[in] subRegion the element subregion + * @param[out] residualNorm the residual norm on the subRegion + * @param[out] residualNormalizer the residual normalizer on the subRegion + */ + template< typename POLICY > + static void + createAndLaunch( physicsSolverBaseKernels::NormType const normType, + integer const numPrimarySpecies, + globalIndex const rankOffset, + string const & dofKey, + arrayView1d< real64 const > const & localResidual, + ElementSubRegionBase const & subRegion, + real64 const minNormalizer, + real64 (& residualNorm)[3], + real64 (& residualNormalizer)[3] ) + { + arrayView1d< globalIndex const > const dofNumber = subRegion.getReference< array1d< globalIndex > >( dofKey ); + arrayView1d< integer const > const ghostRank = subRegion.ghostRank(); + + ThermalResidualNormKernel kernel( rankOffset, localResidual, dofNumber, ghostRank, numPrimarySpecies, subRegion, minNormalizer ); + if( normType == physicsSolverBaseKernels::NormType::Linf ) + { + ThermalResidualNormKernel::launchLinf< POLICY >( subRegion.size(), kernel, residualNorm ); + } + else // L2 norm + { + ThermalResidualNormKernel::launchL2< POLICY >( subRegion.size(), kernel, residualNorm, residualNormalizer ); + } + } + +}; + +} // namespace singlePhaseReactiveBaseKernels + +} // namespace geos + + +#endif //GEOS_PHYSICSSOLVERS_FLUIDFLOW_SINGLEPHASE_REACTIVE_RESIDUALNORMKERNEL_HPP diff --git a/src/coreComponents/physicsSolvers/fluidFlow/kernels/singlePhase/reactive/SourceFluxComputeKernel.hpp b/src/coreComponents/physicsSolvers/fluidFlow/kernels/singlePhase/reactive/SourceFluxComputeKernel.hpp new file mode 100644 index 00000000000..1ef58f942aa --- /dev/null +++ b/src/coreComponents/physicsSolvers/fluidFlow/kernels/singlePhase/reactive/SourceFluxComputeKernel.hpp @@ -0,0 +1,310 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * 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 SourceFluxComputeKernel.hpp + */ + +#ifndef GEOS_PHYSICSSOLVERS_FLUIDFLOW_SINGLEPHASEREACTIVE_SOURCEFLUXCOMPUTEKERNELS_HPP +#define GEOS_PHYSICSSOLVERS_FLUIDFLOW_SINGLEPHASEREACTIVE_SOURCEFLUXCOMPUTEKERNELS_HPP + +#include "common/DataTypes.hpp" +#include "common/GEOS_RAJA_Interface.hpp" +#include "constitutive/fluid/reactivefluid/ReactiveSinglePhaseFluid.hpp" +#include "constitutive/fluid/reactivefluid/ReactiveFluidLayouts.hpp" +#include "constitutive/fluid/singlefluid/SingleFluidBase.hpp" +#include "constitutive/fluid/singlefluid/SingleFluidUtils.hpp" +#include "codingUtilities/Utilities.hpp" + +#include "physicsSolvers/fluidFlow/kernels/singlePhase/reactive/KernelLaunchSelectors.hpp" + +namespace geos +{ + +namespace singlePhaseReactiveBaseKernels +{ + +/******************************** SourceFluxComputeKernel ********************************/ + +/** + * @class SourceFluxComputeKernel + * @brief Define the interface for the assembly kernel in charge of source flux + */ +template< integer NUM_DOF, integer NUM_SPECIES, typename BASE_FLUID_TYPE > +class SourceFluxComputeKernel +{ + +public: + + /// Compute time value for the number of degrees of freedom + static constexpr integer numDof = NUM_DOF; + + /// Compute time value for the number of equations + static constexpr integer numEqn = NUM_DOF; + + /// Compile time value for the number of primary species + static constexpr integer numSpecies = NUM_SPECIES; + + using DerivOffset = constitutive::singlefluid::DerivativeOffsetC< 1 >; + + SourceFluxComputeKernel( globalIndex const rankOffset, + arrayView1d< globalIndex const > const dofNumber, + arrayView1d< integer const > const elemGhostRank, + arrayView1d< real64 const > const rhsContributionArrayView, + real64 const sizeScalingFactor, + constitutive::reactivefluid::ReactiveSinglePhaseFluid< BASE_FLUID_TYPE > const & fluid, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs, + RAJA::ReduceSum< parallelDeviceReduce, real64 > massProd ) + : + m_rankOffset( rankOffset ), + m_dofNumber( dofNumber ), + m_elemGhostRank( elemGhostRank ), + m_rhsContributionArrayView( rhsContributionArrayView ), + m_sizeScalingFactor( sizeScalingFactor ), + m_primarySpeciesAggregateConcentration( fluid.primarySpeciesAggregateConcentration() ), + m_dPrimarySpeciesAggregateConcentration_dLogPrimarySpeciesConcentrations( fluid.dPrimarySpeciesAggregateConcentration_dLogPrimarySpeciesConcentrations() ), + m_density( fluid.density() ), + m_dDensity( fluid.dDensity() ), + m_localMatrix( localMatrix ), + m_localRhs( localRhs ), + m_massProd( massProd ) + {} + + /** + * @struct StackVariables + * @brief Kernel variables (dof numbers, jacobian and residual) located on the stack + */ + struct StackVariables + { +public: + + /// Index of the local row corresponding to this element + localIndex massRowIndex = -1; + + /// Index of the matrix row/column corresponding to the dof in this element + globalIndex dofIndices[numDof]{}; + + /// Storage for the element local residual vector for species rows + real64 localSpeciesRhs[numSpecies]{}; + + /// Storage for the element local Jacobian matrix for species rows + real64 localSpeciesJacobian[numSpecies][numDof]{}; + + real64 totalInflowMass = 0.0; + + }; + + /** + * @brief Getter for the ghost rank of an element + * @param[in] ei the element index + * @return the ghost rank of the element + */ + GEOS_HOST_DEVICE + integer elemGhostRank( localIndex const ei ) const + { return m_elemGhostRank( ei ); } + + /** + * @brief Performs the setup phase for the kernel. + * @param[in] ei the element index + * @param[in] a the target set index + * @param[in] stack the stack variables + */ + GEOS_HOST_DEVICE + void setup( localIndex const ei, + localIndex const a, + StackVariables & stack ) const + { + // set row index and degrees of freedom indices for this element + stack.massRowIndex = m_dofNumber[ei] - m_rankOffset; + for( integer idof = 0; idof < numDof; ++idof ) + { + stack.dofIndices[idof] = m_dofNumber[ei] + idof - m_rankOffset; + } + + stack.totalInflowMass = m_rhsContributionArrayView[a]; + } + + /** + * @brief Compute the local source flux contributions to the residual and Jacobian + * @param[in] ei the element index + * @param[inout] stack the stack variables + * @param[in] kernelOp the function used to customize the kernel + */ + GEOS_HOST_DEVICE + void computeSourceFlux( localIndex const ei, + StackVariables & stack ) const + { + real64 const scaledInflowMass = stack.totalInflowMass / m_sizeScalingFactor; + + for( integer i = 0; i < numSpecies; ++i ) + { + stack.localSpeciesRhs[i] += m_primarySpeciesAggregateConcentration[ei][0][i] / m_density[ei][0] * scaledInflowMass; + stack.localSpeciesJacobian[i][0] += -m_primarySpeciesAggregateConcentration[ei][0][i] * m_dDensity[ei][0][DerivOffset::dP] / (m_density[ei][0] * m_density[ei][0]) * scaledInflowMass; + + for( integer j = 0; j < numSpecies; ++j ) + { + stack.localSpeciesJacobian[i][j+numDof-numSpecies] += m_dPrimarySpeciesAggregateConcentration_dLogPrimarySpeciesConcentrations[ei][0][i][j] / m_density[ei][0] * scaledInflowMass; + } + } + } + + /** + * @brief Performs the complete phase for the kernel. + * @param[in] ei the element index + * @param[inout] stack the stack variables + */ + GEOS_HOST_DEVICE + void complete( localIndex const GEOS_UNUSED_PARAM( ei ), + StackVariables & stack ) const + { + m_massProd += stack.totalInflowMass / m_sizeScalingFactor; + m_localRhs[stack.massRowIndex] += stack.totalInflowMass / m_sizeScalingFactor; + + if( stack.totalInflowMass > 0.0 ) + { + for( integer i = 0; i < numSpecies; ++i ) + { + globalIndex const speciesRowBeginIndex = stack.massRowIndex + numEqn - numSpecies; + m_localRhs[speciesRowBeginIndex + i] += stack.localSpeciesRhs[i]; + + // add contribution to global residual and jacobian (no need for atomics here) + m_localMatrix.template addToRow< serialAtomic >( speciesRowBeginIndex+i, + stack.dofIndices, + stack.localSpeciesJacobian[i], + numDof ); + } + } + } + + /** + * @brief Performs the kernel launch + * @tparam POLICY the policy used in the RAJA kernels + * @tparam KERNEL_TYPE the kernel type + * @param[in] targetSet the target set for source flux + * @param[inout] kernelComponent the kernel component providing access to setup/compute/complete functions and stack variables + */ + template< typename POLICY, typename KERNEL_TYPE > + static void + launch( SortedArrayView< localIndex const > const targetSet, + KERNEL_TYPE const & kernelComponent ) + { + GEOS_MARK_FUNCTION; + + forAll< POLICY >( targetSet.size(), [=] GEOS_HOST_DEVICE ( localIndex const a ) + { + // we need to filter out ghosts here, because targetSet may contain them + localIndex const ei = targetSet[a]; + + if( kernelComponent.elemGhostRank( ei ) >= 0 ) + { + return; + } + + typename KERNEL_TYPE::StackVariables stack; + + kernelComponent.setup( ei, a, stack ); + kernelComponent.computeSourceFlux( ei, stack ); + kernelComponent.complete( ei, stack ); + } ); + } + +protected: + + /// Offset for my MPI rank + globalIndex const m_rankOffset; + + /// View on the dof numbers + arrayView1d< globalIndex const > const m_dofNumber; + + /// View on the ghost ranks + arrayView1d< integer const > const m_elemGhostRank; + + /// View on the rhs contribution + arrayView1d< real64 const > const m_rhsContributionArrayView; + /// size scaling factor + real64 const m_sizeScalingFactor; + + // View on the total concentration of ions that contain the primary species + arrayView3d< real64 const, constitutive::reactivefluid::USD_SPECIES > const m_primarySpeciesAggregateConcentration; + // View on the derivatives of total ion concentration for the primary species wrt log of primary species concentration + arrayView4d< real64 const, constitutive::reactivefluid::USD_SPECIES_DC > const m_dPrimarySpeciesAggregateConcentration_dLogPrimarySpeciesConcentrations; + + // View on the fluid density + arrayView2d< real64 const, constitutive::singlefluid::USD_FLUID > const m_density; + // View on the derivatives of fluid density + arrayView3d< real64 const, constitutive::singlefluid::USD_FLUID_DER > const m_dDensity; + + /// View on the local CRS matrix + CRSMatrixView< real64, globalIndex const > const m_localMatrix; + /// View on the local RHS + arrayView1d< real64 > const m_localRhs; + + RAJA::ReduceSum< parallelDeviceReduce, real64 > m_massProd; + +}; + +/** + * @class SourceFluxComputeKernelFactory + */ +class SourceFluxComputeKernelFactory +{ +public: + + /** + * @brief Create a new kernel and launch + * @tparam POLICY the policy used in the RAJA kernel + * @param[in] numSpecies the number of primary species + * @param[in] rankOffset the offset of my MPI rank + * @param[in] dofNumber the degress of freedom numbers + * @param[in] elemGhostRank the array of element ghost rank + * @param[in] targetSet the target set array + * @param[in] rhsContributionArrayView the rhs contribution array + * @param[in] sizeScalingFactor the size scaling factor + * @param[in] fluid the fluid model + * @param[inout] localMatrix the local CRS matrix + * @param[inout] localRhs the local right-hand side vector + * @param[inout] massProd the total mass produced + */ + template< typename POLICY, typename BASE_FLUID_TYPE > + static void + createAndLaunch( integer const numSpecies, + globalIndex const rankOffset, + arrayView1d< globalIndex const > const dofNumber, + arrayView1d< integer const > const elemGhostRank, + SortedArrayView< localIndex const > const targetSet, + arrayView1d< real64 const > const rhsContributionArrayView, + real64 const sizeScalingFactor, + constitutive::reactivefluid::ReactiveSinglePhaseFluid< BASE_FLUID_TYPE > const & fluid, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs, + RAJA::ReduceSum< parallelDeviceReduce, real64 > massProd ) + { + internal::kernelLaunchSelectorCompSwitch( numSpecies, [&] ( auto NS ) + { + integer constexpr NUM_SPECIES = NS(); + integer constexpr NUM_DOF = 1+NS(); + + SourceFluxComputeKernel< NUM_DOF, NUM_SPECIES, BASE_FLUID_TYPE > kernel( rankOffset, dofNumber, elemGhostRank, rhsContributionArrayView, sizeScalingFactor, fluid, localMatrix, localRhs, + massProd ); + SourceFluxComputeKernel< NUM_DOF, NUM_SPECIES, BASE_FLUID_TYPE >::template launch< POLICY >( targetSet, kernel ); + } ); + } +}; +} // namespace singlePhaseReactiveBaseKernels + +} // namespace geos + +#endif //GEOS_PHYSICSSOLVERS_FLUIDFLOW_SINGLEPHASEREACTIVE_SOURCEFLUXCOMPUTEKERNELS_HPP diff --git a/src/coreComponents/physicsSolvers/fluidFlow/kernels/singlePhase/reactive/ThermalAccumulationKernels.hpp b/src/coreComponents/physicsSolvers/fluidFlow/kernels/singlePhase/reactive/ThermalAccumulationKernels.hpp new file mode 100644 index 00000000000..33fa2ac0f3b --- /dev/null +++ b/src/coreComponents/physicsSolvers/fluidFlow/kernels/singlePhase/reactive/ThermalAccumulationKernels.hpp @@ -0,0 +1,259 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * 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 ThermalAccumulationKernels.hpp + */ + +#ifndef GEOS_PHYSICSSOLVERS_FLUIDFLOW_SINGLEPHASEREACTIVE_THERMALACCUMULATIONKERNELS_HPP +#define GEOS_PHYSICSSOLVERS_FLUIDFLOW_SINGLEPHASEREACTIVE_THERMALACCUMULATIONKERNELS_HPP + +#include "physicsSolvers/fluidFlow/kernels/singlePhase/reactive/AccumulationKernels.hpp" + +namespace geos +{ + +namespace thermalSinglePhaseReactiveBaseKernels +{ + +/******************************** AccumulationKernel ********************************/ + +/** + * @class AccumulationKernel + * @brief Define the interface for the assembly kernel in charge of accumulation + */ +template< typename SUBREGION_TYPE, integer NUM_DOF, integer NUM_SPECIES, typename BASE_FLUID_TYPE > +class AccumulationKernel : public singlePhaseReactiveBaseKernels::AccumulationKernel< SUBREGION_TYPE, NUM_DOF, NUM_SPECIES, BASE_FLUID_TYPE > +{ + +public: + + using Base = singlePhaseReactiveBaseKernels::AccumulationKernel< SUBREGION_TYPE, NUM_DOF, NUM_SPECIES, BASE_FLUID_TYPE >; + using Base::numDof; + using Base::numEqn; + using Base::numSpecies; + using Base::m_rankOffset; + using Base::m_dofNumber; + using Base::m_elemGhostRank; + using Base::m_localMatrix; + using Base::m_localRhs; + using Base::m_dMass; + using Base::m_volume; + using Base::m_deltaVolume; + using Base::m_primarySpeciesAggregateConcentration; + + /// Note: Derivative lineup only supports dP & dT, not component terms + using DerivOffset = constitutive::singlefluid::DerivativeOffsetC< 1 >; + /** + * @brief Constructor + * @param[in] rankOffset the offset of my MPI rank + * @param[in] dofKey the string key to retrieve the degress of freedom numbers + * @param[in] subRegion the element subregion + * @param[in] fluid the fluid model + * @param[in] solid the solid model + * @param[inout] localMatrix the local CRS matrix + * @param[inout] localRhs the local right-hand side vector + */ + AccumulationKernel( globalIndex const rankOffset, + string const dofKey, + SUBREGION_TYPE const & subRegion, + constitutive::reactivefluid::ReactiveSinglePhaseFluid< BASE_FLUID_TYPE > const & fluid, + constitutive::CoupledSolidBase const & solid, + real64 const & dt, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) + : Base( rankOffset, dofKey, subRegion, fluid, solid, dt, localMatrix, localRhs ), + m_energy( subRegion.template getField< fields::flow::energy >() ), + m_energy_n( subRegion.template getField< fields::flow::energy_n >() ), + m_dEnergy( subRegion.template getField< fields::flow::dEnergy >() ), + m_dPoro_dTemp( solid.getDporosity_dTemperature() ) + // m_dPrimarySpeciesAggregateConcentration_dTemp( fluid.dPrimarySpeciesAggregateConcentration_dTemp() ), + // m_dPrimarySpeciesTotalKineticRate_dTemp( fluid.dPrimarySpeciesTotalKineticRate_dTemp() ), + {} + + /** + * @struct StackVariables + * @brief Kernel variables (dof numbers, jacobian and residual) located on the stack + */ + struct StackVariables : public Base::StackVariables + { +public: + + GEOS_HOST_DEVICE + StackVariables() + : Base::StackVariables() + {} + + using Base::StackVariables::localRow; + using Base::StackVariables::dofIndices; + using Base::StackVariables::localResidual; + using Base::StackVariables::localJacobian; + using Base::StackVariables::poreVolume; + using Base::StackVariables::dPoreVolume_dLogPrimaryConc; + + // Pore volume information + + /// Derivative of pore volume with respect to temperature + real64 dPoreVolume_dTemp = 0.0; + }; + + /** + * @brief Performs the setup phase for the kernel. + * @param[in] ei the element index + * @param[in] stack the stack variables + */ + GEOS_HOST_DEVICE + void setup( localIndex const ei, + StackVariables & stack ) const + { + Base::setup( ei, stack ); + + stack.dPoreVolume_dTemp = ( m_volume[ei] + m_deltaVolume[ei] ) * m_dPoro_dTemp[ei][0]; + } + + /** + * @brief Compute the local accumulation contributions to the residual and Jacobian + * @param[in] ei the element index + * @param[inout] stack the stack variables + * @param[in] kernelOp the function used to customize the kernel + */ + GEOS_HOST_DEVICE + void computeAccumulation( localIndex const ei, + StackVariables & stack ) const + { + Base::computeAccumulation( ei, stack ); + + // Step 1: assemble the derivatives of the mass balance equation w.r.t temperature + stack.localJacobian[0][numDof-numSpecies-1] = m_dMass[ei][DerivOffset::dT]; + + // Step 2: assemble the accumulation term of the energy equation + // Step 2.1: assemble the residual and derivatives wrt pressure and temperature + stack.localResidual[numEqn-numSpecies-1] = m_energy[ei] - m_energy_n[ei]; + stack.localJacobian[numEqn-numSpecies-1][0] += m_dEnergy[ei][DerivOffset::dP]; + stack.localJacobian[numEqn-numSpecies-1][numDof-numSpecies-1] += m_dEnergy[ei][DerivOffset::dT]; + + // Step 2.2: assemble the derivatives of the energy equation w.r.t log primary species concentration + // for( integer is = 0; is < numSpecies; ++is ) + // { + // stack.localJacobian[numEqn-numSpecies-1][is+numDof-numSpecies] += stack.dPoreVolume_dLogPrimaryConc[is] * m_density[ei][0] * + // m_fluidInternalEnergy[ei][0] + // - stack.dPoreVolume_dLogPrimaryConc[is] * + // m_rockInternalEnergy[ei][0] + // + stack.poreVolume * m_dDensity_dLogPrimaryConc[ei][is] * + // m_fluidInternalEnergy[ei][0] + // + stack.poreVolume * m_density[ei][0] * + // m_dFluidInternalEnergy_dLogPrimaryConc[ei][is]; + // } + + // Step 3: assemble the derivatives of the species amount balance equation w.r.t temperature + for( integer is = 0; is < numSpecies; ++is ) + { + // Drivative of primary species amount in pore volume wrt temperature + stack.localJacobian[is+numEqn-numSpecies][numDof-numSpecies-1] += stack.dPoreVolume_dTemp * m_primarySpeciesAggregateConcentration[ei][0][is] + /* + stack.poreVolume * + m_dPrimarySpeciesAggregateConcentration_dTemp[ei][is] */; + // // Derivative of reaction term wrt temperature + // stack.localJacobian[is+numEqn-numSpecies][numDof-numSpecies-1] -= m_dt * ( m_volume[ei] + m_deltaVolume[ei] ) * + // m_dPrimarySpeciesTotalKineticRate_dTemp[is]; + } + } + + /** + * @brief Performs the complete phase for the kernel. + * @param[in] ei the element index + * @param[inout] stack the stack variables + */ + GEOS_HOST_DEVICE + void complete( localIndex const ei, + StackVariables & stack ) const + { + // Step 1: assemble the total mass balance equation (i = 0) + // and species amount balance equation (i = numEqn-numSpecies to i = numEqn-1) + Base::complete( ei, stack ); + + // Step 2: assemble the energy equation (i = numEqn-numSpecies-1) + m_localRhs[stack.localRow + numEqn-numSpecies-1] += stack.localResidual[numEqn-numSpecies-1]; + m_localMatrix.template addToRow< serialAtomic >( stack.localRow + numEqn-numSpecies-1, + stack.dofIndices, + stack.localJacobian[numEqn-numSpecies-1], + numDof ); + } + +protected: + + /// View on energy + arrayView1d< real64 const > const m_energy; + arrayView1d< real64 const > const m_energy_n; + arrayView2d< real64 const, constitutive::singlefluid::USD_FLUID > const m_dEnergy; + + /// Views on the porosity derivative + arrayView2d< real64 const > const m_dPoro_dTemp; + + // // View on the derivatives of aggregate concentration for the primary species wrt temperature + // arrayView2d< real64 const, compflow::USD_COMP > m_dPrimarySpeciesAggregateConcentration_dTemp; + + // // View on the derivatives of total kinetic rate of primary species wrt temperature + // arrayView2d< real64 const, compflow::USD_COMP > m_dPrimarySpeciesTotalKineticRate_dTemp; + +}; + +/** + * @class AccumulationKernelFactory + */ +class AccumulationKernelFactory +{ +public: + + /** + * @brief Create a new kernel and launch + * @tparam POLICY the policy used in the RAJA kernel + * @param[in] numSpecies the number of primary species + * @param[in] dt time step + * @param[in] rankOffset the offset of my MPI rank + * @param[in] dofKey the string key to retrieve the degress of freedom numbers + * @param[in] subRegion the element subregion + * @param[in] fluid the fluid model + * @param[in] solid the solid model + * @param[inout] localMatrix the local CRS matrix + * @param[inout] localRhs the local right-hand side vector + */ + template< typename POLICY, typename SUBREGION_TYPE, typename BASE_FLUID_TYPE > + static void + createAndLaunch( integer const numSpecies, + real64 const dt, + globalIndex const rankOffset, + string const dofKey, + SUBREGION_TYPE const & subRegion, + constitutive::reactivefluid::ReactiveSinglePhaseFluid< BASE_FLUID_TYPE > const & fluid, + constitutive::CoupledSolidBase const & solid, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) + { + singlePhaseReactiveBaseKernels:: + internal::kernelLaunchSelectorCompSwitch( numSpecies, [&] ( auto NS ) + { + integer constexpr NUM_SPECIES = NS(); + integer constexpr NUM_DOF = 2+NS(); + AccumulationKernel< SUBREGION_TYPE, NUM_DOF, NUM_SPECIES, BASE_FLUID_TYPE > kernel( rankOffset, dofKey, subRegion, fluid, solid, dt, localMatrix, localRhs ); + AccumulationKernel< SUBREGION_TYPE, NUM_DOF, NUM_SPECIES, BASE_FLUID_TYPE >::template launch< POLICY >( subRegion.size(), kernel ); + } ); + } +}; + +} // namespace thermalSinglePhaseReactiveBaseKernels + +} // namespace geos + +#endif //GEOS_PHYSICSSOLVERS_FLUIDFLOW_SINGLEPHASEREACTIVE_THERMALACCUMULATIONKERNELS_HPP diff --git a/src/coreComponents/physicsSolvers/fluidFlow/kernels/singlePhase/reactive/ThermalDirichletFluxComputeKernel.hpp b/src/coreComponents/physicsSolvers/fluidFlow/kernels/singlePhase/reactive/ThermalDirichletFluxComputeKernel.hpp new file mode 100644 index 00000000000..7a87bdfbc50 --- /dev/null +++ b/src/coreComponents/physicsSolvers/fluidFlow/kernels/singlePhase/reactive/ThermalDirichletFluxComputeKernel.hpp @@ -0,0 +1,410 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * 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 ThermalDirichletFluxComputeKernel.hpp + */ + +#ifndef GEOS_PHYSICSSOLVERS_FLUIDFLOW_SINGLEPHASEREACTIVE_THERMALDIRICHLETFLUXCOMPUTEKERNEL_HPP +#define GEOS_PHYSICSSOLVERS_FLUIDFLOW_SINGLEPHASEREACTIVE_THERMALDIRICHLETFLUXCOMPUTEKERNEL_HPP + +#include "physicsSolvers/fluidFlow/kernels/singlePhase/reactive/DirichletFluxComputeKernel.hpp" + +#include "constitutive/thermalConductivity/SinglePhaseThermalConductivityBase.hpp" +#include "constitutive/thermalConductivity/ThermalConductivityFields.hpp" + +namespace geos +{ + +namespace thermalSinglePhaseReactiveFVMKernels +{ + +/******************************** DirichletFluxComputeKernel ********************************/ + +/** + * @class DirichletFluxComputeKernel + * @tparam NUM_SPECIES number of fluid primary species + * @tparam NUM_EQN number of equations + * @tparam NUM_DOF number of degrees of freedom + * @tparam FLUIDWRAPPER the type of the fluid wrapper + * @tparam BASE_FLUID_TYPE the type of the base model for the reactive fluid model + * @brief Define the interface for the assembly kernel in charge of Dirichlet face flux terms + */ +template< integer NUM_SPECIES, integer NUM_EQN, integer NUM_DOF, typename FLUIDWRAPPER, typename BASE_FLUID_TYPE > +class DirichletFluxComputeKernel : public singlePhaseReactiveFVMKernels::DirichletFluxComputeKernel< NUM_SPECIES, NUM_EQN, NUM_DOF, FLUIDWRAPPER, BASE_FLUID_TYPE > +{ +public: + +/** + * @brief The type for element-based data. Consists entirely of ArrayView's. + * + * Can be converted from ElementRegionManager::ElementViewConstAccessor + * by calling .toView() or .toViewConst() on an accessor instance + */ + + using DerivOffset = constitutive::singlefluid::DerivativeOffsetC< 1 >; + template< typename VIEWTYPE > + using ElementViewConst = ElementRegionManager::ElementViewConst< VIEWTYPE >; + + using AbstractBase = singlePhaseFVMKernels::FluxComputeKernelBase; + using DofNumberAccessor = AbstractBase::DofNumberAccessor; + using PermeabilityAccessors = AbstractBase::PermeabilityAccessors; + using SinglePhaseFlowAccessors = AbstractBase::SinglePhaseFlowAccessors; + using SinglePhaseFluidAccessors = AbstractBase::SinglePhaseFluidAccessors; + + using AbstractBase::m_dt; + using AbstractBase::m_rankOffset; + using AbstractBase::m_dofNumber; + using AbstractBase::m_ghostRank; + using AbstractBase::m_gravCoef; + using AbstractBase::m_mob; + using AbstractBase::m_pres; + using AbstractBase::m_permeability; + using AbstractBase::m_dPerm_dPres; + using AbstractBase::m_dDens; + using AbstractBase::m_dMob; + + using Base = singlePhaseReactiveFVMKernels::DirichletFluxComputeKernel< NUM_SPECIES, NUM_EQN, NUM_DOF, FLUIDWRAPPER, BASE_FLUID_TYPE >; + using Base::numSpecies; + using Base::numDof; + using Base::numEqn; + using Base::m_stencilWrapper; + using Base::m_seri; + using Base::m_sesri; + using Base::m_sei; + using Base::m_facePres; + using Base::m_faceGravCoef; + + using ReactiveSinglePhaseFlowAccessors = typename Base::ReactiveSinglePhaseFlowAccessors; + using ReactiveSinglePhaseFluidAccessors = typename Base::ReactiveSinglePhaseFluidAccessors; + + using ThermalSinglePhaseFlowAccessors = + StencilAccessors< fields::flow::temperature >; + + using ThermalReactiveSinglePhaseFluidAccessors = + StencilMaterialAccessors< constitutive::reactivefluid::ReactiveSinglePhaseFluid< BASE_FLUID_TYPE >, + fields::singlefluid::enthalpy, + fields::singlefluid::dEnthalpy >; + + using ThermalConductivityAccessors = + StencilMaterialAccessors< constitutive::SinglePhaseThermalConductivityBase, + fields::thermalconductivity::effectiveConductivity, + fields::thermalconductivity::dEffectiveConductivity_dT >; + + /** + * @brief Constructor for the kernel interface + * @param[in] rankOffset the offset of the MPI rank + * @param[in] faceManager the face manager + * @param[in] stencilWrapper reference to the stencil wrapper + * @param[in] fluidWrapper reference to the fluid wrapper + * @param[in] dofNumberAccessor the degree of freedom number accessor + * @param[in] singlePhaseFlowAccessors the single phase flow accessor + * @param[in] reactiveSinglePhaseFlowAccessors accessor for *reactive* wrappers registered by the solver + * @param[in] thermalSinglePhaseFlowAccessors accessor for *thermal* wrappers registered by the solver + * @param[in] singlePhaseFluidAccessors the single phase fluid accessor + * @param[in] reactiveSinglePhaseFluidAccessors accessor for *reactive* wrappers registered by the single fluid model + * @param[in] thermalReactiveSinglePhaseFluidAccessors accessor for *thermal reactive* wrappers registered by the single fluid model + * @param[in] permeabilityAccessors the permeability accessor + * @param[in] thermalConductivityAccessors the thermal conductivity accessor + * @param[in] mobilePrimarySpeciesFlags the array of flags to indicate mobile primary species + * @param[in] dt the time step size + * @param[inout] localMatrix the local CRS matrix + * @param[inout] localRhs the local right-hand side vector + */ + DirichletFluxComputeKernel( globalIndex const rankOffset, + FaceManager const & faceManager, + BoundaryStencilWrapper const & stencilWrapper, + FLUIDWRAPPER const & fluidWrapper, + DofNumberAccessor const & dofNumberAccessor, + SinglePhaseFlowAccessors const & singlePhaseFlowAccessors, + ReactiveSinglePhaseFlowAccessors const & reactiveSinglePhaseFlowAccessors, + ThermalSinglePhaseFlowAccessors const & thermalSinglePhaseFlowAccessors, + SinglePhaseFluidAccessors const & singlePhaseFluidAccessors, + ReactiveSinglePhaseFluidAccessors const & reactiveSinglePhaseFluidAccessors, + ThermalReactiveSinglePhaseFluidAccessors const & thermalReactiveSinglePhaseFluidAccessors, + PermeabilityAccessors const & permeabilityAccessors, + ThermalConductivityAccessors const & thermalConductivityAccessors, + arrayView1d< integer const > const & mobilePrimarySpeciesFlags, + real64 const & dt, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) + + : Base( rankOffset, + faceManager, + stencilWrapper, + fluidWrapper, + dofNumberAccessor, + singlePhaseFlowAccessors, + reactiveSinglePhaseFlowAccessors, + singlePhaseFluidAccessors, + reactiveSinglePhaseFluidAccessors, + permeabilityAccessors, + mobilePrimarySpeciesFlags, + dt, + localMatrix, + localRhs ), + m_temp( thermalSinglePhaseFlowAccessors.get( fields::flow::temperature {} ) ), + m_faceTemp( faceManager.getField< fields::flow::faceTemperature >() ), + m_enthalpy( thermalReactiveSinglePhaseFluidAccessors.get( fields::singlefluid::enthalpy {} ) ), + m_dEnthalpy( thermalReactiveSinglePhaseFluidAccessors.get( fields::singlefluid::dEnthalpy {} ) ), + m_thermalConductivity( thermalConductivityAccessors.get( fields::thermalconductivity::effectiveConductivity {} ) ), + m_dThermalCond_dT( thermalConductivityAccessors.get( fields::thermalconductivity::dEffectiveConductivity_dT {} ) ) + {} + + + /** + * @struct StackVariables + * @brief Kernel variables (dof numbers, jacobian and residual) located on the stack + */ + struct StackVariables : Base::StackVariables + { +public: + + /** + * @brief Constructor for the stack variables + * @param[in] size size of the stencil for this connection + * @param[in] numElems number of elements for this connection + */ + GEOS_HOST_DEVICE + StackVariables( localIndex const size, + localIndex numElems ): + Base::StackVariables( size, + numElems ) + {} + + using Base::StackVariables::localFlux; + using Base::StackVariables::localFluxJacobian; + using Base::StackVariables::dofColIndices; + using Base::StackVariables::transmissibility; + + /// Energy fluxes and derivatives wrt pressure and temperature + real64 energyFlux = 0.0; + real64 dEnergyFlux_dP = 0.0; + real64 dEnergyFlux_dT = 0.0; + }; + + /** + * @brief Compute the local Dirichlet face flux contributions to the residual and Jacobian + * @tparam FUNC the type of the function that can be used to customize the computation of the phase fluxes + * @param[in] iconn the connection index + * @param[inout] stack the stack variables + * @param[in] compFluxKernelOp the function used to customize the computation of the component fluxes + */ + GEOS_HOST_DEVICE + void computeFlux( localIndex const iconn, + StackVariables & stack ) const + { + Base::computeFlux( iconn, stack, [&] ( localIndex const er, + localIndex const esr, + localIndex const ei, + localIndex const kf, + real64 const & f, + real64 const & dF_dP, + real64 const & mobility_up, + real64 const & dMobility_dP_up ) + { + // Compute the derivatives of the density wrt temperature + + real64 const dDens_dT = 0.5 * m_dDens[er][esr][ei][0][DerivOffset::dT]; + // Compute the derivatives of the phase potential difference wrt temperature + + real64 const dF_dT = -stack.transmissibility * dDens_dT * ( m_gravCoef[er][esr][ei] - m_faceGravCoef[kf] ); + + // Compute the (upwinded) energy flux + + real64 const flux = mobility_up * f; + real64 const enthalpy = m_enthalpy[er][esr][ei][0]; + stack.energyFlux += flux * enthalpy; + + // Compute the derivatives of the (upwinded) energy flux wrt pressure and temperature + + if( f >= 0 ) // the element is upstream + { + real64 const dFlux_dP = mobility_up * dF_dP + dMobility_dP_up * f; + real64 const dFlux_dT = mobility_up * dF_dT + m_dMob[er][esr][ei][DerivOffset::dT] * f; + + stack.dEnergyFlux_dP += dFlux_dP * enthalpy + flux * m_dEnthalpy[er][esr][ei][0][DerivOffset::dP]; + stack.dEnergyFlux_dT += dFlux_dT * enthalpy + flux * m_dEnthalpy[er][esr][ei][0][DerivOffset::dT]; + } + else + { + real64 const dFlux_dP = mobility_up * dF_dP; + real64 const dFlux_dT = mobility_up * dF_dT; + + stack.dEnergyFlux_dP += dFlux_dP * enthalpy; + stack.dEnergyFlux_dT += dFlux_dT * enthalpy; + } + + // Contribution of energy conduction through the solid phase + real64 thermalTrans = 0.0; + real64 dThermalTrans_dThermalCond[3]{}; + m_stencilWrapper.computeWeights( iconn, + m_thermalConductivity, + thermalTrans, + dThermalTrans_dThermalCond ); + + real64 const dThermalTrans_dT = LvArray::tensorOps::AiBi< 3 >( dThermalTrans_dThermalCond, m_dThermalCond_dT[er][esr][ei][0] ); + + real64 const deltaT = m_temp[er][esr][ei] - m_faceTemp[kf]; + stack.energyFlux += thermalTrans * deltaT; + stack.dEnergyFlux_dT += thermalTrans + dThermalTrans_dT * deltaT; + + // Add energyFlux and its derivatives to localFlux and localFluxJacobian + integer const localRowIndexEnergy = numEqn - numSpecies - 1; + stack.localFlux[localRowIndexEnergy] = m_dt * stack.energyFlux; + + stack.localFluxJacobian[localRowIndexEnergy][0] = m_dt * stack.dEnergyFlux_dP; + stack.localFluxJacobian[localRowIndexEnergy][numDof-numSpecies-1] = m_dt * stack.dEnergyFlux_dT; + } ); + + } + + /** + * @brief Performs the complete phase for the kernel. + * @param[in] iconn the connection index + * @param[inout] stack the stack variables + */ + GEOS_HOST_DEVICE + void complete( localIndex const iconn, + StackVariables & stack ) const + { + Base::complete( iconn, stack, [&] ( localIndex const localRow ) + { + RAJA::atomicAdd( parallelDeviceAtomic{}, &AbstractBase::m_localRhs[localRow + numEqn - numSpecies - 1], + stack.localFlux[numEqn - numSpecies - 1] ); + + AbstractBase::m_localMatrix.addToRowBinarySearchUnsorted< parallelDeviceAtomic > + ( localRow + numEqn - numSpecies - 1, + stack.dofColIndices, + stack.localFluxJacobian[numEqn - numSpecies - 1], + numDof ); + } ); + } + +protected: + + /// Views on temperature + ElementViewConst< arrayView1d< real64 const > > const m_temp; + + /// Views on face temperature + arrayView1d< real64 const > const m_faceTemp; + + /// Views on enthalpies + ElementViewConst< arrayView2d< real64 const, constitutive::singlefluid::USD_FLUID > > const m_enthalpy; + ElementViewConst< arrayView3d< real64 const, constitutive::singlefluid::USD_FLUID_DER > > const m_dEnthalpy; + + /// View on thermal conductivity + ElementViewConst< arrayView3d< real64 const > > m_thermalConductivity; + + /// View on derivatives of thermal conductivity w.r.t. temperature + ElementViewConst< arrayView3d< real64 const > > m_dThermalCond_dT; + +}; + +/** + * @class DirichletFluxComputeKernelFactory + */ +class DirichletFluxComputeKernelFactory +{ +public: + + /** + * @brief Create a new kernel and launch + * @tparam POLICY the policy used in the RAJA kernel + * @param[in] numSpecies the number of primary species + * @param[in] mobilePrimarySpeciesFlags the array of flags to indicate mobile primary species + * @param[in] rankOffset the offset of my MPI rank + * @param[in] dofKey string to get the element degrees of freedom numbers + * @param[in] solverName name of the solver (to name accessors) + * @param[in] faceManager reference to the face manager + * @param[in] elemManager reference to the element region manager + * @param[in] stencilWrapper reference to the boundary stencil wrapper + * @param[in] reactiveFluid the single phase reactive fluid constitutive model + * @param[in] dt time step size + * @param[inout] localMatrix the local CRS matrix + * @param[inout] localRhs the local right-hand side vector + */ + template< typename POLICY > + static void + createAndLaunch( integer const numSpecies, + arrayView1d< integer const > const mobilePrimarySpeciesFlags, + globalIndex const rankOffset, + string const & dofKey, + string const & solverName, + FaceManager const & faceManager, + ElementRegionManager const & elemManager, + BoundaryStencilWrapper const & stencilWrapper, + constitutive::reactivefluid::ReactiveThermalCompressibleSinglePhaseFluid & reactiveFluid, + real64 const & dt, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) + { + constitutiveUpdatePassThru( reactiveFluid, [&]( auto & fluid ) + { + using FluidType = TYPEOFREF( fluid ); + typename FluidType::KernelWrapper fluidWrapper = fluid.createKernelWrapper(); + + singlePhaseReactiveBaseKernels::internal::kernelLaunchSelectorCompSwitch( numSpecies, [&]( auto NS ) + { + integer constexpr NUM_SPECIES = NS(); + integer constexpr NUM_DOF = 2+NS(); + integer constexpr NUM_EQN = 2+NS(); + + using kernelType = DirichletFluxComputeKernel< NUM_SPECIES, NUM_EQN, NUM_DOF, typename FluidType::KernelWrapper, constitutive::ThermalCompressibleSinglePhaseFluid >; + + ElementRegionManager::ElementViewAccessor< arrayView1d< globalIndex const > > dofNumberAccessor = + elemManager.constructArrayViewAccessor< globalIndex, 1 >( dofKey ); + + dofNumberAccessor.setName( solverName + "/accessors/" + dofKey ); + + typename kernelType::SinglePhaseFlowAccessors singlePhaseFlowAccessors( elemManager, solverName ); + typename kernelType::ReactiveSinglePhaseFlowAccessors reactiveFlowAccessors( elemManager, solverName ); + typename kernelType::ThermalSinglePhaseFlowAccessors thermalSinglePhaseFlowAccessors( elemManager, solverName ); + typename kernelType::SinglePhaseFluidAccessors singlePhaseFluidAccessors( elemManager, solverName ); + typename kernelType::ReactiveSinglePhaseFluidAccessors reactiveFluidAccessors( elemManager, solverName ); + typename kernelType::ThermalReactiveSinglePhaseFluidAccessors thermalFluidAccessors( elemManager, solverName ); + typename kernelType::PermeabilityAccessors permeabilityAccessors( elemManager, solverName ); + typename kernelType::ThermalConductivityAccessors thermalConductivityAccessors( elemManager, solverName ); + + kernelType kernel( rankOffset, + faceManager, + stencilWrapper, + fluidWrapper, + dofNumberAccessor, + singlePhaseFlowAccessors, + reactiveFlowAccessors, + thermalSinglePhaseFlowAccessors, + singlePhaseFluidAccessors, + reactiveFluidAccessors, + thermalFluidAccessors, + permeabilityAccessors, + thermalConductivityAccessors, + mobilePrimarySpeciesFlags, + dt, + localMatrix, + localRhs ); + + kernelType::template launch< POLICY >( stencilWrapper.size(), kernel ); + } ); + } ); + } + +}; + +} // namespace thermalSinglePhaseReactiveFVMKernels + +} // namespace geos + +#endif //GEOS_PHYSICSSOLVERS_FLUIDFLOW_SINGLEPHASEREACTIVE_THERMALDIRICHLETFLUXCOMPUTEKERNEL_HPP diff --git a/src/coreComponents/physicsSolvers/fluidFlow/kernels/singlePhase/reactive/ThermalFluxComputeKernel.hpp b/src/coreComponents/physicsSolvers/fluidFlow/kernels/singlePhase/reactive/ThermalFluxComputeKernel.hpp new file mode 100644 index 00000000000..d436b261628 --- /dev/null +++ b/src/coreComponents/physicsSolvers/fluidFlow/kernels/singlePhase/reactive/ThermalFluxComputeKernel.hpp @@ -0,0 +1,635 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * 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 ThermalFluxComputeKernel.hpp + */ + +#ifndef GEOS_PHYSICSSOLVERS_FLUIDFLOW_SINGLEPHASEREACTIVE_THERMALFLUXCOMPUTEKERNEL_HPP +#define GEOS_PHYSICSSOLVERS_FLUIDFLOW_SINGLEPHASEREACTIVE_THERMALFLUXCOMPUTEKERNEL_HPP + +#include "physicsSolvers/fluidFlow/kernels/singlePhase/reactive/FluxComputeKernel.hpp" + +#include "constitutive/thermalConductivity/SinglePhaseThermalConductivityBase.hpp" +#include "constitutive/thermalConductivity/ThermalConductivityFields.hpp" + +namespace geos +{ + +namespace thermalSinglePhaseReactiveFVMKernels +{ +/******************************** FluxComputeKernel ********************************/ + +/** + * @class FluxComputeKernel + * @tparam NUM_SPECIES number of fluid primary species + * @tparam NUM_EQN number of equations + * @tparam NUM_DOF number of degrees of freedom + * @tparam STENCILWRAPPER the type of the stencil wrapper + * @tparam BASE_FLUID_TYPE the type of the base model for the reactive fluid model + * @brief Define the interface for the assembly kernel in charge of flux terms + */ +template< integer NUM_SPECIES, integer NUM_EQN, integer NUM_DOF, typename STENCILWRAPPER, typename BASE_FLUID_TYPE > +class FluxComputeKernel : public singlePhaseReactiveFVMKernels::FluxComputeKernel< NUM_SPECIES, NUM_EQN, NUM_DOF, STENCILWRAPPER, BASE_FLUID_TYPE > +{ +public: + + /** + * @brief The type for element-based data. Consists entirely of ArrayView's. + * + * Can be converted from ElementRegionManager::ElementViewConstAccessor + * by calling .toView() or .toViewConst() on an accessor instance + */ + template< typename VIEWTYPE > + using ElementViewConst = ElementRegionManager::ElementViewConst< VIEWTYPE >; + + using AbstractBase = singlePhaseFVMKernels::FluxComputeKernelBase; + using DofNumberAccessor = AbstractBase::DofNumberAccessor; + using SinglePhaseFlowAccessors = AbstractBase::SinglePhaseFlowAccessors; + using SinglePhaseFluidAccessors = AbstractBase::SinglePhaseFluidAccessors; + using PermeabilityAccessors = AbstractBase::PermeabilityAccessors; + + using AbstractBase::m_dt; + using AbstractBase::m_rankOffset; + using AbstractBase::m_dofNumber; + using AbstractBase::m_gravCoef; + using AbstractBase::m_mob; + using AbstractBase::m_dMob; + using AbstractBase::m_dens; + using AbstractBase::m_dDens; + + using Base = singlePhaseReactiveFVMKernels::FluxComputeKernel< NUM_SPECIES, NUM_EQN, NUM_DOF, STENCILWRAPPER, BASE_FLUID_TYPE >; + using ReactiveSinglePhaseFlowAccessors = typename Base::ReactiveSinglePhaseFlowAccessors; + using ReactiveSinglePhaseFluidAccessors = typename Base::ReactiveSinglePhaseFluidAccessors; + using DiffusionAccessors = typename Base::DiffusionAccessors; + using PorosityAccessors = typename Base::PorosityAccessors; + using Base::numSpecies; + using Base::numFluxSupportPoints; + using Base::numDof; + using Base::numEqn; + using Base::maxNumElems; + using Base::maxNumConns; + using Base::maxStencilSize; + using Base::m_stencilWrapper; + using Base::m_seri; + using Base::m_sesri; + using Base::m_sei; + using Base::m_primarySpeciesMobileAggregateConc; + using Base::m_referencePorosity; + using Base::m_mobilePrimarySpeciesFlags; + + using ThermalSinglePhaseFlowAccessors = + StencilAccessors< fields::flow::temperature >; + + using ThermalReactiveSinglePhaseFluidAccessors = + StencilMaterialAccessors< constitutive::reactivefluid::ReactiveSinglePhaseFluid< BASE_FLUID_TYPE >, + fields::singlefluid::enthalpy, + fields::singlefluid::dEnthalpy >; + + using ThermalConductivityAccessors = + StencilMaterialAccessors< constitutive::SinglePhaseThermalConductivityBase, + fields::thermalconductivity::effectiveConductivity, + fields::thermalconductivity::dEffectiveConductivity_dT >; + + + /** + * @brief Constructor for the kernel interface + * @param[in] rankOffset the offset of my MPI rank + * @param[in] stencilWrapper reference to the stencil wrapper + * @param[in] dofNumberAccessor accessor for the dofs numbers + * @param[in] singlePhaseFlowAccessors accessor for wrappers registered by the solver + * @param[in] reactiveSinglePhaseFlowAccessors accessor for *reactive* wrappers registered by the solver + * @param[in] thermalSinglePhaseFlowAccessors accessor for *thermal* wrappers registered by the solver + * @param[in] singlePhaseFluidAccessors accessor for wrappers registered by the single fluid model + * @param[in] reactiveSinglePhaseFluidAccessors accessor for *reactive* wrappers registered by the single fluid model + * @param[in] thermalReactiveSinglePhaseFluidAccessors accessor for *thermal reactive* wrappers registered by the single fluid model + * @param[in] permeabilityAccessors accessor for wrappers registered by the permeability model + * @param[in] diffusionAccessors accessor for wrappers registered by the diffusion model + * @param[in] porosityAccessors accessor for wrappers registered by the porosity model + * @param[in] thermalConductivityAccessors accessor for wrappers registered by the thermal conductivity model + * @param[in] hasDiffusion the flag to turn on diffusion calculation + * @param[in] mobilePrimarySpeciesFlags the array of flags to indicate mobile primary species + * @param[in] dt time step size + * @param[inout] localMatrix the local CRS matrix + * @param[inout] localRhs the local right-hand side vector + */ + FluxComputeKernel( globalIndex const rankOffset, + STENCILWRAPPER const & stencilWrapper, + DofNumberAccessor const & dofNumberAccessor, + SinglePhaseFlowAccessors const & singlePhaseFlowAccessors, + ReactiveSinglePhaseFlowAccessors const & reactiveSinglePhaseFlowAccessors, + ThermalSinglePhaseFlowAccessors const & thermalSinglePhaseFlowAccessors, + SinglePhaseFluidAccessors const & singlePhaseFluidAccessors, + ReactiveSinglePhaseFluidAccessors const & reactiveSinglePhaseFluidAccessors, + ThermalReactiveSinglePhaseFluidAccessors const & thermalReactiveSinglePhaseFluidAccessors, + PermeabilityAccessors const & permeabilityAccessors, + DiffusionAccessors const & diffusionAccessors, + PorosityAccessors const & porosityAccessors, + ThermalConductivityAccessors const & thermalConductivityAccessors, + integer const & hasDiffusion, + arrayView1d< integer const > const & mobilePrimarySpeciesFlags, + real64 const & dt, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) + : Base( rankOffset, + stencilWrapper, + dofNumberAccessor, + singlePhaseFlowAccessors, + reactiveSinglePhaseFlowAccessors, + singlePhaseFluidAccessors, + reactiveSinglePhaseFluidAccessors, + permeabilityAccessors, + diffusionAccessors, + porosityAccessors, + hasDiffusion, + mobilePrimarySpeciesFlags, + dt, + localMatrix, + localRhs ), + m_temp( thermalSinglePhaseFlowAccessors.get( fields::flow::temperature {} ) ), + m_enthalpy( thermalReactiveSinglePhaseFluidAccessors.get( fields::singlefluid::enthalpy {} ) ), + m_dEnthalpy( thermalReactiveSinglePhaseFluidAccessors.get( fields::singlefluid::dEnthalpy {} ) ), + // m_dPrimarySpeciesMobileAggregateConcentration_dTemp( fluid.dPrimarySpeciesMobileAggregateConcentration_dTemp() ), + m_thermalConductivity( thermalConductivityAccessors.get( fields::thermalconductivity::effectiveConductivity {} ) ), + m_dThermalCond_dT( thermalConductivityAccessors.get( fields::thermalconductivity::dEffectiveConductivity_dT {} ) ) + {} + + struct StackVariables : public Base::StackVariables + { +public: + + GEOS_HOST_DEVICE + StackVariables( localIndex const size, localIndex numElems ) + : Base::StackVariables( size, numElems ), + energyFlux( 0.0 ), + dEnergyFlux_dP( size ), + dEnergyFlux_dT( size ) + {} + + using Base::StackVariables::stencilSize; + using Base::StackVariables::numFluxElems; + using Base::StackVariables::transmissibility; + using Base::StackVariables::dTrans_dPres; + using Base::StackVariables::dofColIndices; + using Base::StackVariables::localFlux; + using Base::StackVariables::localFluxJacobian; + using Base::StackVariables::diffusionTransmissibility; + using Base::StackVariables::dDiffusionTrans_dT; + + + // Thermal transmissibility + real64 thermalTransmissibility[maxNumConns][2]{}; + + /// Derivatives of thermal transmissibility with respect to temperature + real64 dThermalTrans_dT[maxNumConns][2]{}; + + // Energy fluxes and derivatives + + /// Energy fluxes + real64 energyFlux; + /// Derivatives of energy fluxes wrt pressure + stackArray1d< real64, maxStencilSize > dEnergyFlux_dP; + /// Derivatives of energy fluxes wrt temperature + stackArray1d< real64, maxStencilSize > dEnergyFlux_dT; + + }; + + /** + * @brief Compute the local flux contributions to the residual and Jacobian + * @param[in] iconn the connection index + * @param[inout] stack the stack variables + */ + GEOS_HOST_DEVICE + void computeFlux( localIndex const iconn, + StackVariables & stack ) const + { + using DerivOffset = constitutive::singlefluid::DerivativeOffsetC< 1 >; + // *********************************************** + // First, we call the base computeFlux to compute: + // 1) massFlux and speciesFlux and their derivatives (including derivatives wrt temperature), + // 2) enthalpy part of energyFlux and its derivatives (including derivatives wrt temperature) + // + // Computing dFlux_dT and the enthalpy flux requires quantities already computed in the base computeFlux, + // such as potGrad, fluxVal, and the indices of the upwind cell + // We use the lambda below (called **inside** the phase loop of the base computeFlux) to access these variables + Base::computeFlux( iconn, stack, [&] ( localIndex const (&k)[2], + localIndex const (&seri)[2], + localIndex const (&sesri)[2], + localIndex const (&sei)[2], + localIndex const connectionIndex, + real64 const alpha, + real64 const mobility, + real64 const & potGrad, + real64 const & fluxVal, + real64 const (&dFlux_dP)[2], + real64 const fluidDens_up ) + { + // Step 1: compute the derivatives of the (upwinded) massFlux wrt temperature + // -------------------------------------------------------------------------- + // Step 1.1: compute the derivatives of the mean density at the interface wrt temperature + real64 dDensMean_dT[numFluxSupportPoints]{0.0, 0.0}; + + real64 const trans[numFluxSupportPoints] = { stack.transmissibility[connectionIndex][0], stack.transmissibility[connectionIndex][1] }; + + for( integer ke = 0; ke < numFluxSupportPoints; ++ke ) + { + dDensMean_dT[ke] = 0.5 * m_dDens[seri[ke]][sesri[ke]][sei[ke]][0][DerivOffset::dT]; + } + + // Step 1.2: compute the derivatives of the potential difference wrt temperature + real64 dGravHead_dT[numFluxSupportPoints]{0.0, 0.0}; + + // compute derivative of gravity potential difference wrt temperature + for( integer ke = 0; ke < numFluxSupportPoints; ++ke ) + { + localIndex const er = seri[ke]; + localIndex const esr = sesri[ke]; + localIndex const ei = sei[ke]; + + real64 const gravD = trans[ke] * m_gravCoef[er][esr][ei]; + + for( integer i = 0; i < numFluxSupportPoints; ++i ) + { + dGravHead_dT[i] += dDensMean_dT[i] * gravD; + } + } + + real64 dFlux_dT[numFluxSupportPoints]{0.0, 0.0}; + + for( integer ke = 0; ke < numFluxSupportPoints; ++ke ) + { + dFlux_dT[ke] -= dGravHead_dT[ke]; + } + + for( integer ke = 0; ke < numFluxSupportPoints; ++ke ) + { + dFlux_dT[ke] *= mobility; + } + + // compute the derivatives of the mobility wrt temperature + // *** upwinding *** + real64 dMob_dT[numFluxSupportPoints]{}; + + if( alpha <= 0.0 || alpha >= 1.0 ) + { + localIndex const k_up = 1 - localIndex( fmax( fmin( alpha, 1.0 ), 0.0 ) ); + dMob_dT[k_up] = m_dMob[seri[k_up]][sesri[k_up]][sei[k_up]][DerivOffset::dT]; + } + else + { + real64 const mobWeights[numFluxSupportPoints] = { alpha, 1.0 - alpha }; + for( integer ke = 0; ke < numFluxSupportPoints; ++ke ) + { + dMob_dT[ke] = mobWeights[ke] * m_dMob[seri[ke]][sesri[ke]][sei[ke]][DerivOffset::dT]; + } + } + + // add contribution from upstream cell mobility derivatives + for( integer ke = 0; ke < numFluxSupportPoints; ++ke ) + { + dFlux_dT[ke] += dMob_dT[ke] * potGrad; + } + + // Step 1.3: populate local jacobian + for( integer ke = 0; ke < numFluxSupportPoints; ++ke ) + { + localIndex const localDofIndexTemp = k[ke] * numDof + numDof - numSpecies - 1; + stack.localFluxJacobian[k[0]*numEqn][localDofIndexTemp] += m_dt * dFlux_dT[ke]; + stack.localFluxJacobian[k[1]*numEqn][localDofIndexTemp] -= m_dt * dFlux_dT[ke]; + } + + // Step 2: compute the derivatives of the speciesFlux wrt temperature + // ------------------------------------------------------------------- + real64 dSpeciesFlux_dT[numFluxSupportPoints][numSpecies]{}; + + { + // Step 2.1: compute the derivatives of the upstream density wrt temperature + // choose upstream cell + localIndex const k_up = (potGrad >= 0) ? 0 : 1; + + localIndex const er_up = seri[k_up]; + localIndex const esr_up = sesri[k_up]; + localIndex const ei_up = sei[k_up]; + + real64 const dDens_dTemp = m_dDens[er_up][esr_up][ei_up][0][DerivOffset::dT]; + + // Step 2.2: compute speciesFlux derivative wrt temperature + for( integer is = 0; is < numSpecies; ++is ) + { + real64 const aggregateConc_i = m_primarySpeciesMobileAggregateConc[er_up][esr_up][ei_up][0][is]; + + // real64 const dAggregateConc_i_dTemp = m_dPrimarySpeciesMobileAggregateConcentration_dTemp[er_up][esr_up][ei_up][is]; + // dSpeciesFlux_dT[k_up][is] += dAggregateConc_i_dTemp * fluxVal / fluidDens_up; + dSpeciesFlux_dT[k_up][is] += -aggregateConc_i * fluxVal * dDens_dTemp / (fluidDens_up * fluidDens_up); + + for( integer ke = 0; ke < numFluxSupportPoints; ++ke ) + { + dSpeciesFlux_dT[ke][is] += aggregateConc_i / fluidDens_up * dFlux_dT[ke]; + } + } + } + + // Step 2.3: populate local jacobian + for( integer is = 0; is < numSpecies; ++is ) + { + integer const eqIndex0 = k[0] * numEqn + numEqn - numSpecies + is; + integer const eqIndex1 = k[1] * numEqn + numEqn - numSpecies + is; + + for( integer ke = 0; ke < numFluxSupportPoints; ++ke ) + { + localIndex const localDofIndexTemp = k[ke] * numDof + numDof - numSpecies - 1; + + stack.localFluxJacobian[eqIndex0][localDofIndexTemp] += m_dt * dSpeciesFlux_dT[ke][is] * m_mobilePrimarySpeciesFlags[is]; + stack.localFluxJacobian[eqIndex1][localDofIndexTemp] -= m_dt * dSpeciesFlux_dT[ke][is] * m_mobilePrimarySpeciesFlags[is]; + } + } + + // Step 3: compute the enthalpy flux + // ---------------------------------- + real64 enthalpy = 0.0; + real64 dEnthalpy_dP[numFluxSupportPoints]{0.0, 0.0}; + real64 dEnthalpy_dT[numFluxSupportPoints]{0.0, 0.0}; + // Todo: to add the enthalpy derivatives wrt speciesConc if needed + // real64 dEnthalpy_dLogConc[numFluxSupportPoints][numSpecies]{}; + + if( alpha <= 0.0 || alpha >= 1.0 ) + { + localIndex const k_up = 1 - localIndex( fmax( fmin( alpha, 1.0 ), 0.0 ) ); + + enthalpy = m_enthalpy[seri[k_up]][sesri[k_up]][sei[k_up]][0]; + dEnthalpy_dP[k_up] = m_dEnthalpy[seri[k_up]][sesri[k_up]][sei[k_up]][0][DerivOffset::dP]; + dEnthalpy_dT[k_up] = m_dEnthalpy[seri[k_up]][sesri[k_up]][sei[k_up]][0][DerivOffset::dT]; + } + else + { + real64 const mobWeights[numFluxSupportPoints] = { alpha, 1.0 - alpha }; + for( integer ke = 0; ke < numFluxSupportPoints; ++ke ) + { + enthalpy += mobWeights[ke] * m_enthalpy[seri[ke]][sesri[ke]][sei[ke]][0]; + dEnthalpy_dP[ke] = mobWeights[ke] * m_dEnthalpy[seri[ke]][sesri[ke]][sei[ke]][0][DerivOffset::dP]; + dEnthalpy_dT[ke] = mobWeights[ke] * m_dEnthalpy[seri[ke]][sesri[ke]][sei[ke]][0][DerivOffset::dT]; + } + } + + stack.energyFlux += fluxVal * enthalpy; + + for( integer ke = 0; ke < numFluxSupportPoints; ++ke ) + { + stack.dEnergyFlux_dP[ke] += dFlux_dP[ke] * enthalpy; + stack.dEnergyFlux_dT[ke] += dFlux_dT[ke] * enthalpy; + } + + for( integer ke = 0; ke < numFluxSupportPoints; ++ke ) + { + stack.dEnergyFlux_dP[ke] += fluxVal * dEnthalpy_dP[ke]; + stack.dEnergyFlux_dT[ke] += fluxVal * dEnthalpy_dT[ke]; + } + + } ); + + // ***************************************************** + // Computation of the conduction term in the energy flux + // Note that the enthalpy term in the energy was computed above + // Note that this term is computed using an explicit treatment of conductivity for now + + // Step 1: compute the thermal transmissibilities at this face + // We follow how the thermal compositional multi-phase solver does to update the thermal transmissibility + m_stencilWrapper.computeWeights( iconn, + m_thermalConductivity, + m_dThermalCond_dT, + stack.thermalTransmissibility, + stack.dThermalTrans_dT ); + + localIndex k[numFluxSupportPoints]; + localIndex connectionIndex = 0; + + for( k[0] = 0; k[0] < stack.numFluxElems; ++k[0] ) + { + for( k[1] = k[0] + 1; k[1] < stack.numFluxElems; ++k[1] ) + { + real64 const thermalTrans[numFluxSupportPoints] = { stack.thermalTransmissibility[connectionIndex][0], stack.thermalTransmissibility[connectionIndex][1] }; + real64 const dThermalTrans_dT[numFluxSupportPoints] = { stack.dThermalTrans_dT[connectionIndex][0], stack.dThermalTrans_dT[connectionIndex][1] }; + + localIndex const seri[numFluxSupportPoints] = {m_seri( iconn, k[0] ), m_seri( iconn, k[1] )}; + localIndex const sesri[numFluxSupportPoints] = {m_sesri( iconn, k[0] ), m_sesri( iconn, k[1] )}; + localIndex const sei[numFluxSupportPoints] = {m_sei( iconn, k[0] ), m_sei( iconn, k[1] )}; + + // Step 2: compute temperature difference at the interface + for( integer ke = 0; ke < numFluxSupportPoints; ++ke ) + { + localIndex const er = seri[ke]; + localIndex const esr = sesri[ke]; + localIndex const ei = sei[ke]; + + stack.energyFlux += thermalTrans[ke] * m_temp[er][esr][ei]; + stack.dEnergyFlux_dT[ke] += thermalTrans[ke] + dThermalTrans_dT[ke] * m_temp[er][esr][ei]; + } + + integer const eqIndex0 = k[0] * numEqn + numEqn - numSpecies - 1; + integer const eqIndex1 = k[1] * numEqn + numEqn - numSpecies - 1; + + // add energyFlux and its derivatives to localFlux and localFluxJacobian + stack.localFlux[eqIndex0] += m_dt * stack.energyFlux; + stack.localFlux[eqIndex1] -= m_dt * stack.energyFlux; + + for( integer ke = 0; ke < numFluxSupportPoints; ++ke ) + { + integer const localDofIndexPres = k[ke] * numDof; + stack.localFluxJacobian[eqIndex0][localDofIndexPres] = m_dt * stack.dEnergyFlux_dP[ke]; + stack.localFluxJacobian[eqIndex1][localDofIndexPres] = -m_dt * stack.dEnergyFlux_dP[ke]; + integer const localDofIndexTemp = localDofIndexPres + numDof - numSpecies - 1; + stack.localFluxJacobian[eqIndex0][localDofIndexTemp] = m_dt * stack.dEnergyFlux_dT[ke]; + stack.localFluxJacobian[eqIndex1][localDofIndexTemp] = -m_dt * stack.dEnergyFlux_dT[ke]; + } + + connectionIndex++; + } + } + } + + /** + * @brief Compute the local flux contributions to the residual and Jacobian + * @param[in] iconn the connection index + * @param[inout] stack the stack variables + */ + GEOS_HOST_DEVICE + void computeDiffusion( localIndex const iconn, + StackVariables & stack ) const + { + Base::computeDiffusion( iconn, stack, [&] ( integer const is, + localIndex const (&k)[2], + localIndex const (&seri)[2], + localIndex const (&sesri)[2], + localIndex const (&sei)[2], + localIndex const connectionIndex, + localIndex const k_up ) + { + real64 dDiffusionFlux_dT[numFluxSupportPoints]{}; + real64 dSpeciesGrad_dT[numFluxSupportPoints]{}; + + // Calculate diffusion derivative wrt temperature + for( integer ke = 0; ke < numFluxSupportPoints; ++ke ) + { + localIndex const er = seri[ke]; + localIndex const esr = sesri[ke]; + localIndex const ei = sei[ke]; + + // dSpeciesGrad_dT[ke] += stack.diffusionTransmissibility[connectionIndex][ke] + // * m_dPrimarySpeciesMobileAggregateConcentration_dTemp[er][esr][ei][is]; + + dSpeciesGrad_dT[ke] += stack.dDiffusionTrans_dT[connectionIndex][ke] * m_primarySpeciesMobileAggregateConc[er][esr][ei][0][is]; + } + + for( integer ke = 0; ke < numFluxSupportPoints; ke++ ) + { + localIndex const er_up = seri[k_up]; + localIndex const esr_up = sesri[k_up]; + localIndex const ei_up = sei[k_up]; + + dDiffusionFlux_dT[ke] += m_referencePorosity[er_up][esr_up][ei_up] * dSpeciesGrad_dT[ke]; + } + + // populate local Jacobian + integer const eqIndex0 = k[0] * numEqn + numEqn - numSpecies + is; + integer const eqIndex1 = k[1] * numEqn + numEqn - numSpecies + is; + + for( integer ke = 0; ke < numFluxSupportPoints; ++ke ) + { + localIndex const localDofIndexTemp = k[ke] * numDof + numDof - numSpecies - 1; + stack.localFluxJacobian[eqIndex0][localDofIndexTemp] += m_dt * dDiffusionFlux_dT[ke] * m_mobilePrimarySpeciesFlags[is]; + stack.localFluxJacobian[eqIndex1][localDofIndexTemp] -= m_dt * dDiffusionFlux_dT[ke] * m_mobilePrimarySpeciesFlags[is]; + } + } ); + } + + /** + * @brief Performs the complete phase for the kernel. + * @param[in] iconn the connection index + * @param[inout] stack the stack variables + */ + GEOS_HOST_DEVICE + void complete( localIndex const iconn, + StackVariables & stack ) const + { + // Call Case::complete to assemble the mass balance equations + // In the lambda, add contribution to residual and jacobian into the energy balance equation + Base::complete( iconn, stack, [&] ( integer const i, + localIndex const localRow ) + { + // The no. of fluxes is equal to the no. of equations in m_localRhs and m_localMatrix + // Different from the one in compositional multi-phase flow, which has a volume balance eqn. + RAJA::atomicAdd( parallelDeviceAtomic{}, &AbstractBase::m_localRhs[localRow + numEqn - numSpecies - 1], stack.localFlux[i * numEqn + numEqn - numSpecies - 1] ); + + AbstractBase::m_localMatrix.addToRowBinarySearchUnsorted< parallelDeviceAtomic >( localRow + numEqn - numSpecies - 1, + stack.dofColIndices.data(), + stack.localFluxJacobian[i * numEqn + numEqn - numSpecies - 1].dataIfContiguous(), + stack.stencilSize * numDof ); + + } ); + } + +protected: + + /// Views on temperature + ElementViewConst< arrayView1d< real64 const > > const m_temp; + + /// Views on enthalpies + ElementViewConst< arrayView2d< real64 const, constitutive::singlefluid::USD_FLUID > > const m_enthalpy; + ElementViewConst< arrayView3d< real64 const, constitutive::singlefluid::USD_FLUID_DER > > const m_dEnthalpy; + + // /// Views on the derivative of primary species aggregate concentration wrt temperature + // ElementViewConst< arrayView2d< real64 const, compflow::USD_COMP > > const m_dPrimarySpeciesAggregateConc_dTemp; + + /// View on thermal conductivity + ElementViewConst< arrayView3d< real64 const > > m_thermalConductivity; + + /// View on derivatives of thermal conductivity w.r.t. temperature + ElementViewConst< arrayView3d< real64 const > > m_dThermalCond_dT; + +}; + +/** + * @class FluxComputeKernelFactory + */ +class FluxComputeKernelFactory +{ +public: + + /** + * @brief Create a new kernel and launch + * @tparam POLICY the policy used in the RAJA kernel + * @tparam STENCILWRAPPER the type of the stencil wrapper + * @param[in] numSpecies the number of primary species + * @param[in] hasDiffusion the flag of adding diffusion term + * @param[in] mobilePrimarySpeciesFlags the array of flags to indicate mobile primary species + * @param[in] rankOffset the offset of my MPI rank + * @param[in] dofKey string to get the element degrees of freedom numbers + * @param[in] solverName name of the solver (to name accessors) + * @param[in] elemManager reference to the element region manager + * @param[in] stencilWrapper reference to the stencil wrapper + * @param[in] dt time step size + * @param[inout] localMatrix the local CRS matrix + * @param[inout] localRhs the local right-hand side vector + */ + template< typename POLICY, typename STENCILWRAPPER > + static void + createAndLaunch( integer const numSpecies, + integer const hasDiffusion, + arrayView1d< integer const > const mobilePrimarySpeciesFlags, + globalIndex const rankOffset, + string const & dofKey, + string const & solverName, + ElementRegionManager const & elemManager, + STENCILWRAPPER const & stencilWrapper, + real64 const & dt, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) + { + singlePhaseReactiveBaseKernels::internal::kernelLaunchSelectorCompSwitch( numSpecies, [&]( auto NS ) + { + integer constexpr NUM_SPECIES = NS(); + integer constexpr NUM_DOF = 2+NS(); + integer constexpr NUM_EQN = 2+NS(); + + ElementRegionManager::ElementViewAccessor< arrayView1d< globalIndex const > > dofNumberAccessor = + elemManager.constructArrayViewAccessor< globalIndex, 1 >( dofKey ); + dofNumberAccessor.setName( solverName + "/accessors/" + dofKey ); + + using KernelType = FluxComputeKernel< NUM_SPECIES, NUM_EQN, NUM_DOF, STENCILWRAPPER, constitutive::ThermalCompressibleSinglePhaseFluid >; + typename KernelType::SinglePhaseFlowAccessors flowAccessors( elemManager, solverName ); + typename KernelType::ReactiveSinglePhaseFlowAccessors reactiveFlowAccessors( elemManager, solverName ); + typename KernelType::ThermalSinglePhaseFlowAccessors thermalFlowAccessors( elemManager, solverName ); + typename KernelType::SinglePhaseFluidAccessors fluidAccessors( elemManager, solverName ); + typename KernelType::ReactiveSinglePhaseFluidAccessors reactiveFluidAccessors( elemManager, solverName ); + typename KernelType::ThermalReactiveSinglePhaseFluidAccessors thermalFluidAccessors( elemManager, solverName ); + typename KernelType::PermeabilityAccessors permAccessors( elemManager, solverName ); + typename KernelType::DiffusionAccessors diffusionAccessors( elemManager, solverName ); + typename KernelType::PorosityAccessors porosityAccessors( elemManager, solverName ); + typename KernelType::ThermalConductivityAccessors thermalConductivityAccessors( elemManager, solverName ); + + KernelType kernel( rankOffset, stencilWrapper, dofNumberAccessor, + flowAccessors, reactiveFlowAccessors, thermalFlowAccessors, fluidAccessors, reactiveFluidAccessors, thermalFluidAccessors, + permAccessors, diffusionAccessors, porosityAccessors, thermalConductivityAccessors, + hasDiffusion, mobilePrimarySpeciesFlags, dt, localMatrix, localRhs ); + KernelType::template launch< POLICY >( stencilWrapper.size(), kernel ); + } ); + } +}; + +} // namespace thermalSinglePhaseReactiveFVMKernels + +} // namespace geos + +#endif //GEOS_PHYSICSSOLVERS_FLUIDFLOW_SINGLEPHASEREACTIVE_THERMALFLUXCOMPUTEKERNEL_HPP diff --git a/src/coreComponents/physicsSolvers/fluidFlow/kernels/singlePhase/reactive/ThermalSourceFluxComputeKernel.hpp b/src/coreComponents/physicsSolvers/fluidFlow/kernels/singlePhase/reactive/ThermalSourceFluxComputeKernel.hpp new file mode 100644 index 00000000000..5fa7f2c201c --- /dev/null +++ b/src/coreComponents/physicsSolvers/fluidFlow/kernels/singlePhase/reactive/ThermalSourceFluxComputeKernel.hpp @@ -0,0 +1,213 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * 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 ThermalSourceFluxComputeKernel.hpp + */ + +#ifndef GEOS_PHYSICSSOLVERS_FLUIDFLOW_SINGLEPHASEREACTIVE_THERMALSOURCEFLUXCOMPUTEKERNELS_HPP +#define GEOS_PHYSICSSOLVERS_FLUIDFLOW_SINGLEPHASEREACTIVE_THERMALSOURCEFLUXCOMPUTEKERNELS_HPP + +#include "physicsSolvers/fluidFlow/kernels/singlePhase/reactive/SourceFluxComputeKernel.hpp" + +namespace geos +{ + +namespace thermalSinglePhaseReactiveBaseKernels +{ + +/******************************** SourceFluxComputeKernel ********************************/ + +/** + * @class SourceFluxComputeKernel + * @brief Define the interface for the assembly kernel in charge of source flux + */ +template< integer NUM_DOF, integer NUM_SPECIES, typename BASE_FLUID_TYPE > +class SourceFluxComputeKernel : public singlePhaseReactiveBaseKernels::SourceFluxComputeKernel< NUM_DOF, NUM_SPECIES, BASE_FLUID_TYPE > +{ + +public: + + using Base = singlePhaseReactiveBaseKernels::SourceFluxComputeKernel< NUM_DOF, NUM_SPECIES, BASE_FLUID_TYPE >; + using Base::numSpecies; + using Base::numDof; + using Base::numEqn; + using Base::m_sizeScalingFactor; + using Base::m_primarySpeciesAggregateConcentration; + using Base::m_density; + using Base::m_dDensity; + using Base::m_localMatrix; + using Base::m_localRhs; + + using DerivOffset = constitutive::singlefluid::DerivativeOffsetC< 1 >; + + SourceFluxComputeKernel( globalIndex const rankOffset, + arrayView1d< globalIndex const > const dofNumber, + arrayView1d< integer const > const elemGhostRank, + arrayView1d< real64 const > const rhsContributionArrayView, + real64 const sizeScalingFactor, + constitutive::reactivefluid::ReactiveSinglePhaseFluid< BASE_FLUID_TYPE > const & fluid, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs, + RAJA::ReduceSum< parallelDeviceReduce, real64 > massProd ) + : + Base( rankOffset, + dofNumber, + elemGhostRank, + rhsContributionArrayView, + sizeScalingFactor, + fluid, + localMatrix, + localRhs, + massProd ), + m_enthalpy( fluid.enthalpy() ), + m_dEnthalpy( fluid.dEnthalpy() ) + {} + + /** + * @struct StackVariables + * @brief Kernel variables (dof numbers, jacobian and residual) located on the stack + */ + struct StackVariables : Base::StackVariables + { +public: + + GEOS_HOST_DEVICE + StackVariables() + : Base::StackVariables() + {} + + using Base::StackVariables::massRowIndex; + using Base::StackVariables::dofIndices; + using Base::StackVariables::localSpeciesJacobian; + using Base::StackVariables::totalInflowMass; + + /// Storage for the element local residual vector for energy row + real64 localEnergyRhs=0.0; + + /// Storage for the element local Jacobian matrix for energy row + real64 localEnergyJacobian[numDof]{}; + + }; + + /** + * @brief Compute the local source flux contributions to the residual and Jacobian + * @param[in] ei the element index + * @param[inout] stack the stack variables + * @param[in] kernelOp the function used to customize the kernel + */ + GEOS_HOST_DEVICE + void computeSourceFlux( localIndex const ei, + StackVariables & stack ) const + { + Base::computeSourceFlux( ei, stack ); + + real64 const scaledInflowMass = stack.totalInflowMass / m_sizeScalingFactor; + + stack.localEnergyRhs += m_enthalpy[ei][0] * scaledInflowMass; + stack.localEnergyJacobian[0] = scaledInflowMass * m_dEnthalpy[ei][0][DerivOffset::dP]; + stack.localEnergyJacobian[numDof-numSpecies-1] = scaledInflowMass * m_dEnthalpy[ei][0][DerivOffset::dT]; + + for( integer i = 0; i < numSpecies; ++i ) + { + stack.localSpeciesJacobian[i][numDof-numSpecies-1] += -m_primarySpeciesAggregateConcentration[ei][0][i] * m_dDensity[ei][0][DerivOffset::dT] / (m_density[ei][0] * m_density[ei][0]) * + scaledInflowMass; + } + } + + /** + * @brief Performs the complete phase for the kernel. + * @param[in] ei the element index + * @param[inout] stack the stack variables + */ + GEOS_HOST_DEVICE + void complete( localIndex const ei, + StackVariables & stack ) const + { + Base::complete( ei, stack ); + + if( stack.totalInflowMass > 0.0 ) + { + globalIndex const energyRowIndex = stack.massRowIndex + 1; + m_localRhs[energyRowIndex] += stack.localEnergyRhs; + + m_localMatrix.template addToRow< serialAtomic >( energyRowIndex, + stack.dofIndices, + stack.localEnergyJacobian, + numDof ); + } + } + +protected: + + /// Views on enthalpies + arrayView2d< real64 const, constitutive::singlefluid::USD_FLUID > const m_enthalpy; + arrayView3d< real64 const, constitutive::singlefluid::USD_FLUID_DER > const m_dEnthalpy; + +}; + +/** + * @class SourceFluxComputeKernelFactory + */ +class SourceFluxComputeKernelFactory +{ +public: + + /** + * @brief Create a new kernel and launch + * @tparam POLICY the policy used in the RAJA kernel + * @param[in] numSpecies the number of primary species + * @param[in] rankOffset the offset of my MPI rank + * @param[in] dofNumber the degress of freedom numbers + * @param[in] elemGhostRank the array of element ghost rank + * @param[in] targetSet the target set array + * @param[in] rhsContributionArrayView the rhs contribution array + * @param[in] sizeScalingFactor the size scaling factor + * @param[in] fluid the fluid model + * @param[inout] localMatrix the local CRS matrix + * @param[inout] localRhs the local right-hand side vector + * @param[inout] massProd the total mass produced + */ + template< typename POLICY, typename BASE_FLUID_TYPE > + static void + createAndLaunch( integer const numSpecies, + globalIndex const rankOffset, + arrayView1d< globalIndex const > const dofNumber, + arrayView1d< integer const > const elemGhostRank, + SortedArrayView< localIndex const > const targetSet, + arrayView1d< real64 const > const rhsContributionArrayView, + real64 const sizeScalingFactor, + constitutive::reactivefluid::ReactiveSinglePhaseFluid< BASE_FLUID_TYPE > const & fluid, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs, + RAJA::ReduceSum< parallelDeviceReduce, real64 > massProd ) + { + singlePhaseReactiveBaseKernels:: + internal::kernelLaunchSelectorCompSwitch( numSpecies, [&] ( auto NS ) + { + integer constexpr NUM_SPECIES = NS(); + integer constexpr NUM_DOF = 2+NS(); + + SourceFluxComputeKernel< NUM_DOF, NUM_SPECIES, BASE_FLUID_TYPE > kernel( rankOffset, dofNumber, elemGhostRank, rhsContributionArrayView, sizeScalingFactor, fluid, localMatrix, localRhs, + massProd ); + SourceFluxComputeKernel< NUM_DOF, NUM_SPECIES, BASE_FLUID_TYPE >::template launch< POLICY >( targetSet, kernel ); + } ); + } +}; +} // namespace thermalSinglePhaseReactiveBaseKernels + +} // namespace geos + +#endif //GEOS_PHYSICSSOLVERS_FLUIDFLOW_SINGLEPHASEREACTIVE_THERMALSOURCEFLUXCOMPUTEKERNELS_HPP diff --git a/src/coreComponents/schema/schema.xsd b/src/coreComponents/schema/schema.xsd index 98b399613cb..9c5a0188b78 100644 --- a/src/coreComponents/schema/schema.xsd +++ b/src/coreComponents/schema/schema.xsd @@ -483,6 +483,10 @@ + + + + @@ -569,10 +573,6 @@ - - - - @@ -975,12 +975,28 @@ - - + + + + + + + + + + - - + + + + + + + + + + @@ -2763,6 +2779,7 @@ Information output from lower logLevels is added with the desired log level + @@ -3655,8 +3672,9 @@ Information output from lower logLevels is added with the desired log level +Frequency of pressure update is set in SinglePhase/CompositionalMultiphaseStatistics definition. +Setting cycleFrequency='1' will update the pressure every timestep, note that is a lagged property in constraint propertiesNote the event associated with the statists task must be entered before the solver event. +--> @@ -5293,6 +5311,67 @@ Information output from lower logLevels is added with the desired log level + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -6038,7 +6117,6 @@ When set to `all` output both convergence & iteration information to a csv.--> - @@ -6198,29 +6276,6 @@ Information output from lower logLevels is added with the desired log level - - - - - - - - - - - - - - - - - - - - @@ -6510,8 +6565,12 @@ Information output from lower logLevels is added with the desired log level - - + + + + + + @@ -8158,49 +8217,129 @@ If you want to do a three-phase simulation, please use instead wettingIntermedia - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -8403,7 +8542,7 @@ The expected format is "{ waterMax, oilMax }", in that order--> - + diff --git a/src/coreComponents/schema/schema.xsd.other b/src/coreComponents/schema/schema.xsd.other index 56e70fab27c..4a2f9c949c6 100644 --- a/src/coreComponents/schema/schema.xsd.other +++ b/src/coreComponents/schema/schema.xsd.other @@ -600,6 +600,7 @@ A field can represent a physical variable. (pressure, temperature, global compos + @@ -1264,6 +1265,15 @@ A field can represent a physical variable. (pressure, temperature, global compos + + + + + + + + + @@ -1474,7 +1484,6 @@ A field can represent a physical variable. (pressure, temperature, global compos - @@ -1508,7 +1517,6 @@ A field can represent a physical variable. (pressure, temperature, global compos - @@ -1647,8 +1655,12 @@ A field can represent a physical variable. (pressure, temperature, global compos - - + + + + + + @@ -3063,113 +3075,112 @@ A field can represent a physical variable. (pressure, temperature, global compos - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + - - - - - - - + + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + - - - - - - - + + + diff --git a/src/docs/doxygen/Doxyfile.in b/src/docs/doxygen/Doxyfile.in index f3869f78f70..491e9d0b754 100644 --- a/src/docs/doxygen/Doxyfile.in +++ b/src/docs/doxygen/Doxyfile.in @@ -821,7 +821,8 @@ RECURSIVE = YES EXCLUDE = @PROJECT_SOURCE_DIR@/coreComponents/dataRepository/BufferOps_inline.hpp \ @PROJECT_SOURCE_DIR@/coreComponents/common/MpiWrapper.hpp \ @PROJECT_SOURCE_DIR@/coreComponents/common/GEOS_RAJA_Interface.hpp \ - @PROJECT_SOURCE_DIR@/coreComponents/constitutive/PVTPackage + @PROJECT_SOURCE_DIR@/coreComponents/constitutive/PVTPackage \ + @PROJECT_SOURCE_DIR@/coreComponents/constitutive/HPCReact # The EXCLUDE_SYMLINKS tag can be used to select whether or not files or # directories that are symbolic links (a Unix file system feature) are excluded