diff --git a/.gitmodules b/.gitmodules index 5552178f5ce..b2e33109a09 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 = git@github.com:GEOS-DEV/HPCReact.git diff --git a/inputFiles/chemoMechanics/ChemoMechanics_CarbonateSystem_base.xml b/inputFiles/chemoMechanics/ChemoMechanics_CarbonateSystem_base.xml new file mode 100644 index 00000000000..0f79543cf3a --- /dev/null +++ b/inputFiles/chemoMechanics/ChemoMechanics_CarbonateSystem_base.xml @@ -0,0 +1,139 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inputFiles/chemoMechanics/ChemoMechanics_CarbonateSystem_explicitUpdate_1DBar.xml b/inputFiles/chemoMechanics/ChemoMechanics_CarbonateSystem_explicitUpdate_1DBar.xml new file mode 100644 index 00000000000..d33afd9f178 --- /dev/null +++ b/inputFiles/chemoMechanics/ChemoMechanics_CarbonateSystem_explicitUpdate_1DBar.xml @@ -0,0 +1,85 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inputFiles/chemoMechanics/ChemoMechanics_CarbonateSystem_initialAggregate.xml b/inputFiles/chemoMechanics/ChemoMechanics_CarbonateSystem_initialAggregate.xml new file mode 100644 index 00000000000..1d5feb21230 --- /dev/null +++ b/inputFiles/chemoMechanics/ChemoMechanics_CarbonateSystem_initialAggregate.xml @@ -0,0 +1,133 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inputFiles/chemoMechanics/ChemoMechanics_CarbonateSystem_initialPrimaryConc.xml b/inputFiles/chemoMechanics/ChemoMechanics_CarbonateSystem_initialPrimaryConc.xml new file mode 100644 index 00000000000..8ce64e0159d --- /dev/null +++ b/inputFiles/chemoMechanics/ChemoMechanics_CarbonateSystem_initialPrimaryConc.xml @@ -0,0 +1,133 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inputFiles/singlePhaseFlow/reactiveTransport/1DCarbonateSystem/mixedReactionCarbonateSystemPorosityUpdate_base.xml b/inputFiles/singlePhaseFlow/reactiveTransport/1DCarbonateSystem/mixedReactionCarbonateSystemPorosityUpdate_base.xml new file mode 100644 index 00000000000..fd414cd5ba2 --- /dev/null +++ b/inputFiles/singlePhaseFlow/reactiveTransport/1DCarbonateSystem/mixedReactionCarbonateSystemPorosityUpdate_base.xml @@ -0,0 +1,162 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inputFiles/singlePhaseFlow/reactiveTransport/1DCarbonateSystem/mixedReactionCarbonateSystemPorosityUpdate_main.xml b/inputFiles/singlePhaseFlow/reactiveTransport/1DCarbonateSystem/mixedReactionCarbonateSystemPorosityUpdate_main.xml new file mode 100644 index 00000000000..26074ddb384 --- /dev/null +++ b/inputFiles/singlePhaseFlow/reactiveTransport/1DCarbonateSystem/mixedReactionCarbonateSystemPorosityUpdate_main.xml @@ -0,0 +1,85 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inputFiles/singlePhaseFlow/reactiveTransport/1DCarbonateSystem/mixedReactionCarbonateSystem_base.xml b/inputFiles/singlePhaseFlow/reactiveTransport/1DCarbonateSystem/mixedReactionCarbonateSystem_base.xml new file mode 100644 index 00000000000..7ef7360e004 --- /dev/null +++ b/inputFiles/singlePhaseFlow/reactiveTransport/1DCarbonateSystem/mixedReactionCarbonateSystem_base.xml @@ -0,0 +1,153 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inputFiles/singlePhaseFlow/reactiveTransport/1DCarbonateSystem/mixedReactionCarbonateSystem_initialAggregate.xml b/inputFiles/singlePhaseFlow/reactiveTransport/1DCarbonateSystem/mixedReactionCarbonateSystem_initialAggregate.xml new file mode 100644 index 00000000000..767b9387978 --- /dev/null +++ b/inputFiles/singlePhaseFlow/reactiveTransport/1DCarbonateSystem/mixedReactionCarbonateSystem_initialAggregate.xml @@ -0,0 +1,133 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inputFiles/singlePhaseFlow/reactiveTransport/1DCarbonateSystem/mixedReactionCarbonateSystem_initialPrimaryConc.xml b/inputFiles/singlePhaseFlow/reactiveTransport/1DCarbonateSystem/mixedReactionCarbonateSystem_initialPrimaryConc.xml new file mode 100644 index 00000000000..9fe0e5abeae --- /dev/null +++ b/inputFiles/singlePhaseFlow/reactiveTransport/1DCarbonateSystem/mixedReactionCarbonateSystem_initialPrimaryConc.xml @@ -0,0 +1,133 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inputFiles/singlePhaseFlow/reactiveTransport/1DCarbonateSystem/mixedReactionCarbonateSystem_logConc.xml b/inputFiles/singlePhaseFlow/reactiveTransport/1DCarbonateSystem/mixedReactionCarbonateSystem_logConc.xml new file mode 100644 index 00000000000..da291b97da0 --- /dev/null +++ b/inputFiles/singlePhaseFlow/reactiveTransport/1DCarbonateSystem/mixedReactionCarbonateSystem_logConc.xml @@ -0,0 +1,62 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/inputFiles/singlePhaseFlow/reactiveTransport/1DCarbonateSystem/mixedReactionCarbonateSystem_main.xml b/inputFiles/singlePhaseFlow/reactiveTransport/1DCarbonateSystem/mixedReactionCarbonateSystem_main.xml new file mode 100644 index 00000000000..3d6a6de7754 --- /dev/null +++ b/inputFiles/singlePhaseFlow/reactiveTransport/1DCarbonateSystem/mixedReactionCarbonateSystem_main.xml @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inputFiles/singlePhaseFlow/reactiveTransport/ChainGenericSerial1D/ChainSerial_allKinetic_base.xml b/inputFiles/singlePhaseFlow/reactiveTransport/ChainGenericSerial1D/ChainSerial_allKinetic_base.xml new file mode 100644 index 00000000000..27df60a6124 --- /dev/null +++ b/inputFiles/singlePhaseFlow/reactiveTransport/ChainGenericSerial1D/ChainSerial_allKinetic_base.xml @@ -0,0 +1,74 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/inputFiles/singlePhaseFlow/reactiveTransport/ChainGenericSerial1D/ChainSerial_allKinetic_main.xml b/inputFiles/singlePhaseFlow/reactiveTransport/ChainGenericSerial1D/ChainSerial_allKinetic_main.xml new file mode 100644 index 00000000000..d2d3be7c2b1 --- /dev/null +++ b/inputFiles/singlePhaseFlow/reactiveTransport/ChainGenericSerial1D/ChainSerial_allKinetic_main.xml @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inputFiles/singlePhaseFlow/reactiveTransport/ChainGenericSerial1D/ChainSerial_base.xml b/inputFiles/singlePhaseFlow/reactiveTransport/ChainGenericSerial1D/ChainSerial_base.xml new file mode 100644 index 00000000000..4bc502df425 --- /dev/null +++ b/inputFiles/singlePhaseFlow/reactiveTransport/ChainGenericSerial1D/ChainSerial_base.xml @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inputFiles/singlePhaseFlow/reactiveTransport/ChainGenericSerial1D/ChainSerial_initialAggregate.xml b/inputFiles/singlePhaseFlow/reactiveTransport/ChainGenericSerial1D/ChainSerial_initialAggregate.xml new file mode 100644 index 00000000000..a72597cb050 --- /dev/null +++ b/inputFiles/singlePhaseFlow/reactiveTransport/ChainGenericSerial1D/ChainSerial_initialAggregate.xml @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + + + + + + diff --git a/inputFiles/singlePhaseFlow/reactiveTransport/ChainGenericSerial1D/ChainSerial_logConc.xml b/inputFiles/singlePhaseFlow/reactiveTransport/ChainGenericSerial1D/ChainSerial_logConc.xml new file mode 100644 index 00000000000..0cb9d89de44 --- /dev/null +++ b/inputFiles/singlePhaseFlow/reactiveTransport/ChainGenericSerial1D/ChainSerial_logConc.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + + + diff --git a/inputFiles/singlePhaseFlow/reactiveTransport/MoMaSBenchmarkEasy1D/MoMaS_advective_base.xml b/inputFiles/singlePhaseFlow/reactiveTransport/MoMaSBenchmarkEasy1D/MoMaS_advective_base.xml new file mode 100644 index 00000000000..220cbce513c --- /dev/null +++ b/inputFiles/singlePhaseFlow/reactiveTransport/MoMaSBenchmarkEasy1D/MoMaS_advective_base.xml @@ -0,0 +1,102 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/inputFiles/singlePhaseFlow/reactiveTransport/MoMaSBenchmarkEasy1D/MoMaS_advective_main.xml b/inputFiles/singlePhaseFlow/reactiveTransport/MoMaSBenchmarkEasy1D/MoMaS_advective_main.xml new file mode 100644 index 00000000000..f44b3fde1d0 --- /dev/null +++ b/inputFiles/singlePhaseFlow/reactiveTransport/MoMaSBenchmarkEasy1D/MoMaS_advective_main.xml @@ -0,0 +1,93 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inputFiles/singlePhaseFlow/reactiveTransport/MoMaSBenchmarkEasy1D/MoMaS_base.xml b/inputFiles/singlePhaseFlow/reactiveTransport/MoMaSBenchmarkEasy1D/MoMaS_base.xml new file mode 100644 index 00000000000..aa3cf8c3932 --- /dev/null +++ b/inputFiles/singlePhaseFlow/reactiveTransport/MoMaSBenchmarkEasy1D/MoMaS_base.xml @@ -0,0 +1,133 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inputFiles/singlePhaseFlow/reactiveTransport/MoMaSBenchmarkEasy1D/MoMaS_diffusive_base.xml b/inputFiles/singlePhaseFlow/reactiveTransport/MoMaSBenchmarkEasy1D/MoMaS_diffusive_base.xml new file mode 100644 index 00000000000..311fa6e9d4a --- /dev/null +++ b/inputFiles/singlePhaseFlow/reactiveTransport/MoMaSBenchmarkEasy1D/MoMaS_diffusive_base.xml @@ -0,0 +1,102 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inputFiles/singlePhaseFlow/reactiveTransport/MoMaSBenchmarkEasy1D/MoMaS_diffusive_main.xml b/inputFiles/singlePhaseFlow/reactiveTransport/MoMaSBenchmarkEasy1D/MoMaS_diffusive_main.xml new file mode 100644 index 00000000000..09cdc2ef3f3 --- /dev/null +++ b/inputFiles/singlePhaseFlow/reactiveTransport/MoMaSBenchmarkEasy1D/MoMaS_diffusive_main.xml @@ -0,0 +1,93 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inputFiles/singlePhaseFlow/reactiveTransport/MoMaSBenchmarkEasy1D/MoMaS_initialAggregate.xml b/inputFiles/singlePhaseFlow/reactiveTransport/MoMaSBenchmarkEasy1D/MoMaS_initialAggregate.xml new file mode 100644 index 00000000000..d17545e2d17 --- /dev/null +++ b/inputFiles/singlePhaseFlow/reactiveTransport/MoMaSBenchmarkEasy1D/MoMaS_initialAggregate.xml @@ -0,0 +1,143 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inputFiles/singlePhaseFlow/reactiveTransport/MoMaSBenchmarkEasy1D/MoMaS_initialPrimaryConc.xml b/inputFiles/singlePhaseFlow/reactiveTransport/MoMaSBenchmarkEasy1D/MoMaS_initialPrimaryConc.xml new file mode 100644 index 00000000000..0c996c4f429 --- /dev/null +++ b/inputFiles/singlePhaseFlow/reactiveTransport/MoMaSBenchmarkEasy1D/MoMaS_initialPrimaryConc.xml @@ -0,0 +1,143 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inputFiles/singlePhaseFlow/reactiveTransport/MoMaSBenchmarkEasy1D/MoMaS_logConc.xml b/inputFiles/singlePhaseFlow/reactiveTransport/MoMaSBenchmarkEasy1D/MoMaS_logConc.xml new file mode 100644 index 00000000000..b694ac4c669 --- /dev/null +++ b/inputFiles/singlePhaseFlow/reactiveTransport/MoMaSBenchmarkEasy1D/MoMaS_logConc.xml @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + diff --git a/inputFiles/singlePhaseFlow/reactiveTransport/MoMaSBenchmarkEasy2D/MoMaS_2DAdvective_base.xml b/inputFiles/singlePhaseFlow/reactiveTransport/MoMaSBenchmarkEasy2D/MoMaS_2DAdvective_base.xml new file mode 100644 index 00000000000..a7f6e107b6d --- /dev/null +++ b/inputFiles/singlePhaseFlow/reactiveTransport/MoMaSBenchmarkEasy2D/MoMaS_2DAdvective_base.xml @@ -0,0 +1,103 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/inputFiles/singlePhaseFlow/reactiveTransport/MoMaSBenchmarkEasy2D/MoMaS_2DAdvective_main.xml b/inputFiles/singlePhaseFlow/reactiveTransport/MoMaSBenchmarkEasy2D/MoMaS_2DAdvective_main.xml new file mode 100644 index 00000000000..756a23901ab --- /dev/null +++ b/inputFiles/singlePhaseFlow/reactiveTransport/MoMaSBenchmarkEasy2D/MoMaS_2DAdvective_main.xml @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inputFiles/singlePhaseFlow/reactiveTransport/MoMaSBenchmarkEasy2D/MoMaS_base.xml b/inputFiles/singlePhaseFlow/reactiveTransport/MoMaSBenchmarkEasy2D/MoMaS_base.xml new file mode 100644 index 00000000000..7c4059813cc --- /dev/null +++ b/inputFiles/singlePhaseFlow/reactiveTransport/MoMaSBenchmarkEasy2D/MoMaS_base.xml @@ -0,0 +1,81 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inputFiles/singlePhaseFlow/reactiveTransport/MoMaSBenchmarkEasy2D/MoMaS_initialAggregate.xml b/inputFiles/singlePhaseFlow/reactiveTransport/MoMaSBenchmarkEasy2D/MoMaS_initialAggregate.xml new file mode 100644 index 00000000000..d17545e2d17 --- /dev/null +++ b/inputFiles/singlePhaseFlow/reactiveTransport/MoMaSBenchmarkEasy2D/MoMaS_initialAggregate.xml @@ -0,0 +1,143 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inputFiles/singlePhaseFlow/reactiveTransport/MoMaSBenchmarkEasy2D/MoMaS_initialPrimaryConc.xml b/inputFiles/singlePhaseFlow/reactiveTransport/MoMaSBenchmarkEasy2D/MoMaS_initialPrimaryConc.xml new file mode 100644 index 00000000000..0c996c4f429 --- /dev/null +++ b/inputFiles/singlePhaseFlow/reactiveTransport/MoMaSBenchmarkEasy2D/MoMaS_initialPrimaryConc.xml @@ -0,0 +1,143 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inputFiles/singlePhaseFlow/reactiveTransport/MoMaSBenchmarkEasy2D/MoMaS_logConc.xml b/inputFiles/singlePhaseFlow/reactiveTransport/MoMaSBenchmarkEasy2D/MoMaS_logConc.xml new file mode 100644 index 00000000000..b694ac4c669 --- /dev/null +++ b/inputFiles/singlePhaseFlow/reactiveTransport/MoMaSBenchmarkEasy2D/MoMaS_logConc.xml @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + diff --git a/inputFiles/singlePhaseFlow/reactiveTransport/MoMaSBenchmarkMedium1D/MoMaS_advective_base.xml b/inputFiles/singlePhaseFlow/reactiveTransport/MoMaSBenchmarkMedium1D/MoMaS_advective_base.xml new file mode 100644 index 00000000000..2e180c91c2c --- /dev/null +++ b/inputFiles/singlePhaseFlow/reactiveTransport/MoMaSBenchmarkMedium1D/MoMaS_advective_base.xml @@ -0,0 +1,102 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/inputFiles/singlePhaseFlow/reactiveTransport/MoMaSBenchmarkMedium1D/MoMaS_advective_main.xml b/inputFiles/singlePhaseFlow/reactiveTransport/MoMaSBenchmarkMedium1D/MoMaS_advective_main.xml new file mode 100644 index 00000000000..ab2396f9633 --- /dev/null +++ b/inputFiles/singlePhaseFlow/reactiveTransport/MoMaSBenchmarkMedium1D/MoMaS_advective_main.xml @@ -0,0 +1,93 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inputFiles/singlePhaseFlow/reactiveTransport/MoMaSBenchmarkMedium1D/MoMaS_base.xml b/inputFiles/singlePhaseFlow/reactiveTransport/MoMaSBenchmarkMedium1D/MoMaS_base.xml new file mode 100644 index 00000000000..f1ed51c7534 --- /dev/null +++ b/inputFiles/singlePhaseFlow/reactiveTransport/MoMaSBenchmarkMedium1D/MoMaS_base.xml @@ -0,0 +1,151 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inputFiles/singlePhaseFlow/reactiveTransport/MoMaSBenchmarkMedium1D/MoMaS_initialAggregate.xml b/inputFiles/singlePhaseFlow/reactiveTransport/MoMaSBenchmarkMedium1D/MoMaS_initialAggregate.xml new file mode 100644 index 00000000000..2fb76a9a089 --- /dev/null +++ b/inputFiles/singlePhaseFlow/reactiveTransport/MoMaSBenchmarkMedium1D/MoMaS_initialAggregate.xml @@ -0,0 +1,143 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inputFiles/singlePhaseFlow/reactiveTransport/MoMaSBenchmarkMedium1D/MoMaS_initialPrimaryConc.xml b/inputFiles/singlePhaseFlow/reactiveTransport/MoMaSBenchmarkMedium1D/MoMaS_initialPrimaryConc.xml new file mode 100644 index 00000000000..01742f28856 --- /dev/null +++ b/inputFiles/singlePhaseFlow/reactiveTransport/MoMaSBenchmarkMedium1D/MoMaS_initialPrimaryConc.xml @@ -0,0 +1,143 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inputFiles/singlePhaseFlow/reactiveTransport/MoMaSBenchmarkMedium1D/MoMaS_logConc.xml b/inputFiles/singlePhaseFlow/reactiveTransport/MoMaSBenchmarkMedium1D/MoMaS_logConc.xml new file mode 100644 index 00000000000..1b39e7f1cfa --- /dev/null +++ b/inputFiles/singlePhaseFlow/reactiveTransport/MoMaSBenchmarkMedium1D/MoMaS_logConc.xml @@ -0,0 +1,89 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inputFiles/singlePhaseFlow/reactiveTransport/MoMaSSmoke/MoMaS_base.xml b/inputFiles/singlePhaseFlow/reactiveTransport/MoMaSSmoke/MoMaS_base.xml new file mode 100644 index 00000000000..d68d6f952a9 --- /dev/null +++ b/inputFiles/singlePhaseFlow/reactiveTransport/MoMaSSmoke/MoMaS_base.xml @@ -0,0 +1,125 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inputFiles/singlePhaseFlow/reactiveTransport/MoMaSSmoke/MoMaS_initialAggregate.xml b/inputFiles/singlePhaseFlow/reactiveTransport/MoMaSSmoke/MoMaS_initialAggregate.xml new file mode 100644 index 00000000000..67937e658db --- /dev/null +++ b/inputFiles/singlePhaseFlow/reactiveTransport/MoMaSSmoke/MoMaS_initialAggregate.xml @@ -0,0 +1,97 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inputFiles/singlePhaseFlow/reactiveTransport/MoMaSSmoke/MoMaS_initialPrimaryConc.xml b/inputFiles/singlePhaseFlow/reactiveTransport/MoMaSSmoke/MoMaS_initialPrimaryConc.xml new file mode 100644 index 00000000000..d876342094d --- /dev/null +++ b/inputFiles/singlePhaseFlow/reactiveTransport/MoMaSSmoke/MoMaS_initialPrimaryConc.xml @@ -0,0 +1,98 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inputFiles/singlePhaseFlow/reactiveTransport/MoMaSSmoke/MoMaS_logConc.xml b/inputFiles/singlePhaseFlow/reactiveTransport/MoMaSSmoke/MoMaS_logConc.xml new file mode 100644 index 00000000000..b694ac4c669 --- /dev/null +++ b/inputFiles/singlePhaseFlow/reactiveTransport/MoMaSSmoke/MoMaS_logConc.xml @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + diff --git a/inputFiles/singlePhaseFlow/reactiveTransport/MoMaSSmoke/MoMaS_main.xml b/inputFiles/singlePhaseFlow/reactiveTransport/MoMaSSmoke/MoMaS_main.xml new file mode 100644 index 00000000000..2700613496e --- /dev/null +++ b/inputFiles/singlePhaseFlow/reactiveTransport/MoMaSSmoke/MoMaS_main.xml @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + 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/constitutive/CMakeLists.txt b/src/coreComponents/constitutive/CMakeLists.txt index c835c57966f..0f3409902f5 100644 --- a/src/coreComponents/constitutive/CMakeLists.txt +++ b/src/coreComponents/constitutive/CMakeLists.txt @@ -125,12 +125,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 @@ -191,8 +187,10 @@ set( constitutive_headers solid/InvariantDecompositions.hpp solid/PorousDamageSolid.hpp solid/PerfectlyPlastic.hpp + solid/PorousReactiveSolid.hpp solid/PorousSolid.hpp solid/PropertyConversions.hpp + solid/ReactiveSolid.hpp solid/SolidBase.hpp solid/SolidUtilities.hpp solid/SolidInternalEnergy.hpp @@ -208,6 +206,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 @@ -282,11 +281,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 @@ -335,7 +329,9 @@ set( constitutive_sources solid/ElasticOrthotropic.cpp solid/PorousDamageSolid.cpp solid/PerfectlyPlastic.cpp + solid/PorousReactiveSolid.cpp solid/PorousSolid.cpp + solid/ReactiveSolid.cpp solid/SolidBase.cpp solid/SolidInternalEnergy.cpp solid/CeramicDamage.cpp @@ -343,6 +339,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 @@ -367,6 +364,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 48467e5c1d8..1b8bd91fe79 100644 --- a/src/coreComponents/constitutive/ConstitutivePassThru.hpp +++ b/src/coreComponents/constitutive/ConstitutivePassThru.hpp @@ -36,13 +36,16 @@ #include "solid/ElasticIsotropicPressureDependent.hpp" #include "solid/ElasticTransverseIsotropic.hpp" #include "solid/ElasticOrthotropic.hpp" +#include "solid/PorousReactiveSolid.hpp" #include "solid/PorousSolid.hpp" #include "solid/PorousDamageSolid.hpp" #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" @@ -337,6 +340,21 @@ struct ConstitutivePassThru< PorousDamageSolidBase > } }; +/** + * Specialization for the PorousReactiveSolid models. + */ +template<> +struct ConstitutivePassThru< PorousReactiveSolidBase > +{ + template< typename LAMBDA > + static void execute( ConstitutiveBase & constitutiveRelation, LAMBDA && lambda ) + { + ConstitutivePassThruHandler< PorousReactiveSolid< ElasticIsotropic, ConstantPermeability >, + PorousReactiveSolid< ElasticIsotropic, CarmanKozenyPermeability > >::execute( constitutiveRelation, + std::forward< LAMBDA >( lambda ) ); + } +}; + /** * Specialization for the CompressibleSolid models. */ @@ -372,6 +390,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. */ @@ -412,6 +458,8 @@ struct ConstitutivePassThru< CoupledSolidBase > CompressibleSolid< PressurePorosity, PressurePermeability >, CompressibleSolid< PressurePorosity, SlipDependentPermeability >, CompressibleSolid< PressurePorosity, WillisRichardsPermeability >, + PorousReactiveSolid< ElasticIsotropic, ConstantPermeability >, + PorousReactiveSolid< ElasticIsotropic, CarmanKozenyPermeability >, PorousSolid< DruckerPragerExtended, ConstantPermeability >, PorousSolid< ModifiedCamClay, ConstantPermeability >, PorousSolid< DelftEgg, ConstantPermeability >, @@ -436,8 +484,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 > @@ -450,6 +501,8 @@ struct ConstitutivePassThru< CoupledSolidBase > CompressibleSolid< PressurePorosity, PressurePermeability >, CompressibleSolid< PressurePorosity, SlipDependentPermeability >, CompressibleSolid< PressurePorosity, WillisRichardsPermeability >, + PorousReactiveSolid< ElasticIsotropic, ConstantPermeability >, + PorousReactiveSolid< ElasticIsotropic, CarmanKozenyPermeability >, PorousSolid< DruckerPragerExtended, ConstantPermeability >, PorousSolid< ModifiedCamClay, ConstantPermeability >, PorousSolid< DelftEgg, ConstantPermeability >, @@ -474,8 +527,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..d8dd4b7de58 --- /dev/null +++ b/src/coreComponents/constitutive/HPCReact @@ -0,0 +1 @@ +Subproject commit d8dd4b7de58cdf9f37f63042fab18ea4777991fb 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/CoupledSolid.hpp b/src/coreComponents/constitutive/solid/CoupledSolid.hpp index 93e26caa84a..244c899fee7 100644 --- a/src/coreComponents/constitutive/solid/CoupledSolid.hpp +++ b/src/coreComponents/constitutive/solid/CoupledSolid.hpp @@ -22,6 +22,7 @@ #define GEOS_CONSTITUTIVE_SOLID_COUPLEDSOLID_HPP_ #include "constitutive/solid/CoupledSolidBase.hpp" +#include "constitutive/fluid/reactivefluid/ReactiveFluidLayouts.hpp" namespace geos { @@ -105,6 +106,25 @@ class CoupledSolidUpdates temperature, temperature_k, temperature_n ); } + GEOS_HOST_DEVICE + virtual void updateStateFromPressureTemperatureAndReactions( localIndex const k, + localIndex const q, + real64 const & pressure, + real64 const & temperature, + arraySlice1d< real64 const, compflow::USD_COMP - 1 > const & kineticReactionMolarIncrements ) const + { + GEOS_UNUSED_VAR( k, q, pressure, temperature, kineticReactionMolarIncrements ); + } + + GEOS_HOST_DEVICE + virtual 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 + { + GEOS_UNUSED_VAR( k, q, initialSurfaceArea, surfaceArea ); + } + GEOS_HOST_DEVICE virtual real64 getShearModulus( localIndex const k ) const { diff --git a/src/coreComponents/constitutive/solid/PorousReactiveSolid.cpp b/src/coreComponents/constitutive/solid/PorousReactiveSolid.cpp new file mode 100644 index 00000000000..fb36db2a959 --- /dev/null +++ b/src/coreComponents/constitutive/solid/PorousReactiveSolid.cpp @@ -0,0 +1,57 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * 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 PorousReactiveSolid.cpp + */ + +#include "PorousReactiveSolid.hpp" +#include "ElasticIsotropic.hpp" +#include "constitutive/permeability/ConstantPermeability.hpp" +#include "constitutive/permeability/CarmanKozenyPermeability.hpp" + +namespace geos +{ + +using namespace dataRepository; + +namespace constitutive +{ + +template< typename SOLID_TYPE, + typename PERM_TYPE > +PorousReactiveSolid< SOLID_TYPE, PERM_TYPE >::PorousReactiveSolid( string const & name, Group * const parent ): + CoupledSolid< SOLID_TYPE, ReactivePorosity, PERM_TYPE >( name, parent ) +{} + +template< typename SOLID_TYPE, + typename PERM_TYPE > +void PorousReactiveSolid< SOLID_TYPE, PERM_TYPE >::initializeState() const +{ + CoupledSolid< SOLID_TYPE, ReactivePorosity, PERM_TYPE >::initializeState(); +} + +// Register all PorousReactiveSolid model types. +typedef PorousReactiveSolid< ElasticIsotropic, ConstantPermeability > PorousReactiveElasticIsotropicConstant; +typedef PorousReactiveSolid< ElasticIsotropic, CarmanKozenyPermeability > PorousReactiveElasticIsotropicCK; + + +REGISTER_CATALOG_ENTRY( ConstitutiveBase, PorousReactiveElasticIsotropicConstant, string const &, Group * const ) +REGISTER_CATALOG_ENTRY( ConstitutiveBase, PorousReactiveElasticIsotropicCK, string const &, Group * const ) + + +} +} /* namespace geos */ diff --git a/src/coreComponents/constitutive/solid/PorousReactiveSolid.hpp b/src/coreComponents/constitutive/solid/PorousReactiveSolid.hpp new file mode 100644 index 00000000000..ccebb893ea3 --- /dev/null +++ b/src/coreComponents/constitutive/solid/PorousReactiveSolid.hpp @@ -0,0 +1,315 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * 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 PorousReactiveSolid.hpp + */ + +#ifndef GEOS_CONSTITUTIVE_SOLID_POROUSREACTIVESOLID_HPP_ +#define GEOS_CONSTITUTIVE_SOLID_POROUSREACTIVESOLID_HPP_ + +#include "constitutive/solid/CoupledSolid.hpp" +#include "constitutive/solid/porosity/ReactivePorosity.hpp" +#include "constitutive/solid/SolidBase.hpp" +#include "constitutive/permeability/ConstantPermeability.hpp" + +#include "constitutive/fluid/reactivefluid/ReactiveFluidLayouts.hpp" + +namespace geos +{ +namespace constitutive +{ + +/** + * @brief Provides kernel-callable constitutive update routines + * + * + * @tparam SOLID_TYPE type of the porosity model + */ +template< typename SOLID_TYPE, + typename PERM_TYPE > +class PorousReactiveSolidUpdates : public CoupledSolidUpdates< SOLID_TYPE, ReactivePorosity, PERM_TYPE > +{ +public: + + using DiscretizationOps = typename SOLID_TYPE::KernelWrapper::DiscretizationOps; + + /** + * @brief Constructor + */ + PorousReactiveSolidUpdates( SOLID_TYPE const & solidModel, + ReactivePorosity const & porosityModel, + PERM_TYPE const & permModel ): + CoupledSolidUpdates< SOLID_TYPE, ReactivePorosity, PERM_TYPE >( solidModel, porosityModel, permModel ) + {} + + GEOS_HOST_DEVICE + virtual void updateStateFromPressureTemperatureAndReactions( localIndex const k, + localIndex const q, + real64 const & pressure, + real64 const & temperature, + arraySlice1d< real64 const, compflow::USD_COMP - 1 > const & kineticReactionMolarIncrements ) const override final + { + GEOS_UNUSED_VAR( temperature ); + + m_porosityUpdate.updateFromReactions( k, q, kineticReactionMolarIncrements ); + real64 const porosity = m_porosityUpdate.getPorosity( k, q ); + m_permUpdate.updateFromPressureAndPorosity( k, q, pressure, porosity ); + } + + GEOS_HOST_DEVICE + virtual 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 override final + { + 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 ); + } + } + + GEOS_HOST_DEVICE + void smallStrainUpdateExplicitChemoMechanics( localIndex const k, + localIndex const q, + real64 const & timeIncrement, + real64 const & pressure, + real64 const & pressure_n, + real64 const & temperature, + real64 const & temperature_n, + real64 const & referenceTemperature, + arraySlice1d< real64 const, compflow::USD_COMP - 1 > mineralReactionMolarIncrements, + real64 const ( &strainIncrement )[6], + real64 ( & totalStress )[6], + DiscretizationOps & stiffness ) const + { + GEOS_UNUSED_VAR( pressure_n, referenceTemperature ); + + real64 anelasticStrainIncrement = 0.0; + + for( integer r=0; r < mineralReactionMolarIncrements.size(); ++r ) + { + real64 const molarWeight = m_porosityUpdate.getMolarWeights( r ); + real64 const mineralDensity = m_porosityUpdate.getMineralDensities( r ); + + anelasticStrainIncrement -= mineralReactionMolarIncrements[r] * molarWeight/mineralDensity; + + // std::cout << "mineralReactionMolarIncrements[" << r << "] = " << mineralReactionMolarIncrements[r] << std::endl + // << "molarWeight[" << r << "] = " << molarWeight << std::endl + // << "mineralDensity[" << r << "] = " << mineralDensity << std::endl; + } + + // Compute total stress increment and its derivative + real64 const deltaTemperatureFromLastStep = temperature - temperature_n; + computeTotalStress( k, + q, + timeIncrement, + pressure, + deltaTemperatureFromLastStep, + anelasticStrainIncrement, + strainIncrement, + totalStress, + stiffness ); + } + + /** + * @brief Return the stiffness at a given element (small-strain interface) + * + * @note If the material model has a strain-dependent material stiffness (e.g. + * any plasticity, damage, or nonlinear elastic model) then this interface will + * not work. Users should instead use one of the interfaces where a strain + * tensor is provided as input. + * + * @param k the element number + * @param stiffness the stiffness array + */ + GEOS_HOST_DEVICE + inline + void getElasticStiffness( localIndex const k, localIndex const q, real64 ( & stiffness )[6][6] ) const + { + m_solidUpdate.getElasticStiffness( k, q, stiffness ); + } + + /** + * @brief Return the stiffness at a given element (small-strain interface) + * + * @param [in] k the element number + * @param [out] thermalExpansionCoefficient the thermal expansion coefficient + */ + GEOS_HOST_DEVICE + inline + void getThermalExpansionCoefficient( localIndex const k, real64 & thermalExpansionCoefficient ) const + { + thermalExpansionCoefficient = m_solidUpdate.getThermalExpansionCoefficient( k ); + } + +private: + + using CoupledSolidUpdates< SOLID_TYPE, ReactivePorosity, PERM_TYPE >::m_solidUpdate; + using CoupledSolidUpdates< SOLID_TYPE, ReactivePorosity, PERM_TYPE >::m_porosityUpdate; + using CoupledSolidUpdates< SOLID_TYPE, ReactivePorosity, PERM_TYPE >::m_permUpdate; + + GEOS_HOST_DEVICE + inline + void computeTotalStress( localIndex const k, + localIndex const q, + real64 const & timeIncrement, + real64 const & pressure, + real64 const & deltaTemperatureFromLastStep, + real64 const & anelasticStrainIncrement, + real64 const ( &strainIncrement )[6], + real64 ( & totalStress )[6], + DiscretizationOps & stiffness ) const + { + real64 const thermalExpansionCoefficient = m_solidUpdate.getThermalExpansionCoefficient( k ); + + // std::cout << "anelasticStrainIncrement = " << anelasticStrainIncrement << std::endl; + + real64 mechanicsStrainIncrement[6]{}; + mechanicsStrainIncrement[0] = strainIncrement[0] - thermalExpansionCoefficient * deltaTemperatureFromLastStep - anelasticStrainIncrement; + mechanicsStrainIncrement[1] = strainIncrement[1] - thermalExpansionCoefficient * deltaTemperatureFromLastStep - anelasticStrainIncrement; + mechanicsStrainIncrement[2] = strainIncrement[2] - thermalExpansionCoefficient * deltaTemperatureFromLastStep - anelasticStrainIncrement; + mechanicsStrainIncrement[3] = strainIncrement[3]; + mechanicsStrainIncrement[4] = strainIncrement[4]; + mechanicsStrainIncrement[5] = strainIncrement[5]; + + // Compute total stress increment and its derivative w.r.t. pressure + m_solidUpdate.smallStrainUpdate( k, + q, + timeIncrement, + mechanicsStrainIncrement, + totalStress, // first effective stress increment accumulated + stiffness ); + + // Add the contributions of pressure to the total stress + LvArray::tensorOps::symAddIdentity< 3 >( totalStress, -pressure ); + + } + +}; + +/** + * @brief PorousReactiveSolidBase class used for dispatch of all Porous solids. + */ +class PorousReactiveSolidBase +{}; + +/** + * @brief Class to represent a porous material for poromechanics simulations. + * It is used as an interface to access all constitutive models relative to the properties of a porous material. + * + * @tparam SOLID_TYPE type of solid model + */ +template< typename SOLID_TYPE, + typename PERM_TYPE > +class PorousReactiveSolid : public CoupledSolid< SOLID_TYPE, ReactivePorosity, PERM_TYPE > +{ +public: + + /// Alias for ElasticIsotropicUpdates + using KernelWrapper = PorousReactiveSolidUpdates< SOLID_TYPE, PERM_TYPE >; + + /** + * @brief Constructor + * @param name Object name + * @param parent Object's parent group + */ + PorousReactiveSolid( string const & name, dataRepository::Group * const parent ); + + /** + * @brief Catalog name + * @return Static catalog string + */ + static string catalogName() + { + if constexpr ( std::is_same_v< PERM_TYPE, ConstantPermeability > ) // default case + { + return string( "PorousReactive" ) + SOLID_TYPE::catalogName(); + } + else // special cases + { + return string( "PorousReactive" ) + SOLID_TYPE::catalogName() + PERM_TYPE::catalogName(); + } + } + + /** + * @brief Get catalog name + * @return Catalog name string + */ + virtual string getCatalogName() const override { return catalogName(); } + + /** + * @brief Create a instantiation of the PorousReactiveSolidUpdates class + * that refers to the data in this. + * @return An instantiation of PorousReactiveSolidUpdates. + */ + KernelWrapper createKernelUpdates() const + { + return KernelWrapper( getSolidModel(), + getPorosityModel(), + getPermModel() ); + } + + /** + * @brief initialize the constitutive models fields. + */ + virtual void initializeState() const override final; + + /** + * @brief Const/non-mutable accessor for density + * @return Accessor + */ + arrayView2d< real64 const > const getDensity() const + { + return getSolidModel().getDensity(); + } + + /** + * @brief Const/non-mutable accessor for the mean stress increment at the previous sequential iteration + * @return Accessor + */ + arrayView2d< real64 const > const getMeanStressIncrement_k() const + { + return getPorosityModel().getMeanStressIncrement_k(); + } + + /** + * @brief Non-const accessor for the mean stress increment at the previous sequential iteration + * @return Accessor + */ + arrayView1d< real64 > const getAverageMeanStressIncrement_k() + { + return getPorosityModel().getAverageMeanStressIncrement_k(); + } + +private: + using CoupledSolid< SOLID_TYPE, ReactivePorosity, PERM_TYPE >::getSolidModel; + using CoupledSolid< SOLID_TYPE, ReactivePorosity, PERM_TYPE >::getPorosityModel; + using CoupledSolid< SOLID_TYPE, ReactivePorosity, PERM_TYPE >::getPermModel; +}; + + + +} +} /* namespace geos */ + +#endif /* GEOS_CONSTITUTIVE_SOLID_POROUSREACTIVESOLID_HPP_ */ 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..2c489ba92b5 --- /dev/null +++ b/src/coreComponents/constitutive/solid/ReactiveSolid.hpp @@ -0,0 +1,186 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * 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 + virtual void updateStateFromPressureTemperatureAndReactions( localIndex const k, + localIndex const q, + real64 const & pressure, + real64 const & temperature, + arraySlice1d< real64 const, compflow::USD_COMP - 1 > const & kineticReactionMolarIncrements ) const override final + { + GEOS_UNUSED_VAR( temperature ); + + m_porosityUpdate.updateFromReactions( k, q, kineticReactionMolarIncrements ); + real64 const porosity = m_porosityUpdate.getPorosity( k, q ); + m_permUpdate.updateFromPressureAndPorosity( k, q, pressure, porosity ); + } + + GEOS_HOST_DEVICE + virtual 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 override final + { + 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..adafd421783 --- /dev/null +++ b/src/coreComponents/constitutive/solid/porosity/ReactivePorosity.hpp @@ -0,0 +1,233 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * 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; // To check if it should be plus or minus + + 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]; + } + + GEOS_HOST_DEVICE + inline + real64 getMolarWeights( localIndex const r ) const + { + return m_molarWeights[r]; + } + + GEOS_HOST_DEVICE + inline + real64 getMineralDensities( localIndex const r ) const + { + return m_mineralDensities[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 eac876388b2..0d79d151661 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 @@ -45,7 +44,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 129b10676c6..47fa7f990eb 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..25074a146e9 --- /dev/null +++ b/src/coreComponents/integrationTests/fluidFlowTests/testSinglePhaseReactiveTransport.cpp @@ -0,0 +1,570 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * 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 "constitutive/fluid/singlefluid/SingleFluidBase.hpp" +#include "finiteVolume/FiniteVolumeManager.hpp" +#include "finiteVolume/FluxApproximationBase.hpp" +#include "mainInterface/initialization.hpp" +#include "mainInterface/GeosxState.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 = 1000; + 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 28ffa57e587..c406ebe631d 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/CMakeLists.txt +++ b/src/coreComponents/physicsSolvers/fluidFlow/CMakeLists.txt @@ -65,6 +65,16 @@ 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/ThermalAccumulationKernels.hpp + kernels/singlePhase/reactive/ThermalDirichletFluxComputeKernel.hpp + kernels/singlePhase/reactive/ThermalFluxComputeKernel.hpp kernels/compositional/AccumulationKernel.hpp kernels/compositional/AquiferBCKernel.hpp kernels/compositional/PPUPhaseFlux.hpp @@ -139,6 +149,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 d57772ffe81..069ac3dfb3c 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 ); - 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 ); 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..23fe8454a38 --- /dev/null +++ b/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseReactiveTransport.cpp @@ -0,0 +1,1747 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * 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 updatePorosityAndPermeabilityFromPressureTemperatureAndReactions( POROUSWRAPPER_TYPE porousWrapper, + ElementSubRegionBase & subRegion, + arrayView1d< real64 const > const & pressure, + arrayView1d< real64 const > const & temperature, + 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.updateStateFromPressureTemperatureAndReactions( k, q, + pressure[k], + temperature[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 >(); + + 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 < m_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 < m_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 >(); + 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 < m_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 < m_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 >(); + arrayView1d< real64 const > const & temperature = subRegion.getField< fields::flow::temperature >(); + 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< CoupledSolidBase >::execute( porousSolid, [=, &subRegion] ( auto & castedPorousSolid ) + { + typename TYPEOFREF( castedPorousSolid ) ::KernelWrapper porousWrapper = castedPorousSolid.createKernelUpdates(); + updatePorosityAndPermeabilityFromPressureTemperatureAndReactions( porousWrapper, subRegion, pressure, temperature, 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< CoupledSolidBase >::execute( porousSolid, [=, &subRegion] ( auto & castedPorousSolid ) + { + typename TYPEOFREF( castedPorousSolid ) ::KernelWrapper porousWrapper = castedPorousSolid.createKernelUpdates(); + updateSurfaceAreaFromReactions( porousWrapper, subRegion, initialSurfaceArea, surfaceArea ); + } ); + } + else + { + forAll< parallelDevicePolicy<> >( subRegion.size(), [=] GEOS_HOST_DEVICE ( localIndex const ei ) + { + for( integer ir = 0; ir < m_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 & ) + { + integer const isThermal = m_isThermal; + integer const numPrimarySpecies = m_numPrimarySpecies; + + fsManager.apply< ElementSubRegionBase, + SourceFluxBoundaryCondition >( time_n + dt, + mesh, + SourceFluxBoundaryCondition::catalogName(), + [&, isThermal, numPrimarySpecies]( 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( isThermal ) + { + using DerivOffset = constitutive::singlefluid::DerivativeOffsetC< 1 >; + reactivefluid::ReactiveThermalCompressibleSinglePhaseFluid const & fluid = + getConstitutiveModel< reactivefluid::ReactiveThermalCompressibleSinglePhaseFluid >( subRegion, subRegion.template getReference< string >( viewKeyStruct::fluidNamesString() ) ); + + arrayView3d< real64 const, reactivefluid::USD_SPECIES > const primarySpeciesAggregateConcentration = fluid.primarySpeciesAggregateConcentration(); + arrayView4d< real64 const, + reactivefluid::USD_SPECIES_DC > const dPrimarySpeciesAggregateConcentration_dLogPrimarySpeciesConcentrations = + fluid.dPrimarySpeciesAggregateConcentration_dLogPrimarySpeciesConcentrations(); + arrayView2d< real64 const, singlefluid::USD_FLUID > const density = fluid.density(); + arrayView3d< real64 const, constitutive::singlefluid::USD_FLUID_DER > const dDensity = fluid.dDensity(); + arrayView2d< real64 const, constitutive::singlefluid::USD_FLUID > const enthalpy = fluid.enthalpy(); + arrayView3d< real64 const, constitutive::singlefluid::USD_FLUID_DER > const dEnthalpy = fluid.dEnthalpy(); + + forAll< parallelDevicePolicy<> >( targetSet.size(), [sizeScalingFactor, + targetSet, + rankOffset, + ghostRank, + dofNumber, + numPrimarySpecies, + primarySpeciesAggregateConcentration, + dPrimarySpeciesAggregateConcentration_dLogPrimarySpeciesConcentrations, + density, + dDensity, + enthalpy, + dEnthalpy, + rhsContributionArrayView, + localRhs, + localMatrix, + massProd] GEOS_HOST_DEVICE ( localIndex const a ) + { + singlePhaseReactiveBaseKernels::internal::kernelLaunchSelectorCompSwitch( numPrimarySpecies, [&] ( auto NS ) + { + integer constexpr NUM_SPECIES = NS(); + + // we need to filter out ghosts here, because targetSet may contain them + localIndex const ei = targetSet[a]; + if( ghostRank[ei] >= 0 ) + { + return; + } + + // add the value to the mass balance equation + globalIndex const massRowIndex = dofNumber[ei] - rankOffset; + globalIndex const energyRowIndex = massRowIndex + 1; + globalIndex const speciesRowBeginIndex = massRowIndex + 2; + real64 const rhsValue = rhsContributionArrayView[a] / sizeScalingFactor; // scale the contribution by the sizeScalingFactor + // here! + localRhs[massRowIndex] += rhsValue; + massProd += rhsValue; + + // add the value to the energy balance equation and species mass balance equations if the flux is positive (i.e., it's a + // producer) + if( rhsContributionArrayView[a] > 0.0 ) + { + globalIndex const pressureDofIndex = dofNumber[ei] - rankOffset; + globalIndex const temperatureDofIndex = pressureDofIndex + 1; + + globalIndex dofIndices[2+NUM_SPECIES]{}; + + dofIndices[0] = pressureDofIndex; + dofIndices[1] = temperatureDofIndex; + + for( integer i = 0; i < NUM_SPECIES; ++i ) + { + dofIndices[i+2] = pressureDofIndex + i + 2; + } + + // add the value to the energy balance equation + localRhs[energyRowIndex] += enthalpy[ei][0] * rhsValue; + + real64 jacobianEnergyFlux[2+NUM_SPECIES]{0.0}; + + jacobianEnergyFlux[0] = rhsValue * dEnthalpy[ei][0][DerivOffset::dP]; + jacobianEnergyFlux[1] = rhsValue * dEnthalpy[ei][0][DerivOffset::dT]; + + localMatrix.template addToRow< serialAtomic >( energyRowIndex, + dofIndices, + jacobianEnergyFlux, + 2+NUM_SPECIES ); + + // add the value to the species mass balance equations + for( integer i = 0; i < NUM_SPECIES; ++i ) + { + localRhs[speciesRowBeginIndex + i] += primarySpeciesAggregateConcentration[ei][0][i] / density[ei][0] * rhsValue; + + real64 jacobianSpeciesFlux[2+NUM_SPECIES] = {0.0}; + jacobianSpeciesFlux[0] += -primarySpeciesAggregateConcentration[ei][0][i] * dDensity[ei][0][DerivOffset::dP] / (density[ei][0] * density[ei][0]) * rhsValue; + jacobianSpeciesFlux[1] += -primarySpeciesAggregateConcentration[ei][0][i] * dDensity[ei][0][DerivOffset::dT] / (density[ei][0] * density[ei][0]) * rhsValue; + + for( integer j = 0; j < NUM_SPECIES; ++j ) + { + jacobianSpeciesFlux[j+2] += dPrimarySpeciesAggregateConcentration_dLogPrimarySpeciesConcentrations[ei][0][i][j] / density[ei][0] * rhsValue; + } + + localMatrix.template addToRow< serialAtomic >( speciesRowBeginIndex + i, + dofIndices, + jacobianSpeciesFlux, + 2+NUM_SPECIES ); + } + } + } ); + } ); + } + else + { + using DerivOffset = constitutive::singlefluid::DerivativeOffsetC< 1 >; + reactivefluid::ReactiveCompressibleSinglePhaseFluid const & fluid = + getConstitutiveModel< reactivefluid::ReactiveCompressibleSinglePhaseFluid >( subRegion, subRegion.template getReference< string >( viewKeyStruct::fluidNamesString() ) ); + + arrayView3d< real64 const, reactivefluid::USD_SPECIES > const primarySpeciesAggregateConcentration = fluid.primarySpeciesAggregateConcentration(); + arrayView4d< real64 const, + reactivefluid::USD_SPECIES_DC > const dPrimarySpeciesAggregateConcentration_dLogPrimarySpeciesConcentrations = + fluid.dPrimarySpeciesAggregateConcentration_dLogPrimarySpeciesConcentrations(); + arrayView2d< real64 const, singlefluid::USD_FLUID > const density = fluid.density(); + arrayView3d< real64 const, constitutive::singlefluid::USD_FLUID_DER > const dDensity = fluid.dDensity(); + + forAll< parallelDevicePolicy<> >( targetSet.size(), [sizeScalingFactor, + targetSet, + rankOffset, + ghostRank, + dofNumber, + numPrimarySpecies, + primarySpeciesAggregateConcentration, + dPrimarySpeciesAggregateConcentration_dLogPrimarySpeciesConcentrations, + density, + dDensity, + rhsContributionArrayView, + localRhs, + localMatrix, + massProd] GEOS_HOST_DEVICE ( localIndex const a ) + { + singlePhaseReactiveBaseKernels::internal::kernelLaunchSelectorCompSwitch( numPrimarySpecies, [&] ( auto NS ) + { + integer constexpr NUM_SPECIES = NS(); + + // we need to filter out ghosts here, because targetSet may contain them + localIndex const ei = targetSet[a]; + if( ghostRank[ei] >= 0 ) + { + return; + } + + // add the value to the mass balance equation + globalIndex const massRowIndex = dofNumber[ei] - rankOffset; + globalIndex const speciesRowBeginIndex = massRowIndex + 1; + real64 const rhsValue = rhsContributionArrayView[a] / sizeScalingFactor; // scale the contribution by the sizeScalingFactor + // here! + localRhs[massRowIndex] += rhsValue; + massProd += rhsValue; + + //add the value to the species mass balance equations if the flux is positive (i.e., it's a producer) + if( rhsContributionArrayView[a] > 0.0 ) + { + globalIndex const pressureDofIndex = dofNumber[ei] - rankOffset; + + globalIndex dofIndices[1+NUM_SPECIES]{}; + + dofIndices[0] = pressureDofIndex; + + for( integer i = 0; i < NUM_SPECIES; ++i ) + { + dofIndices[i+1] = pressureDofIndex + i + 1; + } + + for( integer i = 0; i < NUM_SPECIES; ++i ) + { + localRhs[speciesRowBeginIndex + i] += primarySpeciesAggregateConcentration[ei][0][i] / density[ei][0] * rhsValue; + + real64 jacobian[1+NUM_SPECIES] = {0.0}; + jacobian[0] += -primarySpeciesAggregateConcentration[ei][0][i] * dDensity[ei][0][DerivOffset::dP] / (density[ei][0] * density[ei][0]) * rhsValue; + + for( integer j = 0; j < NUM_SPECIES; ++j ) + { + jacobian[j+1] += dPrimarySpeciesAggregateConcentration_dLogPrimarySpeciesConcentrations[ei][0][i][j] / density[ei][0] * rhsValue; + } + + localMatrix.template addToRow< serialAtomic >( speciesRowBeginIndex + i, + dofIndices, + jacobian, + 1+NUM_SPECIES ); + } + } + } ); + } ); + } + + 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; + 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 = m_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..92fad383f29 --- /dev/null +++ b/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseReactiveTransport.hpp @@ -0,0 +1,371 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * 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/ThermalAccumulationKernels.hpp" +#include "physicsSolvers/fluidFlow/kernels/singlePhase/reactive/ThermalDirichletFluxComputeKernel.hpp" +#include "physicsSolvers/fluidFlow/kernels/singlePhase/reactive/ThermalFluxComputeKernel.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/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/multiphysics/CoupledSolver.hpp b/src/coreComponents/physicsSolvers/multiphysics/CoupledSolver.hpp index 333220bff67..300088cf592 100644 --- a/src/coreComponents/physicsSolvers/multiphysics/CoupledSolver.hpp +++ b/src/coreComponents/physicsSolvers/multiphysics/CoupledSolver.hpp @@ -511,7 +511,7 @@ class CoupledSolver : public PhysicsSolverBase solver->saveSequentialIterationState( domain ); } - mapSolutionBetweenSolvers( domain, idx() ); + mapSolutionBetweenSolvers( stepDt, domain, idx() ); if( solverDt < stepDt ) // subsolver had to cut the time step { @@ -588,13 +588,15 @@ class CoupledSolver : public PhysicsSolverBase /** * @brief Maps the solution obtained from one solver to the fields used by the other solver(s) * + * @param dt timestep size * @param domain the domain partition * @param solverType the index of the solver withing this coupled solver. */ - virtual void mapSolutionBetweenSolvers( DomainPartition & domain, + virtual void mapSolutionBetweenSolvers( real64 const & dt, + DomainPartition & domain, integer const solverType ) { - GEOS_UNUSED_VAR( domain, solverType ); + GEOS_UNUSED_VAR( dt, domain, solverType ); } virtual bool checkSequentialConvergence( integer const cycleNumber, diff --git a/src/coreComponents/physicsSolvers/multiphysics/PhaseFieldFractureSolver.cpp b/src/coreComponents/physicsSolvers/multiphysics/PhaseFieldFractureSolver.cpp index 751dd7dea77..033929e4fd3 100644 --- a/src/coreComponents/physicsSolvers/multiphysics/PhaseFieldFractureSolver.cpp +++ b/src/coreComponents/physicsSolvers/multiphysics/PhaseFieldFractureSolver.cpp @@ -50,9 +50,10 @@ void PhaseFieldFractureSolver::postInputInitialization() getNonlinearSolverParameters().m_couplingType = NonlinearSolverParameters::CouplingType::Sequential; } -void PhaseFieldFractureSolver::mapSolutionBetweenSolvers( DomainPartition & domain, integer const solverType ) +void PhaseFieldFractureSolver::mapSolutionBetweenSolvers( real64 const & dt, DomainPartition & domain, integer const solverType ) { - + GEOS_UNUSED_VAR( dt ); + GEOS_MARK_FUNCTION; if( solverType == static_cast< integer >( SolverType::Damage ) ) { diff --git a/src/coreComponents/physicsSolvers/multiphysics/PhaseFieldFractureSolver.hpp b/src/coreComponents/physicsSolvers/multiphysics/PhaseFieldFractureSolver.hpp index a1aae598694..af63526d7ca 100644 --- a/src/coreComponents/physicsSolvers/multiphysics/PhaseFieldFractureSolver.hpp +++ b/src/coreComponents/physicsSolvers/multiphysics/PhaseFieldFractureSolver.hpp @@ -86,7 +86,7 @@ class PhaseFieldFractureSolver : public CoupledSolver< SolidMechanicsLagrangianF return std::get< toUnderlying( SolverType::Damage ) >( m_solvers ); } - virtual void mapSolutionBetweenSolvers( DomainPartition & Domain, integer const idx ) override final; + virtual void mapSolutionBetweenSolvers( real64 const & dt, DomainPartition & Domain, integer const idx ) override final; protected: diff --git a/src/coreComponents/physicsSolvers/multiphysics/PhaseFieldPoromechanicsSolver.cpp b/src/coreComponents/physicsSolvers/multiphysics/PhaseFieldPoromechanicsSolver.cpp index 06bb5259f06..e18a7343cf7 100644 --- a/src/coreComponents/physicsSolvers/multiphysics/PhaseFieldPoromechanicsSolver.cpp +++ b/src/coreComponents/physicsSolvers/multiphysics/PhaseFieldPoromechanicsSolver.cpp @@ -57,8 +57,10 @@ PhaseFieldPoromechanicsSolver::~PhaseFieldPoromechanicsSolver() // TODO Auto-generated destructor stub } -void PhaseFieldPoromechanicsSolver::mapSolutionBetweenSolvers( DomainPartition & domain, integer const solverType ) +void PhaseFieldPoromechanicsSolver::mapSolutionBetweenSolvers( real64 const & dt, DomainPartition & domain, integer const solverType ) { + GEOS_UNUSED_VAR( dt ); + if( solverType == static_cast< integer >( SolverType::Damage ) ) { GEOS_MARK_FUNCTION; diff --git a/src/coreComponents/physicsSolvers/multiphysics/PhaseFieldPoromechanicsSolver.hpp b/src/coreComponents/physicsSolvers/multiphysics/PhaseFieldPoromechanicsSolver.hpp index 359f428a3c3..76eac71b4c3 100644 --- a/src/coreComponents/physicsSolvers/multiphysics/PhaseFieldPoromechanicsSolver.hpp +++ b/src/coreComponents/physicsSolvers/multiphysics/PhaseFieldPoromechanicsSolver.hpp @@ -87,7 +87,7 @@ class PhaseFieldPoromechanicsSolver : public CoupledSolver< SinglePhasePoromecha return std::get< toUnderlying( SolverType::Damage ) >( m_solvers ); } - virtual void mapSolutionBetweenSolvers( DomainPartition & Domain, integer const idx ) override final; + virtual void mapSolutionBetweenSolvers( real64 const & dt, DomainPartition & Domain, integer const idx ) override final; void mapDamageAndGradientToQuadrature( DomainPartition & domain ); diff --git a/src/coreComponents/physicsSolvers/multiphysics/PoromechanicsConformingFractures.hpp b/src/coreComponents/physicsSolvers/multiphysics/PoromechanicsConformingFractures.hpp index 2366f915fd4..5a25e5241ec 100644 --- a/src/coreComponents/physicsSolvers/multiphysics/PoromechanicsConformingFractures.hpp +++ b/src/coreComponents/physicsSolvers/multiphysics/PoromechanicsConformingFractures.hpp @@ -564,7 +564,7 @@ class PoromechanicsConformingFractures : public POROMECHANICS_BASE< FLOW_SOLVER, CRSMatrixView< real64, globalIndex const > const & localMatrix, arrayView1d< real64 > const & localRhs ) = 0; - virtual void mapSolutionBetweenSolvers( DomainPartition & domain, integer const solverType ) override + virtual void mapSolutionBetweenSolvers( real64 const & dt, DomainPartition & domain, integer const solverType ) override { GEOS_MARK_FUNCTION; @@ -581,7 +581,7 @@ class PoromechanicsConformingFractures : public POROMECHANICS_BASE< FLOW_SOLVER, this->flowSolver()->updateStencilWeights( domain ); } - Base::mapSolutionBetweenSolvers( domain, solverType ); + Base::mapSolutionBetweenSolvers( dt, domain, solverType ); } void updateHydraulicApertureAndFracturePermeability( DomainPartition & domain ) diff --git a/src/coreComponents/physicsSolvers/multiphysics/PoromechanicsSolver.hpp b/src/coreComponents/physicsSolvers/multiphysics/PoromechanicsSolver.hpp index f553759e327..ffabe0a92ad 100644 --- a/src/coreComponents/physicsSolvers/multiphysics/PoromechanicsSolver.hpp +++ b/src/coreComponents/physicsSolvers/multiphysics/PoromechanicsSolver.hpp @@ -127,6 +127,12 @@ class PoromechanicsSolver : public CoupledSolver< FLOW_SOLVER, MECHANICS_SOLVER EnumStrings< SolidMechanicsLagrangianFEM::TimeIntegrationOption >::toString( SolidMechanicsLagrangianFEM::TimeIntegrationOption::QuasiStatic ) ), InputError ); + GEOS_THROW_IF( this->flowSolver()->getCatalogName() == "SinglePhaseReactiveTransport" && + this->getNonlinearSolverParameters().m_couplingType != NonlinearSolverParameters::CouplingType::Sequential, + GEOS_FMT( "{} {}: The coupling type must be Sequential since it is coupled with {}", + this->getCatalogName(), this->getName(), this->flowSolver()->getCatalogName() ), + InputError ); + setMGRStrategy(); } @@ -186,10 +192,18 @@ class PoromechanicsSolver : public CoupledSolver< FLOW_SOLVER, MECHANICS_SOLVER if( this->getNonlinearSolverParameters().m_couplingType == NonlinearSolverParameters::CouplingType::Sequential ) { - // to let the solid mechanics solver that there is a pressure and temperature RHS in the mechanics solve - solidMechanicsSolver()->enableFixedStressPoromechanicsUpdate(); - // to let the flow solver that saving pressure_k and temperature_k is necessary (for the fixed-stress porosity terms) - flowSolver()->enableFixedStressPoromechanicsUpdate(); + if( flowSolver()->getCatalogName() == "SinglePhaseReactiveTransport" ) + { + // to let the solid mechanics solver to account for anelastic strain due to chemistry + solidMechanicsSolver()->enableExplicitChemomechanicsUpdate(); + } + else + { + // to let the solid mechanics solver that there is a pressure and temperature RHS in the mechanics solve + solidMechanicsSolver()->enableFixedStressPoromechanicsUpdate(); + // to let the flow solver that saving pressure_k and temperature_k is necessary (for the fixed-stress porosity terms) + flowSolver()->enableFixedStressPoromechanicsUpdate(); + } } if( m_stabilizationType == stabilization::StabilizationType::Global || m_stabilizationType == stabilization::StabilizationType::Local ) @@ -649,8 +663,10 @@ class PoromechanicsSolver : public CoupledSolver< FLOW_SOLVER, MECHANICS_SOLVER } } - virtual void mapSolutionBetweenSolvers( DomainPartition & domain, integer const solverType ) override + virtual void mapSolutionBetweenSolvers( real64 const & dt, DomainPartition & domain, integer const solverType ) override { + GEOS_UNUSED_VAR( dt ); + GEOS_MARK_FUNCTION; /// After the flow solver @@ -663,8 +679,11 @@ class PoromechanicsSolver : public CoupledSolver< FLOW_SOLVER, MECHANICS_SOLVER if( solverType == static_cast< integer >( SolverType::SolidMechanics ) && !m_performStressInitialization ) // do not update during poromechanics initialization { - // compute the average of the mean total stress increment over quadrature points - averageMeanTotalStressIncrement( domain ); + if( flowSolver()->getCatalogName() != "SinglePhaseReactiveTransport" ) // For now, Biot Poromechanics is not considered for ChemoMechanics + { + // compute the average of the mean total stress increment over quadrature points + averageMeanTotalStressIncrement( domain ); + } this->template forDiscretizationOnMeshTargets<>( domain.getMeshBodies(), [&]( string const &, MeshLevel & mesh, @@ -687,7 +706,10 @@ class PoromechanicsSolver : public CoupledSolver< FLOW_SOLVER, MECHANICS_SOLVER if( solverType == static_cast< integer >( SolverType::SolidMechanics ) && this->getNonlinearSolverParameters().m_nonlinearAccelerationType== NonlinearSolverParameters::NonlinearAccelerationType::Aitken ) { - recordAverageMeanTotalStressIncrement( domain, m_s2_tilde ); + if( flowSolver()->getCatalogName() != "SinglePhaseReactiveTransport" ) // For now, Biot Poromechanics is not considered for ChemoMechanics + { + recordAverageMeanTotalStressIncrement( domain, m_s2_tilde ); + } } } diff --git a/src/coreComponents/physicsSolvers/multiphysics/SinglePhasePoromechanics.cpp b/src/coreComponents/physicsSolvers/multiphysics/SinglePhasePoromechanics.cpp index 7a3fdc6386a..c8ecaadc617 100644 --- a/src/coreComponents/physicsSolvers/multiphysics/SinglePhasePoromechanics.cpp +++ b/src/coreComponents/physicsSolvers/multiphysics/SinglePhasePoromechanics.cpp @@ -179,6 +179,30 @@ void SinglePhasePoromechanics< SinglePhaseReservoirAndWells<>, SolidMechanicsLag flowSolver()->assembleCouplingTerms( time_n, dt, domain, dofManager, localMatrix, localRhs ); } +template<> +void SinglePhasePoromechanics< SinglePhaseReactiveTransport, SolidMechanicsLagrangianFEM >::mapSolutionBetweenSolvers( real64 const & dt, + DomainPartition & domain, + integer const solverType ) +{ + GEOS_MARK_FUNCTION; + + Base::mapSolutionBetweenSolvers( dt, domain, solverType ); + + if( solverType == static_cast< integer >( SolverType::Flow ) ) + { + this->template forDiscretizationOnMeshTargets<>( domain.getMeshBodies(), [&]( string const &, + MeshLevel & mesh, + string_array const & regionNames ) + { + mesh.getElemManager().forElementSubRegions( regionNames, [&]( localIndex const, + ElementSubRegionBase & subRegion ) + { + flowSolver()->updateKineticReactionMolarIncrements( dt, subRegion ); + } ); + } ); + } +} + template< typename FLOW_SOLVER, typename MECHANICS_SOLVER > void SinglePhasePoromechanics< FLOW_SOLVER, MECHANICS_SOLVER >::assembleElementBasedTerms( real64 const time_n, real64 const dt, @@ -347,9 +371,12 @@ template class SinglePhasePoromechanics< SinglePhaseBase, SolidMechanicsEmbedded template class SinglePhasePoromechanics< SinglePhaseReservoirAndWells<> >; template class SinglePhasePoromechanics< SinglePhaseReservoirAndWells<>, SolidMechanicsLagrangeContact >; //template class SinglePhasePoromechanics< SinglePhaseReservoirAndWells<>, SolidMechanicsEmbeddedFractures >; +template class SinglePhasePoromechanics< SinglePhaseReactiveTransport >; namespace { +typedef SinglePhasePoromechanics< SinglePhaseReactiveTransport > SinglePhaseChemomechanics; +REGISTER_CATALOG_ENTRY( PhysicsSolverBase, SinglePhaseChemomechanics, string const &, Group * const ) typedef SinglePhasePoromechanics< SinglePhaseReservoirAndWells<> > SinglePhaseReservoirPoromechanics; REGISTER_CATALOG_ENTRY( PhysicsSolverBase, SinglePhaseReservoirPoromechanics, string const &, Group * const ) typedef SinglePhasePoromechanics<> SinglePhasePoromechanics; diff --git a/src/coreComponents/physicsSolvers/multiphysics/SinglePhasePoromechanics.hpp b/src/coreComponents/physicsSolvers/multiphysics/SinglePhasePoromechanics.hpp index 0c564bf3d23..e9d0cc9d7ff 100644 --- a/src/coreComponents/physicsSolvers/multiphysics/SinglePhasePoromechanics.hpp +++ b/src/coreComponents/physicsSolvers/multiphysics/SinglePhasePoromechanics.hpp @@ -22,6 +22,7 @@ #include "physicsSolvers/multiphysics/PoromechanicsSolver.hpp" #include "physicsSolvers/fluidFlow/SinglePhaseBase.hpp" +#include "physicsSolvers/fluidFlow/SinglePhaseReactiveTransport.hpp" #include "physicsSolvers/multiphysics/SinglePhaseReservoirAndWells.hpp" namespace geos @@ -97,6 +98,11 @@ class SinglePhasePoromechanics : public PoromechanicsSolver< FLOW_SOLVER, MECHAN arrayView1d< real64 > const & localRhs ) override { Base::assembleSystem( time, dt, domain, dofManager, localMatrix, localRhs ); } + virtual void mapSolutionBetweenSolvers( real64 const & dt, + DomainPartition & domain, + integer const solverType ) override + { Base::mapSolutionBetweenSolvers( dt, domain, solverType ); } + virtual void assembleElementBasedTerms( real64 const time_n, real64 const dt, DomainPartition & domain, diff --git a/src/coreComponents/physicsSolvers/solidMechanics/CMakeLists.txt b/src/coreComponents/physicsSolvers/solidMechanics/CMakeLists.txt index ced15c47ba5..c399776c304 100644 --- a/src/coreComponents/physicsSolvers/solidMechanics/CMakeLists.txt +++ b/src/coreComponents/physicsSolvers/solidMechanics/CMakeLists.txt @@ -25,6 +25,8 @@ set( solidMechanicsSolvers_headers kernels/SolidMechanicsLagrangianFEMKernels.hpp SolidMechanicsMPM.hpp MPMSolverFields.hpp + kernels/ExplicitChemoMechanics.hpp + kernels/ExplicitChemoMechanics_impl.hpp kernels/ExplicitFiniteStrain.hpp kernels/ExplicitFiniteStrain_impl.hpp kernels/ExplicitMPM.hpp @@ -82,7 +84,8 @@ set( kernelTemplateFileList "" ) list( APPEND kernelTemplateFileList kernels/SolidMechanicsKernels.cpp.template - kernels/SolidMechanicsFixedStressThermoPoromechanicsKernels.cpp.template ) + kernels/SolidMechanicsFixedStressThermoPoromechanicsKernels.cpp.template + kernels/SolidMechanicsExplicitChemoMechanicsKernels.cpp.template ) foreach( kernelTemplateFile ${kernelTemplateFileList} ) diff --git a/src/coreComponents/physicsSolvers/solidMechanics/SolidMechanicsLagrangianFEM.cpp b/src/coreComponents/physicsSolvers/solidMechanics/SolidMechanicsLagrangianFEM.cpp index f3dd742fa6d..d1c5bfce738 100644 --- a/src/coreComponents/physicsSolvers/solidMechanics/SolidMechanicsLagrangianFEM.cpp +++ b/src/coreComponents/physicsSolvers/solidMechanics/SolidMechanicsLagrangianFEM.cpp @@ -25,6 +25,7 @@ #include "kernels/ExplicitSmallStrain.hpp" #include "kernels/ExplicitFiniteStrain.hpp" #include "kernels/FixedStressThermoPoromechanics.hpp" +#include "kernels/ExplicitChemoMechanics.hpp" #include "common/GEOS_RAJA_Interface.hpp" #include "constitutive/ConstitutiveManager.hpp" @@ -48,6 +49,7 @@ #include "physicsSolvers/LogLevelsInfo.hpp" #include "physicsSolvers/solidMechanics/kernels/SolidMechanicsKernelsDispatchTypeList.hpp" #include "physicsSolvers/solidMechanics/kernels/SolidMechanicsFixedStressThermoPoromechanicsKernelsDispatchTypeList.hpp" +#include "physicsSolvers/solidMechanics/kernels/SolidMechanicsExplicitChemoMechanicsKernelsDispatchTypeList.hpp" #include "physicsSolvers/fluidFlow/FlowSolverBase.hpp" namespace geos @@ -69,7 +71,8 @@ SolidMechanicsLagrangianFEM::SolidMechanicsLagrangianFEM( const string & name, m_maxNumResolves( 10 ), m_strainTheory( 0 ), m_isFixedStressPoromechanicsUpdate( false ), - m_performStressInitialization( false ) + m_performStressInitialization( false ), + m_isExplicitChemomechanicsUpdate( false ) { registerWrapper( viewKeyStruct::newmarkGammaString(), &m_newmarkGamma ). @@ -1132,6 +1135,22 @@ void SolidMechanicsLagrangianFEM::assembleSystem( real64 const GEOS_UNUSED_PARAM m_maxForce = LvArray::math::max( mechanicsMaxForce, poromechanicsMaxForce ); } + else if( m_isExplicitChemomechanicsUpdate ) + { + + // first pass for coupled poromechanics regions + real64 const chemomechanicsMaxForce= assemblyLaunch< SolidMechanicsExplicitChemoMechanicsKernelsDispatchTypeList, + solidMechanicsLagrangianFEMKernels::ExplicitChemoMechanicsFactory >( mesh, + dofManager, + regionNames, + FlowSolverBase::viewKeyStruct::solidNamesString(), + localMatrix, + localRhs, + dt ); + + + m_maxForce = chemomechanicsMaxForce; + } else { if( m_timeIntegrationOption == TimeIntegrationOption::QuasiStatic ) @@ -1508,6 +1527,11 @@ void SolidMechanicsLagrangianFEM::enableFixedStressPoromechanicsUpdate() m_isFixedStressPoromechanicsUpdate = true; } +void SolidMechanicsLagrangianFEM::enableExplicitChemomechanicsUpdate() +{ + m_isExplicitChemomechanicsUpdate = true; +} + void SolidMechanicsLagrangianFEM::saveSequentialIterationState( DomainPartition & GEOS_UNUSED_PARAM( domain ) ) { // nothing to save diff --git a/src/coreComponents/physicsSolvers/solidMechanics/SolidMechanicsLagrangianFEM.hpp b/src/coreComponents/physicsSolvers/solidMechanics/SolidMechanicsLagrangianFEM.hpp index de4c7630f42..3f1d0b777fe 100644 --- a/src/coreComponents/physicsSolvers/solidMechanics/SolidMechanicsLagrangianFEM.hpp +++ b/src/coreComponents/physicsSolvers/solidMechanics/SolidMechanicsLagrangianFEM.hpp @@ -226,6 +226,8 @@ class SolidMechanicsLagrangianFEM : public PhysicsSolverBase void enableFixedStressPoromechanicsUpdate(); + void enableExplicitChemomechanicsUpdate(); + virtual void saveSequentialIterationState( DomainPartition & domain ) override; struct viewKeyStruct : PhysicsSolverBase::viewKeyStruct @@ -307,6 +309,9 @@ class SolidMechanicsLagrangianFEM : public PhysicsSolverBase /// Flag to indicate that the solver is going to perform stress initialization bool m_performStressInitialization; + /// Flag to indicate that the solver is running with explicit chemomechancis update + bool m_isExplicitChemomechanicsUpdate; + /// Rigid body modes; TODO remove mutable hack mutable array1d< ParallelVector > m_rigidBodyModes; diff --git a/src/coreComponents/physicsSolvers/solidMechanics/kernelSpecs.json b/src/coreComponents/physicsSolvers/solidMechanics/kernelSpecs.json index ca527443f5a..84824c78ec5 100644 --- a/src/coreComponents/physicsSolvers/solidMechanics/kernelSpecs.json +++ b/src/coreComponents/physicsSolvers/solidMechanics/kernelSpecs.json @@ -29,6 +29,7 @@ "constants": [ [ "ExplicitSmallStrainPolicy", "geos::parallelDevicePolicy< GEOS_BLOCK_SIZE >" ], [ "ExplicitFiniteStrainPolicy", "geos::parallelDevicePolicy< GEOS_BLOCK_SIZE >" ], + [ "ExplicitChemoMechanicsPolicy", "geos::parallelDevicePolicy< GEOS_BLOCK_SIZE >" ], [ "FixedStressThermoPoromechanicsPolicy", "geos::parallelDevicePolicy< GEOS_BLOCK_SIZE >" ], [ "ImplicitSmallStrainNewmarkPolicy", "geos::parallelDevicePolicy< GEOS_BLOCK_SIZE >" ], [ "ImplicitSmallStrainQuasiStaticPolicy", "geos::parallelDevicePolicy< GEOS_BLOCK_SIZE >" ] @@ -144,5 +145,37 @@ ] }, "explicit": [] + }, + + "SolidMechanicsExplicitChemoMechanicsKernels": { + "vars": [ + "SUBREGION_TYPE", + "CONSTITUTIVE_TYPE", + "FE_TYPE" + ], + "constants": [ + [ "ExplicitSmallStrainPolicy", "geos::parallelDevicePolicy< GEOS_BLOCK_SIZE >" ], + [ "ExplicitFiniteStrainPolicy", "geos::parallelDevicePolicy< GEOS_BLOCK_SIZE >" ], + [ "ExplicitChemoMechanicsPolicy", "geos::parallelDevicePolicy< GEOS_BLOCK_SIZE >" ], + [ "FixedStressThermoPoromechanicsPolicy", "geos::parallelDevicePolicy< GEOS_BLOCK_SIZE >" ], + [ "ImplicitSmallStrainNewmarkPolicy", "geos::parallelDevicePolicy< GEOS_BLOCK_SIZE >" ], + [ "ImplicitSmallStrainQuasiStaticPolicy", "geos::parallelDevicePolicy< GEOS_BLOCK_SIZE >" ] + ], + "combinations": { + "SUBREGION_TYPE": [ + "CellElementSubRegion" + ], + "CONSTITUTIVE_TYPE": [ + "PorousReactiveSolid", + "PorousReactiveSolid" + ], + "FE_TYPE": [ + "H1_Hexahedron_Lagrange1_GaussLegendre2", + "H1_Wedge_Lagrange1_Gauss6", + "H1_Tetrahedron_Lagrange1_Gauss1", + "H1_Pyramid_Lagrange1_Gauss5" + ] + }, + "explicit": [] } } diff --git a/src/coreComponents/physicsSolvers/solidMechanics/kernels/ExplicitChemoMechanics.hpp b/src/coreComponents/physicsSolvers/solidMechanics/kernels/ExplicitChemoMechanics.hpp new file mode 100644 index 00000000000..c0321447f26 --- /dev/null +++ b/src/coreComponents/physicsSolvers/solidMechanics/kernels/ExplicitChemoMechanics.hpp @@ -0,0 +1,239 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * 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 ExplicitChemoMechanics.hpp + */ + +#ifndef GEOS_PHYSICSSOLVERS_SOLIDMECHANICS_KERNELS_EXPLICITCHEMOMECHANICS_HPP_ +#define GEOS_PHYSICSSOLVERS_SOLIDMECHANICS_KERNELS_EXPLICITCHEMOMECHANICS_HPP_ + +#include "finiteElement/kernelInterface/ImplicitKernelBase.hpp" + +namespace geos +{ + +namespace solidMechanicsLagrangianFEMKernels +{ + +/** + * @brief Implements kernels for solving the solid part of the chemomechanics problem. + * @copydoc geos::finiteElement::ImplicitKernelBase + * @tparam NUM_NODES_PER_ELEM The number of nodes per element for the + * @p SUBREGION_TYPE. + * @tparam UNUSED An unused parameter since we are assuming that the test and + * trial space have the same number of support points. + * + * ### ExplicitChemoMechanics Description + * Implements the KernelBase interface functions required for using the + * effective stress for the integration of the stress divergence. This is + * templated on one of the "finite element kernel application" functions + * such as geos::finiteElement::RegionBasedKernelApplication. + */ +template< typename SUBREGION_TYPE, + typename CONSTITUTIVE_TYPE, + typename FE_TYPE > +class ExplicitChemoMechanics : + public finiteElement::ImplicitKernelBase< SUBREGION_TYPE, + CONSTITUTIVE_TYPE, + FE_TYPE, + 3, + 3 > +{ +public: + /// Alias for the base class; + using Base = finiteElement::ImplicitKernelBase< SUBREGION_TYPE, + CONSTITUTIVE_TYPE, + FE_TYPE, + 3, + 3 >; + + /// Maximum number of nodes per element, which is equal to the maxNumTestSupportPointPerElem and + /// maxNumTrialSupportPointPerElem by definition. When the FE_TYPE is not a Virtual Element, this + /// will be the actual number of nodes per element. + static constexpr int numNodesPerElem = Base::maxNumTestSupportPointsPerElem; + using Base::numDofPerTestSupportPoint; + using Base::numDofPerTrialSupportPoint; + using Base::m_dofNumber; + using Base::m_dofRankOffset; + using Base::m_matrix; + using Base::m_rhs; + using Base::m_elemsToNodes; + using Base::m_constitutiveUpdate; + using Base::m_finiteElementSpace; + using Base::m_meshData; + using Base::m_dt; + + /** + * @brief Constructor + * @copydoc geos::finiteElement::ImplicitKernelBase::ImplicitKernelBase + * @param inputGravityVector The gravity vector. + */ + ExplicitChemoMechanics( NodeManager const & nodeManager, + EdgeManager const & edgeManager, + FaceManager const & faceManager, + localIndex const targetRegionIndex, + SUBREGION_TYPE const & elementSubRegion, + FE_TYPE const & finiteElementSpace, + CONSTITUTIVE_TYPE & inputConstitutiveType, + arrayView1d< globalIndex const > const inputDofNumber, + globalIndex const rankOffset, + CRSMatrixView< real64, globalIndex const > const inputMatrix, + arrayView1d< real64 > const inputRhs, + real64 const inputDt, + real64 const (&inputGravityVector)[3] ); + + //***************************************************************************** + /** + * @class StackVariables + * @copydoc geos::finiteElement::ImplicitKernelBase::StackVariables + * + * Adds a stack array for the displacement, incremental displacement, and the + * constitutive stiffness. + */ + struct StackVariables : public Base::StackVariables + { +public: + + /// Constructor. + GEOS_HOST_DEVICE + StackVariables(): + Base::StackVariables(), + xLocal(), + u_local(), + uhat_local(), + constitutiveStiffness() + {} + +#if !defined(CALC_FEM_SHAPE_IN_KERNEL) + /// Dummy + int xLocal; +#else + /// C-array stack storage for element local the nodal positions. + real64 xLocal[ numNodesPerElem ][ 3 ]; +#endif + + /// Stack storage for the element local nodal displacement + real64 u_local[numNodesPerElem][numDofPerTrialSupportPoint]; + + /// Stack storage for the element local nodal incremental displacement + real64 uhat_local[numNodesPerElem][numDofPerTrialSupportPoint]; + + /// Stack storage for the constitutive stiffness at a quadrature point. + real64 constitutiveStiffness[ 6 ][ 6 ]; + }; + //***************************************************************************** + + /** + * @brief Copy global values from primary field to a local stack array. + * @copydoc ::geos::finiteElement::ImplicitKernelBase::setup + * + * For the ExplicitChemoMechanics implementation, global values from the displacement, + * incremental displacement, and degree of freedom numbers are placed into + * element local stack storage. + */ + GEOS_HOST_DEVICE + void setup( localIndex const k, + StackVariables & stack ) const; + + /** + * @copydoc geos::finiteElement::KernelBase::quadraturePointKernel + * For solid mechanics kernels, the strain increment is calculated, and the + * constitutive update is called. In addition, the constitutive stiffness + * stack variable is filled by the constitutive model. + */ + GEOS_HOST_DEVICE + void quadraturePointKernel( localIndex const k, + localIndex const q, + StackVariables & stack ) const; + + /** + * @copydoc geos::finiteElement::ImplicitKernelBase::complete + */ + GEOS_HOST_DEVICE + GEOS_FORCE_INLINE + real64 complete( localIndex const k, + StackVariables & stack ) const; + + /** + * @copydoc geos::finiteElement::KernelBase::kernelLaunch + */ + template< typename POLICY, + typename KERNEL_TYPE > + static real64 + kernelLaunch( localIndex const numElems, + KERNEL_TYPE const & kernelComponent ); + +protected: + /// The array containing the nodal position array. + arrayView2d< real64 const, nodes::REFERENCE_POSITION_USD > const m_X; + + /// The rank-global displacement array. + arrayView2d< real64 const, nodes::TOTAL_DISPLACEMENT_USD > const m_disp; + + /// The rank-global incremental displacement array. + arrayView2d< real64 const, nodes::INCR_DISPLACEMENT_USD > const m_uhat; + + /// The gravity vector. + real64 const m_gravityVector[3]; + + /// The rank global bulk density + arrayView2d< real64 const > const m_bulkDensity; + + /// The rank-global fluid pressure arrays. + arrayView1d< real64 const > const m_pressure; + arrayView1d< real64 const > const m_pressure_n; + + /// The rank-global initial temperature array + arrayView1d< real64 const > const m_initialTemperature; + + /// The rank-global temperature arrays. + arrayView1d< real64 const > const m_temperature; + arrayView1d< real64 const > const m_temperature_n; + + /// The rank-global mineral reaction molar increments arrays. + arrayView2d< real64 const, compflow::USD_COMP > const m_mineralReactionMolarIncrements; + + /** + * @brief Get a parameter representative of the stiffness, used as physical scaling for the + * stabilization matrix. + * @param[in] k Element index. + * @return A parameter representative of the stiffness matrix dstress/dstrain + */ + GEOS_HOST_DEVICE + GEOS_FORCE_INLINE + real64 computeStabilizationScaling( localIndex const k ) const + { + // TODO: generalize this to other constitutive models (currently we assume linear elasticity). + return 2.0 * m_constitutiveUpdate.getShearModulus( k ); + } +}; + +/// The factory used to construct a ExplicitChemoMechanics kernel. +using ExplicitChemoMechanicsFactory = finiteElement::KernelFactory< ExplicitChemoMechanics, + arrayView1d< globalIndex const > const, + globalIndex, + CRSMatrixView< real64, globalIndex const > const, + arrayView1d< real64 > const, + real64 const, + real64 const (&)[3] >; + +} // namespace solidMechanicsLagrangianFEMKernels + +} // namespace geos + +#include "finiteElement/kernelInterface/SparsityKernelBase.hpp" + +#endif // GEOS_PHYSICSSOLVERS_SOLIDMECHANICS_KERNELS_EXPLICITCHEMOMECHANICS_HPP_ diff --git a/src/coreComponents/physicsSolvers/solidMechanics/kernels/ExplicitChemoMechanics_impl.hpp b/src/coreComponents/physicsSolvers/solidMechanics/kernels/ExplicitChemoMechanics_impl.hpp new file mode 100644 index 00000000000..f8a943df746 --- /dev/null +++ b/src/coreComponents/physicsSolvers/solidMechanics/kernels/ExplicitChemoMechanics_impl.hpp @@ -0,0 +1,234 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * 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 ExplicitChemoMechanics_impl.hpp + */ + +#ifndef GEOS_PHYSICSSOLVERS_SOLIDMECHANICS_KERNELS_EXPLICITCHEMOMECHANICS_IMPL_HPP_ +#define GEOS_PHYSICSSOLVERS_SOLIDMECHANICS_KERNELS_EXPLICITCHEMOMECHANICS_IMPL_HPP_ + +#include "ExplicitChemoMechanics.hpp" +#include "physicsSolvers/fluidFlow/FlowSolverBaseFields.hpp" +#include "physicsSolvers/fluidFlow/SinglePhaseReactiveTransportFields.hpp" +#include "physicsSolvers/multiphysics/PoromechanicsFields.hpp" +#include "physicsSolvers/solidMechanics/SolidMechanicsFields.hpp" + +namespace geos +{ + +namespace solidMechanicsLagrangianFEMKernels +{ + +template< typename SUBREGION_TYPE, + typename CONSTITUTIVE_TYPE, + typename FE_TYPE > + +ExplicitChemoMechanics< SUBREGION_TYPE, CONSTITUTIVE_TYPE, FE_TYPE >:: +ExplicitChemoMechanics( NodeManager const & nodeManager, + EdgeManager const & edgeManager, + FaceManager const & faceManager, + localIndex const targetRegionIndex, + SUBREGION_TYPE const & elementSubRegion, + FE_TYPE const & finiteElementSpace, + CONSTITUTIVE_TYPE & inputConstitutiveType, + arrayView1d< globalIndex const > const inputDofNumber, + globalIndex const rankOffset, + CRSMatrixView< real64, globalIndex const > const inputMatrix, + arrayView1d< real64 > const inputRhs, + real64 const inputDt, + real64 const (&inputGravityVector)[3] ): + Base( nodeManager, + edgeManager, + faceManager, + targetRegionIndex, + elementSubRegion, + finiteElementSpace, + inputConstitutiveType, + inputDofNumber, + rankOffset, + inputMatrix, + inputRhs, + inputDt ), + m_X( nodeManager.referencePosition()), + m_disp( nodeManager.getField< fields::solidMechanics::totalDisplacement >() ), + m_uhat( nodeManager.getField< fields::solidMechanics::incrementalDisplacement >() ), + m_gravityVector{ inputGravityVector[0], inputGravityVector[1], inputGravityVector[2] }, + m_bulkDensity( elementSubRegion.template getField< fields::poromechanics::bulkDensity >() ), + m_pressure( elementSubRegion.template getField< fields::flow::pressure >() ), + m_pressure_n( elementSubRegion.template getField< fields::flow::pressure_n >() ), + m_initialTemperature( elementSubRegion.template getField< fields::flow::initialTemperature >() ), + m_temperature( elementSubRegion.template getField< fields::flow::temperature >() ), + m_temperature_n( elementSubRegion.template getField< fields::flow::temperature_n >() ), + m_mineralReactionMolarIncrements( elementSubRegion.template getField< fields::flow::kineticReactionMolarIncrements >() ) +{} + +template< typename SUBREGION_TYPE, + typename CONSTITUTIVE_TYPE, + typename FE_TYPE > +GEOS_HOST_DEVICE +GEOS_FORCE_INLINE +void ExplicitChemoMechanics< SUBREGION_TYPE, CONSTITUTIVE_TYPE, FE_TYPE >:: +setup( localIndex const k, + StackVariables & stack ) const +{ + m_finiteElementSpace.template setup< FE_TYPE >( k, m_meshData, stack.feStack ); + localIndex const numSupportPoints = + m_finiteElementSpace.template numSupportPoints< FE_TYPE >( stack.feStack ); + stack.numRows = 3 * numSupportPoints; + stack.numCols = stack.numRows; + + for( localIndex a = 0; a < numSupportPoints; ++a ) + { + localIndex const localNodeIndex = m_elemsToNodes( k, a ); + + for( int i = 0; i < 3; ++i ) + { +#if defined(CALC_FEM_SHAPE_IN_KERNEL) + stack.xLocal[ a ][ i ] = m_X[ localNodeIndex ][ i ]; +#endif + stack.u_local[ a ][i] = m_disp[ localNodeIndex ][i]; + stack.uhat_local[ a ][i] = m_uhat[ localNodeIndex ][i]; + stack.localRowDofIndex[a*3+i] = m_dofNumber[localNodeIndex]+i; + stack.localColDofIndex[a*3+i] = m_dofNumber[localNodeIndex]+i; + } + } + + // Add stabilization to block diagonal parts of the local jacobian + // (this is a no-operation with FEM classes) + real64 const stabilizationScaling = computeStabilizationScaling( k ); + m_finiteElementSpace.template addGradGradStabilizationMatrix + < FE_TYPE, numDofPerTrialSupportPoint, true >( stack.feStack, + stack.localJacobian, + -stabilizationScaling ); +} + +template< typename SUBREGION_TYPE, + typename CONSTITUTIVE_TYPE, + typename FE_TYPE > +GEOS_HOST_DEVICE +GEOS_FORCE_INLINE +void ExplicitChemoMechanics< SUBREGION_TYPE, CONSTITUTIVE_TYPE, FE_TYPE >:: +quadraturePointKernel( localIndex const k, + localIndex const q, + StackVariables & stack ) const +{ + real64 dNdX[ numNodesPerElem ][ 3 ]; + real64 const detJxW = m_finiteElementSpace.template getGradN< FE_TYPE >( k, q, stack.xLocal, + stack.feStack, dNdX ); + + real64 strainInc[6] = {0}; + real64 totalStress[6] = {0}; + + typename CONSTITUTIVE_TYPE::KernelWrapper::DiscretizationOps stiffness; + + FE_TYPE::symmetricGradient( dNdX, stack.uhat_local, strainInc ); + + // Evaluate total stress and its derivatives + // TODO: allow for a customization of the kernel to pass the average pressure to the small strain update (to account for cap pressure + // later) + m_constitutiveUpdate.smallStrainUpdateExplicitChemoMechanics( k, q, + m_dt, + m_pressure[k], + m_pressure_n[k], + m_temperature[k], + m_temperature_n[k], + m_initialTemperature[k], + m_mineralReactionMolarIncrements[k], + strainInc, + totalStress, + stiffness ); + + for( localIndex i=0; i<6; ++i ) + { + totalStress[i] *= -detJxW; + } + + // Here we consider the bodyForce is purely from the solid + // Warning: here, we lag (in iteration) the displacement dependence of bulkDensity + real64 const gravityForce[3] = { m_gravityVector[0] * m_bulkDensity( k, q )* detJxW, + m_gravityVector[1] * m_bulkDensity( k, q )* detJxW, + m_gravityVector[2] * m_bulkDensity( k, q )* detJxW }; + + real64 N[numNodesPerElem]; + FE_TYPE::calcN( q, stack.feStack, N ); + FE_TYPE::plusGradNajAijPlusNaFi( dNdX, + totalStress, + N, + gravityForce, + reinterpret_cast< real64 (&)[numNodesPerElem][3] >(stack.localResidual) ); + real64 const stabilizationScaling = computeStabilizationScaling( k ); + m_finiteElementSpace.template + addEvaluatedGradGradStabilizationVector< FE_TYPE, + numDofPerTrialSupportPoint >( stack.feStack, + stack.uhat_local, + reinterpret_cast< real64 (&)[numNodesPerElem][3] >(stack.localResidual), + -stabilizationScaling ); + stiffness.template upperBTDB< numNodesPerElem >( dNdX, -detJxW, stack.localJacobian ); +} + +template< typename SUBREGION_TYPE, + typename CONSTITUTIVE_TYPE, + typename FE_TYPE > +GEOS_HOST_DEVICE +GEOS_FORCE_INLINE +real64 ExplicitChemoMechanics< SUBREGION_TYPE, CONSTITUTIVE_TYPE, FE_TYPE >:: +complete( localIndex const k, + StackVariables & stack ) const +{ + GEOS_UNUSED_VAR( k ); + real64 maxForce = 0; + + // TODO: Does this work if BTDB is non-symmetric? + CONSTITUTIVE_TYPE::KernelWrapper::DiscretizationOps::template fillLowerBTDB< numNodesPerElem >( stack.localJacobian ); + localIndex const numSupportPoints = + m_finiteElementSpace.template numSupportPoints< FE_TYPE >( stack.feStack ); + for( int localNode = 0; localNode < numSupportPoints; ++localNode ) + { + for( int dim = 0; dim < numDofPerTestSupportPoint; ++dim ) + { + localIndex const dof = + LvArray::integerConversion< localIndex >( stack.localRowDofIndex[ numDofPerTestSupportPoint * localNode + dim ] - m_dofRankOffset ); + if( dof < 0 || dof >= m_matrix.numRows() ) + continue; + m_matrix.template addToRowBinarySearchUnsorted< parallelDeviceAtomic >( dof, + stack.localRowDofIndex, + stack.localJacobian[ numDofPerTestSupportPoint * localNode + dim ], + stack.numRows ); + + RAJA::atomicAdd< parallelDeviceAtomic >( &m_rhs[ dof ], stack.localResidual[ numDofPerTestSupportPoint * localNode + dim ] ); + maxForce = fmax( maxForce, fabs( stack.localResidual[ numDofPerTestSupportPoint * localNode + dim ] ) ); + } + } + return maxForce; +} + +template< typename SUBREGION_TYPE, + typename CONSTITUTIVE_TYPE, + typename FE_TYPE > +template< typename POLICY, + typename KERNEL_TYPE > +real64 +ExplicitChemoMechanics< SUBREGION_TYPE, CONSTITUTIVE_TYPE, FE_TYPE >::kernelLaunch( localIndex const numElems, + KERNEL_TYPE const & kernelComponent ) +{ + return Base::template kernelLaunch< POLICY, KERNEL_TYPE >( numElems, kernelComponent ); +} + +} // namespace solidMechanicsLagrangianFEMKernels + +} // namespace geos + +#endif // GEOS_PHYSICSSOLVERS_SOLIDMECHANICS_KERNELS_EXPLICITCHEMOMECHANICS_IMPL_HPP_ diff --git a/src/coreComponents/physicsSolvers/solidMechanics/kernels/SolidMechanicsExplicitChemoMechanicsKernels.cpp.template b/src/coreComponents/physicsSolvers/solidMechanics/kernels/SolidMechanicsExplicitChemoMechanicsKernels.cpp.template new file mode 100644 index 00000000000..a7fc5514a81 --- /dev/null +++ b/src/coreComponents/physicsSolvers/solidMechanics/kernels/SolidMechanicsExplicitChemoMechanicsKernels.cpp.template @@ -0,0 +1,38 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * 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 "physicsSolvers/solidMechanics/kernels/ExplicitChemoMechanics_impl.hpp" + +using ExplicitChemoMechanicsPolicy = @ExplicitChemoMechanicsPolicy@; + +#define INSTANTIATION( NAME )\ +template class NAME < @SUBREGION_TYPE@, @CONSTITUTIVE_TYPE@, @FE_TYPE@ >; \ +template real64 NAME < @SUBREGION_TYPE@, @CONSTITUTIVE_TYPE@, @FE_TYPE@ >::kernelLaunch< NAME##Policy, \ + NAME < @SUBREGION_TYPE@, @CONSTITUTIVE_TYPE@, @FE_TYPE@ > > \ + ( localIndex const, \ + NAME < @SUBREGION_TYPE@, @CONSTITUTIVE_TYPE@, @FE_TYPE@ > const & ); \ + + +namespace geos +{ +using namespace constitutive; +using namespace finiteElement; +namespace solidMechanicsLagrangianFEMKernels +{ + INSTANTIATION( ExplicitChemoMechanics ) +} +} + +#undef INSTANTIATION diff --git a/src/coreComponents/physicsSolvers/solidMechanics/kernels/policies.hpp.in b/src/coreComponents/physicsSolvers/solidMechanics/kernels/policies.hpp.in index c14654cf0c7..bafc3a064b9 100644 --- a/src/coreComponents/physicsSolvers/solidMechanics/kernels/policies.hpp.in +++ b/src/coreComponents/physicsSolvers/solidMechanics/kernels/policies.hpp.in @@ -18,6 +18,7 @@ using ExplicitSmallStrainPolicy = @ExplicitSmallStrainPolicy@; using ExplicitFiniteStrainPolicy = @ExplicitFiniteStrainPolicy@; +using ExplicitChemoMechanicsPolicy = @ExplicitChemoMechanicsPolicy@; using FixedStressThermoPoromechanicsPolicy = @FixedStressThermoPoromechanicsPolicy@; using ImplicitSmallStrainNewmarkPolicy = @ImplicitSmallStrainNewmarkPolicy@; using ImplicitSmallStrainQuasiStaticPolicy = @ImplicitSmallStrainQuasiStaticPolicy@;