diff --git a/.integrated_tests.yaml b/.integrated_tests.yaml index 2cb02daa192..622c1da3d1b 100644 --- a/.integrated_tests.yaml +++ b/.integrated_tests.yaml @@ -1,6 +1,7 @@ baselines: bucket: geosx - baseline: integratedTests/baseline_integratedTests-pr3228-9676-61994fe + baseline: integratedTests/baseline_integratedTests-pr3416-9790-5bdb1fa + allow_fail: all: '' streak: '' diff --git a/BASELINE_NOTES.md b/BASELINE_NOTES.md index ea19f4ac5cd..4304e1b9671 100644 --- a/BASELINE_NOTES.md +++ b/BASELINE_NOTES.md @@ -6,6 +6,14 @@ This file is designed to track changes to the integrated test baselines. Any developer who updates the baseline ID in the .integrated_tests.yaml file is expected to create an entry in this file with the pull request number, date, and their justification for rebaselining. These notes should be in reverse-chronological order, and use the following time format: (YYYY-MM-DD). +PR #3416 (2024-01-21) +===================== +Refactoring of induced seismicity EQ solvers to add coupling. + +PR #3310 (2024-01-21) +====================== +Scalable rock toughness required new field. + PR #3228 (2024-01-15) ===================== deltaVolume added in multiphase. diff --git a/examples/pygeosxExamples/modifyBoundaryCondition/pkn_example.xml b/examples/pygeosxExamples/modifyBoundaryCondition/pkn_example.xml index 94db4f07eb6..23aed51fc40 100644 --- a/examples/pygeosxExamples/modifyBoundaryCondition/pkn_example.xml +++ b/examples/pygeosxExamples/modifyBoundaryCondition/pkn_example.xml @@ -62,7 +62,7 @@ diff --git a/inputFiles/hydraulicFracturing/Sneddon_hydroFrac_base.xml b/inputFiles/hydraulicFracturing/Sneddon_hydroFrac_base.xml index df05297ad3d..e23f6d8b315 100644 --- a/inputFiles/hydraulicFracturing/Sneddon_hydroFrac_base.xml +++ b/inputFiles/hydraulicFracturing/Sneddon_hydroFrac_base.xml @@ -37,7 +37,7 @@ name="SurfaceGen" targetRegions="{ Domain }" nodeBasedSIF="1" - rockToughness="10.0e6" + initialRockToughness="10.0e6" mpiCommOrder="1"/> diff --git a/inputFiles/hydraulicFracturing/heterogeneousInSitu_base.xml b/inputFiles/hydraulicFracturing/heterogeneousInSitu_base.xml index c8d741170ae..d4547ebc54f 100644 --- a/inputFiles/hydraulicFracturing/heterogeneousInSitu_base.xml +++ b/inputFiles/hydraulicFracturing/heterogeneousInSitu_base.xml @@ -53,7 +53,7 @@ diff --git a/inputFiles/hydraulicFracturing/heterogeneousInSitu_smoke.xml b/inputFiles/hydraulicFracturing/heterogeneousInSitu_smoke.xml index eb7b96bf286..3fc20c5c95a 100644 --- a/inputFiles/hydraulicFracturing/heterogeneousInSitu_smoke.xml +++ b/inputFiles/hydraulicFracturing/heterogeneousInSitu_smoke.xml @@ -71,7 +71,7 @@ diff --git a/inputFiles/hydraulicFracturing/hydrofractureSinglePhase2d.xml b/inputFiles/hydraulicFracturing/hydrofractureSinglePhase2d.xml index 7ac70ce7a6f..9fe4afed5b4 100644 --- a/inputFiles/hydraulicFracturing/hydrofractureSinglePhase2d.xml +++ b/inputFiles/hydraulicFracturing/hydrofractureSinglePhase2d.xml @@ -34,7 +34,7 @@ + initialRockToughness="1.0e6"/> diff --git a/inputFiles/hydraulicFracturing/kgdEdgeBased_C3D6_base.xml b/inputFiles/hydraulicFracturing/kgdEdgeBased_C3D6_base.xml index cc461b52ba3..0120cd3f6dd 100644 --- a/inputFiles/hydraulicFracturing/kgdEdgeBased_C3D6_base.xml +++ b/inputFiles/hydraulicFracturing/kgdEdgeBased_C3D6_base.xml @@ -49,7 +49,7 @@ diff --git a/inputFiles/hydraulicFracturing/kgdNodeBased_C3D6_base.xml b/inputFiles/hydraulicFracturing/kgdNodeBased_C3D6_base.xml index d7fbbd5d077..f9d7ac88c37 100644 --- a/inputFiles/hydraulicFracturing/kgdNodeBased_C3D6_base.xml +++ b/inputFiles/hydraulicFracturing/kgdNodeBased_C3D6_base.xml @@ -39,7 +39,7 @@ diff --git a/inputFiles/hydraulicFracturing/kgdToughnessDominated_base.xml b/inputFiles/hydraulicFracturing/kgdToughnessDominated_base.xml index 96d400a8390..74b8003e223 100644 --- a/inputFiles/hydraulicFracturing/kgdToughnessDominated_base.xml +++ b/inputFiles/hydraulicFracturing/kgdToughnessDominated_base.xml @@ -45,7 +45,7 @@ name="SurfaceGen" targetRegions="{ Domain }" nodeBasedSIF="1" - rockToughness="1e6" + initialRockToughness="1e6" mpiCommOrder="1"/> diff --git a/inputFiles/hydraulicFracturing/kgdToughnessDominated_poroelastic_base.xml b/inputFiles/hydraulicFracturing/kgdToughnessDominated_poroelastic_base.xml index 8f4284dd838..ad91b77dfaf 100644 --- a/inputFiles/hydraulicFracturing/kgdToughnessDominated_poroelastic_base.xml +++ b/inputFiles/hydraulicFracturing/kgdToughnessDominated_poroelastic_base.xml @@ -38,7 +38,7 @@ name="SurfaceGen" targetRegions="{ Domain }" nodeBasedSIF="1" - rockToughness="1e6" + initialRockToughness="1e6" mpiCommOrder="1"/> diff --git a/inputFiles/hydraulicFracturing/kgdValidation_base.xml b/inputFiles/hydraulicFracturing/kgdValidation_base.xml index e2d16b6d247..a902e5f5fab 100644 --- a/inputFiles/hydraulicFracturing/kgdValidation_base.xml +++ b/inputFiles/hydraulicFracturing/kgdValidation_base.xml @@ -44,7 +44,7 @@ logLevel="1" targetRegions="{ Domain }" nodeBasedSIF="0" - rockToughness="1.2e6" + initialRockToughness="1.2e6" mpiCommOrder="1"/> diff --git a/inputFiles/hydraulicFracturing/kgdViscosityDominated_base.xml b/inputFiles/hydraulicFracturing/kgdViscosityDominated_base.xml index e9b55058b17..5138b7c1eca 100644 --- a/inputFiles/hydraulicFracturing/kgdViscosityDominated_base.xml +++ b/inputFiles/hydraulicFracturing/kgdViscosityDominated_base.xml @@ -45,7 +45,7 @@ name="SurfaceGen" targetRegions="{ Domain }" nodeBasedSIF="1" - rockToughness="1e4" + initialRockToughness="1e4" mpiCommOrder="1"/> diff --git a/inputFiles/hydraulicFracturing/kgdViscosityDominated_poroelastic_base.xml b/inputFiles/hydraulicFracturing/kgdViscosityDominated_poroelastic_base.xml index 15a381c8365..32f2f25e7eb 100644 --- a/inputFiles/hydraulicFracturing/kgdViscosityDominated_poroelastic_base.xml +++ b/inputFiles/hydraulicFracturing/kgdViscosityDominated_poroelastic_base.xml @@ -38,7 +38,7 @@ name="SurfaceGen" targetRegions="{ Domain }" nodeBasedSIF="1" - rockToughness="1e4" + initialRockToughness="1e4" mpiCommOrder="1" isPoroelastic="1"/> diff --git a/inputFiles/hydraulicFracturing/pennyShapedAnisotropicScaledToughness_benchmark.xml b/inputFiles/hydraulicFracturing/pennyShapedAnisotropicScaledToughness_benchmark.xml new file mode 100644 index 00000000000..6d05d924d31 --- /dev/null +++ b/inputFiles/hydraulicFracturing/pennyShapedAnisotropicScaledToughness_benchmark.xml @@ -0,0 +1,182 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inputFiles/hydraulicFracturing/pennyShapedAnisotropicToughness_base.xml b/inputFiles/hydraulicFracturing/pennyShapedAnisotropicToughness_base.xml new file mode 100644 index 00000000000..63c2c6fb575 --- /dev/null +++ b/inputFiles/hydraulicFracturing/pennyShapedAnisotropicToughness_base.xml @@ -0,0 +1,228 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inputFiles/hydraulicFracturing/pennyShapedAnisotropicToughness_benchmark.xml b/inputFiles/hydraulicFracturing/pennyShapedAnisotropicToughness_benchmark.xml new file mode 100644 index 00000000000..6affb33bc1b --- /dev/null +++ b/inputFiles/hydraulicFracturing/pennyShapedAnisotropicToughness_benchmark.xml @@ -0,0 +1,180 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inputFiles/hydraulicFracturing/pennyShapedAnisotropicToughness_smoke.xml b/inputFiles/hydraulicFracturing/pennyShapedAnisotropicToughness_smoke.xml new file mode 100644 index 00000000000..a566f9c4c42 --- /dev/null +++ b/inputFiles/hydraulicFracturing/pennyShapedAnisotropicToughness_smoke.xml @@ -0,0 +1,106 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inputFiles/hydraulicFracturing/pennyShapedToughnessDominated_benchmark.xml b/inputFiles/hydraulicFracturing/pennyShapedToughnessDominated_benchmark.xml index 59abb35871a..eb9183bcf82 100644 --- a/inputFiles/hydraulicFracturing/pennyShapedToughnessDominated_benchmark.xml +++ b/inputFiles/hydraulicFracturing/pennyShapedToughnessDominated_benchmark.xml @@ -67,7 +67,7 @@ name="SurfaceGen" targetRegions="{ Domain }" nodeBasedSIF="1" - rockToughness="3.0e6" + initialRockToughness="3.0e6" mpiCommOrder="1"/> diff --git a/inputFiles/hydraulicFracturing/pennyShapedToughnessDominated_poroelastic_benchmark.xml b/inputFiles/hydraulicFracturing/pennyShapedToughnessDominated_poroelastic_benchmark.xml index e830a52c5fd..b20f9c443af 100644 --- a/inputFiles/hydraulicFracturing/pennyShapedToughnessDominated_poroelastic_benchmark.xml +++ b/inputFiles/hydraulicFracturing/pennyShapedToughnessDominated_poroelastic_benchmark.xml @@ -68,7 +68,7 @@ name="SurfaceGen" targetRegions="{ Domain }" nodeBasedSIF="1" - rockToughness="3.0e6" + initialRockToughness="3.0e6" mpiCommOrder="1" isPoroelastic="1"/> diff --git a/inputFiles/hydraulicFracturing/pennyShapedToughnessDominated_poroelastic_smoke.xml b/inputFiles/hydraulicFracturing/pennyShapedToughnessDominated_poroelastic_smoke.xml index a1c1c302b8d..8d074c2ea9e 100644 --- a/inputFiles/hydraulicFracturing/pennyShapedToughnessDominated_poroelastic_smoke.xml +++ b/inputFiles/hydraulicFracturing/pennyShapedToughnessDominated_poroelastic_smoke.xml @@ -44,7 +44,7 @@ name="SurfaceGen" targetRegions="{ Domain }" nodeBasedSIF="1" - rockToughness="3.0e6" + initialRockToughness="3.0e6" mpiCommOrder="1" isPoroelastic="1"/> diff --git a/inputFiles/hydraulicFracturing/pennyShapedToughnessDominated_smoke.xml b/inputFiles/hydraulicFracturing/pennyShapedToughnessDominated_smoke.xml index f433335ab6e..acf8e93e6b7 100644 --- a/inputFiles/hydraulicFracturing/pennyShapedToughnessDominated_smoke.xml +++ b/inputFiles/hydraulicFracturing/pennyShapedToughnessDominated_smoke.xml @@ -43,7 +43,7 @@ name="SurfaceGen" targetRegions="{ Domain }" nodeBasedSIF="1" - rockToughness="3.0e6" + initialRockToughness="3.0e6" mpiCommOrder="1"/> diff --git a/inputFiles/hydraulicFracturing/pennyShapedViscosityDominated_benchmark.xml b/inputFiles/hydraulicFracturing/pennyShapedViscosityDominated_benchmark.xml index a8f78adf606..eef546604d1 100644 --- a/inputFiles/hydraulicFracturing/pennyShapedViscosityDominated_benchmark.xml +++ b/inputFiles/hydraulicFracturing/pennyShapedViscosityDominated_benchmark.xml @@ -68,7 +68,7 @@ name="SurfaceGen" targetRegions="{ Domain }" nodeBasedSIF="1" - rockToughness="0.3e6" + initialRockToughness="0.3e6" mpiCommOrder="1"/> diff --git a/inputFiles/hydraulicFracturing/pennyShapedViscosityDominated_poroelastic_benchmark.xml b/inputFiles/hydraulicFracturing/pennyShapedViscosityDominated_poroelastic_benchmark.xml index 0d1b9485772..eb476baf5fe 100644 --- a/inputFiles/hydraulicFracturing/pennyShapedViscosityDominated_poroelastic_benchmark.xml +++ b/inputFiles/hydraulicFracturing/pennyShapedViscosityDominated_poroelastic_benchmark.xml @@ -69,7 +69,7 @@ name="SurfaceGen" targetRegions="{ Domain }" nodeBasedSIF="1" - rockToughness="0.3e6" + initialRockToughness="0.3e6" mpiCommOrder="1"/> diff --git a/inputFiles/hydraulicFracturing/pennyShapedViscosityDominated_poroelastic_smoke.xml b/inputFiles/hydraulicFracturing/pennyShapedViscosityDominated_poroelastic_smoke.xml index b3a8341224e..5cb156cd184 100644 --- a/inputFiles/hydraulicFracturing/pennyShapedViscosityDominated_poroelastic_smoke.xml +++ b/inputFiles/hydraulicFracturing/pennyShapedViscosityDominated_poroelastic_smoke.xml @@ -44,7 +44,7 @@ name="SurfaceGen" targetRegions="{ Domain }" nodeBasedSIF="1" - rockToughness="0.3e6" + initialRockToughness="0.3e6" mpiCommOrder="1" isPoroelastic="1"/> diff --git a/inputFiles/hydraulicFracturing/pennyShapedViscosityDominated_smoke.xml b/inputFiles/hydraulicFracturing/pennyShapedViscosityDominated_smoke.xml index 4b20bae5125..c84b65ef8f5 100644 --- a/inputFiles/hydraulicFracturing/pennyShapedViscosityDominated_smoke.xml +++ b/inputFiles/hydraulicFracturing/pennyShapedViscosityDominated_smoke.xml @@ -43,7 +43,7 @@ name="SurfaceGen" targetRegions="{ Domain }" nodeBasedSIF="1" - rockToughness="0.3e6" + initialRockToughness="0.3e6" mpiCommOrder="1"/> diff --git a/inputFiles/hydraulicFracturing/pknViscosityDominated_benchmark.xml b/inputFiles/hydraulicFracturing/pknViscosityDominated_benchmark.xml index 2b8dce7a719..4943e0f6d78 100644 --- a/inputFiles/hydraulicFracturing/pknViscosityDominated_benchmark.xml +++ b/inputFiles/hydraulicFracturing/pknViscosityDominated_benchmark.xml @@ -66,7 +66,7 @@ name="SurfaceGen" targetRegions="{ Domain }" nodeBasedSIF="1" - rockToughness="0.1e6" + initialRockToughness="0.1e6" mpiCommOrder="1"/> diff --git a/inputFiles/hydraulicFracturing/pknViscosityDominated_poroelastic_benchmark.xml b/inputFiles/hydraulicFracturing/pknViscosityDominated_poroelastic_benchmark.xml index 72b1be2f4d8..e73a06ddcc4 100644 --- a/inputFiles/hydraulicFracturing/pknViscosityDominated_poroelastic_benchmark.xml +++ b/inputFiles/hydraulicFracturing/pknViscosityDominated_poroelastic_benchmark.xml @@ -66,7 +66,7 @@ name="SurfaceGen" targetRegions="{ Domain }" nodeBasedSIF="1" - rockToughness="0.1e6" + initialRockToughness="0.1e6" mpiCommOrder="1"/> diff --git a/inputFiles/hydraulicFracturing/pknViscosityDominated_poroelastic_smoke.xml b/inputFiles/hydraulicFracturing/pknViscosityDominated_poroelastic_smoke.xml index 7e8e3463e18..f6e44b9bc91 100644 --- a/inputFiles/hydraulicFracturing/pknViscosityDominated_poroelastic_smoke.xml +++ b/inputFiles/hydraulicFracturing/pknViscosityDominated_poroelastic_smoke.xml @@ -44,7 +44,7 @@ name="SurfaceGen" targetRegions="{ Domain }" nodeBasedSIF="1" - rockToughness="0.1e6" + initialRockToughness="0.1e6" mpiCommOrder="1" isPoroelastic="1"/> diff --git a/inputFiles/hydraulicFracturing/pknViscosityDominated_smoke.xml b/inputFiles/hydraulicFracturing/pknViscosityDominated_smoke.xml index 0545171c3bc..87e732e3f37 100644 --- a/inputFiles/hydraulicFracturing/pknViscosityDominated_smoke.xml +++ b/inputFiles/hydraulicFracturing/pknViscosityDominated_smoke.xml @@ -43,7 +43,7 @@ name="SurfaceGen" targetRegions="{ Domain }" nodeBasedSIF="1" - rockToughness="1e4" + initialRockToughness="1e4" mpiCommOrder="1"/> diff --git a/inputFiles/hydraulicFracturing/scripts/hydrofractureFigure.py b/inputFiles/hydraulicFracturing/scripts/hydrofractureFigure.py index c8cb897201d..81dde7f93f7 100644 --- a/inputFiles/hydraulicFracturing/scripts/hydrofractureFigure.py +++ b/inputFiles/hydraulicFracturing/scripts/hydrofractureFigure.py @@ -36,7 +36,7 @@ def getParametersFromXML( geosDir, xmlFilePrefix): poissonRatio = float(elasticParam.get('defaultPoissonRatio')) injectionRate = -2.0 * float(tree.find('FieldSpecifications/SourceFlux').get('scale')) / fluidDensity tree = ElementTree.parse(prefix + xmlFilePrefix + "_base.xml") - toughness = float(tree.find('Solvers/SurfaceGenerator').get('rockToughness')) + toughness = float(tree.find('Solvers/SurfaceGenerator').get('initialRockToughness')) elif xmlFilePrefix == 'pennyShapedToughnessDominated' or xmlFilePrefix == 'pennyShapedViscosityDominated': K = float(elasticParam.get('defaultBulkModulus')) @@ -45,7 +45,7 @@ def getParametersFromXML( geosDir, xmlFilePrefix): poissonRatio = youngModulus / (2.0 * G) - 1.0 injectionRate = -4.0 * float(tree.find('FieldSpecifications/SourceFlux').get('scale')) / fluidDensity tree = ElementTree.parse(prefix + xmlFilePrefix + "_benchmark.xml") - toughness = float(tree.find('Solvers/SurfaceGenerator').get('rockToughness')) + toughness = float(tree.find('Solvers/SurfaceGenerator').get('initialRockToughness')) elif xmlFilePrefix == 'pknViscosityDominated': K = float(elasticParam.get('defaultBulkModulus')) @@ -54,7 +54,7 @@ def getParametersFromXML( geosDir, xmlFilePrefix): poissonRatio = youngModulus / (2.0 * G) - 1.0 injectionRate = -4.0 * float(tree.find('FieldSpecifications/SourceFlux').get('scale')) / fluidDensity tree = ElementTree.parse(prefix + xmlFilePrefix + "_benchmark.xml") - toughness = float(tree.find('Solvers/SurfaceGenerator').get('rockToughness')) + toughness = float(tree.find('Solvers/SurfaceGenerator').get('initialRockToughness')) found_core = False for elem in param: if elem.get("name") == "core": diff --git a/inputFiles/hydraulicFracturing/walshQuarterNoChombo_benchmark.xml b/inputFiles/hydraulicFracturing/walshQuarterNoChombo_benchmark.xml index 957b63bd278..a14bdc856f1 100644 --- a/inputFiles/hydraulicFracturing/walshQuarterNoChombo_benchmark.xml +++ b/inputFiles/hydraulicFracturing/walshQuarterNoChombo_benchmark.xml @@ -79,7 +79,7 @@ fractureRegion="Fracture" targetRegions="{Domain, void}" nodeBasedSIF="1" - rockToughness="3.0e6"> + initialRockToughness="3.0e6"> diff --git a/inputFiles/hydraulicFracturing/walshQuarterNoChombo_smoke.xml b/inputFiles/hydraulicFracturing/walshQuarterNoChombo_smoke.xml index 9990dd5c291..1a9a5a0ae73 100644 --- a/inputFiles/hydraulicFracturing/walshQuarterNoChombo_smoke.xml +++ b/inputFiles/hydraulicFracturing/walshQuarterNoChombo_smoke.xml @@ -64,7 +64,7 @@ fractureRegion="Fracture" targetRegions="{Domain, void}" nodeBasedSIF="1" - rockToughness="3.0e6" + initialRockToughness="3.0e6" mpiCommOrder="1"> diff --git a/inputFiles/inducedSeismicity/SCEC_BP6_QD_S_base.xml b/inputFiles/inducedSeismicity/SCEC_BP6_QD_S_base.xml new file mode 100644 index 00000000000..1257f1ae3ec --- /dev/null +++ b/inputFiles/inducedSeismicity/SCEC_BP6_QD_S_base.xml @@ -0,0 +1,217 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/inputFiles/inducedSeismicity/SCEC_BP6_QD_S_explicit.xml b/inputFiles/inducedSeismicity/SCEC_BP6_QD_S_explicit.xml new file mode 100644 index 00000000000..fbbfccd5b38 --- /dev/null +++ b/inputFiles/inducedSeismicity/SCEC_BP6_QD_S_explicit.xml @@ -0,0 +1,139 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/inputFiles/inducedSeismicity/SCEC_BP6_QD_S_implicit.xml b/inputFiles/inducedSeismicity/SCEC_BP6_QD_S_implicit.xml new file mode 100644 index 00000000000..1b74521b1ad --- /dev/null +++ b/inputFiles/inducedSeismicity/SCEC_BP6_QD_S_implicit.xml @@ -0,0 +1,140 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/inputFiles/inducedSeismicity/SCEC_BP7_QD_base.xml b/inputFiles/inducedSeismicity/SCEC_BP7_QD_base.xml new file mode 100644 index 00000000000..08fc7cb9930 --- /dev/null +++ b/inputFiles/inducedSeismicity/SCEC_BP7_QD_base.xml @@ -0,0 +1,251 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/inputFiles/inducedSeismicity/SpringSliderExplicit_base.xml b/inputFiles/inducedSeismicity/SpringSliderExplicit_base.xml deleted file mode 100644 index a0c0381fb23..00000000000 --- a/inputFiles/inducedSeismicity/SpringSliderExplicit_base.xml +++ /dev/null @@ -1,167 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/inputFiles/inducedSeismicity/SpringSliderExplicit_smoke.xml b/inputFiles/inducedSeismicity/SpringSliderExplicit_smoke.xml index 2de09b79a03..a1a67094f7c 100644 --- a/inputFiles/inducedSeismicity/SpringSliderExplicit_smoke.xml +++ b/inputFiles/inducedSeismicity/SpringSliderExplicit_smoke.xml @@ -2,8 +2,19 @@ + name="./SpringSlider_base.xml"/> + + + + + @@ -23,6 +34,32 @@ maxEventDt="1e4" target="/Solvers/SpringSlider"/> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/inputFiles/inducedSeismicity/SpringSlider_base.xml b/inputFiles/inducedSeismicity/SpringSlider_base.xml index 5f6aefc94ad..876457fa2dd 100644 --- a/inputFiles/inducedSeismicity/SpringSlider_base.xml +++ b/inputFiles/inducedSeismicity/SpringSlider_base.xml @@ -1,19 +1,10 @@ - - - @@ -80,27 +71,26 @@ - + diff --git a/inputFiles/inducedSeismicity/SpringSlider_smoke.xml b/inputFiles/inducedSeismicity/SpringSlider_smoke.xml deleted file mode 100644 index b10580131d8..00000000000 --- a/inputFiles/inducedSeismicity/SpringSlider_smoke.xml +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/inputFiles/inducedSeismicity/inducedSeismicity.ats b/inputFiles/inducedSeismicity/inducedSeismicity.ats index 813bdf08b8b..56aea6b5820 100644 --- a/inputFiles/inducedSeismicity/inducedSeismicity.ats +++ b/inputFiles/inducedSeismicity/inducedSeismicity.ats @@ -23,7 +23,7 @@ decks = [ check_step=100, curvecheck_params=CurveCheckParameters(**curvecheck_params)), TestDeck( - name="SpringSlider_smoke", + name="SpringSliderImplicit_smoke", description="Spring slider 0D system", partitions=((1, 1, 1), ), restart_step=0, @@ -34,7 +34,7 @@ decks = [ description="Spring slider 0D system", partitions=((1, 1, 1), ), restart_step=0, - check_step=532, + check_step=343, restartcheck_params=RestartcheckParameters(atol=1e-4, rtol=1e-3)) ] generate_geos_tests(decks) diff --git a/inputFiles/inducedSeismicity/scripts/plotBP6_results.py b/inputFiles/inducedSeismicity/scripts/plotBP6_results.py new file mode 100644 index 00000000000..e869c3d5b5a --- /dev/null +++ b/inputFiles/inducedSeismicity/scripts/plotBP6_results.py @@ -0,0 +1,78 @@ +import argparse +import numpy as np +from geos.hdf5_wrapper import hdf5_wrapper +import matplotlib.pyplot as plt +import os + +def remove_padding(data): + """Removes trailing zeros from a NumPy array.""" + nonzero_indices = np.nonzero(data)[0] + if nonzero_indices.size == 0: # If all elements are zero + return np.array([]), np.array([]) + last_nonzero = nonzero_indices[-1] + return data[:last_nonzero + 1] + +def getDataFromHDF5( hdf5FilePath, var_name ): + # Read HDF5 + data = hdf5_wrapper(f'{hdf5FilePath}').get_copy() + var = data[f'{var_name} source'] + var = np.asarray(var) + time = data[f'{var_name} Time'] + + # Remove padding + var = remove_padding(var) + time = time[:len(var)] # Match the length of the time array to the variable + return time, var + + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument('-d', '--dir', type=str, help='Path to hdf5 file') + args = parser.parse_args() + + normalized_dir = os.path.abspath(args.dir) + if not os.path.isdir(normalized_dir): + print(f"Error: {normalized_dir} is not a valid directory.") + exit(1) + + filePath = f"{normalized_dir}/BP6_DQ_S.hdf5" + if not os.path.exists(filePath): + print(f"Error: {filePath} not found.") + exit(1) + + time, pressure = getDataFromHDF5( filePath, "pressure" ) + time, slipRate = getDataFromHDF5( filePath, "slipRate" ) + + # Convert time to years + time_in_years = time / (365 * 24 * 3600) # Assuming time is in seconds + + # Plotting + fig, ax1 = plt.subplots() + + print(len(time_in_years)) + print(len(pressure)) + + # Plot pressure on the left y-axis + ax1.set_xlabel('Time (years)') + ax1.set_ylabel('Pressure (Pa)', color='tab:blue') + ax1.plot(time_in_years, pressure, label="Pressure", color='tab:blue') + ax1.tick_params(axis='y', labelcolor='tab:blue') + + + # Plot slipRate on the right y-axis (log scale) + ax2 = ax1.twinx() + ax2.set_ylabel('Slip Rate (m/s)', color='tab:red') + ax2.plot(time_in_years, slipRate, label="Slip Rate", color='tab:red') + ax2.set_yscale('log') + ax2.tick_params(axis='y', labelcolor='tab:red') + + # Set x-axis limits to 0 to 2 years + ax1.set_xlim(0, np.max(time_in_years)) + + + # Add grid and title + plt.title("Pressure and Slip Rate vs Time") + plt.grid() + + # Show plot + plt.show() \ No newline at end of file diff --git a/inputFiles/inducedSeismicity/scripts/plot_springSlider_results.py b/inputFiles/inducedSeismicity/scripts/plot_springSlider_results.py new file mode 100644 index 00000000000..ac56838015b --- /dev/null +++ b/inputFiles/inducedSeismicity/scripts/plot_springSlider_results.py @@ -0,0 +1,74 @@ +import argparse +import numpy as np +from geos.hdf5_wrapper import hdf5_wrapper +import matplotlib.pyplot as plt +import os + +def remove_padding(data): + """Removes trailing zeros from a NumPy array.""" + nonzero_indices = np.nonzero(data)[0] + if nonzero_indices.size == 0: # If all elements are zero + return np.array([]), np.array([]) + last_nonzero = nonzero_indices[-1] + return data[:last_nonzero + 1] + +def getDataFromHDF5( hdf5FilePath, var_name ): + # Read HDF5 + data = hdf5_wrapper(f'{hdf5FilePath}').get_copy() + var = data[f'{var_name}'] + var = np.asarray(var) + time = data[f'{var_name} Time'] + + # Remove padding + var = remove_padding(var) + time = time[:len(var)] # Match the length of the time array to the variable + return time, var + + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument('-d', '--dir', type=str, help='Path to hdf5 file') + args = parser.parse_args() + + normalized_dir = os.path.abspath(args.dir) + if not os.path.isdir(normalized_dir): + print(f"Error: {normalized_dir} is not a valid directory.") + exit(1) + + filePath = f"{normalized_dir}/springSlider.hdf5" + if not os.path.exists(filePath): + print(f"Error: {filePath} not found.") + exit(1) + + time, slipRate = getDataFromHDF5( filePath, "slipRate" ) + time, stateVariable = getDataFromHDF5( filePath, "stateVariable" ) + + # Convert time to years + time_in_years = time / (365 * 24 * 3600) # Assuming time is in seconds + + # Plotting + fig, (ax1, ax2, ax3) = plt.subplots(3, 1, figsize=(8, 8), sharex=True) + + # Plot pressure on the left y-axis + ax1.set_xlabel('Time (years)') + ax1.set_ylabel('slip rate (Pa)', color='tab:blue') + ax1.plot(time_in_years, slipRate, label="slip rate", color='tab:blue') + ax1.set_yscale('log') + ax1.tick_params(axis='y', labelcolor='tab:blue') + + # Set x-axis limits to 0 to 2 years + ax1.set_xlim(0, np.max(time_in_years)) + + # Plot stateVariable on the right y-axis (log scale) + ax2.set_ylabel('state variable', color='tab:red') + ax2.plot(time_in_years, stateVariable, label="state variable", color='tab:red') + ax2.tick_params(axis='y', labelcolor='tab:red') + ax2.set_xlim(0, np.max(time_in_years)) + + # Add grid and title + fig.suptitle('Spring slider solution', fontsize=16) + plt.tight_layout() + plt.grid() + + # Show plot + plt.show() \ No newline at end of file diff --git a/inputFiles/lagrangianContactMechanics/LagrangeContactBubbleStab_FixedSlip_smoke.xml b/inputFiles/lagrangianContactMechanics/LagrangeContactBubbleStab_FixedSlip_smoke.xml index 8258d2cc4a3..5da14675d94 100644 --- a/inputFiles/lagrangianContactMechanics/LagrangeContactBubbleStab_FixedSlip_smoke.xml +++ b/inputFiles/lagrangianContactMechanics/LagrangeContactBubbleStab_FixedSlip_smoke.xml @@ -30,7 +30,7 @@ diff --git a/inputFiles/lagrangianContactMechanics/LagrangeContactBubbleStab_singleFracCompression_base.xml b/inputFiles/lagrangianContactMechanics/LagrangeContactBubbleStab_singleFracCompression_base.xml index 854136ed032..3be52a9bcdc 100644 --- a/inputFiles/lagrangianContactMechanics/LagrangeContactBubbleStab_singleFracCompression_base.xml +++ b/inputFiles/lagrangianContactMechanics/LagrangeContactBubbleStab_singleFracCompression_base.xml @@ -30,7 +30,7 @@ logLevel="0" fractureRegion="Fracture" targetRegions="{ Region }" - rockToughness="1.0e6" + initialRockToughness="1.0e6" mpiCommOrder="1"/> diff --git a/inputFiles/lagrangianContactMechanics/PassingCrack_base.xml b/inputFiles/lagrangianContactMechanics/PassingCrack_base.xml index 1f38c231aa9..603fcf59ed7 100644 --- a/inputFiles/lagrangianContactMechanics/PassingCrack_base.xml +++ b/inputFiles/lagrangianContactMechanics/PassingCrack_base.xml @@ -7,7 +7,7 @@ diff --git a/inputFiles/lagrangianContactMechanics/SimpleCubes_base.xml b/inputFiles/lagrangianContactMechanics/SimpleCubes_base.xml index 32e64f498de..ce7f07de63b 100644 --- a/inputFiles/lagrangianContactMechanics/SimpleCubes_base.xml +++ b/inputFiles/lagrangianContactMechanics/SimpleCubes_base.xml @@ -6,7 +6,7 @@ @@ -134,7 +134,7 @@ - diff --git a/inputFiles/lagrangianContactMechanics/SingleFracCompression_base.xml b/inputFiles/lagrangianContactMechanics/SingleFracCompression_base.xml index d9da50caf95..c3f39da2632 100644 --- a/inputFiles/lagrangianContactMechanics/SingleFracCompression_base.xml +++ b/inputFiles/lagrangianContactMechanics/SingleFracCompression_base.xml @@ -9,7 +9,7 @@ logLevel="0" fractureRegion="Fracture" targetRegions="{ Region }" - rockToughness="1.0e6" + initialRockToughness="1.0e6" mpiCommOrder="1"/> diff --git a/inputFiles/lagrangianContactMechanics/SlippingFault_base.xml b/inputFiles/lagrangianContactMechanics/SlippingFault_base.xml index 5299794fe0f..d9f4997f3c7 100644 --- a/inputFiles/lagrangianContactMechanics/SlippingFault_base.xml +++ b/inputFiles/lagrangianContactMechanics/SlippingFault_base.xml @@ -9,7 +9,7 @@ logLevel="0" fractureRegion="Fracture" targetRegions="{ Region }" - rockToughness="1.0e6" + initialRockToughness="1.0e6" mpiCommOrder="1"/> diff --git a/inputFiles/lagrangianContactMechanics/Sneddon_benchmark.xml b/inputFiles/lagrangianContactMechanics/Sneddon_benchmark.xml index 7750053d6fa..d45bc7ded1f 100644 --- a/inputFiles/lagrangianContactMechanics/Sneddon_benchmark.xml +++ b/inputFiles/lagrangianContactMechanics/Sneddon_benchmark.xml @@ -13,7 +13,7 @@ logLevel="0" fractureRegion="Fracture" targetRegions="{ Region }" - rockToughness="1.0e6" + initialRockToughness="1.0e6" mpiCommOrder="1"/> diff --git a/inputFiles/lagrangianContactMechanics/Sneddon_smoke.xml b/inputFiles/lagrangianContactMechanics/Sneddon_smoke.xml index 5e64b204ad1..5794ed344e5 100644 --- a/inputFiles/lagrangianContactMechanics/Sneddon_smoke.xml +++ b/inputFiles/lagrangianContactMechanics/Sneddon_smoke.xml @@ -13,7 +13,7 @@ logLevel="0" fractureRegion="Fracture" targetRegions="{ Region }" - rockToughness="1.0e6" + initialRockToughness="1.0e6" mpiCommOrder="1"/> diff --git a/inputFiles/lagrangianContactMechanics/TFrac_benchmark.xml b/inputFiles/lagrangianContactMechanics/TFrac_benchmark.xml index 60f1a1f537a..5a6d6b5a8b7 100644 --- a/inputFiles/lagrangianContactMechanics/TFrac_benchmark.xml +++ b/inputFiles/lagrangianContactMechanics/TFrac_benchmark.xml @@ -13,7 +13,7 @@ logLevel="0" fractureRegion="Fracture" targetRegions="{ Region }" - rockToughness="1.0e6" + initialRockToughness="1.0e6" mpiCommOrder="1"/> diff --git a/inputFiles/lagrangianContactMechanics/TFrac_smoke.xml b/inputFiles/lagrangianContactMechanics/TFrac_smoke.xml index 5ab56b69af9..cd3543f0806 100644 --- a/inputFiles/lagrangianContactMechanics/TFrac_smoke.xml +++ b/inputFiles/lagrangianContactMechanics/TFrac_smoke.xml @@ -12,7 +12,7 @@ logLevel="0" fractureRegion="Fracture" targetRegions="{ Region }" - rockToughness="1.0e6" + initialRockToughness="1.0e6" mpiCommOrder="1"/> diff --git a/inputFiles/lagrangianContactMechanics/UnstructuredCrack_base.xml b/inputFiles/lagrangianContactMechanics/UnstructuredCrack_base.xml index 628fd884589..81cddb50913 100644 --- a/inputFiles/lagrangianContactMechanics/UnstructuredCrack_base.xml +++ b/inputFiles/lagrangianContactMechanics/UnstructuredCrack_base.xml @@ -6,7 +6,7 @@ diff --git a/inputFiles/lagrangianContactMechanics/scripts/generateTables.py b/inputFiles/lagrangianContactMechanics/scripts/generateTables.py new file mode 100644 index 00000000000..9c83614b2f4 --- /dev/null +++ b/inputFiles/lagrangianContactMechanics/scripts/generateTables.py @@ -0,0 +1,179 @@ +import numpy as np +import os +import sys +import xml.etree.ElementTree as ElementTree +import matplotlib +import matplotlib.pyplot as plt + +class SingularCrackSlip: + + def __init__(self, mechanicalParameters, length ): + K = mechanicalParameters["bulkModulus"] + G = mechanicalParameters["shearModulus"] + poisson_ratio= (3 * K - 2 * G) / (2 * (3 * K + G)) + + mu_star = G /( 1 - poisson_ratio) + self.tau_0 = 0.0 + self.tau_r = -1.0 + + self.scaling = 2*(self.tau_0 - self.tau_r)/mu_star + self.halfLength = length + + def computeSlip(self, x): + return self.scaling * np.sqrt(self.halfLength**2 - x**2) + + def computeTraction(self, x): + if x < -self.halfLength or x > self.halfLength: + return self.tau_0 + (self.tau_0-self.tau_r) * ( np.abs(x)/np.sqrt(x**2 - self.halfLength**2) - 1 ) + else: + return self.tau_r +class GaussianSlip: + + def __init__(self, peakStrength, length ): + self.scaling = peakStrength + self.halfLength = length + + def computeSlip(self, x): + denom = 1 / (self.halfLength/2) + return self.scaling*np.exp(-0.5*((x)/denom)**2) + +def getMechanicalParametersFromXML(xmlFilePath): + tree = ElementTree.parse(xmlFilePath) + + param = tree.find('Constitutive/ElasticIsotropic') + + mechanicalParameters = dict.fromkeys(["bulkModulus", "shearModulus"]) + mechanicalParameters["bulkModulus"] = float(param.get("defaultBulkModulus")) + mechanicalParameters["shearModulus"] = float(param.get("defaultShearModulus")) + return mechanicalParameters + +def getFractureLengthFromXML(xmlFilePath): + tree = ElementTree.parse(xmlFilePath) + + rectangle = tree.find('Geometry/Box') + xmin = rectangle.get("xMin") + xmax = rectangle.get("xMax") + xmin = [float(i) for i in xmin[1:-1].split(",")] + xmax = [float(i) for i in xmax[1:-1].split(",")] + length = ( xmax[0] - xmin[0] ) / 2 + origin = 0.0 + + return length, origin + +def plot_traction_solution(): + # Read HDF5 + import hdf5_wrapper + hdf5File1Path = "Output/traction.hdf5" + + # Read HDF5 + data = hdf5_wrapper.hdf5_wrapper(hdf5File1Path).get_copy() + traction = data['traction'] + traction = np.asarray(traction) + traction_geos = traction[0, :, 1] + x = data['traction elementCenter'] + x_geos = x[0, :, 0] + + #-------- Extract info from XML + xmlFilePath = "/Users/cusini1/geosx/GEOSX/inputFiles/lagrangianContactMechanics/LagrangeContactBubbleStab_FixedSlip_base.xml" + + mechanicalParameters = getMechanicalParametersFromXML(xmlFilePath) + + # Get length of the fracture + xmlFilePath = "/Users/cusini1/geosx/GEOSX/inputFiles/lagrangianContactMechanics/LagrangeContactBubbleStab_FixedSlip_smoke.xml" + totalHalfLength, originShift = getFractureLengthFromXML(xmlFilePath) + halfLength = 2.0 + + singularCrackSlipSolution = SingularCrackSlip(mechanicalParameters, halfLength) + x = np.linspace(-totalHalfLength, totalHalfLength, 10000, endpoint=True) + traction_analytical = np.zeros(len(x)) + i = 0 + for xCell in x: + traction_analytical[i] = singularCrackSlipSolution.computeTraction(xCell) + i += 1 + + fsize = 30 + msize = 15 + lw = 2 + fig, ax = plt.subplots(1, figsize=(16, 12)) + cmap = plt.get_cmap("tab10") + + # Plot analytical (continuous line) and numerical (markers) aperture solution + ax.plot(x, traction_analytical, color='r', label='Traction analytical', lw=lw) + ax.plot(x_geos, traction_geos, color='k', label='geos', marker="o", lw=lw) + + ax.set_xlabel('Fault coordinate [m]', size=fsize, weight="bold") + ax.set_ylabel('Shear traction', size=fsize, weight="bold") + ax.legend(bbox_to_anchor=(0.75, 0.9), loc='center', borderaxespad=0., fontsize=fsize) + ax.xaxis.set_tick_params(labelsize=fsize) + ax.yaxis.set_tick_params(labelsize=fsize) + plt.savefig("traction.png") + +def output_tables(x, slip, name): + # Save x to x.csv with one value per row + np.savetxt('x.csv', x, fmt='%f') + + # Save aperture_analytical to jump.csv with one value per row + np.savetxt(f'{name}.csv', slip, fmt='%f') + + +def debug(): + #-------- Extract info from XML + xmlFilePath = "/Users/cusini1/geosx/GEOSX/inputFiles/lagrangianContactMechanics/LagrangeContactBubbleStab_FixedSlip_base.xml" + + mechanicalParameters = getMechanicalParametersFromXML(xmlFilePath) + appliedPressure = 1.0 + + # Get length of the fracture + xmlFilePath = "/Users/cusini1/geosx/GEOSX/inputFiles/lagrangianContactMechanics/LagrangeContactBubbleStab_FixedSlip_smoke.xml" + totalHalfLength, originShift = getFractureLengthFromXML(xmlFilePath) + halfLength = 2.0 + + # Initialize Sneddon's analytical solution + singularCrackSlipSolution = SingularCrackSlip(mechanicalParameters, halfLength ) + peakStrength = 3.0 + gaussianSlipSolution = GaussianSlip( peakStrength, halfLength) + + # Plot analytical (continuous line) and numerical (markers) aperture solution + x = np.linspace(-totalHalfLength, totalHalfLength, 100000, endpoint=True) + singularCrackSlip = np.zeros(len(x)) + gaussianSlip = np.zeros(len(x)) + i = 0 + for xCell in x: + if xCell > -halfLength and xCell < halfLength: + singularCrackSlip[i] = singularCrackSlipSolution.computeSlip(xCell) + gaussianSlip[i] = gaussianSlipSolution.computeSlip(xCell) + i += 1 + + fsize = 24 + msize = 15 + lw = 6 + fig, ax = plt.subplots(1, figsize=(16, 12)) + cmap = plt.get_cmap("tab10") + + ax.plot(x, singularCrackSlip , color='k', label='Analytical Solution', lw=lw) + ax.grid() + ax.set_xlabel('Fault coordinate [m]', size=fsize, weight="bold") + ax.set_ylabel('slip [m]', size=fsize, weight="bold") + ax.legend(bbox_to_anchor=(0.7, 1), loc='center', borderaxespad=0., fontsize=fsize) + ax.xaxis.set_tick_params(labelsize=fsize) + ax.yaxis.set_tick_params(labelsize=fsize) + plt.savefig("singularCrackSlip.png") + + fig, ax = plt.subplots(1, figsize=(16, 12)) + cmap = plt.get_cmap("tab10") + + ax.plot(x, gaussianSlip , color='k', label='Analytical Solution', lw=lw) + ax.grid() + ax.set_xlabel('Fault coordinate [m]', size=fsize, weight="bold") + ax.set_ylabel('slip [m]', size=fsize, weight="bold") + ax.legend(bbox_to_anchor=(0.75, 0.9), loc='center', borderaxespad=0., fontsize=fsize) + ax.xaxis.set_tick_params(labelsize=fsize) + ax.yaxis.set_tick_params(labelsize=fsize) + plt.savefig("gaussianSlip.png") + + output_tables(x, singularCrackSlip, "singularCrackSlip") + output_tables(x, gaussianSlip, "gaussianSlip") + +if __name__ == "__main__": + debug() + plot_traction_solution() diff --git a/inputFiles/phaseField/PhaseFieldFracture_Nucleation_base.xml b/inputFiles/phaseField/PhaseFieldFracture_Nucleation_base.xml index e339684d116..c69b54535cc 100644 --- a/inputFiles/phaseField/PhaseFieldFracture_Nucleation_base.xml +++ b/inputFiles/phaseField/PhaseFieldFracture_Nucleation_base.xml @@ -52,7 +52,7 @@ diff --git a/inputFiles/poromechanicsFractures/ExponentialDecayPermeability_conformingFracture_base.xml b/inputFiles/poromechanicsFractures/ExponentialDecayPermeability_conformingFracture_base.xml index d21f7f7fb80..d47a5af1807 100644 --- a/inputFiles/poromechanicsFractures/ExponentialDecayPermeability_conformingFracture_base.xml +++ b/inputFiles/poromechanicsFractures/ExponentialDecayPermeability_conformingFracture_base.xml @@ -44,7 +44,7 @@ name="SurfaceGen" targetRegions="{ Domain }" fractureRegion="Fracture" - rockToughness="1e6" + initialRockToughness="1e6" mpiCommOrder="1"/> diff --git a/inputFiles/poromechanicsFractures/PoroElastic_conformingFracture_2d_faultSlip_sequential_solvers.xml b/inputFiles/poromechanicsFractures/PoroElastic_conformingFracture_2d_faultSlip_sequential_solvers.xml index 845f3151232..a5e47f2cc74 100755 --- a/inputFiles/poromechanicsFractures/PoroElastic_conformingFracture_2d_faultSlip_sequential_solvers.xml +++ b/inputFiles/poromechanicsFractures/PoroElastic_conformingFracture_2d_faultSlip_sequential_solvers.xml @@ -53,7 +53,7 @@ name="SurfaceGen" targetRegions="{ Domain }" fractureRegion="Fracture" - rockToughness="1e6" + initialRockToughness="1e6" mpiCommOrder="1"/> diff --git a/inputFiles/poromechanicsFractures/PoroElastic_conformingFracture_2d_faultSlip_solvers.xml b/inputFiles/poromechanicsFractures/PoroElastic_conformingFracture_2d_faultSlip_solvers.xml index 2c8d9f8936b..73f5391756c 100755 --- a/inputFiles/poromechanicsFractures/PoroElastic_conformingFracture_2d_faultSlip_solvers.xml +++ b/inputFiles/poromechanicsFractures/PoroElastic_conformingFracture_2d_faultSlip_solvers.xml @@ -45,7 +45,7 @@ name="SurfaceGen" targetRegions="{ Domain }" fractureRegion="Fracture" - rockToughness="1e6" + initialRockToughness="1e6" mpiCommOrder="1"/> diff --git a/inputFiles/poromechanicsFractures/PoroElastic_conformingFracture_2d_openingFrac_sequential_solvers.xml b/inputFiles/poromechanicsFractures/PoroElastic_conformingFracture_2d_openingFrac_sequential_solvers.xml index 4112be144f1..d11f90503d8 100755 --- a/inputFiles/poromechanicsFractures/PoroElastic_conformingFracture_2d_openingFrac_sequential_solvers.xml +++ b/inputFiles/poromechanicsFractures/PoroElastic_conformingFracture_2d_openingFrac_sequential_solvers.xml @@ -52,7 +52,7 @@ name="SurfaceGen" targetRegions="{ Domain }" fractureRegion="Fracture" - rockToughness="1e6" + initialRockToughness="1e6" mpiCommOrder="1"/> diff --git a/inputFiles/poromechanicsFractures/PoroElastic_conformingFracture_2d_openingFrac_solvers.xml b/inputFiles/poromechanicsFractures/PoroElastic_conformingFracture_2d_openingFrac_solvers.xml index cc365b429c6..d2c9d1fd433 100755 --- a/inputFiles/poromechanicsFractures/PoroElastic_conformingFracture_2d_openingFrac_solvers.xml +++ b/inputFiles/poromechanicsFractures/PoroElastic_conformingFracture_2d_openingFrac_solvers.xml @@ -44,7 +44,7 @@ name="SurfaceGen" targetRegions="{ Domain }" fractureRegion="Fracture" - rockToughness="1e6" + initialRockToughness="1e6" mpiCommOrder="1"/> diff --git a/inputFiles/proppant/FlowProppantBedTransport2d_base.xml b/inputFiles/proppant/FlowProppantBedTransport2d_base.xml index 177a83e6c00..d8bc681d588 100644 --- a/inputFiles/proppant/FlowProppantBedTransport2d_base.xml +++ b/inputFiles/proppant/FlowProppantBedTransport2d_base.xml @@ -50,7 +50,7 @@ diff --git a/inputFiles/proppant/FlowProppantTransport2d_base.xml b/inputFiles/proppant/FlowProppantTransport2d_base.xml index ab806c51cf2..8f4608a6577 100644 --- a/inputFiles/proppant/FlowProppantTransport2d_base.xml +++ b/inputFiles/proppant/FlowProppantTransport2d_base.xml @@ -53,7 +53,7 @@ diff --git a/inputFiles/proppant/ProppantGelTransport_base.xml b/inputFiles/proppant/ProppantGelTransport_base.xml index b4e7e74b4d6..425b5bdb5da 100644 --- a/inputFiles/proppant/ProppantGelTransport_base.xml +++ b/inputFiles/proppant/ProppantGelTransport_base.xml @@ -53,7 +53,7 @@ + initialRockToughness="1e6"/> diff --git a/inputFiles/proppant/ProppantSlotTest_benchmark.xml b/inputFiles/proppant/ProppantSlotTest_benchmark.xml index 4c123fc1fc6..73025434ce8 100755 --- a/inputFiles/proppant/ProppantSlotTest_benchmark.xml +++ b/inputFiles/proppant/ProppantSlotTest_benchmark.xml @@ -92,7 +92,7 @@ diff --git a/inputFiles/proppant/ProppantSlotTest_smoke.xml b/inputFiles/proppant/ProppantSlotTest_smoke.xml index 0576291d3b3..757e67d5f0d 100755 --- a/inputFiles/proppant/ProppantSlotTest_smoke.xml +++ b/inputFiles/proppant/ProppantSlotTest_smoke.xml @@ -102,7 +102,7 @@ diff --git a/inputFiles/singlePhaseFlowFractures/fractureFlowWithGravity_conforming_2d_smoke.xml b/inputFiles/singlePhaseFlowFractures/fractureFlowWithGravity_conforming_2d_smoke.xml index 9dbce69a6a6..9045289c402 100644 --- a/inputFiles/singlePhaseFlowFractures/fractureFlowWithGravity_conforming_2d_smoke.xml +++ b/inputFiles/singlePhaseFlowFractures/fractureFlowWithGravity_conforming_2d_smoke.xml @@ -18,7 +18,7 @@ diff --git a/inputFiles/singlePhaseFlowFractures/fractureFlow_conforming_2d.xml b/inputFiles/singlePhaseFlowFractures/fractureFlow_conforming_2d.xml index 3e5ebf1c94f..93eba8a2ebd 100644 --- a/inputFiles/singlePhaseFlowFractures/fractureFlow_conforming_2d.xml +++ b/inputFiles/singlePhaseFlowFractures/fractureFlow_conforming_2d.xml @@ -17,7 +17,7 @@ diff --git a/inputFiles/singlePhaseFlowFractures/fractureJunctionFlow_conforming_2d.xml b/inputFiles/singlePhaseFlowFractures/fractureJunctionFlow_conforming_2d.xml index e1902e65f51..a85f2e3b715 100644 --- a/inputFiles/singlePhaseFlowFractures/fractureJunctionFlow_conforming_2d.xml +++ b/inputFiles/singlePhaseFlowFractures/fractureJunctionFlow_conforming_2d.xml @@ -17,7 +17,7 @@ diff --git a/inputFiles/singlePhaseFlowFractures/fractureMatrixFlowWithGravity_conforming_2d_smoke.xml b/inputFiles/singlePhaseFlowFractures/fractureMatrixFlowWithGravity_conforming_2d_smoke.xml index 8f7226b2f85..2250f3b5fe5 100644 --- a/inputFiles/singlePhaseFlowFractures/fractureMatrixFlowWithGravity_conforming_2d_smoke.xml +++ b/inputFiles/singlePhaseFlowFractures/fractureMatrixFlowWithGravity_conforming_2d_smoke.xml @@ -18,7 +18,7 @@ diff --git a/inputFiles/singlePhaseFlowFractures/fractureMatrixFlow_conforming_2d.xml b/inputFiles/singlePhaseFlowFractures/fractureMatrixFlow_conforming_2d.xml index 6d9f7f2692e..7f475b928ab 100644 --- a/inputFiles/singlePhaseFlowFractures/fractureMatrixFlow_conforming_2d.xml +++ b/inputFiles/singlePhaseFlowFractures/fractureMatrixFlow_conforming_2d.xml @@ -18,7 +18,7 @@ diff --git a/inputFiles/singlePhaseFlowFractures/impermeableFault_conforming_base.xml b/inputFiles/singlePhaseFlowFractures/impermeableFault_conforming_base.xml index c05815fe353..13ed96f0b8c 100644 --- a/inputFiles/singlePhaseFlowFractures/impermeableFault_conforming_base.xml +++ b/inputFiles/singlePhaseFlowFractures/impermeableFault_conforming_base.xml @@ -18,7 +18,7 @@ diff --git a/inputFiles/surfaceGeneration/DryFrac_StaticPenny_PrismElem.xml b/inputFiles/surfaceGeneration/DryFrac_StaticPenny_PrismElem.xml index f398d7beb79..ada8953f37b 100644 --- a/inputFiles/surfaceGeneration/DryFrac_StaticPenny_PrismElem.xml +++ b/inputFiles/surfaceGeneration/DryFrac_StaticPenny_PrismElem.xml @@ -20,7 +20,7 @@ @@ -190,5 +190,5 @@ name="restartOutput"/> - + diff --git a/inputFiles/surfaceGeneration/cube_8.xml b/inputFiles/surfaceGeneration/cube_8.xml index 2f002bb34b9..b5920ca47fa 100644 --- a/inputFiles/surfaceGeneration/cube_8.xml +++ b/inputFiles/surfaceGeneration/cube_8.xml @@ -21,7 +21,7 @@ name="SurfaceGen" logLevel="1" targetRegions="{ Region2 }" - rockToughness="1e60" + initialRockToughness="1e60" mpiCommOrder="1" /> @@ -231,5 +231,5 @@ name="restartOutput"/> - + diff --git a/inputFiles/thermalSinglePhaseFlowFractures/fractureMatrixThermalFlow_conforming_base.xml b/inputFiles/thermalSinglePhaseFlowFractures/fractureMatrixThermalFlow_conforming_base.xml index 57cfd350256..9c903ebf0e4 100644 --- a/inputFiles/thermalSinglePhaseFlowFractures/fractureMatrixThermalFlow_conforming_base.xml +++ b/inputFiles/thermalSinglePhaseFlowFractures/fractureMatrixThermalFlow_conforming_base.xml @@ -25,7 +25,7 @@ diff --git a/inputFiles/thermoPoromechanicsFractures/ThermoPoroElastic_conforming_base.xml b/inputFiles/thermoPoromechanicsFractures/ThermoPoroElastic_conforming_base.xml index e32a27879d0..69ebbdcaaba 100644 --- a/inputFiles/thermoPoromechanicsFractures/ThermoPoroElastic_conforming_base.xml +++ b/inputFiles/thermoPoromechanicsFractures/ThermoPoroElastic_conforming_base.xml @@ -47,7 +47,7 @@ name="SurfaceGenerator" targetRegions="{ RockMatrix }" fractureRegion="Fracture" - rockToughness="1e6" + initialRockToughness="1e6" mpiCommOrder="1"/> diff --git a/inputFiles/wellbore/CasedElasticWellbore_ImperfectInterfaces_base.xml b/inputFiles/wellbore/CasedElasticWellbore_ImperfectInterfaces_base.xml index 0f782918ec9..fb52fd5f479 100644 --- a/inputFiles/wellbore/CasedElasticWellbore_ImperfectInterfaces_base.xml +++ b/inputFiles/wellbore/CasedElasticWellbore_ImperfectInterfaces_base.xml @@ -22,7 +22,7 @@ name="SurfaceGen" fractureRegion="Fracture" targetRegions="{ casing, cement, rock }" - rockToughness="1.0e6" + initialRockToughness="1.0e6" mpiCommOrder="1"/> diff --git a/inputFiles/wellbore/CasedThermoElasticWellbore_ImperfectInterfaces_base.xml b/inputFiles/wellbore/CasedThermoElasticWellbore_ImperfectInterfaces_base.xml index ae8b6baea35..ab4ccf65952 100644 --- a/inputFiles/wellbore/CasedThermoElasticWellbore_ImperfectInterfaces_base.xml +++ b/inputFiles/wellbore/CasedThermoElasticWellbore_ImperfectInterfaces_base.xml @@ -53,7 +53,7 @@ name="SurfaceGen" fractureRegion="Fracture" targetRegions="{ casing, cement, rock }" - rockToughness="1.0e6" + initialRockToughness="1.0e6" mpiCommOrder="1"/> diff --git a/scripts/ci_build_and_test_in_container.sh b/scripts/ci_build_and_test_in_container.sh index 6e6ed6e1609..348fe6ad229 100755 --- a/scripts/ci_build_and_test_in_container.sh +++ b/scripts/ci_build_and_test_in_container.sh @@ -337,11 +337,13 @@ fi # Cleaning the build directory. or_die cmake --build . --target clean - # Clean the repository or_die cd ${GEOS_SRC_DIR}/inputFiles find . -name *.pyc | xargs rm -f +# Clean the rst files +echo "Cleaning the rst files..." +or_die rm -rf ${GEOS_SRC_DIR}/src/docs/sphinx/datastructure # If we're here, either everything went OK or we have to deal with the integrated tests manually. if [[ ! -z "${INTEGRATED_TEST_EXIT_STATUS+x}" ]]; then diff --git a/src/coreComponents/common/MpiWrapper.hpp b/src/coreComponents/common/MpiWrapper.hpp index 22def51e1e8..4d017ef8393 100644 --- a/src/coreComponents/common/MpiWrapper.hpp +++ b/src/coreComponents/common/MpiWrapper.hpp @@ -315,7 +315,7 @@ struct MpiWrapper int sendcount, T_RECV * recvbuf, int recvcount, - MPI_Comm comm ); + MPI_Comm comm = MPI_COMM_GEOS ); /** * @brief Strongly typed wrapper around MPI_Allgatherv. @@ -335,7 +335,7 @@ struct MpiWrapper T_RECV * recvbuf, int * recvcounts, int * displacements, - MPI_Comm comm ); + MPI_Comm comm = MPI_COMM_GEOS ); /** * @brief Convenience function for MPI_Allgather. @@ -422,10 +422,10 @@ struct MpiWrapper template< typename T > - static int scan( T const * sendbuf, T * recvbuf, int count, MPI_Op op, MPI_Comm comm ); + static int scan( T const * sendbuf, T * recvbuf, int count, MPI_Op op, MPI_Comm comm = MPI_COMM_GEOS ); template< typename T > - static int exscan( T const * sendbuf, T * recvbuf, int count, MPI_Op op, MPI_Comm comm ); + static int exscan( T const * sendbuf, T * recvbuf, int count, MPI_Op op, MPI_Comm comm = MPI_COMM_GEOS ); /** * @brief Strongly typed wrapper around MPI_Bcast. @@ -436,7 +436,7 @@ struct MpiWrapper * @return The return value of the underlying call to MPI_Bcast(). */ template< typename T > - static int bcast( T * buffer, int count, int root, MPI_Comm comm ); + static int bcast( T * buffer, int count, int root, MPI_Comm comm = MPI_COMM_GEOS ); /** @@ -466,7 +466,28 @@ struct MpiWrapper TR * const recvbuf, int recvcount, int root, - MPI_Comm comm ); + MPI_Comm comm = MPI_COMM_GEOS ); + + /** + * @brief Strongly typed wrapper around MPI_Gather(). + * @tparam TS The pointer type for \p sendbuf + * @tparam TR The pointer type for \p recvbuf + * @param[in] sendbuf The pointer to the sending buffer. + * @param[out] recvbuf The pointer to the receive buffer. + * @param[in] recvcount The number of values to receive. + * @param[in] root The rank recieving the data. + * @param[in] comm The MPI_Comm over which the gather operates. + * @return + */ + template< typename T, typename DST_CONTAINER, + typename = std::enable_if_t< + std::is_trivially_copyable_v< T > && + std::is_same_v< decltype(std::declval< DST_CONTAINER >().size()), std::size_t > && + std::is_same_v< decltype(std::declval< DST_CONTAINER >().data()), T * > > > + static int gather( T const & value, + DST_CONTAINER & destValuesBuffer, + int root, + MPI_Comm comm = MPI_COMM_GEOS ); /** * @brief Strongly typed wrapper around MPI_Gatherv. @@ -489,7 +510,7 @@ struct MpiWrapper const int * recvcounts, const int * displs, int root, - MPI_Comm comm ); + MPI_Comm comm = MPI_COMM_GEOS ); /** * @brief Returns an MPI_Op associated with our strongly typed Reduction enum. @@ -900,6 +921,25 @@ int MpiWrapper::gather( TS const * const sendbuf, #endif } +template< typename T, typename DST_CONTAINER, typename > +int MpiWrapper::gather( T const & value, + DST_CONTAINER & destValuesBuffer, + int root, + MPI_Comm MPI_PARAM( comm ) ) +{ + if( commRank() == 0 ) + GEOS_ERROR_IF_LT_MSG( destValuesBuffer.size(), size_t( commSize() ), + "Receive buffer is not large enough to contain the values to receive." ); +#ifdef GEOS_USE_MPI + return MPI_Gather( &value, sizeof( T ), internal::getMpiType< uint8_t >(), + destValuesBuffer.data(), sizeof( T ), internal::getMpiType< uint8_t >(), + root, comm ); +#else + memcpy( destValuesBuffer.data(), &value, sendBufferSize ); + return 0; +#endif +} + template< typename TS, typename TR > int MpiWrapper::gatherv( TS const * const sendbuf, int sendcount, diff --git a/src/coreComponents/common/format/table/TableData.cpp b/src/coreComponents/common/format/table/TableData.cpp index 5153bf58752..517613fcf50 100644 --- a/src/coreComponents/common/format/table/TableData.cpp +++ b/src/coreComponents/common/format/table/TableData.cpp @@ -18,39 +18,38 @@ */ #include "TableData.hpp" +#include "common/logger/Logger.hpp" namespace geos { -void TableData::addRow( std::vector< string > const & row ) +void TableData::addRow( std::vector< TableData::CellData > const & row ) { - if( m_rows.size() != 0 && row.size() != m_rows[m_rows.size() - 1].size() ) + m_rows.push_back( row ); +} + +void TableData::addSeparator() +{ + if( m_rows.empty()) { - string msg = "Remarks : some cells may be missing"; - if( std::find( m_errorsMsg.begin(), m_errorsMsg.end(), msg ) == m_errorsMsg.end()) - { - m_errorsMsg.push_back( msg ); - } + GEOS_ERROR( "Bad use of a Tabledata::addSeparator(). Make sure you have added values in TableData" ); } - m_rows.push_back( row ); + + integer rowSize = m_rows[0].size(); + m_rows.emplace_back( std::vector< TableData::CellData >( rowSize, { CellType::Separator, "-" } )); + } void TableData::clear() { m_rows.clear(); - m_errorsMsg.clear(); } -std::vector< std::vector< string > > const & TableData::getTableDataRows() const +std::vector< std::vector< TableData::CellData > > const & TableData::getTableDataRows() const { return m_rows; } -std::vector< string > const & TableData::getErrorMsgs() const -{ - return m_errorsMsg; -} - void TableData2D::collectTableValues( arraySlice1d< real64 const > rowAxisValues, arraySlice1d< real64 const > columnAxisValues, arrayView1d< real64 const > values ) @@ -75,7 +74,6 @@ TableData2D::TableDataHolder TableData2D::convertTable2D( arrayView1d< real64 co { string const rowFmt = GEOS_FMT( "{} = {{}}", rowAxisDescription ); string const columnFmt = GEOS_FMT( "{} = {{}}", columnAxisDescription ); - collectTableValues( coordinates[0], coordinates[1], values ); return buildTableData( string( units::getDescription( valueUnit )), rowFmt, @@ -87,7 +85,6 @@ TableData2D::TableDataHolder TableData2D::buildTableData( string_view targetUnit string_view columnFmt ) const { TableData2D::TableDataHolder tableData1D; - std::vector< size_t > rowsLength; tableData1D.headerNames.push_back( string( targetUnit ) ); @@ -99,9 +96,9 @@ TableData2D::TableDataHolder TableData2D::buildTableData( string_view targetUnit // insert row value and row cell values for( auto const & [rowValue, rowMap] : m_data ) { - std::vector< string > currentRowValues; + std::vector< TableData::CellData > currentRowValues; currentRowValues.reserve( rowMap.size() ); - currentRowValues.push_back( GEOS_FMT( rowFmt, rowValue ) ); + currentRowValues.push_back( {CellType::Value, GEOS_FMT( rowFmt, rowValue )} ); std::set< real64 >::const_iterator columnIt = m_columnValues.begin(); for( auto const & [columnValue, cellValue] : rowMap ) @@ -109,13 +106,12 @@ TableData2D::TableDataHolder TableData2D::buildTableData( string_view targetUnit // if a column value(s) is/are missing, insert empty entry(ies) while( columnValue > *( columnIt++ ) && columnIt != m_columnValues.end() ) { - currentRowValues.push_back( "" ); + currentRowValues.push_back( {CellType::Value, ""} ); } - currentRowValues.push_back( GEOS_FMT( "{}", cellValue ) ); + currentRowValues.push_back( {CellType::Value, GEOS_FMT( "{}", cellValue )} ); } - tableData1D.tableData.addRow( std::move( currentRowValues ) ); - rowsLength.push_back( currentRowValues.size() ); + tableData1D.tableData.addRow( currentRowValues ); } return tableData1D; diff --git a/src/coreComponents/common/format/table/TableData.hpp b/src/coreComponents/common/format/table/TableData.hpp index 0842c3a4fbe..04d524c5f6e 100644 --- a/src/coreComponents/common/format/table/TableData.hpp +++ b/src/coreComponents/common/format/table/TableData.hpp @@ -23,6 +23,7 @@ #include "common/Units.hpp" #include "common/DataTypes.hpp" #include "common/format/Format.hpp" +#include "TableTypes.hpp" namespace geos { @@ -33,10 +34,22 @@ namespace geos class TableData { public: + + /** + * @brief Representing a data in TableData + */ + struct CellData + { + /// The cell type + CellType type; + /// The cell value + string value = ""; + }; + /** * @brief Add a row to the table. * The values passed to addRow (can be any type). - * @param args Cell values to be added to the row. + * @param args CellData values to be added to the row. */ template< typename ... Args > void addRow( Args const & ... args ); @@ -45,7 +58,13 @@ class TableData * @brief Add a row to the table * @param row A vector of string representing a row */ - void addRow( std::vector< string > const & row ); + void addRow( std::vector< CellData > const & row ); + + /** + * @brief Add a line separator to the table + * You must have filled values in TableData before using it + */ + void addSeparator(); /** * @brief Reset data in the table @@ -55,7 +74,7 @@ class TableData /** * @return The rows of the table */ - std::vector< std::vector< string > > const & getTableDataRows() const; + std::vector< std::vector< CellData > > const & getTableDataRows() const; /** * @brief Get all error messages @@ -66,10 +85,7 @@ class TableData private: /// vector containing all rows with cell values - std::vector< std::vector< string > > m_rows; - - /// store error if there are any inconsistencies related to the table - std::vector< string > m_errorsMsg; + std::vector< std::vector< CellData > > m_rows; }; @@ -98,7 +114,7 @@ class TableData2D /** * @brief Add a cell to the table. If necessary, create automatically the containing column & row. * @tparam T The value passed to addCell (can be any type). - * @param value Cell value to be added. + * @param value CellData value to be added. * @param rowValue The value of the row containing the cell. * @param columnValue The value of the column containing the cell. */ @@ -149,17 +165,35 @@ class TableData2D std::set< real64 > m_columnValues; }; +/** + * @brief Trait to check is the args is a special type of cell + * @tparam T The type of a cell + */ +template< typename T > +constexpr bool isCellType = std::is_same_v< T, CellType >; + template< typename ... Args > void TableData::addRow( Args const &... args ) { - std::vector< string > m_cellsValue; + std::vector< CellData > cells; ( [&] { - static_assert( has_formatter_v< decltype(args) >, "Argument passed in addRow cannot be converted to string" ); - string const cellValue = GEOS_FMT( "{}", args ); - m_cellsValue.push_back( cellValue ); + static_assert( has_formatter_v< decltype(args) > || isCellType< std::decay_t< decltype(args) > >, "Argument passed in addRow cannot be converted to string nor a CellType" ); + if constexpr (std::is_same_v< Args, CellType >) { + if( args == CellType::Separator ) + { + cells.push_back( {CellType::Separator} ); + } + else + { + cells.push_back( {CellType::MergeNext} ); + } + } + else + { + cells.push_back( {CellType::Value, GEOS_FMT( "{}", args )} ); + } } (), ...); - - addRow( m_cellsValue ); + addRow( cells ); } template< typename T > diff --git a/src/coreComponents/common/format/table/TableFormatter.cpp b/src/coreComponents/common/format/table/TableFormatter.cpp index 7392834e060..4305fcc8a98 100644 --- a/src/coreComponents/common/format/table/TableFormatter.cpp +++ b/src/coreComponents/common/format/table/TableFormatter.cpp @@ -13,6 +13,7 @@ * ------------------------------------------------------------------------------------------------------------ */ + /** * @file TableFormatter.cpp */ @@ -20,7 +21,9 @@ #include "TableFormatter.hpp" #include #include "common/format/StringUtilities.hpp" +#include "common/logger/Logger.hpp" #include "TableFormatter.hpp" + namespace geos { @@ -38,34 +41,78 @@ TableCSVFormatter::TableCSVFormatter( TableLayout const & tableLayout ): m_tableLayout = tableLayout; } +static constexpr string_view separator = ","; string TableCSVFormatter::headerToString() const { - std::stringstream oss; - static constexpr string_view separator = ","; + string result; + + size_t total_size = 0; + for( const auto & column : m_tableLayout.getColumns()) + { + for( const auto & str : column.m_header.m_lines ) + { + total_size += str.size(); + } + total_size += separator.size(); + } + result.reserve( total_size ); for( std::size_t idxColumn = 0; idxColumn < m_tableLayout.getColumns().size(); ++idxColumn ) { - oss << m_tableLayout.getColumns()[idxColumn].m_parameter.columnName; + std::ostringstream strValue; + for( auto const & str : m_tableLayout.getColumns()[idxColumn].m_header.m_lines ) + { + result.append( str ); + } + if( idxColumn < m_tableLayout.getColumns().size() - 1 ) { - oss << separator; + result.append( separator ); } } - oss << "\n"; - return oss.str(); + result.append( "\n" ); + return result; } string TableCSVFormatter::dataToString( TableData const & tableData ) const { - std::vector< std::vector< string > > const rowsValues = tableData.getTableDataRows(); - std::ostringstream oss; + RowsCellInput const rowsValues( tableData.getTableDataRows() ); + string result; + size_t total_size = 0; for( const auto & row : rowsValues ) { - oss << stringutilities::join( row.cbegin(), row.cend(), "," ) << "\n"; + for( const auto & item : row ) + { + total_size += item.value.size(); + } + total_size += row.size(); } - return oss.str(); + result.reserve( total_size ); + + for( const auto & row : rowsValues ) + { + std::vector< string > rowConverted; + for( const auto & item : row ) + { + std::istringstream strStream( item.value ); + string line; + bool detectNewLine = false; + while( getline( strStream, line, '\n' )) + { + rowConverted.push_back( line ); + detectNewLine = true; + } + + if( !detectNewLine ) + rowConverted.push_back( item.value ); + } + result.append( stringutilities::join( rowConverted.cbegin(), rowConverted.cend(), separator )); + result.append( "\n" ); + } + + return result; } template<> @@ -79,15 +126,15 @@ string TableCSVFormatter::toString< TableData >( TableData const & tableData ) c /////////////////////////////////////////////////////////////////////// /** - * @brief Build cell given an alignment, a value and spaces - * @param alignment The aligment of cell value + * @brief Build cell given an m_alignment, a value and spaces + * @param m_alignment The aligment of cell value * @param value The cell value * @param spaces The number of spaces in the cell * @return A formated cell */ -string buildCell( TableLayout::Alignment const alignment, string_view value, integer const spaces ) +string buildCell( TableLayout::Alignment const m_alignment, string_view value, size_t const spaces ) { - switch( alignment ) + switch( m_alignment ) { case TableLayout::right: return GEOS_FMT( "{:>{}}", value, spaces ); case TableLayout::left: return GEOS_FMT( "{:<{}}", value, spaces ); @@ -96,315 +143,549 @@ string buildCell( TableLayout::Alignment const alignment, string_view value, int } } -/** - * @brief Detect columns who are not displayed from TableLayout and therefore modify columns / tableDataRows vectors - * @param columns Vector built in TableLayout containing all columns with their parameters - * @param tableDataRows Vector built in TableData containing all rows values - */ -void formatColumnsFromLayout( std::vector< TableLayout::Column > & columns, - std::vector< std::vector< string > > & tableDataRows ) -{ - integer idxColumn = 0; - for( auto iterColumn = columns.begin(); iterColumn!=columns.end(); ) - { - if( !iterColumn->m_parameter.enabled ) - { - iterColumn = columns.erase( iterColumn ); - for( auto & row : tableDataRows ) - { - row.erase( row.begin() + idxColumn ); - } - } - else - { - ++iterColumn; - ++idxColumn; - } - } -} - TableTextFormatter::TableTextFormatter( TableLayout const & tableLayout ): TableFormatter( tableLayout ) {} -void TableTextFormatter::fillTableColumnsFromRows( std::vector< TableLayout::Column > & columns, - std::vector< std::vector< string > > & rows ) const +string TableTextFormatter::toString() const { - for( size_t idxRow = 0; idxRow < rows.size(); idxRow++ ) - { - if( rows[idxRow].size() < columns.size() ) - { - rows[idxRow].resize( columns.size(), " " ); - } - - for( size_t idxColumn = 0; idxColumn < columns.size(); idxColumn++ ) - { - if( m_tableLayout.getColumns()[idxColumn].m_parameter.enabled ) - { - columns[idxColumn].m_columnValues.push_back( rows[idxRow][idxColumn] ); - } - } - } + TableData tableData; + return toString( tableData ); } -string TableTextFormatter::layoutToString() const +template<> +string TableTextFormatter::toString< TableData >( TableData const & tableData ) const { std::ostringstream tableOutput; - string sectionSeparatingLine; - std::vector< TableLayout::Column > columns = m_tableLayout.getColumns(); - - outputLayout( tableOutput, columns, {}, sectionSeparatingLine ); + CellLayoutRows cellsHeaderLayout; + CellLayoutRows cellsDataLayout; + string separatorLine; + + TableLayout tableLayout = m_tableLayout; + size_t nbEnabledColumn = 0; + + initalizeTableLayout( tableLayout, tableData, + cellsHeaderLayout, cellsDataLayout, + separatorLine, + nbEnabledColumn ); + outputTable( tableLayout, tableOutput, + cellsHeaderLayout, cellsDataLayout, + separatorLine, + nbEnabledColumn ); return tableOutput.str(); } -template<> -string TableTextFormatter::toString< TableData >( TableData const & tableData ) const +void TableTextFormatter::initalizeTableLayout( TableLayout & tableLayout, + TableData const & tableData, + CellLayoutRows & cellsHeaderLayout, + CellLayoutRows & cellsDataLayout, + string & separatorLine, + size_t & nbEnabledColumn ) const { - std::ostringstream tableOutput; - string sectionSeparatingLine; - std::vector< TableLayout::Column > columns = m_tableLayout.getColumns(); - std::vector< std::vector< string > > tableDataRows = tableData.getTableDataRows(); - std::vector< string > const & msgTableError = tableData.getErrorMsgs(); - integer const nbValuesRows = tableDataRows.size(); + setLinks( tableLayout.getColumns() ); - formatColumnsFromLayout( columns, tableDataRows ); - fillTableColumnsFromRows( columns, tableDataRows ); + populateHeaderCellsLayout( tableLayout, cellsHeaderLayout ); - outputLayout( tableOutput, columns, msgTableError, sectionSeparatingLine ); - outputSectionRows( columns, sectionSeparatingLine, tableOutput, nbValuesRows, TableLayout::Section::values ); - tableOutput << '\n'; + RowsCellInput inputDataValues( tableData.getTableDataRows()); + if( inputDataValues.size() > 0 ) + { + populateDataCellsLayout( tableLayout, cellsDataLayout, inputDataValues ); + } - return tableOutput.str(); + updateColumnMaxLength( tableLayout, cellsHeaderLayout, cellsDataLayout ); + + adjustTableWidth( tableLayout, cellsHeaderLayout, cellsDataLayout, separatorLine, nbEnabledColumn ); } -void TableTextFormatter::outputLayout( std::ostringstream & tableOutput, - std::vector< TableLayout::Column > & columns, - std::vector< string > const & msgTableError, - string & sectionSeparatingLine ) const +void TableTextFormatter::outputTable( TableLayout & tableLayout, + std::ostringstream & tableOutput, + CellLayoutRows const & cellsHeader, + CellLayoutRows const & cellsData, + string_view separatorLine, + size_t & nbEnabledColumn ) const { - string topSeparator; - size_t nbHeaderRows = 0; - std::vector< std::vector< string > > splitHeaders; - string const tableTitle = string( m_tableLayout.getTitle()); - - splitAndSetColumnNames( columns, nbHeaderRows, splitHeaders ); - findAndSetMaxStringSize( columns ); + if( tableLayout.isLineBreakEnabled()) + { + tableOutput << '\n'; + } + outputTitleRow( tableLayout, tableOutput, separatorLine ); + tableOutput << GEOS_FMT( "{}\n", separatorLine ); + outputLines( tableLayout, cellsHeader, tableOutput, tableLayout.getSublineInHeaderCounts(), + CellType::Header, separatorLine, nbEnabledColumn ); + outputLines( tableLayout, cellsData, tableOutput, tableLayout.getNbSubDataLines(), + CellType::Value, separatorLine, nbEnabledColumn ); + if( tableLayout.isLineBreakEnabled()) + { + tableOutput << '\n'; + } +} - computeTableWidth( columns, msgTableError ); - buildTableSeparators( columns, topSeparator, sectionSeparatingLine ); +void TableTextFormatter::setLinks( std::vector< TableLayout::Column > & columns ) const +{ + for( size_t idxColumn = 0; idxColumn < columns.size(); ++idxColumn ) + { + if( idxColumn < columns.size() - 1 ) + { + columns[idxColumn].setNextCell( &columns[idxColumn + 1] ); + } - tableOutput << '\n'; - outputTopRows( tableOutput, {tableTitle}, topSeparator, TableLayout::Alignment::center ); - outputTopRows( tableOutput, msgTableError, topSeparator, TableLayout::Alignment::left ); - tableOutput << GEOS_FMT( "{}\n", sectionSeparatingLine ); + if( !columns[idxColumn].m_subColumn.empty()) + { + for( auto & subCol : columns[idxColumn].m_subColumn ) + { + subCol.setParent( &columns[idxColumn] ); + } - outputSectionRows( columns, sectionSeparatingLine, tableOutput, nbHeaderRows, TableLayout::Section::header ); + setLinks( columns[idxColumn].m_subColumn ); + } + } } -void TableTextFormatter::splitAndSetColumnNames( std::vector< TableLayout::Column > & columns, - size_t & nbHeaderRows, - std::vector< std::vector< string > > & splitHeaders ) const +void TableTextFormatter::populateHeaderCellsLayout( TableLayout & tableLayout, + CellLayoutRows & cellsHeaderLayout ) const { + cellsHeaderLayout.resize( tableLayout.getMaxDepth() ); + size_t const headerLayersCount = tableLayout.getMaxDepth(); + std::vector< size_t > & sublineHeaderCounts = tableLayout.getSublineInHeaderCounts(); + sublineHeaderCounts.resize( headerLayersCount, 1 ); - splitHeaders.reserve( columns.size() ); - for( auto const & column : columns ) + for( auto it = tableLayout.beginDeepFirst(); it != tableLayout.endDeepFirst(); ++it ) { - std::vector< string > splitHeaderParts; - std::istringstream ss( column.m_parameter.columnName ); - string subColumnNames; + size_t const currentLayer = it.getCurrentLayer(); + TableLayout::CellLayout currentCell = it->m_header; - while( getline( ss, subColumnNames, '\n' )) + if( !it->hasChild() && headerLayersCount - 1 != currentLayer ) { - splitHeaderParts.push_back( subColumnNames ); + for( size_t idxRow = currentLayer; idxRow < headerLayersCount - 1; idxRow++ ) + { + TableLayout::CellLayout emptyCell{CellType::Header, "", TableLayout::Alignment::center}; + emptyCell.m_cellWidth = it->m_header.m_cellWidth; + cellsHeaderLayout[idxRow + 1].push_back( emptyCell ); + } } + currentCell.m_cellWidth = it->m_header.m_cellWidth; - splitHeaders.push_back( std::move( splitHeaderParts ) ); - } + if( it->hasParent() ) + { + if( it->getNumberCellMerge() == 0 ) + { + it->getParent()->incrementMergeHeaderCount( 1 ); + } + else + { + it->getParent()->incrementMergeHeaderCount( it->getNumberCellMerge() ); + } + } - nbHeaderRows = std::max_element( splitHeaders.begin(), splitHeaders.end(), - []( auto const & v1, auto const & v2 ) { return v1.size() < v2.size(); } )->size(); + if( it->getNumberCellMerge() > 1 ) + { + it->decrementMergeHeaderCount(); + } - for( auto & headerParts : splitHeaders ) - { - if( headerParts.size() < nbHeaderRows ) + sublineHeaderCounts[currentLayer] = std::max( sublineHeaderCounts[currentLayer], + currentCell.m_lines.size() ); + + for( size_t idxColumn = 0; idxColumn < it->getNumberCellMerge(); idxColumn++ ) { - headerParts.resize( nbHeaderRows, " " ); + TableLayout::CellLayout mergingCell{ CellType::MergeNext, "", TableLayout::Alignment::center }; + cellsHeaderLayout[currentLayer].push_back( mergingCell ); } - columns[&headerParts - &splitHeaders[0]].m_parameter.splitColumnNameLines = headerParts; - } -} + cellsHeaderLayout[currentLayer].push_back( currentCell ); + } -void TableTextFormatter::findAndSetMaxStringSize( std::vector< TableLayout::Column > & columns ) const -{ - for( auto & column : columns ) + size_t idxLayer = 0; + for( auto & lines: cellsHeaderLayout ) { - auto const maxStringSizeHeader = *std::max_element( column.m_parameter.splitColumnNameLines.begin(), - column.m_parameter.splitColumnNameLines.end(), - []( const auto & a, const auto & b ) {return a.size() < b.size();} ); - column.m_maxStringSize = maxStringSizeHeader; + size_t const nbLinesInLayer = sublineHeaderCounts[idxLayer]; - for( auto const & cell : column.m_columnValues ) + if( nbLinesInLayer != 1 ) { - if( column.m_maxStringSize.length() < cell.length()) + for( auto & cell : lines ) { - column.m_maxStringSize = cell; + cell.m_lines.resize( nbLinesInLayer, "" ); } } + idxLayer++; } } -void TableTextFormatter::increaseColumnsSize( std::vector< TableLayout::Column > & columns, - integer const extraCharacters ) const +void TableTextFormatter::populateDataCellsLayout( TableLayout & tableLayout, + CellLayoutRows & cellsDataLayout, + RowsCellInput & inputDataValues ) const { - integer const extraCharactersPerColumn = std::ceil( extraCharacters / columns.size() ); - for( std::size_t idxColumn = 0; idxColumn < columns.size(); ++idxColumn ) + cellsDataLayout = { + inputDataValues.size(), + std::vector< TableLayout::CellLayout >( inputDataValues[0].size(), TableLayout::CellLayout()) + }; + + std::vector< size_t > & subDataLineCounts = tableLayout.getNbSubDataLines(); + for( size_t idxRow = 0; idxRow < inputDataValues.size(); ++idxRow ) { - integer newMaxStringSize = extraCharactersPerColumn + columns[idxColumn].m_maxStringSize.size(); - if( idxColumn == columns.size() - 1 ) + size_t maxLinesPerRow = 0; + size_t idxColumn = 0; + for( auto it = tableLayout.beginDeepFirst(); it != tableLayout.endDeepFirst(); ++it ) { - newMaxStringSize += m_tableLayout.getColumnMargin(); + if( !it->hasChild()) + { + TableData::CellData & cell = inputDataValues[idxRow][idxColumn]; + TableLayout::ColumnAlignement const cellAlignement = it->m_alignment; + TableLayout::Alignment const alignement = cell.type == CellType::Header ? + cellAlignement.headerAlignment : + cellAlignement.valueAlignment; + + if( it->m_header.m_cellType == CellType::Hidden ) + { + cell.type = it->m_header.m_cellType; + } + if( it->m_header.m_cellType == CellType::Separator ) + { + cell.value = m_horizontalLine; + } + + cellsDataLayout[idxRow][idxColumn] = TableLayout::CellLayout( cell.type, cell.value, alignement ); + maxLinesPerRow = std::max( maxLinesPerRow, cellsDataLayout[idxRow][idxColumn].m_lines.size() ); + idxColumn++; + } } + subDataLineCounts.push_back( { maxLinesPerRow } ); + } - columns[idxColumn].m_maxStringSize = GEOS_FMT( "{:>{}}", - columns[idxColumn].m_maxStringSize, - newMaxStringSize ); + size_t idxLayer = 0; + for( auto & lines: cellsDataLayout ) + { + size_t nbLinesInLayer = subDataLineCounts[idxLayer]; + if( nbLinesInLayer != 1 ) + { + for( auto & cell : lines ) + { + cell.m_lines.resize( nbLinesInLayer, "" ); + } + } + idxLayer++; } } -void computeTableErrorLength( std::vector< string > const & msgTableError, string::size_type & msgTableErrorLength ) +void TableTextFormatter::updateColumnMaxLength( TableLayout & tableLayout, + CellLayoutRows & cellsHeaderLayout, + CellLayoutRows & cellsDataLayout ) const { - if( !msgTableError.empty() ) + auto getMaxLineLength = []( std::vector< std::string > const & lines ) -> size_t { - auto maxStringSize = *(std::max_element( msgTableError.begin(), msgTableError.end(), - []( const auto & a, const auto & b ) { - return a.size() < b.size(); - } )); + auto maxLineLength = std::max_element( lines.begin(), lines.end(), + []( std::string const & a, std::string const & b ) { + return a.length() < b.length(); + } ); + return (maxLineLength != lines.end()) ? maxLineLength->length() : 0; + }; + + auto updateCellMaxLength = [&tableLayout]( TableLayout::CellLayout & cell, size_t maxColumnSize, + TableLayout::CellLayout * previousCell = nullptr ) + { + if( previousCell && previousCell->m_cellType == CellType::MergeNext ) + { + size_t const previousCellMaxLength = previousCell->m_cellWidth; + size_t const additionalMargin = tableLayout.getColumnMargin(); + cell.m_cellWidth = maxColumnSize + previousCellMaxLength + additionalMargin; + previousCell->m_cellWidth = 0; + } + else + { + cell.m_cellWidth = maxColumnSize; + } + }; - msgTableErrorLength += maxStringSize.size() + 1; // max string size + line return at the end - } -} + size_t const numColumns = cellsHeaderLayout[0].size(); + //each idx per row + std::vector< size_t > accMaxStringColumn( cellsDataLayout.size(), 0 ); + for( size_t idxColumn = 0; idxColumn < numColumns; ++idxColumn ) + { + size_t maxColumnSize = 1; -void computeTableSectionLength( std::vector< TableLayout::Column > & columns, string::size_type & sectionlineLength ) -{ - sectionlineLength += std::accumulate( columns.begin(), columns.end(), 0, - []( auto sum, const auto & column ) - { return sum + column.m_maxStringSize.length();} ); -} + // init header column max length + for( size_t rowIdx = 0; rowIdx < cellsDataLayout.size(); ++rowIdx ) + { + size_t const cellDataLength = getMaxLineLength( cellsDataLayout[rowIdx][idxColumn].m_lines ); + if( idxColumn == 0 || + (idxColumn > 0 && cellsDataLayout[rowIdx][idxColumn - 1].m_cellType != CellType::MergeNext)) + { + maxColumnSize = std::max( maxColumnSize, cellDataLength ); + } + } -void TableTextFormatter::computeTableWidth( std::vector< TableLayout::Column > & columns, - std::vector< string > const & msgTableError ) const -{ - integer const columnMargin = m_tableLayout.getColumnMargin(); - integer const borderMargin = m_tableLayout.getBorderMargin(); - string const tableTitle = string( m_tableLayout.getTitle() ); + for( size_t rowIdx = 0; rowIdx < cellsHeaderLayout.size(); ++rowIdx ) + { + size_t const cellHeaderLength = getMaxLineLength( cellsHeaderLayout[rowIdx][idxColumn].m_lines ); + maxColumnSize = std::max( {maxColumnSize, cellHeaderLength} ); + cellsHeaderLayout[rowIdx][idxColumn].m_cellWidth = maxColumnSize; + } - string::size_type sectionLengthWithSpacing = ( ( columns.size() - 1 ) * columnMargin ) + (borderMargin * 2); - string::size_type sectionlineLength = sectionLengthWithSpacing; - string::size_type maxTopLineLength = tableTitle.length(); - string::size_type msgTableErrorLength = borderMargin; + // update maxColumnSize for data cell + for( size_t rowIdx = 0; rowIdx < cellsDataLayout.size(); ++rowIdx ) + { + TableLayout::CellLayout & dataCell = cellsDataLayout[rowIdx][idxColumn]; + TableLayout::CellLayout * previousDataCell = (idxColumn > 0) ? + &cellsDataLayout[rowIdx][idxColumn - 1] : + nullptr; - computeTableErrorLength( msgTableError, msgTableErrorLength ); - computeTableSectionLength( columns, sectionlineLength ); + if( dataCell.m_cellType == CellType::MergeNext ) + { + accMaxStringColumn[rowIdx] += cellsHeaderLayout[0][idxColumn].m_cellWidth + tableLayout.getColumnMargin(); + } - maxTopLineLength = std::max( maxTopLineLength, msgTableErrorLength ); - if( sectionlineLength < maxTopLineLength ) - { - integer const extraCharacters = maxTopLineLength - sectionlineLength; - increaseColumnsSize( columns, extraCharacters ); + if( idxColumn > 0 && + previousDataCell->m_cellType == CellType::MergeNext && dataCell.m_cellType != CellType::MergeNext ) + { + // root header cells know the maximum string size in the column + size_t const sumOfMergingCell = accMaxStringColumn[rowIdx] + cellsHeaderLayout[0][idxColumn].m_cellWidth; + if( sumOfMergingCell < dataCell.m_cellWidth ) + { + maxColumnSize -= dataCell.m_cellWidth - sumOfMergingCell; + for( size_t rowIdx2 = 0; rowIdx2 < rowIdx; rowIdx2++ ) + { + updateCellMaxLength( cellsDataLayout[rowIdx2][idxColumn], maxColumnSize ); + if( cellsDataLayout[rowIdx2][idxColumn].m_cellType == CellType::Separator ) + { + size_t const additionnalMargin = (idxColumn == numColumns - 1) ? + tableLayout.getBorderMargin() * 2 + 2 : + tableLayout.getColumnMargin(); + cellsDataLayout[rowIdx2][idxColumn].m_lines[0] = std::string( maxColumnSize + additionnalMargin, '-' ); + } + } + } + + accMaxStringColumn[rowIdx] = 0; + } + else + { + size_t const cellDataLength = getMaxLineLength( cellsDataLayout[rowIdx][idxColumn].m_lines ); + maxColumnSize = std::max( {maxColumnSize, cellDataLength} ); + } + + updateCellMaxLength( dataCell, maxColumnSize, previousDataCell ); + + if( dataCell.m_cellType == CellType::Separator ) + { + size_t separatorLength = dataCell.m_cellWidth; + separatorLength += (idxColumn == numColumns - 1) ? + tableLayout.getBorderMargin() * 2 + 2 : + tableLayout.getColumnMargin(); + + dataCell.m_lines[0] = std::string( separatorLength, '-' ); + } + } + + // last pass for updating cells header + for( size_t rowIdx = 0; rowIdx < cellsHeaderLayout.size(); ++rowIdx ) + { + TableLayout::CellLayout & headerCell = cellsHeaderLayout[rowIdx][idxColumn]; + TableLayout::CellLayout * previousHeaderCell = (idxColumn > 0) ? + &cellsHeaderLayout[rowIdx][idxColumn - 1]: + nullptr; + + updateCellMaxLength( headerCell, maxColumnSize, previousHeaderCell ); + } } } - -void TableTextFormatter::buildTableSeparators( std::vector< TableLayout::Column > const & columns, - string & topSeparator, - string & sectionSeparatingLine ) const +void TableTextFormatter::adjustTableWidth( TableLayout & tableLayout, + CellLayoutRows & cellsHeaderLayout, + CellLayoutRows & cellsDataLayout, + string & separatorLine, + size_t & nbEnabledColumn ) const { - { // section separator line - integer const columnMargin = m_tableLayout.getColumnMargin(); - integer const borderMargin = m_tableLayout.getBorderMargin(); + std::string const tableTitle = std::string( tableLayout.getTitle() ); + size_t const margins = (size_t) tableLayout.getBorderMargin() * 2; + size_t const horizontalBar = 2; + + size_t sectionlineLength = 0; + size_t nbHiddenColumns = 0; + size_t headerColumnCount = 0; - std::vector< string > maxStringPerColumn; - for( auto const & column : columns ) + for( auto const & column : cellsHeaderLayout[0] ) + { + if( column.m_cellType == CellType::Hidden ) + { + nbHiddenColumns++; + } + if( column.m_cellType != CellType::Hidden ) + { + nbEnabledColumn++; + } + if( column.m_cellType == CellType::Value || column.m_cellType == CellType::Header ) { - maxStringPerColumn.push_back( string( column.m_maxStringSize.length(), m_horizontalLine ) ); + sectionlineLength += column.m_cellWidth; + headerColumnCount++; } + } - string const patternBetweenColumns = GEOS_FMT( "{:-^{}}", m_horizontalLine, columnMargin ); - std::string const leftBorder = GEOS_FMT( "{}{:-<{}}", m_horizontalLine, "", borderMargin ); - std::string const rightBorder = GEOS_FMT( "{}{:-<{}}", m_horizontalLine, "", borderMargin ); - std::string const columnJoin = stringutilities::join( maxStringPerColumn, patternBetweenColumns ); + size_t const spacingBetweenColumns = headerColumnCount == 0 ? (size_t) tableLayout.getColumnMargin(): + (headerColumnCount - 1) * (size_t) tableLayout.getColumnMargin(); - std::ostringstream oss; - oss << leftBorder << columnJoin << rightBorder; - sectionSeparatingLine = oss.str(); - } + sectionlineLength += spacingBetweenColumns + margins + horizontalBar; - { // top line separator - // -2 because we can have '+' to the extremity of top separator - integer const topSeparatorLength = sectionSeparatingLine.size() - 2; - topSeparator = GEOS_FMT( "{}{:-<{}}{}", m_horizontalLine, "", topSeparatorLength, m_horizontalLine ); + size_t const titleRowLength = tableTitle.length() + margins + horizontalBar; + size_t const maxLength = std::max( {titleRowLength, sectionlineLength} ); + if( sectionlineLength < maxLength ) + { + size_t const paddingCharacters = maxLength - sectionlineLength; + adjustColumnWidth( cellsHeaderLayout, nbHiddenColumns, paddingCharacters ); + adjustColumnWidth( cellsDataLayout, nbHiddenColumns, paddingCharacters ); + sectionlineLength = maxLength; } + + separatorLine = GEOS_FMT( "{:-^{}}", m_horizontalLine, sectionlineLength ); } -void TableTextFormatter::outputTopRows( std::ostringstream & tableOutput, - std::vector< string > const & msg, - string_view topSeparator, - TableLayout::Alignment alignment ) const +void TableTextFormatter::adjustColumnWidth( CellLayoutRows & cells, + size_t const nbHiddenColumns, + size_t const paddingCharacters ) const { - if( msg.size() != 0 && msg[0] != "" ) + size_t const numRows = cells.size(); + size_t const nbColumns = cells[0].size(); + size_t const remainingPaddingForLastColumn = paddingCharacters % (nbColumns - nbHiddenColumns); + size_t const paddingPerColumn = paddingCharacters / (nbColumns - nbHiddenColumns); + for( size_t idxRow = 0; idxRow < numRows; ++idxRow ) { - tableOutput << GEOS_FMT( "{}\n", topSeparator ); - for( std::string const & str : msg ) + for( size_t idxColumn = 0; idxColumn < nbColumns; ++idxColumn ) { - tableOutput << m_verticalLine << string( m_tableLayout.getBorderMargin(), ' ' ); - tableOutput << buildCell( alignment, str, (topSeparator.length() - 6)); - tableOutput << string( m_tableLayout.getBorderMargin(), ' ' ) << "|\n"; + auto & currentCell = cells[idxRow][idxColumn]; + + if( currentCell.m_cellType != CellType::Hidden ) + { + size_t nextIdxColumn = idxColumn + 1; + while( nextIdxColumn < nbColumns && + cells[idxRow][nextIdxColumn].m_cellType == CellType::Hidden ) + { + nextIdxColumn++; + } + bool isLastVisibleColumn = nextIdxColumn == nbColumns; + + if( idxColumn > 0 && cells[idxRow][idxColumn - 1].m_cellType == CellType::MergeNext ) + { + auto * previousCell = &cells[idxRow][idxColumn - 1]; + currentCell.m_cellWidth += previousCell->m_cellWidth; + previousCell->m_cellWidth = 0; + } + + size_t const additionalPadding = (isLastVisibleColumn || idxColumn == nbColumns - 1) ? + remainingPaddingForLastColumn : + 0; + + currentCell.m_cellWidth += paddingPerColumn + additionalPadding; + } } } + } -void TableTextFormatter::outputSectionRows( std::vector< TableLayout::Column > const & columns, - string_view sectionSeparatingLine, - std::ostringstream & tableOutput, - integer const nbRows, - TableLayout::Section const section ) const +void TableTextFormatter::outputTitleRow( TableLayout & tableLayout, + std::ostringstream & tableOutput, + string_view separatorLine ) const { - integer const columnMargin = m_tableLayout.getColumnMargin(); - integer const borderMargin = m_tableLayout.getBorderMargin(); - - for( integer idxRow = 0; idxRow< nbRows; ++idxRow ) + string const tableTitle = string( tableLayout.getTitle()); + if( !tableTitle.empty() ) { - // Append the left border - tableOutput << GEOS_FMT( "{:<{}}", m_verticalLine, 1 + borderMargin ); - for( std::size_t idxColumn = 0; idxColumn < columns.size(); ++idxColumn ) - { - TableLayout::Column const currentColumn = columns[idxColumn]; - auto const & columnContent = section == TableLayout::Section::header ? - columns[idxColumn].m_parameter.splitColumnNameLines : - columns[idxColumn].m_columnValues; - string cell = columnContent.at( idxRow ); - integer const cellSize = currentColumn.m_maxStringSize.length(); + tableOutput << GEOS_FMT( "{}\n", separatorLine ); + tableOutput << GEOS_FMT( "{:<{}}", m_verticalLine, tableLayout.getBorderMargin() + 1 ); + tableOutput << buildCell( TableLayout::Alignment::center, + tableTitle, + separatorLine.length() - (tableLayout.getBorderMargin() * 2) - 2 ); + tableOutput << GEOS_FMT( "{:>{}}\n", m_verticalLine, tableLayout.getBorderMargin() + 1 ); + } +} - tableOutput << buildCell( currentColumn.m_parameter.alignment, cell, cellSize ); +void TableTextFormatter::formatCell( TableLayout & tableLayout, + std::ostringstream & tableOutput, + TableLayout::CellLayout const & cell, + size_t idxLine ) const +{ + GEOS_UNUSED_VAR( tableLayout ); + tableOutput << buildCell( cell.m_alignment, + cell.m_lines[idxLine], + cell.m_cellWidth ); +} - // Add space between column - if( idxColumn < columns.size() - 1 ) +void TableTextFormatter::outputLines( TableLayout & tableLayout, + CellLayoutRows const & cellsLayout, + std::ostringstream & tableOutput, + std::vector< size_t > const & nbLinesRow, + CellType sectionType, + string_view separatorLine, + size_t & nbEnabledColumn ) const +{ + size_t idxLine = 0; + for( auto const & row : cellsLayout ) + { + for( size_t idxSubLine = 0; idxSubLine < nbLinesRow[idxLine]; idxSubLine++ ) + { + size_t idxColumn = 0; + size_t nbEnabledColumnTemp = nbEnabledColumn; + while( nbEnabledColumnTemp > 0 ) { - tableOutput << GEOS_FMT( "{:^{}}", m_verticalLine, columnMargin ); + auto & cell = row[idxColumn]; + switch( cell.m_cellType ) + { + case CellType::Value: + case CellType::Header: + if( &cell == &(row.front()) ) + { + tableOutput << GEOS_FMT( "{:<{}}", m_verticalLine, tableLayout.getBorderMargin() + 1 ); + } + + formatCell( tableLayout, tableOutput, cell, idxSubLine ); + + if( &cell == &(row.back()) || nbEnabledColumnTemp == 1 ) + { + tableOutput << GEOS_FMT( "{:>{}}", m_verticalLine, tableLayout.getBorderMargin() + 1 ); + } + else + { + tableOutput << GEOS_FMT( "{:^{}}", m_verticalLine, tableLayout.getColumnMargin()); + } + nbEnabledColumnTemp--; + break; + case CellType::MergeNext: + if( &cell == &(row.front()) ) + { + tableOutput << GEOS_FMT( "{:<{}}", m_verticalLine, tableLayout.getBorderMargin() + 1 ); + } + if( &cell == &(row.back()) ) + { + formatCell( tableLayout, tableOutput, cell, idxSubLine ); + tableOutput << GEOS_FMT( "{:>{}}", m_verticalLine, tableLayout.getBorderMargin() + 1 ); + } + nbEnabledColumnTemp--; + break; + case CellType::Separator: + formatCell( tableLayout, tableOutput, cell, idxSubLine ); + nbEnabledColumnTemp--; + break; + case CellType::Hidden: + if( &cell == &(row.back()) ) + { + tableOutput << GEOS_FMT( "{:>{}}", m_verticalLine, tableLayout.getBorderMargin() + 1 ); + } + default: + break; + } + idxColumn++; } + tableOutput <{}}\n", m_verticalLine, borderMargin + 1 ); } - - if( nbRows != 0 ) + if( sectionType == CellType::Value && !cellsLayout.empty()) { - tableOutput << GEOS_FMT( "{}\n", sectionSeparatingLine ); + tableOutput << separatorLine; } } - } diff --git a/src/coreComponents/common/format/table/TableFormatter.hpp b/src/coreComponents/common/format/table/TableFormatter.hpp index 26dd2b16ba4..04392b7691d 100644 --- a/src/coreComponents/common/format/table/TableFormatter.hpp +++ b/src/coreComponents/common/format/table/TableFormatter.hpp @@ -22,6 +22,7 @@ #include "TableData.hpp" #include "TableLayout.hpp" +#include "TableTypes.hpp" namespace geos { @@ -32,6 +33,13 @@ namespace geos class TableFormatter { +public: + /// Represent the TableData values + using RowsCellInput = std::vector< std::vector< TableData::CellData > >; + /// Represent the Table (header or values) structured + using CellLayoutRows = std::vector< std::vector< TableLayout::CellLayout > >; + + protected: /// Layout for a table @@ -41,14 +49,9 @@ class TableFormatter /** * @brief Construct a new Table Formatter from a tableLayout - * @param tableLayout Contain all column names and optionnaly the table title + * @param tableLayout Contain all tableColumnData names and optionnaly the table title */ TableFormatter( TableLayout const & tableLayout ); - - /** - * @brief Destroy the Table Formatter object - */ - virtual ~TableFormatter() = default; }; /** @@ -67,23 +70,18 @@ class TableCSVFormatter : public TableFormatter /** * @brief Construct a new Table Formatter from a tableLayout - * @param tableLayout Contain all column names and optionnaly the table title + * @param tableLayout Contain all tableColumnData names and optionnaly the table title */ TableCSVFormatter( TableLayout const & tableLayout ); /** - * @brief Destroy the TableCSVFormatter object - */ - virtual ~TableCSVFormatter() = default; - - /** - * @return The string with all column names. + * @return The string with all tableColumnData names. */ string headerToString() const; /** - * @brief Convert the table data to a CSV string. - * @param tableData The 1D table data. + * @brief Convert the table data to a CSV string.. + * @param tableData The table data * @return The CSV string representation of the table data. */ string dataToString( TableData const & tableData ) const; @@ -115,7 +113,6 @@ class TableTextFormatter : public TableFormatter { public: - /** * @brief Construct a new TableFormatter */ @@ -125,20 +122,15 @@ class TableTextFormatter : public TableFormatter /** * @brief Construct a new TableFormatter from a tableLayout - * @param tableLayout Contain all column names and optionnaly the table title + * @param tableLayout Contain all tableColumnData names and optionnaly the table title */ TableTextFormatter( TableLayout const & tableLayout ); - - /** - * @brief Destroy the Table Text Formatter object - */ - virtual ~TableTextFormatter() = default; - /** - * @return A TableLayout converted into a formatted representation. + * @return A TableLayout string representation, + * The TableTextFormatter receives hasn't receive any data, so only the header part is returned. */ - string layoutToString() const; + string toString() const; /** * @brief Convert a data source to a table string. @@ -152,99 +144,139 @@ class TableTextFormatter : public TableFormatter /// symbol for separator construction static constexpr char m_verticalLine = '|'; - /// for the extremity of a row + /// for the extremity of a row static constexpr char m_horizontalLine = '-'; - /** - * @brief Fill the vector (m_column) in tableData with values from rows stored in tableData. - * @param columns Vector of columns to be filled. - * @param tableData Vector containing all rows filled with values - */ - void fillTableColumnsFromRows( std::vector< TableLayout::Column > & columns, - std::vector< std::vector< string > > & tableData ) const; + +/** + * @brief Initializes the table layout with the given table data and prepares necessary layouts for headers and data cells. + * @param tableLayout A reference to the `TableLayout` object. + * @param tableData A constant reference to the `TableData` object, which contains the actual data for the table. + * @param cellsHeaderLayout A reference to a `CellLayoutRows` where the header cells will be populated. + * @param cellsDataLayout A reference to a `CellLayoutRows` where the data cells will be populated. + * @param separatorLine A string that will be used as the table separator line + */ + void initalizeTableLayout( TableLayout & tableLayout, + TableData const & tableData, + CellLayoutRows & cellsDataLayout, + CellLayoutRows & cellsHeaderLayout, + string & separatorLine, + size_t & nbEnabledColumn ) const; +/** + * @brief Outputs the formatted table to the provided output stream. + * @param tableLayout The layout of the table + * @param tableOutput A reference to an `std::ostringstream` where the formatted table will be written. + * @param cellsHeader The layout of the header rows + * @param cellsData The layout of the data rows + * @param separatorLine The string to be used as the table separator line + */ + void outputTable( TableLayout & tableLayout, + std::ostringstream & tableOutput, + CellLayoutRows const & cellsHeader, + CellLayoutRows const & cellsData, + string_view separatorLine, + size_t & nbEnabledColumn ) const; /** - * @brief Converts a TableLayout into a formatted representation. - * @param tableOutput The output stream - * @param columns The vector containing all table columns - * @param msgTableError A vector containg all error related to the table - * @param sectionSeparatingLine An empty string for building the section separator + * @brief Sets parent-child relationships between columns and sub-columns. + * @param columns A reference to a vector of `TableLayout::Column` objects. */ - void outputLayout( std::ostringstream & tableOutput, - std::vector< TableLayout::Column > & columns, - std::vector< string > const & msgTableError, - string & sectionSeparatingLine ) const; + void setLinks( std::vector< TableLayout::Column > & columns ) const; /** - * @brief Split all header names by detecting the newline \\n character. and - * set the same vector size for each split header and merge it into columns - * @param columns The vector containg all columns - * @param largestHeaderVectorSize The largest split header vector size - * @param splitHeaders A empty vector who will contain all split header names + * @brief Adjusts the header layout by ensuring all header layers have consistent row sizes and formats. + * @param tableLayout The layout of the table, containing information about columns, headers, and their layers. + * @param cellsHeaderLayout A reference to the collection of header cells that will be updated with the gridified layout. */ - void splitAndSetColumnNames( std::vector< TableLayout::Column > & columns, - size_t & largestHeaderVectorSize, - std::vector< std::vector< string > > & splitHeaders ) const; + void populateHeaderCellsLayout( TableLayout & tableLayout, + CellLayoutRows & cellsDataLayout ) const; + +/** + * @brief Populates the data cells layout based on input data values. + * @param tableLayout The layout of the table, + * @param cellsDataLayout A reference to the layout for the data cells that will be populated. + * @param inputDataValues A 2D vector containing the actual input data values. + */ + void populateDataCellsLayout( TableLayout & tableLayout, + CellLayoutRows & cellsDataLayout, + RowsCellInput & inputDataValues ) const; /** - * @brief For each column find and set the column's longest string - * @param columns The vector containg all columns + * @brief Finds and sets the longest string for each column in the table. + * @param tableLayout The layout of the table, + * @param cellHeaderLength A reference to the collection of data cells. + * The function updates the maximum string + * length for each cell based on the longest string found in the column. + * @param cellsDataLayout A reference to the collection of data cells. + * The function updates the maximum string + * length for each cell based on the longest string found in the column. */ - void findAndSetMaxStringSize( std::vector< TableLayout::Column > & columns ) const; + void updateColumnMaxLength( TableLayout & tableLayout, + CellLayoutRows & cellHeaderLength, + CellLayoutRows & cellsDataLayout ) const; /** - * @brief recalculate the largest string size for each columns - * @param columns Vector containing all table columns - * @param extraCharacters Extra characters to be added to \p m_maxStringSize of each columns + * @brief Computes and constructs the separator lines for the table. + * Adjust columns if the title is the largest row + * @param tableLayout The layout of the table, + * @param cellsHeaderLayout A reference to the collection of header cells that can be affected by column resizing. + * @param cellsDataLayout A reference to the collection of data cells that can be affected by column resizing. + * @param separatorLine A string reference where the table separator line will be created */ - void increaseColumnsSize( std::vector< TableLayout::Column > & columns, - integer const extraCharacters ) const; + void adjustTableWidth( TableLayout & tableLayout, + CellLayoutRows & cellsHeaderLayout, + CellLayoutRows & cellsDataLayout, + string & separatorLine, + size_t & nbEnabledColumn ) const; /** - * @brief Compute the max table line length, taking into account the length of : title, error, columns header and content - * Increase the size of the columns if necessary - * @param columns Vector of column containing containing the largest string for each column - * @param msgTableError Vector containing all error messages + * @brief Increases the size of columns to accommodate extra characters. + * @param cells A reference to the collection of data/header cells + * @param nbHiddenColumns The total number of hidden columns in the table. + * @param extraCharacters The total number of extra characters to be distributed across the columns. */ - void computeTableWidth( std::vector< TableLayout::Column > & columns, - std::vector< string > const & msgTableError ) const; + void adjustColumnWidth( CellLayoutRows & cells, + size_t nbHiddenColumns, + size_t const paddingCharacters ) const; /** - * @brief Build all separators needed from content length contained in the columns vector - * @param columns Vector containing all table columns - * @param topSeparator Top separator to be built - * @param sectionSeparatingLine Line section separator to be built + * @brief Output the title row in the table + * @param tableLayout The layout of the table + * @param tableOutput The output stream + * @param separatorLine The table separator line string */ - void buildTableSeparators( std::vector< TableLayout::Column > const & columns, - string & topSeparator, - string & sectionSeparatingLine ) const; + void outputTitleRow( TableLayout & tableLayout, + std::ostringstream & tableOutput, + string_view separatorLine ) const; /** - * @brief Add a row on top of the table + * @brief Formats a table cell and appends it to the table output. + * @param tableLayout The layout of the table * @param tableOutput The output stream - * @param msg Vector of string(s) to display - * @param topSeparator The top table separator - * @param alignment The aligment for a row + * @param cell The cell to format + * @param idxLine The current line index used to access the specific content for the cell. */ - void outputTopRows( std::ostringstream & tableOutput, - std::vector< string > const & msg, - string_view topSeparator, - TableLayout::Alignment alignment ) const; + void formatCell( TableLayout & tableLayout, + std::ostringstream & tableOutput, + TableLayout::CellLayout const & cell, + size_t idxLine ) const; /** - * @brief Output a section by specifying it's type ( header or section ) - * @param columns Vector containing all table columns - * @param sectionSeparatingLine Line separator between sections - * @param rows A section row - * @param nbRows Indicates the number of lines in a section - * @param section The section to be built - * @note Add the ending line if there are one or more rows + * @brief Outputs the formatted table lines to the output stream. + * @param tableLayout The layout of the table + * @param cellsLayout A collection of rows, each containing a layout of cells to be processed and formatted. + * @param tableOutput The output stream + * @param nbLinesRow A vector containing the number of sub-lines for each row. + * @param sectionType The type of the section being processed (Header, Value, etc.). + * @param separatorLine The table separator line string */ - void outputSectionRows( std::vector< TableLayout::Column > const & columns, - string_view sectionSeparatingLine, - std::ostringstream & rows, - integer const nbRows, - TableLayout::Section const section ) const; + void outputLines( TableLayout & tableLayout, + CellLayoutRows const & cellsLayout, + std::ostringstream & tableOutput, + std::vector< size_t > const & nbLinesRow, + CellType sectionType, + string_view separatorLine, + size_t & nbEnabledColumn ) const; }; /** diff --git a/src/coreComponents/common/format/table/TableLayout.cpp b/src/coreComponents/common/format/table/TableLayout.cpp index 7e6b134d606..10350ac4a6c 100644 --- a/src/coreComponents/common/format/table/TableLayout.cpp +++ b/src/coreComponents/common/format/table/TableLayout.cpp @@ -16,64 +16,234 @@ /** * @file TableData.hpp */ - #include "TableLayout.hpp" +#include namespace geos { -TableLayout::TableLayout( std::vector< string > const & columnNames, string const & title ): - m_tableTitle( title ) +void TableLayout::addToColumns( std::vector< string > const & columnNames ) { - setMargin( MarginValue::medium ); - m_columns.reserve( columnNames.size() ); - for( auto const & name : columnNames ) + for( auto const & m_header : columnNames ) { - m_columns.push_back( {TableLayout::ColumnParam{{name}, Alignment::right, true}, {}, ""} ); + addToColumns( m_header ); } } -TableLayout::TableLayout( std::vector< ColumnParam > const & columnParameters, string const & title ): - m_tableTitle( title ) +void TableLayout::addToColumns( string_view m_header ) { - setMargin( MarginValue::medium ); - m_columns.reserve( columnParameters.size() ); - for( auto const & param : columnParameters ) - { - m_columns.push_back( { param, {}, ""} ); - } + TableLayout::Column column = TableLayout::Column().setName( m_header ); + m_tableColumnsData.push_back( column ); +} + +void TableLayout::addToColumns( TableLayout::Column const & column ) +{ + m_tableColumnsData.push_back( column ); +} + +TableLayout & TableLayout::setTitle( string_view title ) +{ + m_tableTitle = title; + return *this; } -void TableLayout::setMargin( MarginValue marginValue ) +TableLayout & TableLayout::enableLineBreak( bool value ) { + m_wrapLine = value; + return *this; +} + +TableLayout & TableLayout::setMargin( MarginValue marginValue ) +{ + m_marginValue = marginValue; m_borderMargin = marginValue; m_columnMargin = integer( marginValue ) * 2 + 1; + + return *this; +} + +bool TableLayout::isLineBreakEnabled() const +{ return m_wrapLine; } + +size_t TableLayout::getMaxDepth() const +{ + size_t depthMax = 1; + size_t currDepth = 1; + for( auto const & column : m_tableColumnsData ) + { + currDepth = 1; + TableLayout::Column const * currColumn = &column; + while( !currColumn->m_subColumn.empty()) + { + currColumn = &currColumn->m_subColumn[0]; + currDepth++; + } + depthMax = std::max( currDepth, depthMax ); + } + return depthMax; +} + +void divideCell( std::vector< string > & lines, string const & value ) +{ + std::istringstream strStream( value ); + std::string line; + lines.clear(); + while( getline( strStream, line, '\n' )) + { + lines.push_back( line ); + } + + if( line.empty()) + { + lines.push_back( "" ); + } +} + +TableLayout::CellLayout::CellLayout(): + m_lines( {""} ), + m_cellType( CellType::Header ), + m_alignment( TableLayout::Alignment::center ), + m_cellWidth( 0 ) +{} + +TableLayout::CellLayout::CellLayout( CellType type, string const & cellValue, TableLayout::Alignment alignment ): + m_cellType( type ), + m_alignment( alignment ) +{ + divideCell( m_lines, cellValue ); + if( !m_lines.empty()) + { + m_cellWidth = std::max_element( m_lines.begin(), m_lines.end(), []( const auto & a, const auto & b ) + { + return a.length() < b.length(); + } )->length(); + } + else + { + m_cellWidth = 0; + } +} + +TableLayout::Column::Column(): + m_parent( nullptr ), m_next( nullptr ) +{ + m_header.m_lines = {}; + m_header.m_cellType = CellType::Header; + m_header.m_alignment = Alignment::center; +} + +TableLayout::Column::Column( TableLayout::CellLayout cell ): + m_parent( nullptr ), m_next( nullptr ) +{ m_header = cell; } + + +TableLayout::Column & TableLayout::Column::setName( string_view name ) +{ + m_header.m_lines.push_back( std::string( name ) ); + divideCell( m_header.m_lines, m_header.m_lines[0] ); + m_header.m_cellType = CellType::Header; + return *this; +} + +TableLayout::Column & TableLayout::Column::setVisibility( CellType celltype ) +{ + m_header.m_cellType = celltype; + return *this; +} + +TableLayout::Column & TableLayout::Column::addSubColumns( std::initializer_list< string > subColName ) +{ + std::vector< TableLayout::Column > subColumns; + for( auto const & name : subColName ) + { + TableLayout::CellLayout cell{CellType::Header, name, TableLayout::Alignment::center}; + TableLayout::Column col{cell}; + subColumns.emplace_back( col ); + } + m_subColumn = subColumns; + return *this; } -std::vector< TableLayout::Column > const & TableLayout::getColumns() const +TableLayout::Column & TableLayout::Column::addSubColumns( std::initializer_list< TableLayout::Column > subCol ) { - return m_columns; + m_subColumn = subCol; + return *this; } -string_view TableLayout::getTitle() const +TableLayout::Column & TableLayout::Column::addSubColumns( string const & subColName ) { - return m_tableTitle; + TableLayout::CellLayout cell{CellType::Header, subColName, TableLayout::Alignment::center}; + TableLayout::Column col{cell}; + this->m_subColumn.push_back( col ); + return *this; } +TableLayout::Column & TableLayout::Column::setHeaderAlignment( Alignment headerAlignment ) +{ + m_alignment.headerAlignment = headerAlignment; + m_header.m_alignment = headerAlignment; + return *this; +} -integer const & TableLayout::getBorderMargin() const +TableLayout::Column & TableLayout::Column::setValuesAlignment( Alignment valueAlignment ) { - return m_borderMargin; + m_alignment.valueAlignment = valueAlignment; + return *this; } -integer const & TableLayout::getColumnMargin() const +TableLayout::DeepFirstIterator & TableLayout::DeepFirstIterator::operator++() { - return m_columnMargin; + if( m_currentColumn->getNextCell() != nullptr ) + { + m_currentColumn = m_currentColumn->getNextCell(); + while( m_currentColumn->hasChild() ) + { + m_currentLayer++; + m_currentColumn = &m_currentColumn->m_subColumn[0]; + } + } + else + { + bool const hasParent = (m_currentColumn->getParent() != nullptr); + m_currentLayer -= size_t( hasParent ); + m_currentColumn = hasParent ? m_currentColumn->getParent() : nullptr; + } + return *this; } -integer const & TableLayout::getMarginTitle() const +TableLayout::DeepFirstIterator TableLayout::DeepFirstIterator::operator++( int ) { - return m_titleMargin; + TableLayout::DeepFirstIterator temp = *this; + ++(*this); + return temp; } +TableLayout::DeepFirstIterator TableLayout::beginDeepFirst() +{ + TableLayout::Column * startColumn = &(*m_tableColumnsData.begin()); + size_t idxLayer = 0; + if( startColumn->hasChild() ) + { + while( startColumn->hasChild() ) + { + idxLayer++; + startColumn = &startColumn->m_subColumn[0]; + } + } + return DeepFirstIterator( startColumn, idxLayer ); +} + +bool TableLayout::DeepFirstIterator::isLastColumn() +{ + if( m_currentColumn == nullptr ) + return true; + TableLayout::Column * tempColumn = m_currentColumn; + while( tempColumn->getParent() ) + { + tempColumn = tempColumn->getParent(); + } + return tempColumn->getNextCell() == nullptr; +} + + } diff --git a/src/coreComponents/common/format/table/TableLayout.hpp b/src/coreComponents/common/format/table/TableLayout.hpp index e6f96a878eb..ee8f6a02323 100644 --- a/src/coreComponents/common/format/table/TableLayout.hpp +++ b/src/coreComponents/common/format/table/TableLayout.hpp @@ -21,6 +21,10 @@ #define GEOS_COMMON_FORMAT_TABLE_TABLELAYOUT_HPP #include "common/DataTypes.hpp" +#include "TableTypes.hpp" +#include +#include "common/logger/Logger.hpp" + namespace geos { @@ -51,106 +55,510 @@ class TableLayout enum Section { header, values }; /** - * @brief Structure to set up each colum parameters. + * @brief Structure to set up values m_alignment for each colum. */ - struct ColumnParam + struct ColumnAlignement { - /// Name for a column - string columnName; - /// Alignment for a column. By default aligned to the right side - Alignment alignment = Alignment::right; - /// A boolean to display a colummn - bool enabled = true; - /// Vector containing substring column name delimited by "\n" - std::vector< string > splitColumnNameLines; - - /** - * @brief Construct a ColumnParam object with the specified name and alignment. - * @param name The name of the column - * @param align The alignment of the column - */ - ColumnParam( std::string const & name, Alignment align ) - : columnName( name ), alignment( align ) - {} + /// Alignment for column name. By default aligned to center + Alignment headerAlignment = Alignment::center; + /// Alignment for column values. By default aligned to right side + Alignment valueAlignment = Alignment::right; + }; + +/** + * @struct CellLayout + * @brief Structure grouping the cell information to display it in a table (content, type, alignment, ...). + */ + struct CellLayout + { + /// vector containing each cell content, separated by lines. + std::vector< string > m_lines; + /// The type of the cell (Header,Value, Merge, ...). + CellType m_cellType; + /// The alignment of the cell (left, center, right). + Alignment m_alignment; + /// Maximum length of the data in the cell. + size_t m_cellWidth; /** - * @brief Construct a ColumnParam object with the specified name, alignment, and display flag. - * @param name The name of the column - * @param align The alignment of the column - * @param display Flag indicating whether the column is enabled + * @brief Constructor to initialize a Cell with a default settings. */ - ColumnParam( std::string const & name, Alignment align, bool display ) - : columnName( name ), alignment( align ), enabled( display ) - {} + CellLayout(); + + /** + * @brief Constructor to initialize a cell given celltype, value and alignment. + * @param cellType The type of the cell. + * @param value The value to be assigned to the cell. + * @param alignment The alignment of the cell (left, right, or center). + */ + CellLayout( CellType cellType, string const & value, TableLayout::Alignment alignment ); }; /** - * @brief Struct for a column. + * @class Column + * @brief Class representing a column in a table layout. */ - struct Column + class Column { - /// Structure who contains parameters for a column - ColumnParam m_parameter; - /// A vector containing all column values - std::vector< string > m_columnValues; - /// The largest string in the column - string m_maxStringSize; +public: + /// The header cell layout. + CellLayout m_header; + /// A vector containing all sub-columns in the column. + std::vector< Column > m_subColumn; + /// struct containing m_alignment for the column (header and values) + ColumnAlignement m_alignment; + + /** + * @brief Default constructor. + * Initializes a column with default values. + */ + Column(); + + /** + * @brief Constructor to initialize a column with a specific `CellLayout`. + * @param cellLayout The `CellLayout` object to initialize the column. + * + */ + Column( TableLayout::CellLayout cellLayout ); + + /** + * @brief Get the parent column. + * @return Pointer to the parent column, or `nullptr` if no parent is set. + */ + Column * getParent() + { return m_parent; } + + /** + * @brief Set the parent column. + * @param parent Pointer to the parent column to set. + */ + void setParent( Column * parent ) + { m_parent = parent; } + + /** + * @brief GGet the next column in the layout. + * @return Pointer to the next column or `nullptr` if no next column exists. + */ + Column * getNextCell() + { return m_next; } + + /** + * @brief Set the next column in the layout. + * @param nextCell The next column in the table layout. + */ + void setNextCell( Column * nextCell ) + { m_next = nextCell; } + + /** + * @brief Sets the name of the column. + * @param name The name to set for the column. + * @return The current column object. + */ + Column & setName( string_view name ); + + /** + * @brief Set the column visibility. + * @param celltype Cell type to apply to hide the colmun + * @return The current column . + */ + Column & setVisibility( CellType celltype ); + + /** + * @brief Adds multiple sub-columns to the column. + * @param subCol A list of sub-column names to add. + * @return The current column object + */ + TableLayout::Column & addSubColumns( std::initializer_list< TableLayout::Column > subCol ); + + /** + * @brief Adds multiple sub-columns to the column. + * @param subColName A list of sub-column names to add. + * @return The current column object + */ + TableLayout::Column & addSubColumns( std::initializer_list< string > subColName ); + + /** + * @brief Adds a single sub-column to the column. + * @param subColName The name of the sub-column to add. + * @return The current column object. + */ + TableLayout::Column & addSubColumns( string const & subColName ); + + /** + * @brief Sets the header alignment for the column. + * @param headerAlignment The alignment to set for the column header (left, right, or center). + * @return The current column object + */ + TableLayout::Column & setHeaderAlignment( Alignment headerAlignment ); + + /** + * @brief Sets the values alignment for the column. + * @param valueAlignment The alignment to set for the column values (left, right, or center). + * @return The current column object + */ + TableLayout::Column & setValuesAlignment( Alignment valueAlignment ); + + /** + * @return number of times we will divide the current cell + */ + size_t getNumberCellMerge() + { return m_headerMergeCount; } + + /** + * @brief Increment number of times we will divide the current cell + * @param value number of division to add + */ + void incrementMergeHeaderCount( size_t value ) + { m_headerMergeCount+= value;} + + /** + * @brief Decremente number of times we will divide the current cell + */ + void decrementMergeHeaderCount() + { m_headerMergeCount--; } + + /** + * @brief Checks if the column has any child columns. + * @return bool True if the column has child columns, otherwise false. + */ + bool hasChild() const + { return !this->m_subColumn.empty(); } + + /** + * @brief Checks if the column has a parent column. + * @return bool True if the column has a parent, otherwise false. + */ + bool hasParent() const + { return this->m_parent != nullptr; } + +private: + /// Pointer to the parent cell (if any). + Column * m_parent; + /// Pointer to the next cell (if any). + Column * m_next; + /// The width of the cell (e.g., for cell containing subColumns). + size_t m_headerMergeCount = 0; }; + /** + * @brief Iterator to loop over all columns, starting by the deepest sub columns, + * then to their parents, then to their siblings. + */ + class DeepFirstIterator + { +public: + ///alias for column + using ColumnType = Column; + + /** + * @brief Construct a new Leaf Iterator object + * @param columnPtr The first deepest column of vector + * @param idxLayer the layer associated with the column + */ + DeepFirstIterator( ColumnType * columnPtr, size_t idxLayer ): + m_currentColumn( columnPtr ), m_currentLayer( idxLayer ) + {} + + /** + * @brief Copy assignment operator + * @param[in] columnPtr Coulmn to copy + * @return Leaf iterator + */ + DeepFirstIterator & operator=( Column * columnPtr ) + { + this->m_currentColumn= columnPtr; + return *this; + } + + /** + * @brief Prefix ++ overload + * @return Leaf iterator + */ + DeepFirstIterator & operator++(); + + /** + * @brief Postfix ++ overload + * @return Leaf iterator + */ + DeepFirstIterator operator++( int ); + + /** + * @brief Dereference operator. + * @return Reference to the current Column object pointed to by the iterator. + */ + ColumnType & operator*() + { return *m_currentColumn; } + + /** + * @brief Arrow operator. + * @return Pointer to the current Column object. + */ + ColumnType * operator->() + { return m_currentColumn; } + + /** + * @brief Equality comparison operator. + * @param a The first iterator. + * @param b The second iterator. + * @return True if both iterators point to the same column; false otherwise. + */ + friend bool operator== ( DeepFirstIterator const & a, DeepFirstIterator const & b ) + { return a.m_currentColumn == b.m_currentColumn; }; + /** + * @brief Inequality comparison operator. + * @param a The first iterator. + * @param b The second iterator. + * @return True if the iterators point to different columns; false otherwise. + */ + friend bool operator!= ( DeepFirstIterator const & a, DeepFirstIterator const & b ) + { return a.m_currentColumn != b.m_currentColumn; }; + + /** + * @brief Gets the current layer (depth) of the iterator. + * @return The current layer (depth) of the iterator. + */ + size_t getCurrentLayer() const + { return m_currentLayer; } + + /** + * @brief Check if the current cell belong the last column + * @return true + * @return false + */ + bool isLastColumn(); + +private: + /// Pointer to the current column + ColumnType * m_currentColumn; + /// The current depth of the iterator + size_t m_currentLayer; + }; + + /** + * @return Return an itarator pointing on the first leaf of the first columns vector + * Example on 2 column with Column A : 2 layer and Column B : 3 layers + * A.A -> A-B -> A-C -> A -> B-A-A -> B-A-B -> B-A -> B-B-A -> B-B-B -> B-B -> B + */ + DeepFirstIterator beginDeepFirst(); + + /** + * @return Return a end itarator + * This iterator is initialized with a null pointer + * representing a position after the last valid element + */ + DeepFirstIterator endDeepFirst() + { + return DeepFirstIterator( nullptr, 0 ); + } + + /// Alias for an initializer list of variants that can contain either a string or a layout column. + using TableLayoutArgs = std::initializer_list< std::variant< string_view, TableLayout::Column > >; + TableLayout() = default; /** - * @brief Construct a new Table object, all values in the table are centered by default - * @param columnNames The names of the columns - * @param title The table name + * @brief Construct a new Table Layout object + * @param title The table title + * @param columns A vector containing all column initialized + */ + TableLayout( string_view title, + std::vector< TableLayout::Column > const & columns ) + { + setMargin( MarginValue::medium ); + setTitle( title ); + for( auto const & column :columns ) + { + addToColumns( column ); + } + } + + /** + * @brief Construct a new Table Layout object + * @param title The table title + * @param args An initializer_list containing string / column + */ + TableLayout( string_view title, + TableLayoutArgs args ) + { + setMargin( MarginValue::medium ); + setTitle( title ); + processArguments( args ); + } + + /** + * @brief Construct a new Table Layout object + * @param args An initializer_list containing string / column */ - TableLayout( std::vector< string > const & columnNames, string const & title = "" ); + + TableLayout( TableLayoutArgs args ) + { + setMargin( MarginValue::medium ); + processArguments( args ); + } /** - * @brief Construct a new Table object by specifying value alignment and optionally their displays based to log levels - * level - * @param columnParameters List of structures to set up each colum parameters. - * @param title The table name + * @brief Construct a new Table Layout object + * @param title The table title + * @param args An initializer_list containing string / column */ - TableLayout( std::vector< ColumnParam > const & columnParameters, string const & title = "" ); + TableLayout( string_view title, + std::vector< string > const & args ) + { + setMargin( MarginValue::medium ); + setTitle( title ); + addToColumns( args ); + } + + /** + * @brief Get the max depth of a column + * @return The max column depth + */ + size_t getMaxDepth() const; /** * @return The columns vector */ - std::vector< Column > const & getColumns() const; + std::vector< Column > & getColumns() + { return m_tableColumnsData; } /** - * @return The table name + * @return The columns vector */ - string_view getTitle() const; + std::vector< Column > const & getColumns() const + { return m_tableColumnsData; } /** - * @return The border margin, number of spaces at both left and right table sides + * @return The table name */ - integer const & getBorderMargin() const; + string_view getTitle() const + { return m_tableTitle; } /** - * @return The column margin, numbers of spaces separating both left and right side from each column content + * @param title The table title + * @return The tableLayout reference */ - integer const & getColumnMargin() const; + TableLayout & setTitle( string_view title ); /** - * @return The margin title + * @brief Remove the return line at the end & begenning of the table + * @param value Value to desactivate or not wrapLine at the end + * @return The tableLayout reference */ - integer const & getMarginTitle() const; + TableLayout & enableLineBreak( bool value ); /** * @brief Set the minimal margin width between cell content and borders. * @param marginValue The margin value + * @return The tableLayout reference */ - void setMargin( MarginValue marginValue ); + TableLayout & setMargin( MarginValue marginValue ); + + /** + * @return check if the line break at the end & beginning is activated + */ + bool isLineBreakEnabled() const; + + + /** + * @return The border margin, + * number of spaces at both left and right table sides plus vertical character + */ + integer const & getBorderMargin() const + { return m_borderMargin; } + + /** + * @return The column margin, + * numbers of spaces separating both left and right side from a vertical line + */ + integer const & getColumnMargin() const + { return m_columnMargin; } + + /** + * @return The table margin value + */ + integer const & getMarginValue() const + { return m_marginValue; } + + /** + * @return The margin title + */ + integer const & getMarginTitle() const + { return m_titleMargin; } + +/** + * @brief Get the Nb Rows object + * @return std::vector< integer >& + */ + std::vector< size_t > & getSublineInHeaderCounts() + { return m_sublineHeaderCounts; } + +/** + * @brief Get the Nb Rows object + * @return std::vector< integer >& + */ + std::vector< size_t > & getNbSubDataLines() + { return m_sublineDataCounts; } + + /** + * @brief Create and add a column to the columns vector given a string + * @param m_header The column name + */ + void addToColumns( string_view m_header ); private: - std::vector< Column > m_columns; + /** + * @brief Add a column to the table given an initializer_list of string & Column + * @param args An initializer_list containing string / column + */ + void processArguments( TableLayoutArgs args ) + { + for( auto const & arg : args ) + { + std::visit( [this]( auto const & value ) { + addToColumns( value ); + }, arg ); + } + } + + /** + * @brief + * + * @tparam Ts The remaining arguments + * @param args The remaining arguments to be processed + */ + template< typename ... Ts > + void processArguments( Ts &... args ) + { + addToColumns( args ... ); + } + + /** + * @brief Create and add columns to the columns vector given a string vector + * @param columnNames The columns name + */ + void addToColumns( std::vector< string > const & columnNames ); + +/** + * + * @brief Create and add a column to the columns vector given a Column + * @param column Vector containing addition information on the column + */ + void addToColumns( TableLayout::Column const & column ); + + /// Contains the columns layout + std::vector< Column > m_tableColumnsData; + /// Contains the subdivision (line) counts for each line in header. + std::vector< size_t > m_sublineHeaderCounts; + /// Contains the subdivision (line) counts for each line in data. + std::vector< size_t > m_sublineDataCounts; + bool m_wrapLine = true; + string m_tableTitle; + integer m_borderMargin; integer m_columnMargin; + integer m_marginValue; integer m_titleMargin = 2; }; diff --git a/src/coreComponents/common/format/table/TableTypes.hpp b/src/coreComponents/common/format/table/TableTypes.hpp new file mode 100644 index 00000000000..56c823ffd28 --- /dev/null +++ b/src/coreComponents/common/format/table/TableTypes.hpp @@ -0,0 +1,43 @@ + + +/* + * ------------------------------------------------------------------------------------------------------------ + * 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 TableData.hpp + */ + + +#ifndef GEOS_COMMON_FORMAT_TABLETYPES_HPP +#define GEOS_COMMON_FORMAT_TABLETYPES_HPP + +namespace geos +{ + +/** + * @brief The different type a cell can handle + */ +enum class CellType : integer +{ + Header, + Value, + Separator, + MergeNext, + Hidden +}; + +} + +#endif /* GEOS_COMMON_FORMAT_TABLETYPES_HPP */ diff --git a/src/coreComponents/common/format/table/unitTests/testTable.cpp b/src/coreComponents/common/format/table/unitTests/testTable.cpp index 0258385b1a1..8fd6a5fca47 100644 --- a/src/coreComponents/common/format/table/unitTests/testTable.cpp +++ b/src/coreComponents/common/format/table/unitTests/testTable.cpp @@ -24,48 +24,77 @@ using namespace geos; +TEST( testTable, testCSVTable ) +{ + TableLayout const tableLayout( { + TableLayout::Column() + .setName( "Cras egestas" ), + TableLayout::Column() + .setName( "CoordX" ), + TableLayout::Column() + .setName( "C" ), + TableLayout::Column() + .setName( "CoordZ" ), + TableLayout::Column() + .setName( "Prev\nelement" ), + TableLayout::Column() + .setName( "Next\nelement" )} ); + + TableData tableData; + tableData.addRow( "value1", "gaz", "3.0", "3.0129877", "2", "1" ); + tableData.addRow( "val1", "val2", "[3.045,42.02,89.25]", 3.0, 10.0f, 3 ); + + TableCSVFormatter const csvOutput( tableLayout ); + + EXPECT_EQ( csvOutput.toString( tableData ), + "Cras egestas,CoordX,C,CoordZ,Prevelement,Nextelement\n" + "value1,gaz,3.0,3.0129877,2,1\n" + "val1,val2,[3.045,42.02,89.25],3,10,3\n" + ); +} + TEST( testTable, tableEmptyRow ) { //table with empty row - TableLayout const tableLayout( {"Well\nelement no.\nPV weighted\nbar", + TableLayout const tableLayout( "InternalWellGenerator well_injector1", + {"Well\nelement no.\nPV weighted\nbar", "CordX", "CoordZ", "Prev\nelement", - "Next\nelement"}, - "InternalWellGenerator well_injector1" - ); + "Next\nelement"} ); TableData tableData; tableData.addRow( "value1", "[30.21543]", "3.0", 54, 0 ); - tableData.addRow( "", "", "", "", "" ); + tableData.addRow( "", " ", "", "", "" ); tableData.addRow( "Duis fringilla, ligula sed porta fringilla, ligula wisi commodo felis,ut adipiscing felis dui in enim. Suspendisse malesuada ultrices ante", "[30.21543]", "30.45465142", 787442, 10 ); - TableTextFormatter const tableText( tableLayout ); + tableText.toString( tableData ); EXPECT_EQ( tableText.toString( tableData ), "\n-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n" "| InternalWellGenerator well_injector1 |\n" "-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n" - "| Well | CordX | CoordZ | Prev | Next |\n" - "| element no. | | | element | element |\n" - "| PV weighted | | | | |\n" - "| bar | | | | |\n" + "| Well | CordX | CoordZ | Prev | Next |\n" + "| element no. | | | element | element |\n" + "| PV weighted | | | | |\n" + "| bar | | | | |\n" "-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n" "| value1 | [30.21543] | 3.0 | 54 | 0 |\n" "| | | | | |\n" "| Duis fringilla, ligula sed porta fringilla, ligula wisi commodo felis,ut adipiscing felis dui in enim. Suspendisse malesuada ultrices ante | [30.21543] | 30.45465142 | 787442 | 10 |\n" - "-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n\n" + "-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n" ); } TEST( testTable, tableClassic ) { - TableLayout const tableLayout( {"Duis fringilla, ligula sed porta fringilla,\nligula wisi commodo felis,ut adipiscing felis dui in enim. Suspendisse malesuada ultrices ante", + TableLayout const tableLayout( "InternalWellGenerator well_injector1", + {"Duis fringilla, ligula sed porta fringilla,\nligula wisi commodo felis,ut adipiscing felis dui in enim. Suspendisse malesuada ultrices ante", "CordX", "CoordZ", "Prev\nelement", - "Next\nelement"}, "InternalWellGenerator well_injector1" ); + "Next\nelement"} ); TableData tableData; tableData.addRow( "value1", "[30.21543]", "3.0", 54, 0 ); @@ -77,77 +106,100 @@ TEST( testTable, tableClassic ) "\n-----------------------------------------------------------------------------------------------------------------------------------------------------------\n" "| InternalWellGenerator well_injector1 |\n" "-----------------------------------------------------------------------------------------------------------------------------------------------------------\n" - "| Duis fringilla, ligula sed porta fringilla, | CordX | CoordZ | Prev | Next |\n" + "| Duis fringilla, ligula sed porta fringilla, | CordX | CoordZ | Prev | Next |\n" "| ligula wisi commodo felis,ut adipiscing felis dui in enim. Suspendisse malesuada ultrices ante | | | element | element |\n" "-----------------------------------------------------------------------------------------------------------------------------------------------------------\n" "| value1 | [30.21543] | 3.0 | 54 | 0 |\n" "| | | | | |\n" "| value23 | [30.21543] | 30.45465142 | 787442 | 10 |\n" - "-----------------------------------------------------------------------------------------------------------------------------------------------------------\n\n" + "-----------------------------------------------------------------------------------------------------------------------------------------------------------\n" ); } TEST( testTable, tableColumnParamClassic ) { TableLayout const tableLayout( { - TableLayout::ColumnParam{{"Cras egestas"}, TableLayout::Alignment::center}, - TableLayout::ColumnParam{{"CoordX"}, TableLayout::Alignment::left}, - TableLayout::ColumnParam{{"C"}, TableLayout::Alignment::left}, - TableLayout::ColumnParam{{"CoordZ"}, TableLayout::Alignment::left}, - TableLayout::ColumnParam{{"Prev\nelement"}, TableLayout::Alignment::right}, - TableLayout::ColumnParam{{"Next\nelement"}, TableLayout::Alignment::right} - }, "InternalWellGenerator well_injector1" ); + TableLayout::Column() + .setName( "Cras egestas" ), + TableLayout::Column() + .setName( "CoordX" ), + TableLayout::Column() + .setName( "C" ), + TableLayout::Column() + .setName( "CoordZ" ), + TableLayout::Column() + .setName( "Prev\nelement" ), + TableLayout::Column() + .setName( "Next\nelement" )} ); TableData tableData; - tableData.addRow( "value1", " ", "3.0", 3.0129877, 2.0f, 1 ); + tableData.addRow( "value1", "gaz\nwater", "3.0\n42.0", "3.0129877\n0.0123456", "2\n3", "1\n4" ); tableData.addRow( "val1", "v", "[3.045,42.02,89.25]", 3.0, 10.0f, 3 ); TableTextFormatter const tableText( tableLayout ); EXPECT_EQ( tableText.toString( tableData ), "\n-------------------------------------------------------------------------------------------\n" - "| InternalWellGenerator well_injector1 |\n" - "-------------------------------------------------------------------------------------------\n" - "| Cras egestas | CoordX | C | CoordZ | Prev | Next |\n" + "| Cras egestas | CoordX | C | CoordZ | Prev | Next |\n" "| | | | | element | element |\n" "-------------------------------------------------------------------------------------------\n" - "| value1 | | 3.0 | 3.0129877 | 2 | 1 |\n" - "| val1 | v | [3.045,42.02,89.25] | 3 | 10 | 3 |\n" - "-------------------------------------------------------------------------------------------\n\n" + "| value1 | gaz | 3.0 | 3.0129877 | 2 | 1 |\n" + "| | water | 42.0 | 0.0123456 | 3 | 4 |\n" + "| val1 | v | [3.045,42.02,89.25] | 3 | 10 | 3 |\n" + "-------------------------------------------------------------------------------------------\n" ); } TEST( testTable, tableHiddenColumn ) { - TableLayout const tableLayout( { - TableLayout::ColumnParam{{"Cras egestas"}, TableLayout::Alignment::center}, - TableLayout::ColumnParam{{"CoordX"}, TableLayout::Alignment::right}, - TableLayout::ColumnParam{{"C"}, TableLayout::Alignment::center}, - TableLayout::ColumnParam{{"CoordZ"}, TableLayout::Alignment::left}, - TableLayout::ColumnParam{{"Prev\nelement"}, TableLayout::Alignment::left, false}, - TableLayout::ColumnParam{{"Next\nelement"}, TableLayout::Alignment::center, false}, - }, "Cras egestas ipsum a nisl. Vivamus variu dolor utsisicdis parturient montes, nascetur ridiculus mus. Duis" ); + string const title = "Cras egestas ipsum a nisl. Vivamus variu dolor utsisicdis parturient montes, nascetur ridiculus mus. Duis"; + TableLayout tableLayout( title, + { + TableLayout::Column() + .setName( "Cras egestas" ) + .setValuesAlignment( TableLayout::Alignment::left ) + .setHeaderAlignment( TableLayout::Alignment::center ), + TableLayout::Column() + .setName( "CoordX" ) + .setValuesAlignment( TableLayout::Alignment::left ) + .setHeaderAlignment( TableLayout::Alignment::right ) + .setVisibility( CellType::Hidden ), + TableLayout::Column() + .setName( "C" ) + .setValuesAlignment( TableLayout::Alignment::left ) + .setHeaderAlignment( TableLayout::Alignment::center ), + TableLayout::Column() + .setName( "CoordZ" ) + .setValuesAlignment( TableLayout::Alignment::left ) + .setHeaderAlignment( TableLayout::Alignment::left ), + TableLayout::Column() + .setName( "Prev\nelement" ) + .setVisibility( CellType::Hidden ), + TableLayout::Column() + .setName( "Next\nelement" ) + .setVisibility( CellType::Hidden ) + } ); TableData tableData; - tableData.addRow( "value1", " ", "3.0", 3.0129877, 2.0f, 1 ); + tableData.addRow( "value1", "", "3.0", 3.0129877, 2.0f, 1 ); tableData.addRow( "val1", "v", "[3.045,42.02,89.25]", 3.0, 10.0f, 3 ); TableTextFormatter const tableText( tableLayout ); EXPECT_EQ( tableText.toString( tableData ), - "\n----------------------------------------------------------------------------------------------------------------\n" - "| Cras egestas ipsum a nisl. Vivamus variu dolor utsisicdis parturient montes, nascetur ridiculus mus. Duis |\n" - "----------------------------------------------------------------------------------------------------------------\n" - "| Cras egestas | CoordX | C | CoordZ |\n" - "----------------------------------------------------------------------------------------------------------------\n" - "| value1 | | 3.0 | 3.0129877 |\n" - "| val1 | v | [3.045,42.02,89.25] | 3 |\n" - "----------------------------------------------------------------------------------------------------------------\n\n" ); + "\n---------------------------------------------------------------------------------------------------------------\n" + "| Cras egestas ipsum a nisl. Vivamus variu dolor utsisicdis parturient montes, nascetur ridiculus mus. Duis |\n" + "---------------------------------------------------------------------------------------------------------------\n" + "| Cras egestas | C | CoordZ |\n" + "| | | |\n" + "---------------------------------------------------------------------------------------------------------------\n" + "| value1 | 3.0 | 3.0129877 |\n" + "| val1 | [3.045,42.02,89.25] | 3 |\n" + "---------------------------------------------------------------------------------------------------------------\n" ); } TEST( testTable, tableUniqueColumn ) { - TableLayout const tableLayout( { - TableLayout::ColumnParam{{"Cras egestas"}, TableLayout::Alignment::center}, - }, "Cras egestas ipsu a nisl. Vivamus variu dolor utsisicdis parturient montes, nascetur ridiculus mus. Duis" ); + string const title = "Cras egestas ipsum a nisl. Vivamus variu dolor utsisicdis parturient montes, nascetur ridiculus mus. Duis"; + TableLayout const tableLayout( title, {"Cras egestas"} ); TableData tableData; tableData.addRow( "value1" ); @@ -156,26 +208,34 @@ TEST( testTable, tableUniqueColumn ) TableTextFormatter const tableText( tableLayout ); EXPECT_EQ( tableText.toString( tableData ), "\n---------------------------------------------------------------------------------------------------------------\n" - "| Cras egestas ipsu a nisl. Vivamus variu dolor utsisicdis parturient montes, nascetur ridiculus mus. Duis |\n" + "| Cras egestas ipsum a nisl. Vivamus variu dolor utsisicdis parturient montes, nascetur ridiculus mus. Duis |\n" "---------------------------------------------------------------------------------------------------------------\n" "| Cras egestas |\n" "---------------------------------------------------------------------------------------------------------------\n" - "| value1 |\n" - "| val1 |\n" - "---------------------------------------------------------------------------------------------------------------\n\n" ); + "| value1 |\n" + "| val1 |\n" + "---------------------------------------------------------------------------------------------------------------\n" ); } TEST( testTable, tableEmptyTitle ) { TableLayout const tableLayout( { - TableLayout::ColumnParam{{"Cras egestas"}, TableLayout::Alignment::center}, - TableLayout::ColumnParam{{"CoordX"}, TableLayout::Alignment::right}, - TableLayout::ColumnParam{{"C"}, TableLayout::Alignment::center}, - TableLayout::ColumnParam{{"CoordZ"}, TableLayout::Alignment::left}, - TableLayout::ColumnParam{{"Prev\nelement"}, TableLayout::Alignment::left}, - TableLayout::ColumnParam{{"Next\nelement"}, TableLayout::Alignment::center}, - } - ); + TableLayout::Column() + .setName( "Cras egestas" ) + .setHeaderAlignment( TableLayout::Alignment::center ), + TableLayout::Column() + .setName( "CoordX" ) + .setHeaderAlignment( TableLayout::Alignment::right ), + "C", + TableLayout::Column() + .setName( "CoordZ" ) + .setHeaderAlignment( TableLayout::Alignment::left ), + TableLayout::Column() + .setName( "Prev\nelement" ) + .setHeaderAlignment( TableLayout::Alignment::left ), + TableLayout::Column() + .setName( "Next\nelement" ) + .setHeaderAlignment( TableLayout::Alignment::center )} ); TableData tableData; tableData.addRow( "value1", " ", "3.0", 3.0129877, 2.0f, 1 ); @@ -187,9 +247,9 @@ TEST( testTable, tableEmptyTitle ) "| Cras egestas | CoordX | C | CoordZ | Prev | Next |\n" "| | | | | element | element |\n" "-------------------------------------------------------------------------------------------\n" - "| value1 | | 3.0 | 3.0129877 | 2 | 1 |\n" - "| val1 | v | [3.045,42.02,89.25] | 3 | 10 | 3 |\n" - "-------------------------------------------------------------------------------------------\n\n" + "| value1 | | 3.0 | 3.0129877 | 2 | 1 |\n" + "| val1 | v | [3.045,42.02,89.25] | 3 | 10 | 3 |\n" + "-------------------------------------------------------------------------------------------\n" ); } @@ -215,115 +275,222 @@ TEST( testTable, table2DTable ) columnFmt ); //format - TableLayout const tableLayout( tableconverted.headerNames ); + TableLayout const tableLayout( "", tableconverted.headerNames ); //log - TableTextFormatter const tableLog( tableLayout ); - EXPECT_EQ( tableLog.toString( tableconverted.tableData ), + TableTextFormatter const tableText( tableLayout ); + EXPECT_EQ( tableText.toString( tableconverted.tableData ), "\n---------------------------------------------------------------------\n" - "| Viscosity kg*s | Pression = 10000 | Pression = 15000 |\n" + "| Viscosity kg*s | Pression = 10000 | Pression = 15000 |\n" "---------------------------------------------------------------------\n" "| Temperature = 300 | 0.03 | 0.02 |\n" "| Temperature = 350 | 0.035 | 0.023333333333333334 |\n" "| Temperature = 400 | 0.04 | 0.02666666666666667 |\n" - "---------------------------------------------------------------------\n\n" + "---------------------------------------------------------------------\n" + ); +} + +TEST( testTable, layoutTable ) +{ + string filename = "fluid1_phaseModel1_PhillipsBrineDensity_table"; + string log = GEOS_FMT( "The {} PVT table exceeding 500 rows.\nTo visualize the tables, go to the generated csv", filename ); + TableLayout const tableLayoutInfos( filename, + { + TableLayout::Column() + .setName( log ) + .setHeaderAlignment( TableLayout::Alignment::left )} ); + TableTextFormatter const tableText( tableLayoutInfos ); + EXPECT_EQ( tableText.toString(), + "\n-------------------------------------------------------------------------------------\n" + "| fluid1_phaseModel1_PhillipsBrineDensity_table |\n" + "-------------------------------------------------------------------------------------\n" + "| The fluid1_phaseModel1_PhillipsBrineDensity_table PVT table exceeding 500 rows. |\n" + "| To visualize the tables, go to the generated csv |\n" + "-------------------------------------------------------------------------------------\n\n" ); } +TEST( testTable, subColumns ) +{ + { + TableLayout const tableLayout( { + " ", + "Column1", + TableLayout::Column() + .setName( "Nodes" ) + .addSubColumns( {"LocalesNodes", "GhostNodes", "ActiveNodes" } ), + "Column3", + TableLayout::Column() + .setName( "Column4" ) + .addSubColumns( { TableLayout::Column() + .setName( "Locales" ).addSubColumns( {"SubLocales1", "SubLocales2"} ), + TableLayout::Column() + .setName( "Ghost" ).addSubColumns( {"SubGhost1", "SubGhost2"} ), + TableLayout::Column() + .setName( "Active" ).addSubColumns( {"SubActive1", "SubActive2"} ) + } ), + "Column5" + } ); + + TableData tableData; + tableData.addRow( "3547", "1289", "7534", "6901", "4832", "9281", "1154", "5360", "2739", "9004", "1497", "6", "7" ); + tableData.addRow( "5142", "8290", "364", "2310", "7011", "1427", "2574", "9043", "5305", "608", "980", "6", "7" ); + tableData.addRow( "3174", "8259", "6092", "1783", "7435", "2891", "914", "178", "4635", "5839", "8124", "6", "7" ); + tableData.addRow( "6193", "7481", "1305", "9037", "4306.1", "6157", "1849", "2753", "910", "2369", "9992", "6", "7" ); + tableData.addRow( "8012", "5729.2112", "6975", "3201.213", "9448", "1820", "4125", "182.12", "7453", "5069", "3912", "6", "7" ); + tableData.addRow( "4381", "6728", "5204", "8663", "2035", "7804", "6310", "9621", "4158", "789", "2537", "6", "7" ); + TableTextFormatter tableText( tableLayout ); + EXPECT_EQ( tableText.toString( + tableData ), + "\n------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n" + "| | Column1 | Nodes | Column3 | Column4 | Column5 |\n" + "------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n" + "| | | LocalesNodes | GhostNodes | ActiveNodes | | Locales | Ghost | Active | |\n" + "------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n" + "| | | | | | | SubLocales1 | SubLocales2 | SubGhost1 | SubGhost2 | SubActive1 | SubActive2 | |\n" + "------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n" + "| 3547 | 1289 | 7534 | 6901 | 4832 | 9281 | 1154 | 5360 | 2739 | 9004 | 1497 | 6 | 7 |\n" + "| 5142 | 8290 | 364 | 2310 | 7011 | 1427 | 2574 | 9043 | 5305 | 608 | 980 | 6 | 7 |\n" + "| 3174 | 8259 | 6092 | 1783 | 7435 | 2891 | 914 | 178 | 4635 | 5839 | 8124 | 6 | 7 |\n" + "| 6193 | 7481 | 1305 | 9037 | 4306.1 | 6157 | 1849 | 2753 | 910 | 2369 | 9992 | 6 | 7 |\n" + "| 8012 | 5729.2112 | 6975 | 3201.213 | 9448 | 1820 | 4125 | 182.12 | 7453 | 5069 | 3912 | 6 | 7 |\n" + "| 4381 | 6728 | 5204 | 8663 | 2035 | 7804 | 6310 | 9621 | 4158 | 789 | 2537 | 6 | 7 |\n" + "------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n" + ); + } +} -TEST( testTable, table2DColumnMismatch ) +TEST( testTable, variadicTest ) { - //test 2D table column mismatch { - // collect - TableData2D tableData; - - tableData.addCell( 300, 10000, 0.03 ); - tableData.addCell( 300, 15000, 0.02 ); - tableData.addCell( 350, 10000, 0.035 ); - tableData.addCell( 350, 10000, 0.035 ); - tableData.addCell( 400, 10000, 0.04 ); - tableData.addCell( 400, 15000, 0.02666666666666667 ); - - //convert - string const rowFmt = GEOS_FMT( "{} = {{}}", "Temperature" ); - string const columnFmt = GEOS_FMT( "{} = {{}}", "Pression" ); - TableData2D::TableDataHolder tableConverted = tableData.buildTableData( "Viscosity kg*s", - rowFmt, - columnFmt ); - - //format - TableLayout const tableLayout( tableConverted.headerNames ); - - //log - TableTextFormatter const tableLog( tableLayout ); - EXPECT_EQ( tableLog.toString( tableConverted.tableData ), - "\n--------------------------------------------------------------------\n" - "| Remarks : some cells may be missing |\n" - "--------------------------------------------------------------------\n" - "| Viscosity kg*s | Pression = 10000 | Pression = 15000 |\n" - "--------------------------------------------------------------------\n" - "| Temperature = 300 | 0.03 | 0.02 |\n" - "| Temperature = 350 | 0.035 | |\n" - "| Temperature = 400 | 0.04 | 0.02666666666666667 |\n" - "--------------------------------------------------------------------\n\n" + TableLayout const layoutTest( "Cras egestas ipsum a nisl. Vivamus variu dolor utsisicdis parturient montes, nascetur ridiculus mus. Duis nascetur ridiculus mus", + { + "Rank", + TableLayout::Column() + .setName( "Nodes" ) + .addSubColumns( {"Locales", "Ghost" } ), + "Edge", + TableLayout::Column() + .setName( "Faces" ) + .addSubColumns( {"Locales", "Ghost" } ), + TableLayout::Column() + .setName( "Elems" ) + .addSubColumns( {"Locales", "Ghost"} ), + } ); + TableData tableData; + tableData.addRow( "min(local/total)", 1, 2, 3, 4, 5, 6, 7 ); + tableData.addRow( "min(local/total)", 1, 2, 3, 4, 5, 6, 7 ); + TableTextFormatter log( layoutTest ); + EXPECT_EQ( log.toString( tableData ), + "\n--------------------------------------------------------------------------------------------------------------------------------------\n" + "| Cras egestas ipsum a nisl. Vivamus variu dolor utsisicdis parturient montes, nascetur ridiculus mus. Duis nascetur ridiculus mus |\n" + "--------------------------------------------------------------------------------------------------------------------------------------\n" + "| Rank | Nodes | Edge | Faces | Elems |\n" + "--------------------------------------------------------------------------------------------------------------------------------------\n" + "| | Locales | Ghost | | Locales | Ghost | Locales | Ghost |\n" + "--------------------------------------------------------------------------------------------------------------------------------------\n" + "| min(local/total) | 1 | 2 | 3 | 4 | 5 | 6 | 7 |\n" + "| min(local/total) | 1 | 2 | 3 | 4 | 5 | 6 | 7 |\n" + "--------------------------------------------------------------------------------------------------------------------------------------\n" ); } } -TEST( testTable, layoutTable ) +TEST( testTable, testLineBreak ) { - string filename = "fluid1_phaseModel1_PhillipsBrineDensity_table"; + TableLayout tableLayout( {"Cras egestas", "CoordX", "C", "CoordZ", "Prev\nelement", "Next\nelement"} ); + tableLayout.setTitle( "title" ).setMargin( TableLayout::MarginValue::tiny ).enableLineBreak( false ); + + TableData tableData; + tableData.addRow( "1", "2", "3.0", 3.0129877, 2.0f, 1 ); + tableData.addRow( "1", "2", "3.0", 3.0129877, 2.0f, 1 ); - string log = GEOS_FMT( "The {} PVT table exceeding 500 rows.\nTo visualize the tables, go to the generated csv \n", filename ); - TableLayout const tableLayoutInfos( {TableLayout::ColumnParam{{log}, TableLayout::Alignment::left}}, filename ); + TableTextFormatter const tableText( tableLayout ); - TableTextFormatter const tableLog( tableLayoutInfos ); - EXPECT_EQ( tableLog.layoutToString(), - "\n-------------------------------------------------------------------------------------\n" - "| fluid1_phaseModel1_PhillipsBrineDensity_table |\n" - "-------------------------------------------------------------------------------------\n" - "| The fluid1_phaseModel1_PhillipsBrineDensity_table PVT table exceeding 500 rows. |\n" - "| To visualize the tables, go to the generated csv |\n" - "-------------------------------------------------------------------------------------\n" + EXPECT_EQ( tableText.toString( tableData ), + "---------------------------------------------------\n" + "| title |\n" + "---------------------------------------------------\n" + "|Cras egestas|CoordX| C | CoordZ | Prev | Next |\n" + "| | | | |element|element|\n" + "---------------------------------------------------\n" + "| 1| 2|3.0|3.0129877| 2| 1|\n" + "| 1| 2|3.0|3.0129877| 2| 1|\n" + "---------------------------------------------------" ); } -TEST( testTable, tableSetMargin ) +TEST( testTable, testCellMerging ) { - //////////// - //////// If setMargin used elsewhere make it public and remove comments for this test - //////////// - //test with tiny margin - // { - // TableLayout tableLayout( { - // TableLayout::ColumnParam{{"Colonne 1"}, TableLayout::Alignment::center}, - // TableLayout::ColumnParam{{"Colonne 2"}, TableLayout::Alignment::center}, - // TableLayout::ColumnParam{{"Colonne 3"}, TableLayout::Alignment::right}, - // TableLayout::ColumnParam{{"Colonne 4"}, TableLayout::Alignment::right}, - // TableLayout::ColumnParam{{"Prev\nelement"}, TableLayout::Alignment::right}, - // TableLayout::ColumnParam{{"Next\nelement"}, TableLayout::Alignment::right}, - // }, "InternalWellGenerator well_injector1" ); - - // //tableLayout.setMargin( TableLayout::MarginValue::tiny ); - - // TableData tableData; - // tableData.addRow( "value 1", "long value 1", "3.0034", 3.0129877, "" , 1 ); - // tableData.addRow( "value 1", "long value 2", "100.45", 4.0129877, 1 , 2 ); - - // TableTextFormatter const tableText( tableLayout ); - // EXPECT_EQ( tableText.toString( tableData ), -// "\n------------------------------------------------------------\n" -// "| InternalWellGenerator well_injector1 |\n" -// "------------------------------------------------------------\n" -// "|Colonne 1| Colonne 2 |Colonne 3|Colonne 4| Prev| Next|\n" -// "| | | | |element|element|\n" -// "------------------------------------------------------------\n" -// "| value 1 |long value 1| 3.0034|3.0129877| | 1|\n" -// "| value 1 |long value 2| 100.45|4.0129877| 1| 2|\n" -// "------------------------------------------------------------\n\n" -// ); -// } + TableLayout const tableLayout( { + TableLayout::Column() + .setName( "Cras egestas" ), + TableLayout::Column() + .setName( "CoordX" ), + "C", + TableLayout::Column() + .setName( "CoordZ" ), + TableLayout::Column() + .setName( "Prev\nelement" ), + TableLayout::Column() + .setName( "Next\nelement" )} ); + + TableData tableData; + tableData.addRow( "ProductA", 1234, 40, "ProductName", 5678, 60 ); + tableData.addRow( "ProductA", 54, 4564575, "long size value", 5454554512, 60 ); + tableData.addSeparator(); + tableData.addRow( "ProductA", 54, 4564575, "long size value", 5454554512, 60 ); + tableData.addRow( 3.14f, 2.718f, CellType::MergeNext, 1.618f, 0.577f, CellType::MergeNext ); + tableData.addRow( "P1\nP2\nP3", "2002\n2003\n2004", CellType::MergeNext, "1212121245452145454545", 4004, CellType::MergeNext ); + tableData.addRow( "Long product size", 54, 4564575, "long size value", 5454554512, 60 ); + tableData.addRow( "ProductAfdggfd", 5445, 4565, "PrName", 5454512, 64650 ); + tableData.addRow( 3.14f, 2.718f, CellType::MergeNext, 1.618f, 0.577f, CellType::MergeNext ); + tableData.addSeparator(); + tableData.addRow( "CellType::MergeNext", CellType::MergeNext, CellType::MergeNext, CellType::MergeNext, CellType::MergeNext, "Item2" ); + tableData.addSeparator(); + tableData.addRow( 1500, 2500, CellType::MergeNext, CellType::MergeNext, CellType::MergeNext, CellType::MergeNext ); + tableData.addSeparator(); + tableData.addRow( 1.23f, 4.56f, CellType::MergeNext, "764654665465465654654646", 0.12f, 40 ); + tableData.addRow( "Long product size", 54, 4564575, "long size value", 5454554512, 60 ); + tableData.addRow( "ProductA", 54, 4564575, "long size value", 5454554512, 60 ); + tableData.addSeparator(); + tableData.addRow( "P1", "2002", CellType::MergeNext, CellType::MergeNext, CellType::MergeNext, "121212465465465666656461245452145454545" ); + tableData.addSeparator(); + tableData.addRow( "Alpha", 1001, 8, "Beta\nwater", "2002\n1.0", CellType::MergeNext ); + + TableTextFormatter const tableText( tableLayout ); + std::cout << tableText.toString( tableData ) << std::endl; + EXPECT_EQ( tableText.toString( tableData ), + "\n-----------------------------------------------------------------------------------------------\n" + "| Cras egestas | CoordX | C | CoordZ | Prev | Next |\n" + "| | | | | element | element |\n" + "-----------------------------------------------------------------------------------------------\n" + "| ProductA | 1234 | 40 | ProductName | 5678 | 60 |\n" + "| ProductA | 54 | 4564575 | long size value | 5454554512 | 60 |\n" + "-----------------------------------------------------------------------------------------------\n" + "| ProductA | 54 | 4564575 | long size value | 5454554512 | 60 |\n" + "| 3.14 | 2.718 | 1.618 | 0.577 | |\n" + "| P1 | 2002 | 1212121245452145454545 | 4004 | |\n" + "| P2 | 2003 | | | |\n" + "| P3 | 2004 | | | |\n" + "| Long product size | 54 | 4564575 | long size value | 5454554512 | 60 |\n" + "| ProductAfdggfd | 5445 | 4565 | PrName | 5454512 | 64650 |\n" + "| 3.14 | 2.718 | 1.618 | 0.577 | |\n" + "-----------------------------------------------------------------------------------------------\n" + "| CellType::MergeNext | Item2 |\n" + "-----------------------------------------------------------------------------------------------\n" + "| 1500 | 2500 | |\n" + "-----------------------------------------------------------------------------------------------\n" + "| 1.23 | 4.56 | 764654665465465654654646 | 0.12 | 40 |\n" + "| Long product size | 54 | 4564575 | long size value | 5454554512 | 60 |\n" + "| ProductA | 54 | 4564575 | long size value | 5454554512 | 60 |\n" + "-----------------------------------------------------------------------------------------------\n" + "| P1 | 2002 | 121212465465465666656461245452145454545 |\n" + "-----------------------------------------------------------------------------------------------\n" + "| Alpha | 1001 | 8 | Beta | 2002 | |\n" + "| | | | water | 1.0 | |\n" + "-----------------------------------------------------------------------------------------------\n" + ); } int main( int argc, char * * argv ) diff --git a/src/coreComponents/common/initializeEnvironment.cpp b/src/coreComponents/common/initializeEnvironment.cpp index 179f7ca4279..832f4fa542f 100644 --- a/src/coreComponents/common/initializeEnvironment.cpp +++ b/src/coreComponents/common/initializeEnvironment.cpp @@ -345,11 +345,11 @@ static void addUmpireHighWaterMarks() pushStatsIntoAdiak( allocatorName + " rank max", mark ); } - TableLayout const memoryStatLayout ( {"Umpire Memory Pool\n(reserved / % over total)", - "Min over ranks", - "Max over ranks", - "Avg over ranks", - "Sum over ranks" } ); + TableLayout const memoryStatLayout ( { "Umpire Memory Pool\n(reserved / % over total)", + "Min over ranks", + "Max over ranks", + "Avg over ranks", + "Sum over ranks" } ); TableTextFormatter const memoryStatLog( memoryStatLayout ); GEOS_LOG_RANK_0( memoryStatLog.toString( tableData )); diff --git a/src/coreComponents/constitutive/ConstitutivePassThru.hpp b/src/coreComponents/constitutive/ConstitutivePassThru.hpp index ec72812cdb1..7b5b1533134 100644 --- a/src/coreComponents/constitutive/ConstitutivePassThru.hpp +++ b/src/coreComponents/constitutive/ConstitutivePassThru.hpp @@ -101,7 +101,6 @@ struct ConstitutivePassThru< FrictionBase > } }; - /** * Specialization for models that derive from CoulombFriction. */ diff --git a/src/coreComponents/constitutive/contact/RateAndStateFriction.cpp b/src/coreComponents/constitutive/contact/RateAndStateFriction.cpp index 30d43c9ae7f..6376553ec85 100644 --- a/src/coreComponents/constitutive/contact/RateAndStateFriction.cpp +++ b/src/coreComponents/constitutive/contact/RateAndStateFriction.cpp @@ -31,12 +31,15 @@ RateAndStateFriction::RateAndStateFriction( string const & name, Group * const p FrictionBase( name, parent ) { registerWrapper( viewKeyStruct::aCoefficientString(), &m_a ). + setPlotLevel( PlotLevel::LEVEL_0 ). setDescription( "Rate- and State-dependent friction coefficient a." ); registerWrapper( viewKeyStruct::bCoefficientString(), &m_b ). + setPlotLevel( PlotLevel::LEVEL_0 ). setDescription( "Rate- and State-dependent friction coefficient b." ); registerWrapper( viewKeyStruct::DcCoefficientString(), &m_Dc ). + setPlotLevel( PlotLevel::LEVEL_0 ). setDescription( "Rate- and State-dependent friction characteristic length." ); registerWrapper( viewKeyStruct::referenceVelocityString(), &m_V0 ). diff --git a/src/coreComponents/functions/TableFunction.cpp b/src/coreComponents/functions/TableFunction.cpp index 74f031fef87..3fc62833d41 100644 --- a/src/coreComponents/functions/TableFunction.cpp +++ b/src/coreComponents/functions/TableFunction.cpp @@ -307,7 +307,7 @@ string TableCSVFormatter::toString< TableFunction >( TableFunction const & table units::getDescription( tableFunction.getDimUnit( 0 ) ), units::getDescription( tableFunction.getDimUnit( 1 ) ) ); - TableLayout tableLayout( tableConverted.headerNames ); + TableLayout const tableLayout( "", tableConverted.headerNames ); TableCSVFormatter csvFormat( tableLayout ); formatterStream << csvFormat.headerToString() << csvFormat.dataToString( tableConverted.tableData ); @@ -322,7 +322,7 @@ string TableTextFormatter::toString< TableFunction >( TableFunction const & tabl units::Unit const valueUnit = tableFunction.getValueUnit(); arrayView1d< real64 const > const values = tableFunction.getValues(); integer const numDimensions = LvArray::integerConversion< integer >( coordinates.size() ); - string const filename = tableFunction.getName(); + std::string_view filename = tableFunction.getName(); string logOutput; GEOS_LOG_RANK_0( GEOS_FMT( "Values in the table are represented by : {}", units::getDescription( valueUnit ))); @@ -335,12 +335,10 @@ string TableTextFormatter::toString< TableFunction >( TableFunction const & tabl { tableData.addRow( coords[idx], values[idx] ); } - - TableLayout const tableLayout( { + TableLayout const tableLayout( filename, { string( units::getDescription( tableFunction.getDimUnit( 0 ))), string( units::getDescription( valueUnit )) - }, filename ); - + } ); TableTextFormatter const logTable( tableLayout ); logOutput = logTable.toString( tableData ); } @@ -358,17 +356,16 @@ string TableTextFormatter::toString< TableFunction >( TableFunction const & tabl units::getDescription( tableFunction.getDimUnit( 0 ) ), units::getDescription( tableFunction.getDimUnit( 1 ) )); - TableLayout tableLayout( tableConverted.headerNames, filename ); - + TableLayout const tableLayout( filename, tableConverted.headerNames ); TableTextFormatter const table2DLog( tableLayout ); logOutput = table2DLog.toString( tableConverted.tableData ); } else { - string log = GEOS_FMT( "The {} PVT table exceeding 500 rows.\nTo visualize the tables, go to the generated csv \n", filename ); - TableLayout const tableLayoutInfos( {TableLayout::ColumnParam{{log}, TableLayout::Alignment::left}}, filename ); + string const log = GEOS_FMT( "The {} PVT table exceeding 500 rows.\nTo visualize the tables, go to the generated csv", filename ); + TableLayout const tableLayoutInfos( filename, {log} ); TableTextFormatter const tableLog( tableLayoutInfos ); - logOutput = tableLog.layoutToString(); + logOutput = tableLog.toString(); } } return logOutput; diff --git a/src/coreComponents/linearAlgebra/CMakeLists.txt b/src/coreComponents/linearAlgebra/CMakeLists.txt index 99cc0bc77d4..b99b0697b03 100644 --- a/src/coreComponents/linearAlgebra/CMakeLists.txt +++ b/src/coreComponents/linearAlgebra/CMakeLists.txt @@ -128,6 +128,7 @@ if( ENABLE_HYPRE ) interfaces/hypre/mgrStrategies/HybridSinglePhasePoromechanics.hpp interfaces/hypre/mgrStrategies/Hydrofracture.hpp interfaces/hypre/mgrStrategies/LagrangianContactMechanics.hpp + interfaces/hypre/mgrStrategies/LagrangianContactMechanicsBubbleStabilization.hpp interfaces/hypre/mgrStrategies/MultiphasePoromechanics.hpp interfaces/hypre/mgrStrategies/SinglePhasePoromechanicsEmbeddedFractures.hpp interfaces/hypre/mgrStrategies/SinglePhasePoromechanicsConformingFractures.hpp diff --git a/src/coreComponents/linearAlgebra/interfaces/hypre/HypreMGR.cpp b/src/coreComponents/linearAlgebra/interfaces/hypre/HypreMGR.cpp index 03791b006e1..466fc6ff04d 100644 --- a/src/coreComponents/linearAlgebra/interfaces/hypre/HypreMGR.cpp +++ b/src/coreComponents/linearAlgebra/interfaces/hypre/HypreMGR.cpp @@ -27,6 +27,7 @@ #include "linearAlgebra/interfaces/hypre/mgrStrategies/HybridSinglePhasePoromechanics.hpp" #include "linearAlgebra/interfaces/hypre/mgrStrategies/Hydrofracture.hpp" #include "linearAlgebra/interfaces/hypre/mgrStrategies/LagrangianContactMechanics.hpp" +#include "linearAlgebra/interfaces/hypre/mgrStrategies/LagrangianContactMechanicsBubbleStabilization.hpp" #include "linearAlgebra/interfaces/hypre/mgrStrategies/MultiphasePoromechanics.hpp" #include "linearAlgebra/interfaces/hypre/mgrStrategies/MultiphasePoromechanicsReservoirFVM.hpp" #include "linearAlgebra/interfaces/hypre/mgrStrategies/ReactiveCompositionalMultiphaseOBL.hpp" @@ -138,6 +139,11 @@ void hypre::mgr::createMGR( LinearSolverParameters const & params, setStrategy< LagrangianContactMechanics >( params.mgr, numComponentsPerField, precond, mgrData ); break; } + case LinearSolverParameters::MGR::StrategyType::lagrangianContactMechanicsBubbleStab: + { + setStrategy< LagrangianContactMechanicsBubbleStabilization >( params.mgr, numComponentsPerField, precond, mgrData ); + break; + } case LinearSolverParameters::MGR::StrategyType::multiphasePoromechanics: { setStrategy< MultiphasePoromechanics >( params.mgr, numComponentsPerField, precond, mgrData ); diff --git a/src/coreComponents/linearAlgebra/interfaces/hypre/HypreMGR.hpp b/src/coreComponents/linearAlgebra/interfaces/hypre/HypreMGR.hpp index 8bacdf8b978..c45bc4fe873 100644 --- a/src/coreComponents/linearAlgebra/interfaces/hypre/HypreMGR.hpp +++ b/src/coreComponents/linearAlgebra/interfaces/hypre/HypreMGR.hpp @@ -269,6 +269,32 @@ class MGRStrategyBase setDisplacementAMG( mgrData.mechSolver, separateComponents ); HYPRE_MGRSetFSolver( precond.ptr, mgrData.mechSolver.solve, mgrData.mechSolver.setup, mgrData.mechSolver.ptr ); } + /** + * @brief + * + * @param solver + */ + void setILUCoarseSolver( HyprePrecWrapper & solver ) + { + /* (Required) Create ILU solver */ + HYPRE_ILUCreate( &solver.ptr ); + + /* (Recommended) General solver options */ + int const ilu_type = 0; /* 0, 1, 10, 11, 20, 21, 30, 31, 40, 41, 50 */ + int const max_iter = 1; + double const tol = 0.0; + int const reordering = 0; /* 0: none, 1: RCM */ + int const print_level = 0; + HYPRE_ILUSetType( solver.ptr, ilu_type ); + HYPRE_ILUSetMaxIter( solver.ptr, max_iter ); + HYPRE_ILUSetTol( solver.ptr, tol ); + HYPRE_ILUSetLocalReordering( solver.ptr, reordering ); + HYPRE_ILUSetPrintLevel( solver.ptr, print_level ); + + solver.setup = HYPRE_ILUSetup; + solver.solve = HYPRE_ILUSolve; + solver.destroy = HYPRE_ILUDestroy; + } }; diff --git a/src/coreComponents/linearAlgebra/interfaces/hypre/mgrStrategies/LagrangianContactMechanicsBubbleStabilization.hpp b/src/coreComponents/linearAlgebra/interfaces/hypre/mgrStrategies/LagrangianContactMechanicsBubbleStabilization.hpp new file mode 100644 index 00000000000..157137b8a8a --- /dev/null +++ b/src/coreComponents/linearAlgebra/interfaces/hypre/mgrStrategies/LagrangianContactMechanicsBubbleStabilization.hpp @@ -0,0 +1,119 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * 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 LagrangianContactMechanicsBubbleStabilization.hpp + */ + +#ifndef GEOS_LINEARALGEBRA_INTERFACES_HYPREMGRLAGRANGIACONTACTMECHANICSBUBBLESTAB_HPP_ +#define GEOS_LINEARALGEBRA_INTERFACES_HYPREMGRLAGRANGIACONTACTMECHANICSBUBBLESTAB_HPP_ + +#include "linearAlgebra/interfaces/hypre/HypreMGR.hpp" + +namespace geos +{ + +namespace hypre +{ + +namespace mgr +{ + +/** + * @brief LagrangianContactMechanicsBubbleStabilization strategy + * + * Contact mechanics with face-centered lagrangian multipliers + * + * dofLabel: 0 = displacement, x-component + * dofLabel: 1 = displacement, y-component + * dofLabel: 2 = displacement, z-component + * dofLabel: 3 = displacement bubble function, x-component + * dofLabel: 4 = displacement bubble function, y-component + * dofLabel: 5 = displacement bubble function, z-component + * dofLabel: 6 = face-centered lagrange multiplier (tn) + * dofLabel: 7 = face-centered lagrange multiplier (tt1) + * dofLabel: 8 = face-centered lagrange multiplier (tt2) + * + * 2-level MGR strategy: + * Level 0: + * 1. F-points: bubbles (3,4,5), C-points: displacements + multipliers (0,1,2,6,7,8) + * 2. F-points smoother: l1jacobi + * 3. Global smoother: none + * Level 1: + * 1. F-points: multipliers (6,7,8), C-points: displacements (0,1,2) + * 2. F-points smoother: l1jacobi + * 3. Global smoother: none + * + * C-points coarse-grid/Schur complement solver: boomer AMG + + */ +class LagrangianContactMechanicsBubbleStabilization : public MGRStrategyBase< 2 > +{ +public: + + /** + * @brief Constructor. + */ + explicit LagrangianContactMechanicsBubbleStabilization( arrayView1d< int const > const & ) + : MGRStrategyBase( 9 ) + { + // Level 0: we keep all three displacements and the Lagrange Multipliers + m_labels[0] = { 0, 1, 2, 6, 7, 8 }; + // Level 1: we keep all three displacements + m_labels[1] = { 0, 1, 2 }; + + setupLabels(); + + // Level 0 + m_levelFRelaxType[0] = MGRFRelaxationType::l1jacobi; + m_levelFRelaxIters[0] = 1; + m_levelInterpType[0] = MGRInterpolationType::blockJacobi; + m_levelRestrictType[0] = MGRRestrictionType::injection; + m_levelCoarseGridMethod[0] = MGRCoarseGridMethod::galerkin; + m_levelGlobalSmootherType[0] = MGRGlobalSmootherType::none; + + // Level 1 + m_levelFRelaxType[1] = MGRFRelaxationType::l1jacobi; + m_levelFRelaxIters[1] = 1; + m_levelInterpType[1] = MGRInterpolationType::blockJacobi; + m_levelRestrictType[1] = MGRRestrictionType::injection; + m_levelCoarseGridMethod[1] = MGRCoarseGridMethod::galerkin; + m_levelGlobalSmootherType[1] = MGRGlobalSmootherType::none; + } + + /** + * @brief Setup the MGR strategy. + * @param mgrParams MGR configuration parameters + * @param precond preconditioner wrapper + * @param mgrData auxiliary MGR data + */ + void setup( LinearSolverParameters::MGR const & mgrParams, + HyprePrecWrapper & precond, + HypreMGRData & mgrData ) + { + setReduction( precond, mgrData ); + + // Configure the BoomerAMG solver used as mgr coarse solver for the displacement reduced system + setDisplacementAMG( mgrData.coarseSolver, mgrParams.separateComponents ); + } +}; + +} // namespace mgr + +} // namespace hypre + +} // namespace geos + +#endif /*GEOS_LINEARALGEBRA_INTERFACES_HYPREMGRLAGRANGIACONTACTMECHANICSBUBBLESTAB_HPP_*/ diff --git a/src/coreComponents/linearAlgebra/utilities/LinearSolverParameters.hpp b/src/coreComponents/linearAlgebra/utilities/LinearSolverParameters.hpp index 803946ac95c..4d43955a0e1 100644 --- a/src/coreComponents/linearAlgebra/utilities/LinearSolverParameters.hpp +++ b/src/coreComponents/linearAlgebra/utilities/LinearSolverParameters.hpp @@ -295,6 +295,7 @@ struct LinearSolverParameters thermalMultiphasePoromechanics, ///< thermal multiphase poromechanics with finite volume compositional multiphase flow hydrofracture, ///< hydrofracture lagrangianContactMechanics, ///< Lagrangian contact mechanics + lagrangianContactMechanicsBubbleStab, ///< Lagrangian contact mechanics with bubble stabilization solidMechanicsEmbeddedFractures ///< Embedded fractures mechanics }; @@ -387,6 +388,7 @@ ENUM_STRINGS( LinearSolverParameters::MGR::StrategyType, "thermalMultiphasePoromechanics", "hydrofracture", "lagrangianContactMechanics", + "lagrangianContactMechanicsBubbleStab", "solidMechanicsEmbeddedFractures" ); /// Declare strings associated with enumeration values. diff --git a/src/coreComponents/mesh/DomainPartition.cpp b/src/coreComponents/mesh/DomainPartition.cpp index befc10b4850..528ac6b08cf 100644 --- a/src/coreComponents/mesh/DomainPartition.cpp +++ b/src/coreComponents/mesh/DomainPartition.cpp @@ -18,6 +18,9 @@ */ #include "DomainPartition.hpp" +#include "common/format/table/TableData.hpp" +#include "common/format/table/TableFormatter.hpp" +#include "common/format/table/TableLayout.hpp" #include "common/DataTypes.hpp" #include "common/TimingMacros.hpp" @@ -322,13 +325,60 @@ void DomainPartition::addNeighbors( const unsigned int idim, void DomainPartition::outputPartitionInformation() const { + using stringutilities::addCommaSeparators; - auto numberOfEntities = []( ObjectManagerBase const & objectManager ) + struct RankMeshStats { - return std::make_pair( objectManager.getNumberOfLocalIndices(), objectManager.getNumberOfGhosts() ); + // Table rows will follow this enum ordering + enum StatIndex + { + Node = 0, Edge, Face, Elem, Count + }; + std::array< globalIndex, 4 > localCount = {}; + std::array< globalIndex, 4 > ghostCount = {}; + std::array< double, 4 > ratio = {}; + }; + + auto fillStats = []( RankMeshStats & stat, + RankMeshStats::StatIndex statIndex, + ObjectManagerBase const & objectManager ) + { + stat.localCount[ statIndex ] += objectManager.getNumberOfLocalIndices(); + stat.ghostCount[ statIndex ] += objectManager.getNumberOfGhosts(); + }; + + auto computeRatios = []( RankMeshStats & stat ) + { + for( size_t i = 0; i < RankMeshStats::StatIndex::Count; ++i ) + { + stat.ratio[i] = stat.localCount[i] + stat.ghostCount[i] == 0 ? 0 : + (double)stat.localCount[i] / (double)(stat.localCount[i] + stat.ghostCount[i]); + } + }; + + auto addLocalGhostRow = []( TableData & tableData, RankMeshStats const & stat, string_view heading ) + { + tableData.addRow( heading, + addCommaSeparators( stat.localCount[0] ), addCommaSeparators( stat.ghostCount[0] ), + addCommaSeparators( stat.localCount[0] + stat.ghostCount[0] ), + addCommaSeparators( stat.localCount[1] ), addCommaSeparators( stat.ghostCount[1] ), + addCommaSeparators( stat.localCount[1] + stat.ghostCount[1] ), + addCommaSeparators( stat.localCount[2] ), addCommaSeparators( stat.ghostCount[2] ), + addCommaSeparators( stat.localCount[2] + stat.ghostCount[2] ), + addCommaSeparators( stat.localCount[3] ), addCommaSeparators( stat.ghostCount[3] ), + addCommaSeparators( stat.localCount[3] + stat.ghostCount[3] ) ); }; - GEOS_LOG_RANK_0( "MPI Partition information:" ); + auto addSummaryRow = []( TableData & tableData, std::array< double, 4 > stats, string_view heading ) + { + tableData.addRow( heading, + CellType::MergeNext, CellType::MergeNext, stats[0], + CellType::MergeNext, CellType::MergeNext, stats[1], + CellType::MergeNext, CellType::MergeNext, stats[2], + CellType::MergeNext, CellType::MergeNext, stats[3] ); + }; + + GEOS_LOG_RANK_0( "MPI Partitioning information:" ); forMeshBodies( [&]( MeshBody const & meshBody ) { @@ -336,128 +386,117 @@ void DomainPartition::outputPartitionInformation() const { if( level!=0 ) { + // formatting is done on rank 0 + std::vector< RankMeshStats > allRankStats; + allRankStats.resize( MpiWrapper::commSize() ); + + { // Compute stats of the current rank, then gather it on rank 0 + RankMeshStats rankStats{}; + fillStats( rankStats, RankMeshStats::Node, meshLevel.getNodeManager() ); + fillStats( rankStats, RankMeshStats::Edge, meshLevel.getEdgeManager() ); + fillStats( rankStats, RankMeshStats::Face, meshLevel.getFaceManager() ); + + meshLevel.getElemManager().forElementSubRegions< CellElementSubRegion >( + [&]( CellElementSubRegion const & subRegion ) + { + fillStats( rankStats, RankMeshStats::Elem, subRegion ); + } ); - // get the number of local and ghost entities for each type - auto const [ numLocalNodes, numGhostNodes ] = numberOfEntities( meshLevel.getNodeManager() ); - real64 const nodeRatio = ( numLocalNodes + numGhostNodes ) > 0 ? real64( numLocalNodes ) / real64( numLocalNodes + numGhostNodes ) : -1.0; - auto const [ numLocalEdges, numGhostEdges ] = numberOfEntities( meshLevel.getEdgeManager() ); - real64 const edgeRatio = ( numLocalEdges + numGhostEdges ) > 0 ? real64( numLocalEdges ) / real64( numLocalEdges + numGhostEdges ) : -1.0; - auto const [ numLocalFaces, numGhostFaces ] = numberOfEntities( meshLevel.getFaceManager() ); - real64 const faceRatio = ( numLocalFaces + numGhostFaces ) > 0 ? real64( numLocalFaces ) / real64( numLocalFaces + numGhostFaces ) : -1.0; - - localIndex numLocalElems = 0; - localIndex numGhostElems = 0; - meshLevel.getElemManager().forElementSubRegions< CellElementSubRegion >( [&]( CellElementSubRegion const & subRegion ) - { - auto [ numLocalElemsInSubRegion, numGhostElemsInSubRegion ] = numberOfEntities( subRegion ); - numLocalElems += numLocalElemsInSubRegion; - numGhostElems += numGhostElemsInSubRegion; - } ); - real64 const elemRatio = ( numLocalElems + numGhostElems ) > 0 ? real64( numLocalElems ) / real64( numLocalElems + numGhostElems ) : -1.0; - - localIndex const values[8] = { numLocalNodes, numGhostNodes, numLocalEdges, numGhostEdges, numLocalFaces, numGhostFaces, numLocalElems, numGhostElems }; - localIndex minValues[8] = {0}; - localIndex maxValues[8] = {0}; - MpiWrapper::allReduce( values, minValues, 8, MPI_MIN, MPI_COMM_WORLD ); - MpiWrapper::allReduce( values, maxValues, 8, MPI_MAX, MPI_COMM_WORLD ); - localIndex const minNumLocalNodes = minValues[0]; - localIndex const maxNumLocalNodes = maxValues[0]; - localIndex const minNumGhostNodes = minValues[1]; - localIndex const maxNumGhostNodes = maxValues[1]; - localIndex const minNumLocalEdges = minValues[2]; - localIndex const maxNumLocalEdges = maxValues[2]; - localIndex const minNumGhostEdges = minValues[3]; - localIndex const maxNumGhostEdges = maxValues[3]; - localIndex const minNumLocalFaces = minValues[4]; - localIndex const maxNumLocalFaces = maxValues[4]; - localIndex const minNumGhostFaces = minValues[5]; - localIndex const maxNumGhostFaces = maxValues[5]; - localIndex const minNumLocalElems = minValues[6]; - localIndex const maxNumLocalElems = maxValues[6]; - localIndex const minNumGhostElems = minValues[7]; - localIndex const maxNumGhostElems = maxValues[7]; - - real64 const ratios[4] = { nodeRatio, edgeRatio, faceRatio, elemRatio }; - real64 minRatios[4] = {0}; - real64 maxRatios[4] = {0}; - MpiWrapper::allReduce( ratios, minRatios, 4, MPI_MIN, MPI_COMM_WORLD ); - MpiWrapper::allReduce( ratios, maxRatios, 4, MPI_MAX, MPI_COMM_WORLD ); - real64 const minNodeRatio = minRatios[0]; - real64 const maxNodeRatio = maxRatios[0]; - real64 const minEdgeRatio = minRatios[1]; - real64 const maxEdgeRatio = maxRatios[1]; - real64 const minFaceRatio = minRatios[2]; - real64 const maxFaceRatio = maxRatios[2]; - real64 const minElemRatio = minRatios[3]; - real64 const maxElemRatio = maxRatios[3]; - - GEOS_LOG_RANK_0( " MeshBody: " + meshBody.getName() + " MeshLevel: " + meshLevel.getName() + "\n" ); - GEOS_LOG_RANK_0( " |------------------------------------------------------------------------------------------------------------------------------------------------|" ); - GEOS_LOG_RANK_0( " | | Nodes | Edges | Faces | Elems |" ); - GEOS_LOG_RANK_0( " |------------------------------------------------------------------------------------------------------------------------------------------------|" ); - GEOS_LOG_RANK_0( GEOS_FMT( " |min(local/total)| {:4.2f} | {:4.2f} | {:4.2f} | {:4.2f} | ", - minNodeRatio, - minEdgeRatio, - minFaceRatio, - minElemRatio ) ); - GEOS_LOG_RANK_0( GEOS_FMT( " |max(local/total)| {:4.2f} | {:4.2f} | {:4.2f} | {:4.2f} | ", - maxNodeRatio, - maxEdgeRatio, - maxFaceRatio, - maxElemRatio ) ); - GEOS_LOG_RANK_0( " |------------------------------------------------------------------------------------------------------------------------------------------------|" ); - GEOS_LOG_RANK_0( " | Rank | local | ghost | local | ghost | local | ghost | local | ghost |" ); - GEOS_LOG_RANK_0( " |----------------|---------------|---------------|---------------|---------------|---------------|---------------|---------------|---------------|" ); - - - GEOS_LOG_RANK_0( GEOS_FMT( " | min | {:>13} | {:>13} | {:>13} | {:>13} | {:>13} | {:>13} | {:>13} | {:>13} |", - stringutilities::addCommaSeparators( minNumLocalNodes ), - stringutilities::addCommaSeparators( minNumGhostNodes ), - stringutilities::addCommaSeparators( minNumLocalEdges ), - stringutilities::addCommaSeparators( minNumGhostEdges ), - stringutilities::addCommaSeparators( minNumLocalFaces ), - stringutilities::addCommaSeparators( minNumGhostFaces ), - stringutilities::addCommaSeparators( minNumLocalElems ), - stringutilities::addCommaSeparators( minNumGhostElems ) ) ); - - GEOS_LOG_RANK_0( GEOS_FMT( " | max | {:>13} | {:>13} | {:>13} | {:>13} | {:>13} | {:>13} | {:>13} | {:>13} |", - stringutilities::addCommaSeparators( maxNumLocalNodes ), - stringutilities::addCommaSeparators( maxNumGhostNodes ), - stringutilities::addCommaSeparators( maxNumLocalEdges ), - stringutilities::addCommaSeparators( maxNumGhostEdges ), - stringutilities::addCommaSeparators( maxNumLocalFaces ), - stringutilities::addCommaSeparators( maxNumGhostFaces ), - stringutilities::addCommaSeparators( maxNumLocalElems ), - stringutilities::addCommaSeparators( maxNumGhostElems ) ) ); - - GEOS_LOG_RANK_0( " |------------------------------------------------------------------------------------------------------------------------------------------------|" ); - - // output in rank order - int const thisRank = MpiWrapper::commRank(); - for( int rank=0; rank13} | {:>13} | {:>13} | {:>13} | {:>13} | {:>13} | {:>13} | {:>13} | ", - rank, - stringutilities::addCommaSeparators( numLocalNodes ), - stringutilities::addCommaSeparators( numGhostNodes ), - stringutilities::addCommaSeparators( numLocalEdges ), - stringutilities::addCommaSeparators( numGhostEdges ), - stringutilities::addCommaSeparators( numLocalFaces ), - stringutilities::addCommaSeparators( numGhostFaces ), - stringutilities::addCommaSeparators( numLocalElems ), - stringutilities::addCommaSeparators( numGhostElems ) ) ); + if( rankId == 1 ) + tableData.addSeparator(); + + addLocalGhostRow( tableData, allRankStats[rankId], std::to_string( rankId ) ); } - MpiWrapper::barrier(); + + RankMeshStats sumStats{}; + RankMeshStats minStats{}; + RankMeshStats maxStats{}; + + for( size_t statId = 0; statId < RankMeshStats::Count; ++statId ) + { + minStats.localCount[statId] = std::numeric_limits< globalIndex >::max(); + minStats.ghostCount[statId] = std::numeric_limits< globalIndex >::max(); + minStats.ratio[statId] = std::numeric_limits< double >::max(); + + maxStats.localCount[statId] = std::numeric_limits< globalIndex >::min(); + maxStats.ghostCount[statId] = std::numeric_limits< globalIndex >::min(); + maxStats.ratio[statId] = std::numeric_limits< double >::min(); + } + + for( int rankId = 0; rankId < MpiWrapper::commSize(); ++rankId ) + { + for( size_t statId = 0; statId < RankMeshStats::Count; ++statId ) + { + sumStats.localCount[statId] += allRankStats[rankId].localCount[statId]; + sumStats.ghostCount[statId] += allRankStats[rankId].ghostCount[statId]; + + minStats.localCount[statId] = std::min( minStats.localCount[statId], allRankStats[rankId].localCount[statId] ); + minStats.ghostCount[statId] = std::min( minStats.ghostCount[statId], allRankStats[rankId].ghostCount[statId] ); + minStats.ratio[statId] = std::min( minStats.ratio[statId], allRankStats[rankId].ratio[statId] ); + + maxStats.localCount[statId] = std::max( maxStats.localCount[statId], allRankStats[rankId].localCount[statId] ); + maxStats.ghostCount[statId] = std::max( maxStats.ghostCount[statId], allRankStats[rankId].ghostCount[statId] ); + maxStats.ratio[statId] = std::max( maxStats.ratio[statId], allRankStats[rankId].ratio[statId] ); + } + } + + tableData.addSeparator(); + addLocalGhostRow( tableData, sumStats, "sum" ); + addLocalGhostRow( tableData, minStats, "min" ); + addLocalGhostRow( tableData, maxStats, "max" ); + + std::array< double, 4 > localTotalMinRatio; + std::array< double, 4 > localTotalMaxRatio; + + for( size_t statId = 0; statId < RankMeshStats::Count; ++statId ) + { + localTotalMinRatio[statId] = std::numeric_limits< double >::max(); + localTotalMaxRatio[statId] = std::numeric_limits< double >::min(); + } + + for( size_t statId = 0; statId < RankMeshStats::Count; ++statId ) + { + localTotalMinRatio[statId] = std::min( localTotalMinRatio[statId], minStats.ratio[statId] ); + localTotalMaxRatio[statId] = std::max( localTotalMinRatio[statId], maxStats.ratio[statId] ); + } + tableData.addSeparator(); + addSummaryRow( tableData, localTotalMinRatio, "min(local/total)" ); + addSummaryRow( tableData, localTotalMaxRatio, "max(local/total)" ); + + TableTextFormatter logPartition( layout ); + GEOS_LOG_RANK_0( logPartition.toString( tableData )); } - MpiWrapper::barrier(); - GEOS_LOG_RANK_0( " |------------------------------------------------------------------------------------------------------------------------------------------------|" ); + } } ); - } - - ); + } ); } diff --git a/src/coreComponents/mesh/generators/WellGeneratorBase.cpp b/src/coreComponents/mesh/generators/WellGeneratorBase.cpp index af80ec09c8a..5debc76589f 100644 --- a/src/coreComponents/mesh/generators/WellGeneratorBase.cpp +++ b/src/coreComponents/mesh/generators/WellGeneratorBase.cpp @@ -513,7 +513,7 @@ void WellGeneratorBase::mergePerforations( array1d< array1d< localIndex > > cons void WellGeneratorBase::logInternalWell() const { - TableData tableWellData; + TableData wellData; for( globalIndex iwelem = 0; iwelem < m_numElems; ++iwelem ) { std::optional< globalIndex > nextElement; @@ -529,40 +529,38 @@ void WellGeneratorBase::logInternalWell() const prevElement = m_prevElemId[iwelem][0]; } - tableWellData.addRow( iwelem, - m_elemCenterCoords[iwelem][0], - m_elemCenterCoords[iwelem][1], - m_elemCenterCoords[iwelem][2], - prevElement, - nextElement ); + wellData.addRow( iwelem, + m_elemCenterCoords[iwelem][0], + m_elemCenterCoords[iwelem][1], + m_elemCenterCoords[iwelem][2], + prevElement, + nextElement ); } - string const wellTitle = GEOS_FMT( "Well '{}' Element Table", getName() ); - TableLayout const tableWellLayout = TableLayout( { - TableLayout::ColumnParam{"Element no.", TableLayout::Alignment::right}, - TableLayout::ColumnParam{"CoordX", TableLayout::Alignment::right}, - TableLayout::ColumnParam{"CoordY", TableLayout::Alignment::right}, - TableLayout::ColumnParam{"CoordZ", TableLayout::Alignment::right}, - TableLayout::ColumnParam{"Prev\nElement", TableLayout::Alignment::right}, - TableLayout::ColumnParam{"Next\nElement", TableLayout::Alignment::right}, - }, wellTitle ); - - TableTextFormatter const tableFormatter( tableWellLayout ); - GEOS_LOG_RANK_0( tableFormatter.toString( tableWellData )); + TableLayout const wellLayout( GEOS_FMT( "Well '{}' Element Table", getName() ), + {"Element no.", + "CoordX", + "CoordY", + "CoordZ", + "Prev\nElement", + "Next\nElement"} ); + + TableTextFormatter const wellFormatter( wellLayout ); + GEOS_LOG_RANK_0( wellFormatter.toString( wellData )); } void WellGeneratorBase::logPerforationTable() const { - TableData tablePerfoData; + TableData dataPerforation; for( globalIndex iperf = 0; iperf < m_numPerforations; ++iperf ) { - tablePerfoData.addRow( iperf, m_perfCoords[iperf], m_perfElemId[iperf] ); + dataPerforation.addRow( iperf, m_perfCoords[iperf], m_perfElemId[iperf] ); } - TableLayout const tableLayoutPerfo ( {"Perforation no.", "Coordinates", "Well element no."}, - GEOS_FMT( "Well '{}' Perforation Table", getName() ) ); - TableTextFormatter const tablePerfoLog( tableLayoutPerfo ); - GEOS_LOG_RANK_0( tablePerfoLog.toString( tablePerfoData )); + TableLayout const layoutPerforation ( GEOS_FMT( "Well '{}' Perforation Table", getName()), + { "Perforation no.", "Coordinates", "Well element no." } ); + TableTextFormatter const logPerforation( layoutPerforation ); + GEOS_LOG_RANK_0( logPerforation.toString( dataPerforation )); } } diff --git a/src/coreComponents/physicsSolvers/LinearSolverParameters.cpp b/src/coreComponents/physicsSolvers/LinearSolverParameters.cpp index a5f5b402760..cdf39353527 100644 --- a/src/coreComponents/physicsSolvers/LinearSolverParameters.cpp +++ b/src/coreComponents/physicsSolvers/LinearSolverParameters.cpp @@ -341,10 +341,8 @@ void LinearSolverParametersInput::print() tableData.addRow( "ILU(T) threshold factor", m_parameters.ifact.threshold ); } } - TableLayout const tableLayout = TableLayout( { - TableLayout::ColumnParam{"Parameter", TableLayout::Alignment::left}, - TableLayout::ColumnParam{"Value", TableLayout::Alignment::left}, - }, GEOS_FMT( "{}: linear solver", getParent().getName() ) ); + TableLayout const tableLayout = TableLayout( GEOS_FMT( "{}: linear solver", getParent().getName() ), + { "Parameter", "Value" } ); TableTextFormatter const tableFormatter( tableLayout ); GEOS_LOG_RANK_0( tableFormatter.toString( tableData )); } diff --git a/src/coreComponents/physicsSolvers/NonlinearSolverParameters.cpp b/src/coreComponents/physicsSolvers/NonlinearSolverParameters.cpp index 86563d4be24..dd9c0d58a7c 100644 --- a/src/coreComponents/physicsSolvers/NonlinearSolverParameters.cpp +++ b/src/coreComponents/physicsSolvers/NonlinearSolverParameters.cpp @@ -235,10 +235,8 @@ void NonlinearSolverParameters::print() const tableData.addRow( "Sequential convergence criterion", m_sequentialConvergenceCriterion ); tableData.addRow( "Subcycling", m_subcyclingOption ); } - TableLayout const tableLayout = TableLayout( { - TableLayout::ColumnParam{"Parameter", TableLayout::Alignment::left}, - TableLayout::ColumnParam{"Value", TableLayout::Alignment::left}, - }, GEOS_FMT( "{}: nonlinear solver", getParent().getName() ) ); + TableLayout const tableLayout = TableLayout( GEOS_FMT( "{}: nonlinear solver", getParent().getName() ), + { "Parameter", "Value" } ); TableTextFormatter const tableFormatter( tableLayout ); GEOS_LOG_RANK_0( tableFormatter.toString( tableData )); } diff --git a/src/coreComponents/physicsSolvers/contact/ContactFields.hpp b/src/coreComponents/physicsSolvers/contact/ContactFields.hpp index df37934c0cd..2c61762010c 100644 --- a/src/coreComponents/physicsSolvers/contact/ContactFields.hpp +++ b/src/coreComponents/physicsSolvers/contact/ContactFields.hpp @@ -75,7 +75,7 @@ DECLARE_FIELD( dispJump, "Displacement jump vector in the local reference system" ); DECLARE_FIELD( dispJump_n, - "displacementJump", + "displacementJump_n", array2d< real64 >, 0, NOPLOT, @@ -98,6 +98,14 @@ DECLARE_FIELD( deltaSlip, WRITE_AND_READ, "Slip increment" ); +DECLARE_FIELD( deltaSlip_n, + "deltaSlip_n", + array2d< real64 >, + 0.0, + NOPLOT, + WRITE_AND_READ, + "Initial slip increment at this time step" ); + DECLARE_FIELD( deltaDispJump, "deltaDisplacementJump", array2d< real64 >, diff --git a/src/coreComponents/physicsSolvers/contact/ContactSolverBase.cpp b/src/coreComponents/physicsSolvers/contact/ContactSolverBase.cpp index 6fecd60e93c..c1c886aa3dd 100644 --- a/src/coreComponents/physicsSolvers/contact/ContactSolverBase.cpp +++ b/src/coreComponents/physicsSolvers/contact/ContactSolverBase.cpp @@ -26,6 +26,7 @@ #include "physicsSolvers/contact/LogLevelsInfo.hpp" #include "physicsSolvers/solidMechanics/SolidMechanicsLagrangianFEM.hpp" #include "common/GEOS_RAJA_Interface.hpp" +#include "fieldSpecification/FieldSpecificationManager.hpp" namespace geos { @@ -66,6 +67,7 @@ void ContactSolverBase::registerDataOnMesh( dataRepository::Group & meshBodies ) forFractureRegionOnMeshTargets( meshBodies, [&] ( SurfaceElementRegion & fractureRegion ) { string const labels[3] = { "normal", "tangent1", "tangent2" }; + string const labelsTangent[2] = { "tangent1", "tangent2" }; fractureRegion.forElementSubRegions< SurfaceElementSubRegion >( [&]( SurfaceElementSubRegion & subRegion ) { @@ -83,6 +85,10 @@ void ContactSolverBase::registerDataOnMesh( dataRepository::Group & meshBodies ) setDimLabels( 1, labels ). reference().resizeDimension< 1 >( 3 ); + subRegion.registerField< fields::contact::dispJump_n >( getName() ). + setDimLabels( 1, labels ). + reference().resizeDimension< 1 >( 3 ); + subRegion.registerField< fields::contact::traction >( getName() ). setDimLabels( 1, labels ). reference().resizeDimension< 1 >( 3 ); @@ -93,7 +99,11 @@ void ContactSolverBase::registerDataOnMesh( dataRepository::Group & meshBodies ) subRegion.registerField< fields::contact::slip >( getName() ); - subRegion.registerField< fields::contact::deltaSlip >( getName() ); + subRegion.registerField< fields::contact::deltaSlip >( getName() ). + setDimLabels( 1, labelsTangent ).reference().resizeDimension< 1 >( 2 ); + + subRegion.registerField< fields::contact::deltaSlip_n >( this->getName() ). + setDimLabels( 1, labelsTangent ).reference().resizeDimension< 1 >( 2 ); } ); } ); diff --git a/src/coreComponents/physicsSolvers/contact/SolidMechanicsLagrangeContactBubbleStab.cpp b/src/coreComponents/physicsSolvers/contact/SolidMechanicsLagrangeContactBubbleStab.cpp index 2d1f139362b..7c2543a3a08 100644 --- a/src/coreComponents/physicsSolvers/contact/SolidMechanicsLagrangeContactBubbleStab.cpp +++ b/src/coreComponents/physicsSolvers/contact/SolidMechanicsLagrangeContactBubbleStab.cpp @@ -46,6 +46,10 @@ SolidMechanicsLagrangeContactBubbleStab::SolidMechanicsLagrangeContactBubbleStab m_faceTypeToFiniteElements["Quadrilateral"] = std::make_unique< finiteElement::H1_QuadrilateralFace_Lagrange1_GaussLegendre2 >(); m_faceTypeToFiniteElements["Triangle"] = std::make_unique< finiteElement::H1_TriangleFace_Lagrange1_Gauss1 >(); + LinearSolverParameters & linSolParams = m_linearSolverParameters.get(); + linSolParams.mgr.strategy = LinearSolverParameters::MGR::StrategyType::lagrangianContactMechanicsBubbleStab; + linSolParams.mgr.separateComponents = true; + linSolParams.dofsPerNode = 3; } SolidMechanicsLagrangeContactBubbleStab::~SolidMechanicsLagrangeContactBubbleStab() @@ -249,6 +253,7 @@ void SolidMechanicsLagrangeContactBubbleStab::setupSystem( DomainPartition & dom solution.create( dofManager.numLocalDofs(), MPI_COMM_GEOS ); computeRotationMatrices( domain ); + } void SolidMechanicsLagrangeContactBubbleStab::computeRotationMatrices( DomainPartition & domain ) const @@ -324,10 +329,6 @@ void SolidMechanicsLagrangeContactBubbleStab::assembleSystem( real64 const time, assembleStabilization( dt, domain, dofManager, localMatrix, localRhs ); assembleContact( dt, domain, dofManager, localMatrix, localRhs ); - - // parallel_matrix.create( localMatrix.toViewConst(), dofManager.numLocalDofs(), MPI_COMM_GEOS ); - // parallel_matrix.write("newMatrix.mtx"); - // std::cout << localRhs << std::endl; } void SolidMechanicsLagrangeContactBubbleStab::assembleStabilization( real64 const dt, diff --git a/src/coreComponents/physicsSolvers/fluidFlow/CompositionalMultiphaseStatistics.cpp b/src/coreComponents/physicsSolvers/fluidFlow/CompositionalMultiphaseStatistics.cpp index 7f44f5f5c16..842c333bafe 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/CompositionalMultiphaseStatistics.cpp +++ b/src/coreComponents/physicsSolvers/fluidFlow/CompositionalMultiphaseStatistics.cpp @@ -29,6 +29,9 @@ #include "physicsSolvers/fluidFlow/FlowSolverBaseFields.hpp" #include "physicsSolvers/fluidFlow/kernels/compositional/StatisticsKernel.hpp" #include "physicsSolvers/fluidFlow/LogLevelsInfo.hpp" +#include "common/format/table/TableData.hpp" +#include "common/format/table/TableFormatter.hpp" +#include "common/format/table/TableLayout.hpp" namespace geos @@ -104,41 +107,75 @@ void CompositionalMultiphaseStatistics::registerDataOnMesh( Group & meshBodies ) region.registerWrapper< RegionStatistics >( viewKeyStruct::regionStatisticsString() ). setRestartFlags( RestartFlags::NO_WRITE ); region.excludeWrappersFromPacking( { viewKeyStruct::regionStatisticsString() } ); - RegionStatistics & regionStatistics = region.getReference< RegionStatistics >( viewKeyStruct::regionStatisticsString() ); + RegionStatistics & stats = region.getReference< RegionStatistics >( viewKeyStruct::regionStatisticsString() ); - regionStatistics.phasePoreVolume.resizeDimension< 0 >( numPhases ); - regionStatistics.phaseMass.resizeDimension< 0 >( numPhases ); - regionStatistics.trappedPhaseMass.resizeDimension< 0 >( numPhases ); - regionStatistics.immobilePhaseMass.resizeDimension< 0 >( numPhases ); - regionStatistics.componentMass.resizeDimension< 0, 1 >( numPhases, numComps ); + stats.phasePoreVolume.resizeDimension< 0 >( numPhases ); + stats.phaseMass.resizeDimension< 0 >( numPhases ); + stats.trappedPhaseMass.resizeDimension< 0 >( numPhases ); + stats.immobilePhaseMass.resizeDimension< 0 >( numPhases ); + stats.componentMass.resizeDimension< 0, 1 >( numPhases, numComps ); - // write output header if( m_writeCSV > 0 && MpiWrapper::commRank() == 0 ) { - std::ofstream outputFile( m_outputDir + "/" + regionNames[i] + ".csv" ); - string_view massUnit = units::getSymbol( m_solver->getMassUnit() ); - outputFile << - "Time [s],Min pressure [Pa],Average pressure [Pa],Max pressure [Pa],Min delta pressure [Pa],Max delta pressure [Pa]," << - "Min temperature [Pa],Average temperature [Pa],Max temperature [Pa],Total dynamic pore volume [rm^3]"; - for( integer ip = 0; ip < numPhases; ++ip ) - outputFile << ",Phase " << ip << " dynamic pore volume [rm^3]"; - for( integer ip = 0; ip < numPhases; ++ip ) - outputFile << ",Phase " << ip << " mass [" << massUnit << "]"; - for( integer ip = 0; ip < numPhases; ++ip ) - outputFile << ",Trapped phase " << ip << " mass (metric 1) [" << massUnit << "]"; - for( integer ip = 0; ip < numPhases; ++ip ) - outputFile << ",Non-trapped phase " << ip << " mass (metric 1) [" << massUnit << "]"; - for( integer ip = 0; ip < numPhases; ++ip ) - outputFile << ",Immobile phase " << ip << " mass (metric 2) [" << massUnit << "]"; - for( integer ip = 0; ip < numPhases; ++ip ) - outputFile << ",Mobile phase " << ip << " mass (metric 2) [" << massUnit << "]"; - for( integer ip = 0; ip < numPhases; ++ip ) + auto addStatsValue = []( std::ostringstream & pstatsLayout, TableLayout & ptableLayout, + string const & description, string_view pmassUnit, + integer pnumPhases, integer pnumComps = 0 ) { - for( integer ic = 0; ic < numComps; ++ic ) - outputFile << ",Component " << ic << " (phase " << ip << ") mass [" << massUnit << "]"; - } - outputFile << std::endl; - outputFile.close(); + for( int ip = 0; ip < pnumPhases; ++ip ) + { + if( pnumComps == 0 ) + { + pstatsLayout << description << " (phase " << ip << ") [" << pmassUnit << "]"; + } + else + { + for( int ic = 0; ic < pnumComps; ++ic ) + { + pstatsLayout << "Component " << ic << " (phase " << ip << ") mass [" << pmassUnit << "]"; + if( ic == 0 ) + { + pstatsLayout << ","; + } + } + } + if( ip == 0 ) + { + pstatsLayout << ","; + } + } + + ptableLayout.addToColumns( pstatsLayout.str()); + pstatsLayout.str( "" ); + }; + + string_view massUnit = units::getSymbol( m_solver->getMassUnit() ); + + TableLayout tableLayout( { + TableLayout::Column().setName( "Time [s]" ), + TableLayout::Column().setName( "Min pressure [Pa]" ), + TableLayout::Column().setName( "Average pressure [Pa]" ), + TableLayout::Column().setName( "Max pressure [Pa]" ), + TableLayout::Column().setName( "Min delta pressure [Pa]" ), + TableLayout::Column().setName( "Max delta pressure [Pa]" ), + TableLayout::Column().setName( "Min temperature [Pa]" ), + TableLayout::Column().setName( "Average temperature [Pa]" ), + TableLayout::Column().setName( "Max temperature [Pa]" ), + TableLayout::Column().setName( "Total dynamic pore volume [rm^3]" ), + TableLayout::Column().setName( GEOS_FMT( "Phase mass [{}] dynamic pore volume [rm^3]", massUnit ) ), + } ); + + std::ostringstream statsLayout; + addStatsValue( statsLayout, tableLayout, "Phase dynamic pore volume", massUnit, numPhases ); + addStatsValue( statsLayout, tableLayout, "Phase", massUnit, numPhases ); + addStatsValue( statsLayout, tableLayout, "Trapped phase mass (metric 1)", massUnit, numPhases ); + addStatsValue( statsLayout, tableLayout, "Non-trapped phase mass (metric 1)", massUnit, numPhases ); + addStatsValue( statsLayout, tableLayout, "Immobile phase mass (metric 2)", massUnit, numPhases ); + addStatsValue( statsLayout, tableLayout, "Mobile phase mass (metric 2)", massUnit, numPhases ); + addStatsValue( statsLayout, tableLayout, "Component", massUnit, numPhases, numComps ); + + std::ofstream outputFile( m_outputDir + "/" + regionNames[i] + ".csv" ); + TableCSVFormatter csvFormatter( tableLayout ); + outputFile << csvFormatter.headerToString(); } } } @@ -192,27 +229,27 @@ void CompositionalMultiphaseStatistics::computeRegionStatistics( real64 const ti for( integer i = 0; i < regionNames.size(); ++i ) { ElementRegionBase & region = elemManager.getRegion( regionNames[i] ); - RegionStatistics & regionStatistics = region.getReference< RegionStatistics >( viewKeyStruct::regionStatisticsString() ); + RegionStatistics & stats = region.getReference< RegionStatistics >( viewKeyStruct::regionStatisticsString() ); - regionStatistics.averagePressure = 0.0; - regionStatistics.maxPressure = 0.0; - regionStatistics.minPressure = LvArray::NumericLimits< real64 >::max; + stats.averagePressure = 0.0; + stats.maxPressure = 0.0; + stats.minPressure = LvArray::NumericLimits< real64 >::max; - regionStatistics.maxDeltaPressure = -LvArray::NumericLimits< real64 >::max; - regionStatistics.minDeltaPressure = LvArray::NumericLimits< real64 >::max; + stats.maxDeltaPressure = -LvArray::NumericLimits< real64 >::max; + stats.minDeltaPressure = LvArray::NumericLimits< real64 >::max; - regionStatistics.averageTemperature = 0.0; - regionStatistics.maxTemperature = 0.0; - regionStatistics.minTemperature = LvArray::NumericLimits< real64 >::max; + stats.averageTemperature = 0.0; + stats.maxTemperature = 0.0; + stats.minTemperature = LvArray::NumericLimits< real64 >::max; - regionStatistics.totalPoreVolume = 0.0; - regionStatistics.totalUncompactedPoreVolume = 0.0; - regionStatistics.phasePoreVolume.setValues< serialPolicy >( 0.0 ); + stats.totalPoreVolume = 0.0; + stats.totalUncompactedPoreVolume = 0.0; + stats.phasePoreVolume.setValues< serialPolicy >( 0.0 ); - regionStatistics.phaseMass.setValues< serialPolicy >( 0.0 ); - regionStatistics.trappedPhaseMass.setValues< serialPolicy >( 0.0 ); - regionStatistics.immobilePhaseMass.setValues< serialPolicy >( 0.0 ); - regionStatistics.componentMass.setValues< serialPolicy >( 0.0 ); + stats.phaseMass.setValues< serialPolicy >( 0.0 ); + stats.trappedPhaseMass.setValues< serialPolicy >( 0.0 ); + stats.immobilePhaseMass.setValues< serialPolicy >( 0.0 ); + stats.componentMass.setValues< serialPolicy >( 0.0 ); } // Step 2: increment the average/min/max quantities for all the subRegions @@ -297,48 +334,48 @@ void CompositionalMultiphaseStatistics::computeRegionStatistics( real64 const ti subRegionComponentMass.toView() ); ElementRegionBase & region = elemManager.getRegion( ElementRegionBase::getParentRegion( subRegion ).getName() ); - RegionStatistics & regionStatistics = region.getReference< RegionStatistics >( viewKeyStruct::regionStatisticsString() ); + RegionStatistics & stats = region.getReference< RegionStatistics >( viewKeyStruct::regionStatisticsString() ); - regionStatistics.averagePressure += subRegionAvgPresNumerator; - if( subRegionMinPres < regionStatistics.minPressure ) + stats.averagePressure += subRegionAvgPresNumerator; + if( subRegionMinPres < stats.minPressure ) { - regionStatistics.minPressure = subRegionMinPres; + stats.minPressure = subRegionMinPres; } - if( subRegionMaxPres > regionStatistics.maxPressure ) + if( subRegionMaxPres > stats.maxPressure ) { - regionStatistics.maxPressure = subRegionMaxPres; + stats.maxPressure = subRegionMaxPres; } - if( subRegionMinDeltaPres < regionStatistics.minDeltaPressure ) + if( subRegionMinDeltaPres < stats.minDeltaPressure ) { - regionStatistics.minDeltaPressure = subRegionMinDeltaPres; + stats.minDeltaPressure = subRegionMinDeltaPres; } - if( subRegionMaxDeltaPres > regionStatistics.maxDeltaPressure ) + if( subRegionMaxDeltaPres > stats.maxDeltaPressure ) { - regionStatistics.maxDeltaPressure = subRegionMaxDeltaPres; + stats.maxDeltaPressure = subRegionMaxDeltaPres; } - regionStatistics.averageTemperature += subRegionAvgTempNumerator; - if( subRegionMinTemp < regionStatistics.minTemperature ) + stats.averageTemperature += subRegionAvgTempNumerator; + if( subRegionMinTemp < stats.minTemperature ) { - regionStatistics.minTemperature = subRegionMinTemp; + stats.minTemperature = subRegionMinTemp; } - if( subRegionMaxTemp > regionStatistics.maxTemperature ) + if( subRegionMaxTemp > stats.maxTemperature ) { - regionStatistics.maxTemperature = subRegionMaxTemp; + stats.maxTemperature = subRegionMaxTemp; } - regionStatistics.totalUncompactedPoreVolume += subRegionTotalUncompactedPoreVol; + stats.totalUncompactedPoreVolume += subRegionTotalUncompactedPoreVol; for( integer ip = 0; ip < numPhases; ++ip ) { - regionStatistics.phasePoreVolume[ip] += subRegionPhaseDynamicPoreVol[ip]; - regionStatistics.phaseMass[ip] += subRegionPhaseMass[ip]; - regionStatistics.trappedPhaseMass[ip] += subRegionTrappedPhaseMass[ip]; - regionStatistics.immobilePhaseMass[ip] += subRegionImmobilePhaseMass[ip]; + stats.phasePoreVolume[ip] += subRegionPhaseDynamicPoreVol[ip]; + stats.phaseMass[ip] += subRegionPhaseMass[ip]; + stats.trappedPhaseMass[ip] += subRegionTrappedPhaseMass[ip]; + stats.immobilePhaseMass[ip] += subRegionImmobilePhaseMass[ip]; for( integer ic = 0; ic < numComps; ++ic ) { - regionStatistics.componentMass[ip][ic] += subRegionComponentMass[ip][ic]; + stats.componentMass[ip][ic] += subRegionComponentMass[ip][ic]; } } @@ -348,40 +385,40 @@ void CompositionalMultiphaseStatistics::computeRegionStatistics( real64 const ti for( integer i = 0; i < regionNames.size(); ++i ) { ElementRegionBase & region = elemManager.getRegion( regionNames[i] ); - RegionStatistics & regionStatistics = region.getReference< RegionStatistics >( viewKeyStruct::regionStatisticsString() ); - - regionStatistics.minPressure = MpiWrapper::min( regionStatistics.minPressure ); - regionStatistics.maxPressure = MpiWrapper::max( regionStatistics.maxPressure ); - regionStatistics.minDeltaPressure = MpiWrapper::min( regionStatistics.minDeltaPressure ); - regionStatistics.maxDeltaPressure = MpiWrapper::max( regionStatistics.maxDeltaPressure ); - regionStatistics.minTemperature = MpiWrapper::min( regionStatistics.minTemperature ); - regionStatistics.maxTemperature = MpiWrapper::max( regionStatistics.maxTemperature ); - regionStatistics.totalUncompactedPoreVolume = MpiWrapper::sum( regionStatistics.totalUncompactedPoreVolume ); - regionStatistics.totalPoreVolume = 0.0; + RegionStatistics & stats = region.getReference< RegionStatistics >( viewKeyStruct::regionStatisticsString() ); + + stats.minPressure = MpiWrapper::min( stats.minPressure ); + stats.maxPressure = MpiWrapper::max( stats.maxPressure ); + stats.minDeltaPressure = MpiWrapper::min( stats.minDeltaPressure ); + stats.maxDeltaPressure = MpiWrapper::max( stats.maxDeltaPressure ); + stats.minTemperature = MpiWrapper::min( stats.minTemperature ); + stats.maxTemperature = MpiWrapper::max( stats.maxTemperature ); + stats.totalUncompactedPoreVolume = MpiWrapper::sum( stats.totalUncompactedPoreVolume ); + stats.totalPoreVolume = 0.0; for( integer ip = 0; ip < numPhases; ++ip ) { - regionStatistics.phasePoreVolume[ip] = MpiWrapper::sum( regionStatistics.phasePoreVolume[ip] ); - regionStatistics.phaseMass[ip] = MpiWrapper::sum( regionStatistics.phaseMass[ip] ); - regionStatistics.trappedPhaseMass[ip] = MpiWrapper::sum( regionStatistics.trappedPhaseMass[ip] ); - regionStatistics.immobilePhaseMass[ip] = MpiWrapper::sum( regionStatistics.immobilePhaseMass[ip] ); - regionStatistics.totalPoreVolume += regionStatistics.phasePoreVolume[ip]; + stats.phasePoreVolume[ip] = MpiWrapper::sum( stats.phasePoreVolume[ip] ); + stats.phaseMass[ip] = MpiWrapper::sum( stats.phaseMass[ip] ); + stats.trappedPhaseMass[ip] = MpiWrapper::sum( stats.trappedPhaseMass[ip] ); + stats.immobilePhaseMass[ip] = MpiWrapper::sum( stats.immobilePhaseMass[ip] ); + stats.totalPoreVolume += stats.phasePoreVolume[ip]; for( integer ic = 0; ic < numComps; ++ic ) { - regionStatistics.componentMass[ip][ic] = MpiWrapper::sum( regionStatistics.componentMass[ip][ic] ); + stats.componentMass[ip][ic] = MpiWrapper::sum( stats.componentMass[ip][ic] ); } } - regionStatistics.averagePressure = MpiWrapper::sum( regionStatistics.averagePressure ); - regionStatistics.averageTemperature = MpiWrapper::sum( regionStatistics.averageTemperature ); - if( regionStatistics.totalUncompactedPoreVolume > 0 ) + stats.averagePressure = MpiWrapper::sum( stats.averagePressure ); + stats.averageTemperature = MpiWrapper::sum( stats.averageTemperature ); + if( stats.totalUncompactedPoreVolume > 0 ) { - float invTotalUncompactedPoreVolume = 1.0 / regionStatistics.totalUncompactedPoreVolume; - regionStatistics.averagePressure *= invTotalUncompactedPoreVolume; - regionStatistics.averageTemperature *= invTotalUncompactedPoreVolume; + float invTotalUncompactedPoreVolume = 1.0 / stats.totalUncompactedPoreVolume; + stats.averagePressure *= invTotalUncompactedPoreVolume; + stats.averageTemperature *= invTotalUncompactedPoreVolume; } else { - regionStatistics.averagePressure = 0.0; - regionStatistics.averageTemperature = 0.0; + stats.averagePressure = 0.0; + stats.averageTemperature = 0.0; GEOS_LOG_LEVEL_INFO_RANK_0( logInfo::Statistics, GEOS_FMT( "{}, {}: Cannot compute average pressure because region pore volume is zero.", getName(), regionNames[i] ) ); } @@ -392,79 +429,108 @@ void CompositionalMultiphaseStatistics::computeRegionStatistics( real64 const ti array1d< real64 > mobilePhaseMass( numPhases ); for( integer ip = 0; ip < numPhases; ++ip ) { - nonTrappedPhaseMass[ip] = regionStatistics.phaseMass[ip] - regionStatistics.trappedPhaseMass[ip]; - mobilePhaseMass[ip] = regionStatistics.phaseMass[ip] - regionStatistics.immobilePhaseMass[ip]; + nonTrappedPhaseMass[ip] = stats.phaseMass[ip] - stats.trappedPhaseMass[ip]; + mobilePhaseMass[ip] = stats.phaseMass[ip] - stats.immobilePhaseMass[ip]; } string_view massUnit = units::getSymbol( m_solver->getMassUnit() ); - string statPrefix = GEOS_FMT( "{}, {} (time {} s):", getName(), regionNames[i], time ); - GEOS_LOG_LEVEL_INFO_RANK_0( logInfo::Statistics, - GEOS_FMT( "{} Pressure (min, average, max): {}, {}, {} Pa", - statPrefix, regionStatistics.minPressure, regionStatistics.averagePressure, regionStatistics.maxPressure ) ); - GEOS_LOG_LEVEL_INFO_RANK_0( logInfo::Statistics, - GEOS_FMT( "{} Delta pressure (min, max): {}, {} Pa", - statPrefix, regionStatistics.minDeltaPressure, regionStatistics.maxDeltaPressure ) ); - GEOS_LOG_LEVEL_INFO_RANK_0( logInfo::Statistics, - GEOS_FMT( "{} Temperature (min, average, max): {}, {}, {} K", - statPrefix, regionStatistics.minTemperature, regionStatistics.averageTemperature, - regionStatistics.maxTemperature ) ); - GEOS_LOG_LEVEL_INFO_RANK_0( logInfo::Statistics, - GEOS_FMT( "{} Total dynamic pore volume: {} rm^3", - statPrefix, regionStatistics.totalPoreVolume ) ); - GEOS_LOG_LEVEL_INFO_RANK_0( logInfo::Statistics, - GEOS_FMT( "{} Phase dynamic pore volume: {} rm^3", - statPrefix, regionStatistics.phasePoreVolume ) ); - GEOS_LOG_LEVEL_INFO_RANK_0( logInfo::Statistics, - GEOS_FMT( "{} Phase mass: {} {}", - statPrefix, regionStatistics.phaseMass, massUnit ) ); - - // metric 1: trapping computed with the Land trapping coefficient - GEOS_LOG_LEVEL_INFO_RANK_0( logInfo::Statistics, - GEOS_FMT( "{} Trapped phase mass (metric 1): {} {}", - statPrefix, regionStatistics.trappedPhaseMass, massUnit ) ); - GEOS_LOG_LEVEL_INFO_RANK_0( logInfo::Statistics, - GEOS_FMT( "{} Non-trapped phase mass (metric 1): {} {}", - statPrefix, nonTrappedPhaseMass, massUnit ) ); - - // metric 2: immobile phase mass computed with a threshold on relative permeability - GEOS_LOG_LEVEL_INFO_RANK_0( logInfo::Statistics, - GEOS_FMT( "{} Immobile phase mass (metric 2): {} {}", - statPrefix, regionStatistics.immobilePhaseMass, massUnit ) ); - GEOS_LOG_LEVEL_INFO_RANK_0( logInfo::Statistics, - GEOS_FMT( "{} Mobile phase mass (metric 2): {} {}", - statPrefix, mobilePhaseMass, massUnit ) ); - - GEOS_LOG_LEVEL_INFO_RANK_0( logInfo::Statistics, - GEOS_FMT( "{} Component mass: {} {}", - statPrefix, regionStatistics.componentMass, massUnit ) ); - if( m_writeCSV > 0 && MpiWrapper::commRank() == 0 ) + std::vector< string > phaseCompName; + phaseCompName.reserve( numPhases*numComps ); + std::vector< string > massValues; + phaseCompName.reserve( numPhases*numComps ); + + ConstitutiveManager const & constitutiveManager = this->getGroupByPath< ConstitutiveManager >( "/Problem/domain/Constitutive" ); + MultiFluidBase const & fluid = constitutiveManager.getGroup< MultiFluidBase >( m_solver->referenceFluidModelName() ); + auto const phaseNames = fluid.phaseNames(); + auto const componentNames = fluid.componentNames(); + for( integer ip = 0; ip < numPhases; ++ip ) { - std::ofstream outputFile( m_outputDir + "/" + regionNames[i] + ".csv", std::ios_base::app ); - outputFile << time << "," << regionStatistics.minPressure << "," << regionStatistics.averagePressure << "," << regionStatistics.maxPressure << "," << - regionStatistics.minDeltaPressure << "," << regionStatistics.maxDeltaPressure << "," << regionStatistics.minTemperature << "," << - regionStatistics.averageTemperature << "," << regionStatistics.maxTemperature << "," << regionStatistics.totalPoreVolume; - for( integer ip = 0; ip < numPhases; ++ip ) - outputFile << "," << regionStatistics.phasePoreVolume[ip]; - for( integer ip = 0; ip < numPhases; ++ip ) - outputFile << "," << regionStatistics.phaseMass[ip]; - for( integer ip = 0; ip < numPhases; ++ip ) - outputFile << "," << regionStatistics.trappedPhaseMass[ip]; - for( integer ip = 0; ip < numPhases; ++ip ) - outputFile << "," << nonTrappedPhaseMass[ip]; - for( integer ip = 0; ip < numPhases; ++ip ) - outputFile << "," << regionStatistics.immobilePhaseMass[ip]; - for( integer ip = 0; ip < numPhases; ++ip ) - outputFile << "," << mobilePhaseMass[ip]; - for( integer ip = 0; ip < numPhases; ++ip ) + for( integer ic = 0; ic < numComps; ++ic ) { - for( integer ic = 0; ic < numComps; ++ic ) - outputFile << "," << regionStatistics.componentMass[ip][ic]; + std::stringstream ss; + ss << phaseNames[ip]<< ", " < 0 && MpiWrapper::commRank() == 0 ) + { + TableData tableData; + tableData.addRow( time, stats.minPressure, stats.averagePressure, stats.maxPressure, stats.minDeltaPressure, stats.maxDeltaPressure, + stats.minTemperature, stats.averageTemperature, stats.maxTemperature, stats.totalPoreVolume, + stringutilities::joinLambda( stats.phasePoreVolume, "\n", []( auto data ) { return data[0]; } ), + stringutilities::joinLambda( stats.phaseMass, "\n", []( auto data ) { return data[0]; } ), + stringutilities::joinLambda( stats.trappedPhaseMass, "\n", []( auto value ) { return value[0]; } ), + stringutilities::joinLambda( nonTrappedPhaseMass, "\n", []( auto value ) { return value[0]; } ), + stringutilities::joinLambda( stats.immobilePhaseMass, "\n", []( auto value ) { return value[0]; } ), + stringutilities::joinLambda( mobilePhaseMass, "\n", []( auto value ) { return value[0]; } ), + stringutilities::join( massValues, '\n' ) ); + + std::ofstream outputFile( m_outputDir + "/" + regionNames[i] + ".csv", std::ios_base::app ); + TableCSVFormatter const csvOutput; + outputFile << csvOutput.dataToString( tableData ); outputFile.close(); } } + } void CompositionalMultiphaseStatistics::computeCFLNumbers( real64 const time, diff --git a/src/coreComponents/physicsSolvers/fluidFlow/CompositionalMultiphaseStatistics.hpp b/src/coreComponents/physicsSolvers/fluidFlow/CompositionalMultiphaseStatistics.hpp index bee1a2468a4..f8f9afafa90 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/CompositionalMultiphaseStatistics.hpp +++ b/src/coreComponents/physicsSolvers/fluidFlow/CompositionalMultiphaseStatistics.hpp @@ -118,8 +118,6 @@ class CompositionalMultiphaseStatistics : public FieldStatisticsBase< Compositio array1d< real64 > immobilePhaseMass; /// region component mass array2d< real64 > componentMass; - - }; /** diff --git a/src/coreComponents/physicsSolvers/fluidFlow/LogLevelsInfo.hpp b/src/coreComponents/physicsSolvers/fluidFlow/LogLevelsInfo.hpp index c42ac620388..0f8c9eb60c3 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/LogLevelsInfo.hpp +++ b/src/coreComponents/physicsSolvers/fluidFlow/LogLevelsInfo.hpp @@ -48,6 +48,25 @@ struct Statistics static constexpr std::string_view getDescription() { return "Print statistics"; } }; +struct AggregatedSourceFluxStats +{ + static constexpr int getMinLogLevel() { return 1; } + static constexpr std::string_view getDescription() { return "Print aggregated statistics of all source fluxes in a mesh"; } +}; + +struct DetailedSourceFluxStats +{ + static constexpr int getMinLogLevel() { return 2; } + static constexpr std::string_view getDescription() { return "Print statistics for each source flux in a mesh"; } +}; + +struct DetailedRegionsSourceFluxStats +{ + static constexpr int getMinLogLevel() { return 3; } + static constexpr std::string_view getDescription() { return "Print statistics for each source flux in each regions"; } +}; + + /// @endcond ///@} diff --git a/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseStatistics.cpp b/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseStatistics.cpp index 26ed404707d..b92701b6ee1 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseStatistics.cpp +++ b/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseStatistics.cpp @@ -27,6 +27,9 @@ #include "physicsSolvers/fluidFlow/SinglePhaseBaseFields.hpp" #include "physicsSolvers/fluidFlow/kernels/singlePhase/StatisticsKernel.hpp" #include "physicsSolvers/fluidFlow/LogLevelsInfo.hpp" +#include "common/format/table/TableData.hpp" +#include "common/format/table/TableFormatter.hpp" +#include "common/format/table/TableLayout.hpp" namespace geos { @@ -101,28 +104,27 @@ void SinglePhaseStatistics::computeRegionStatistics( real64 const time, arrayView1d< string const > const & regionNames ) const { GEOS_MARK_FUNCTION; - // Step 1: initialize the average/min/max quantities ElementRegionManager & elemManager = mesh.getElemManager(); for( integer i = 0; i < regionNames.size(); ++i ) { ElementRegionBase & region = elemManager.getRegion( regionNames[i] ); - RegionStatistics & regionStatistics = region.getReference< RegionStatistics >( viewKeyStruct::regionStatisticsString() ); + RegionStatistics & stats = region.getReference< RegionStatistics >( viewKeyStruct::regionStatisticsString() ); - regionStatistics.averagePressure = 0.0; - regionStatistics.maxPressure = -LvArray::NumericLimits< real64 >::max; - regionStatistics.minPressure = LvArray::NumericLimits< real64 >::max; + stats.averagePressure = 0.0; + stats.maxPressure = -LvArray::NumericLimits< real64 >::max; + stats.minPressure = LvArray::NumericLimits< real64 >::max; - regionStatistics.maxDeltaPressure = -LvArray::NumericLimits< real64 >::max; - regionStatistics.minDeltaPressure = LvArray::NumericLimits< real64 >::max; + stats.maxDeltaPressure = -LvArray::NumericLimits< real64 >::max; + stats.minDeltaPressure = LvArray::NumericLimits< real64 >::max; - regionStatistics.averageTemperature = 0.0; - regionStatistics.maxTemperature = -LvArray::NumericLimits< real64 >::max; - regionStatistics.minTemperature = LvArray::NumericLimits< real64 >::max; + stats.averageTemperature = 0.0; + stats.maxTemperature = -LvArray::NumericLimits< real64 >::max; + stats.minTemperature = LvArray::NumericLimits< real64 >::max; - regionStatistics.totalPoreVolume = 0.0; - regionStatistics.totalUncompactedPoreVolume = 0.0; - regionStatistics.totalMass = 0.0; + stats.totalPoreVolume = 0.0; + stats.totalUncompactedPoreVolume = 0.0; + stats.totalMass = 0.0; } // Step 2: increment the average/min/max quantities for all the subRegions @@ -181,101 +183,103 @@ void SinglePhaseStatistics::computeRegionStatistics( real64 const time, subRegionTotalMass ); ElementRegionBase & region = elemManager.getRegion( ElementRegionBase::getParentRegion( subRegion ).getName() ); - RegionStatistics & regionStatistics = region.getReference< RegionStatistics >( viewKeyStruct::regionStatisticsString() ); + RegionStatistics & stats = region.getReference< RegionStatistics >( viewKeyStruct::regionStatisticsString() ); - regionStatistics.averagePressure += subRegionAvgPresNumerator; - if( subRegionMinPres < regionStatistics.minPressure ) + stats.averagePressure += subRegionAvgPresNumerator; + if( subRegionMinPres < stats.minPressure ) { - regionStatistics.minPressure = subRegionMinPres; + stats.minPressure = subRegionMinPres; } - if( subRegionMaxPres > regionStatistics.maxPressure ) + if( subRegionMaxPres > stats.maxPressure ) { - regionStatistics.maxPressure = subRegionMaxPres; + stats.maxPressure = subRegionMaxPres; } - if( subRegionMinDeltaPres < regionStatistics.minDeltaPressure ) + if( subRegionMinDeltaPres < stats.minDeltaPressure ) { - regionStatistics.minDeltaPressure = subRegionMinDeltaPres; + stats.minDeltaPressure = subRegionMinDeltaPres; } - if( subRegionMaxDeltaPres > regionStatistics.maxDeltaPressure ) + if( subRegionMaxDeltaPres > stats.maxDeltaPressure ) { - regionStatistics.maxDeltaPressure = subRegionMaxDeltaPres; + stats.maxDeltaPressure = subRegionMaxDeltaPres; } - regionStatistics.averageTemperature += subRegionAvgTempNumerator; - if( subRegionMinTemp < regionStatistics.minTemperature ) + stats.averageTemperature += subRegionAvgTempNumerator; + if( subRegionMinTemp < stats.minTemperature ) { - regionStatistics.minTemperature = subRegionMinTemp; + stats.minTemperature = subRegionMinTemp; } - if( subRegionMaxTemp > regionStatistics.maxTemperature ) + if( subRegionMaxTemp > stats.maxTemperature ) { - regionStatistics.maxTemperature = subRegionMaxTemp; + stats.maxTemperature = subRegionMaxTemp; } - regionStatistics.totalUncompactedPoreVolume += subRegionTotalUncompactedPoreVol; - regionStatistics.totalPoreVolume += subRegionTotalPoreVol; - regionStatistics.totalMass += subRegionTotalMass; + stats.totalUncompactedPoreVolume += subRegionTotalUncompactedPoreVol; + stats.totalPoreVolume += subRegionTotalPoreVol; + stats.totalMass += subRegionTotalMass; } ); // Step 3: synchronize the results over the MPI ranks for( integer i = 0; i < regionNames.size(); ++i ) { ElementRegionBase & region = elemManager.getRegion( regionNames[i] ); - RegionStatistics & regionStatistics = region.getReference< RegionStatistics >( viewKeyStruct::regionStatisticsString() ); + RegionStatistics & stats = region.getReference< RegionStatistics >( viewKeyStruct::regionStatisticsString() ); - regionStatistics.minPressure = MpiWrapper::min( regionStatistics.minPressure ); - regionStatistics.averagePressure = MpiWrapper::sum( regionStatistics.averagePressure ); - regionStatistics.maxPressure = MpiWrapper::max( regionStatistics.maxPressure ); + stats.minPressure = MpiWrapper::min( stats.minPressure ); + stats.averagePressure = MpiWrapper::sum( stats.averagePressure ); + stats.maxPressure = MpiWrapper::max( stats.maxPressure ); - regionStatistics.minDeltaPressure = MpiWrapper::min( regionStatistics.minDeltaPressure ); - regionStatistics.maxDeltaPressure = MpiWrapper::max( regionStatistics.maxDeltaPressure ); + stats.minDeltaPressure = MpiWrapper::min( stats.minDeltaPressure ); + stats.maxDeltaPressure = MpiWrapper::max( stats.maxDeltaPressure ); - regionStatistics.minTemperature = MpiWrapper::min( regionStatistics.minTemperature ); - regionStatistics.averageTemperature = MpiWrapper::sum( regionStatistics.averageTemperature ); - regionStatistics.maxTemperature = MpiWrapper::max( regionStatistics.maxTemperature ); + stats.minTemperature = MpiWrapper::min( stats.minTemperature ); + stats.averageTemperature = MpiWrapper::sum( stats.averageTemperature ); + stats.maxTemperature = MpiWrapper::max( stats.maxTemperature ); - regionStatistics.totalUncompactedPoreVolume = MpiWrapper::sum( regionStatistics.totalUncompactedPoreVolume ); - regionStatistics.totalPoreVolume = MpiWrapper::sum( regionStatistics.totalPoreVolume ); - regionStatistics.totalMass = MpiWrapper::sum( regionStatistics.totalMass ); + stats.totalUncompactedPoreVolume = MpiWrapper::sum( stats.totalUncompactedPoreVolume ); + stats.totalPoreVolume = MpiWrapper::sum( stats.totalPoreVolume ); + stats.totalMass = MpiWrapper::sum( stats.totalMass ); - if( regionStatistics.totalUncompactedPoreVolume > 0 ) + if( stats.totalUncompactedPoreVolume > 0 ) { - float invTotalUncompactedPoreVolume = 1.0 / regionStatistics.totalUncompactedPoreVolume; - regionStatistics.averagePressure *= invTotalUncompactedPoreVolume; - regionStatistics.averageTemperature *= invTotalUncompactedPoreVolume; + float invTotalUncompactedPoreVolume = 1.0 / stats.totalUncompactedPoreVolume; + stats.averagePressure *= invTotalUncompactedPoreVolume; + stats.averageTemperature *= invTotalUncompactedPoreVolume; } else { - regionStatistics.averagePressure = 0.0; - regionStatistics.averageTemperature = 0.0; + stats.averagePressure = 0.0; + stats.averageTemperature = 0.0; GEOS_WARNING( GEOS_FMT( "{}, {}: Cannot compute average pressure & temperature because region pore volume is zero.", getName(), regionNames[i] ) ); } - string statPrefix = GEOS_FMT( "{}, {} (time {} s):", getName(), regionNames[i], time ); - GEOS_LOG_LEVEL_INFO_RANK_0( logInfo::Statistics, - GEOS_FMT( "{} Pressure (min, average, max): {}, {}, {} Pa", - statPrefix, regionStatistics.minPressure, regionStatistics.averagePressure, regionStatistics.maxPressure ) ); - GEOS_LOG_LEVEL_INFO_RANK_0( logInfo::Statistics, - GEOS_FMT( "{} Delta pressure (min, max): {}, {} Pa", - statPrefix, regionStatistics.minDeltaPressure, regionStatistics.maxDeltaPressure ) ); - GEOS_LOG_LEVEL_INFO_RANK_0( logInfo::Statistics, - GEOS_FMT( "{} Temperature (min, average, max): {}, {}, {} K", - statPrefix, regionStatistics.minTemperature, regionStatistics.averageTemperature, - regionStatistics.maxTemperature ) ); - GEOS_LOG_LEVEL_INFO_RANK_0( logInfo::Statistics, - GEOS_FMT( "{} Total dynamic pore volume: {} rm^3", - statPrefix, regionStatistics.totalPoreVolume ) ); - GEOS_LOG_LEVEL_INFO_RANK_0( logInfo::Statistics, - GEOS_FMT( "{} Total fluid mass: {} kg", - statPrefix, regionStatistics.totalMass ) ); + string_view massUnit = units::getSymbol( m_solver->getMassUnit() ); + + TableData singPhaseStatsData; + singPhaseStatsData.addRow( "Pressure[Pa]", stats.minPressure, stats.averagePressure, stats.maxPressure ); + singPhaseStatsData.addRow( "Delta pressure [Pa]", stats.minDeltaPressure, "/", stats.maxDeltaPressure ); + singPhaseStatsData.addRow( "Temperature [K]", stats.minTemperature, stats.averageTemperature, stats.maxTemperature ); + singPhaseStatsData.addSeparator(); + singPhaseStatsData.addSeparator(); + singPhaseStatsData.addRow( "statistics", CellType::MergeNext, CellType::MergeNext, "value" ); + singPhaseStatsData.addSeparator(); + + singPhaseStatsData.addRow( "Total dynamic pore volume [rm^3]", CellType::MergeNext, CellType::MergeNext, stats.totalPoreVolume ); + singPhaseStatsData.addSeparator(); + singPhaseStatsData.addRow( GEOS_FMT( "Total fluid mass [{}]", massUnit ), CellType::MergeNext, CellType::MergeNext, stats.totalMass ); + + string const title = GEOS_FMT( "{}, {} (time {} s):", getName(), regionNames[i], time ); + TableLayout const singPhaseStatsLayout( title, { "statistics", "min", "average", "max" } ); + TableTextFormatter tableFormatter( singPhaseStatsLayout ); + GEOS_LOG_RANK_0( tableFormatter.toString( singPhaseStatsData ) ); if( m_writeCSV > 0 && MpiWrapper::commRank() == 0 ) { std::ofstream outputFile( m_outputDir + "/" + regionNames[i] + ".csv", std::ios_base::app ); - outputFile << time << "," << regionStatistics.minPressure << "," << regionStatistics.averagePressure << "," << regionStatistics.maxPressure << "," << - regionStatistics.minDeltaPressure << "," << regionStatistics.maxDeltaPressure << "," << - regionStatistics.minTemperature << "," << regionStatistics.averageTemperature << "," << regionStatistics.maxTemperature << "," << - regionStatistics.totalPoreVolume << "," << regionStatistics.totalMass << std::endl; + outputFile << time << "," << stats.minPressure << "," << stats.averagePressure << "," << stats.maxPressure << "," << + stats.minDeltaPressure << "," << stats.maxDeltaPressure << "," << + stats.minTemperature << "," << stats.averageTemperature << "," << stats.maxTemperature << "," << + stats.totalPoreVolume << "," << stats.totalMass << std::endl; outputFile.close(); } } diff --git a/src/coreComponents/physicsSolvers/fluidFlow/SourceFluxStatistics.cpp b/src/coreComponents/physicsSolvers/fluidFlow/SourceFluxStatistics.cpp index 6c8a1e36cb3..2373d3dd112 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/SourceFluxStatistics.cpp +++ b/src/coreComponents/physicsSolvers/fluidFlow/SourceFluxStatistics.cpp @@ -22,6 +22,7 @@ #include "fieldSpecification/SourceFluxBoundaryCondition.hpp" #include "fieldSpecification/FieldSpecificationManager.hpp" #include "LvArray/src/tensorOps.hpp" +#include "physicsSolvers/fluidFlow/LogLevelsInfo.hpp" namespace geos { @@ -45,6 +46,10 @@ SourceFluxStatsAggregator::SourceFluxStatsAggregator( const string & name, setDescription( GEOS_FMT( "Name(s) array of the {0}(s) for which we want the statistics. " "Use \"*\" to target all {0}.", SourceFluxBoundaryCondition::catalogName() ) ); + + addLogLevel< logInfo::DetailedSourceFluxStats >(); + addLogLevel< logInfo::DetailedRegionsSourceFluxStats >(); + addLogLevel< logInfo::AggregatedSourceFluxStats >(); } void SourceFluxStatsAggregator::postInputInitialization() @@ -79,14 +84,34 @@ void SourceFluxStatsAggregator::postInputInitialization() Wrapper< SourceFluxStatsAggregator::WrappedStats > & SourceFluxStatsAggregator::registerWrappedStats( Group & group, string_view fluxName, - string_view elementSetName ) + string_view elementSetName, + string_array & filenames ) { string const wrapperName = getStatWrapperName( fluxName ); Wrapper< WrappedStats > & statsWrapper = group.registerWrapper< WrappedStats >( wrapperName ); statsWrapper.setRestartFlags( RestartFlags::NO_WRITE ); - statsWrapper.reference().setTarget( getName(), fluxName ); - writeStatsToCSV( elementSetName, statsWrapper.reference(), true ); + WrappedStats & stats = statsWrapper.reference(); + stats.setTarget( getName(), fluxName ); + + { //tableLayout initialisation + string_view massUnit = units::getSymbol( m_solver->getMassUnit() ); + + string const logMassColumn = GEOS_FMT( "Produced mass [{}]", massUnit ); + string const logRateColumn = GEOS_FMT( "Production rate [{}]", massUnit ); + TableLayout statsLogLayout( "", { "region", logMassColumn, logRateColumn } ); + m_logLayout = statsLogLayout; + + string const csvMassColumn = GEOS_FMT( "Produced mass [{}]", massUnit ); + string const csvRateColumn = GEOS_FMT( "Production rate [{}]", massUnit ); + + filenames.emplace_back( GEOS_FMT( "{}/{}_{}_{}.csv", + m_outputDir, + stats.getAggregatorName(), stats.getFluxName(), elementSetName ) ); + + TableLayout statsCSVLayout( "", {"Time [s]", "Element Count", csvMassColumn, csvRateColumn} ); + m_csvLayout = statsCSVLayout; + } return statsWrapper; } @@ -101,22 +126,28 @@ void SourceFluxStatsAggregator::registerDataOnMesh( Group & meshBodies ) MeshLevel & mesh, arrayView1d< string const > const & ) { - registerWrappedStats( mesh, viewKeyStruct::fluxSetWrapperString(), viewKeyStruct::allRegionWrapperString() ); + registerWrappedStats( mesh, + viewKeyStruct::fluxSetWrapperString(), + viewKeyStruct::allRegionWrapperString(), + m_allRegionWrapperFluxFilename ); for( string const & fluxName : m_fluxNames ) { - registerWrappedStats( mesh, fluxName, viewKeyStruct::allRegionWrapperString() ); + registerWrappedStats( mesh, + fluxName, + viewKeyStruct::allRegionWrapperString(), + m_allRegionFluxsfilename ); mesh.getElemManager().forElementRegions( [&]( ElementRegionBase & region ) { Wrapper< WrappedStats > & regionStatsWrapper = - registerWrappedStats( region, fluxName, region.getName() ); + registerWrappedStats( region, fluxName, region.getName(), m_regionsfilename ); region.excludeWrappersFromPacking( { regionStatsWrapper.getName() } ); region.forElementSubRegions( [&]( ElementSubRegionBase & subRegion ) { Wrapper< WrappedStats > & subRegionStatsWrapper = - registerWrappedStats( subRegion, fluxName, subRegion.getName() ); + registerWrappedStats( subRegion, fluxName, subRegion.getName(), m_subRegionsfilename ); subRegion.excludeWrappersFromPacking( { subRegionStatsWrapper.getName() } ); } ); } ); @@ -124,61 +155,59 @@ void SourceFluxStatsAggregator::registerDataOnMesh( Group & meshBodies ) } ); } -void SourceFluxStatsAggregator::writeStatsToLog( integer minLogLevel, - string_view elementSetName, - WrappedStats const & wrappedStats ) +void SourceFluxStatsAggregator::gatherStatsForLog( bool logLevelActive, + string_view elementSetName, + TableData & tableData, + WrappedStats const & wrappedStats ) { - if( getLogLevel() >= minLogLevel && logger::internal::rank == 0 ) + if( logLevelActive && logger::internal::rank == 0 ) { - GEOS_LOG_RANK( GEOS_FMT( "{} {} (of {}, in {}): Producing on {} elements", - catalogName(), getName(), wrappedStats.getFluxName(), elementSetName, - wrappedStats.stats().m_elementCount ) ); - - // we want to format differently if we have got multiple phases or not - string_view massUnit = units::getSymbol( m_solver->getMassUnit() ); if( wrappedStats.stats().m_producedMass.size() == 1 ) { - GEOS_LOG_RANK( GEOS_FMT( "{} {} (of {}, in {}): Produced mass = {} {}", - catalogName(), getName(), wrappedStats.getFluxName(), elementSetName, - wrappedStats.stats().m_producedMass[0], massUnit ) ); - GEOS_LOG_RANK( GEOS_FMT( "{} {} (of {}, in {}): Production rate = {} {}/s", - catalogName(), getName(), wrappedStats.getFluxName(), elementSetName, - wrappedStats.stats().m_productionRate[0], massUnit ) ); + tableData.addRow( elementSetName, + GEOS_FMT( "{}", wrappedStats.stats().m_producedMass[0] ), + GEOS_FMT( "{}", wrappedStats.stats().m_productionRate[0] ) ); } else { - GEOS_LOG_RANK( GEOS_FMT( "{} {} (of {}, in {}): Produced mass = {} {}", - catalogName(), getName(), wrappedStats.getFluxName(), elementSetName, - wrappedStats.stats().m_producedMass, massUnit ) ); - GEOS_LOG_RANK( GEOS_FMT( "{} {} (of {}, in {}): Production rate = {} {}/s", - catalogName(), getName(), wrappedStats.getFluxName(), elementSetName, - wrappedStats.stats().m_productionRate, massUnit ) ); + tableData.addRow( elementSetName, + GEOS_FMT( "{}", wrappedStats.stats().m_producedMass ), + GEOS_FMT( "{}", wrappedStats.stats().m_productionRate ) ); } } } -void SourceFluxStatsAggregator::writeStatsToCSV( string_view elementSetName, WrappedStats const & stats, - bool writeHeader ) +void SourceFluxStatsAggregator::outputStatsToLog( bool logLevelActive, string_view elementSetName, TableData const & tableMeshData ) { - if( m_writeCSV > 0 && MpiWrapper::commRank() == 0 ) + if( logLevelActive && logger::internal::rank == 0 ) { - string const fileName = GEOS_FMT( "{}/{}_{}_{}.csv", - m_outputDir, - stats.getAggregatorName(), stats.getFluxName(), elementSetName ); - std::ofstream outputFile( fileName, - writeHeader ? std::ios_base::out : std::ios_base::app ); - if( writeHeader ) - { - outputFile << GEOS_FMT( "Time [s],Element Count,Producted Mass [{0}],Production Rate [{0}/s]", - units::getSymbol( m_solver->getMassUnit() ) ) << std::endl; - } - else + m_logLayout.setTitle( GEOS_FMT( "Source flux statistics in {}", elementSetName )); + TableTextFormatter const tableStatFormatter( m_logLayout ); + GEOS_LOG_RANK( tableStatFormatter.toString( tableMeshData ) ); + } +} +void SourceFluxStatsAggregator::outputStatsToCSV( string_array const & filenames, TableData & csvData ) +{ + if( m_writeCSV > 0 && logger::internal::rank == 0 ) + { + for( auto const & filename : filenames ) { - outputFile << GEOS_FMT( "{},{},{},{}", - stats.getStatsPeriodStart(), stats.stats().m_elementCount, - stats.stats().m_producedMass, stats.stats().m_productionRate ) << std::endl; + std::ofstream outputFile( filename ); + + TableCSVFormatter const tableStatFormatter( m_csvLayout ); + outputFile << tableStatFormatter.toString( csvData ); + outputFile.close(); } - outputFile.close(); + csvData.clear(); + } +} + +void SourceFluxStatsAggregator::gatherStatsForCSV( TableData & tableData, WrappedStats const & stats ) +{ + if( m_writeCSV > 0 && MpiWrapper::commRank() == 0 ) + { + tableData.addRow( stats.getStatsPeriodStart(), stats.stats().m_elementCount, + stats.stats().m_producedMass, stats.stats().m_productionRate ); } } @@ -189,16 +218,21 @@ bool SourceFluxStatsAggregator::execute( real64 const GEOS_UNUSED_PARAM( time_n real64 const GEOS_UNUSED_PARAM( eventProgress ), DomainPartition & domain ) { + bool const fluxMeshesStats = isLogLevelActive< logInfo::AggregatedSourceFluxStats >( getLogLevel() ); + bool const fluxesStats = isLogLevelActive< logInfo::DetailedSourceFluxStats >( getLogLevel() ); + bool const regionsStats = isLogLevelActive< logInfo::DetailedRegionsSourceFluxStats >( getLogLevel() ); forMeshLevelStatsWrapper( domain, [&] ( MeshLevel & meshLevel, WrappedStats & meshLevelStats ) { + TableData tableMeshData; + TableData csvData; meshLevelStats.stats() = StatData(); - forAllFluxStatsWrappers( meshLevel, [&] ( MeshLevel &, WrappedStats & fluxStats ) { fluxStats.stats() = StatData(); - + TableData tableFluxData; + TableData tableRegionsData; forAllRegionStatsWrappers( meshLevel, fluxStats.getFluxName(), [&] ( ElementRegionBase & region, WrappedStats & regionStats ) { @@ -210,21 +244,31 @@ bool SourceFluxStatsAggregator::execute( real64 const GEOS_UNUSED_PARAM( time_n subRegionStats.finalizePeriod(); regionStats.stats().combine( subRegionStats.stats() ); } ); - fluxStats.stats().combine( regionStats.stats() ); - writeStatsToLog( 3, region.getName(), regionStats ); - writeStatsToCSV( region.getName(), regionStats, false ); + + gatherStatsForLog( regionsStats, region.getName(), tableRegionsData, regionStats ); + gatherStatsForCSV( csvData, regionStats ); } ); + outputStatsToLog( regionsStats, fluxStats.getFluxName(), tableRegionsData ); + outputStatsToCSV( m_regionsfilename, csvData ); + meshLevelStats.stats().combine( fluxStats.stats() ); - writeStatsToLog( 2, viewKeyStruct::allRegionWrapperString(), fluxStats ); - writeStatsToCSV( viewKeyStruct::allRegionWrapperString(), fluxStats, false ); + + gatherStatsForLog( fluxesStats, viewKeyStruct::allRegionWrapperString(), tableFluxData, fluxStats ); + gatherStatsForCSV( csvData, fluxStats ); + + outputStatsToLog( fluxesStats, fluxStats.getFluxName(), tableFluxData ); + outputStatsToCSV( m_allRegionFluxsfilename, csvData ); + } ); + gatherStatsForLog( fluxMeshesStats, + viewKeyStruct::allRegionWrapperString(), tableMeshData, meshLevelStats ); + gatherStatsForCSV( csvData, meshLevelStats ); - writeStatsToLog( 1, viewKeyStruct::allRegionWrapperString(), meshLevelStats ); - writeStatsToCSV( viewKeyStruct::allRegionWrapperString(), meshLevelStats, false ); + outputStatsToLog( fluxMeshesStats, meshLevelStats.getFluxName(), tableMeshData ); + outputStatsToCSV( m_allRegionWrapperFluxFilename, csvData ); } ); - return false; } diff --git a/src/coreComponents/physicsSolvers/fluidFlow/SourceFluxStatistics.hpp b/src/coreComponents/physicsSolvers/fluidFlow/SourceFluxStatistics.hpp index 43dad5d1c3a..d8d87e45b09 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/SourceFluxStatistics.hpp +++ b/src/coreComponents/physicsSolvers/fluidFlow/SourceFluxStatistics.hpp @@ -23,7 +23,10 @@ #include "../FieldStatisticsBase.hpp" #include "FlowSolverBase.hpp" #include "mesh/DomainPartition.hpp" - +#include "common/format/table/TableData.hpp" +#include "common/format/table/TableFormatter.hpp" +#include "common/format/table/TableLayout.hpp" +#include namespace geos { @@ -311,6 +314,15 @@ class SourceFluxStatsAggregator final : public FieldStatisticsBase< FlowSolverBa /// the names of the SourceFlux(s) for which we want the statistics string_array m_fluxNames; + /// + TableLayout m_logLayout; + TableLayout m_csvLayout; + + string_array m_subRegionsfilename; + string_array m_regionsfilename; + string_array m_allRegionFluxsfilename; + string_array m_allRegionWrapperFluxFilename; + /** * @copydoc Group::registerDataOnMesh(Group &) @@ -324,22 +336,41 @@ class SourceFluxStatsAggregator final : public FieldStatisticsBase< FlowSolverBa dataRepository::Wrapper< WrappedStats > & registerWrappedStats( Group & group, string_view fluxName, - string_view elementSetName ); + string_view elementSetName, + string_array & filenames ); /** - * @brief If requested, output in the log and CSV the given statistics. - * @param minLogLevel the min log level to output any line. - * @param elementSetName the region / sub-subregion name concerned by the statistics. - * @param stats the statistics that must be output in the log. + * @brief If requested, collect statistics in a tableData. + * @param logLevel the min log level to collect any statistics. + * @param regionName The region name where we collect statistics + * @param tableData The table data where we collect statistics + * @param wrappedStats the statistics that must be retrieved. + */ + void gatherStatsForLog( bool logLevelActive, + string_view elementSetName, + TableData & tableData, + WrappedStats const & wrappedStats ); + /** + * @brief If requested, collect statistics in a tableData. + * @param tableData The TableData where we collect the statistics + * @param stats the statistics that must be retrieved. */ - void writeStatsToLog( integer minLogLevel, string_view elementSetName, WrappedStats const & stats ); + void gatherStatsForCSV( TableData & tableData, WrappedStats const & stats ); + + /** + * @brief If requested, output statistics in the log. + * @param logLevel the min log level to output any statistics. + * @param statsName The stat name where we collect + * @param tableMeshData The TableData where we have all collected statistics + */ + void outputStatsToLog( bool logLevelActive, string_view elementSetName, TableData const & tableMeshData ); + /** - * @brief If CSV is enabled, create or append a new CSV file. - * @param elementSetName the region / sub-subregion name concerned by the statistics. - * @param stats the statistics that must be output in the log. - * @param writeHeader If true, create the CSV with the header. If false, append it with the statistics. + * @brief If requested, output statistics in csv. + * @param filenames String array containing all filenames to be generated + * @param csvData The TableData where we have all collected statistics */ - void writeStatsToCSV( string_view elementSetName, WrappedStats const & stats, bool writeHeader ); + void outputStatsToCSV( string_array const & filenames, TableData & csvData ); }; diff --git a/src/coreComponents/physicsSolvers/fluidFlow/StencilDataCollection.cpp b/src/coreComponents/physicsSolvers/fluidFlow/StencilDataCollection.cpp index 4b0adc25b20..11e6637c29a 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/StencilDataCollection.cpp +++ b/src/coreComponents/physicsSolvers/fluidFlow/StencilDataCollection.cpp @@ -237,10 +237,8 @@ string formatKernelDataExtract( arrayView1d< StencilDataCollection::KernelConnec kernelIterator->m_transmissibility[0], kernelIterator->m_transmissibility[1] ); } - TableLayout const tableLayout{ - { "regionId A/B", "subRegionId A/B", "elementId A/B", "transmissibilityAB", "transmissibilityBA" }, - GEOS_FMT( "Kernel data (real row count = {})", kernelData.size() ) - }; + TableLayout const tableLayout = TableLayout( GEOS_FMT( "Kernel data (real row count = {})", kernelData.size() ), + {"regionId A/B", "subRegionId A/B", "elementId A/B", "transmissibilityAB", "transmissibilityBA"} ); TableTextFormatter const tableFormatter{ tableLayout }; return tableFormatter.toString( tableData ); } diff --git a/src/coreComponents/physicsSolvers/inducedSeismicity/CMakeLists.txt b/src/coreComponents/physicsSolvers/inducedSeismicity/CMakeLists.txt index c8f23c55323..df05497f6f2 100644 --- a/src/coreComponents/physicsSolvers/inducedSeismicity/CMakeLists.txt +++ b/src/coreComponents/physicsSolvers/inducedSeismicity/CMakeLists.txt @@ -1,19 +1,27 @@ # Specify solver headers set( physicsSolvers_headers ${physicsSolvers_headers} + inducedSeismicity/ExplicitQDRateAndState.hpp + inducedSeismicity/ImplicitQDRateAndState.hpp inducedSeismicity/inducedSeismicityFields.hpp + inducedSeismicity/QDRateAndStateBase.hpp + inducedSeismicity/QuasiDynamicEarthQuake.hpp inducedSeismicity/rateAndStateFields.hpp - inducedSeismicity/QuasiDynamicEQ.hpp - inducedSeismicity/QuasiDynamicEQRK32.hpp inducedSeismicity/SeismicityRate.hpp - inducedSeismicity/kernels/RateAndStateKernels.hpp - inducedSeismicity/kernels/SeismicityRateKernels.hpp + inducedSeismicity/SpringSlider.hpp + inducedSeismicity/kernels/ExplicitRateAndStateKernels.hpp + inducedSeismicity/kernels/ImplicitRateAndStateKernels.hpp + inducedSeismicity/kernels/RateAndStateKernelsBase.hpp + inducedSeismicity/kernels/SeismicityRateKernels.hpp PARENT_SCOPE ) # Specify solver sources set( physicsSolvers_sources ${physicsSolvers_sources} - inducedSeismicity/QuasiDynamicEQ.cpp - inducedSeismicity/QuasiDynamicEQRK32.cpp - inducedSeismicity/SeismicityRate.cpp + inducedSeismicity/ExplicitQDRateAndState.cpp + inducedSeismicity/ImplicitQDRateAndState.cpp + inducedSeismicity/QDRateAndStateBase.cpp + inducedSeismicity/QuasiDynamicEarthQuake.cpp + inducedSeismicity/SeismicityRate.cpp + inducedSeismicity/SpringSlider.cpp PARENT_SCOPE ) diff --git a/src/coreComponents/physicsSolvers/inducedSeismicity/QuasiDynamicEQRK32.cpp b/src/coreComponents/physicsSolvers/inducedSeismicity/ExplicitQDRateAndState.cpp similarity index 51% rename from src/coreComponents/physicsSolvers/inducedSeismicity/QuasiDynamicEQRK32.cpp rename to src/coreComponents/physicsSolvers/inducedSeismicity/ExplicitQDRateAndState.cpp index 28ff8f5ff14..d6fc3c74871 100644 --- a/src/coreComponents/physicsSolvers/inducedSeismicity/QuasiDynamicEQRK32.cpp +++ b/src/coreComponents/physicsSolvers/inducedSeismicity/ExplicitQDRateAndState.cpp @@ -14,14 +14,14 @@ */ /** - * @file QuasiDynamicEQRK32.cpp + * @file ExplicitQDRateAndState.cpp */ -#include "QuasiDynamicEQRK32.hpp" +#include "ExplicitQDRateAndState.hpp" #include "dataRepository/InputFlags.hpp" #include "mesh/DomainPartition.hpp" -#include "kernels/RateAndStateKernels.hpp" +#include "kernels/ExplicitRateAndStateKernels.hpp" #include "rateAndStateFields.hpp" #include "physicsSolvers/contact/ContactFields.hpp" #include "fieldSpecification/FieldSpecificationManager.hpp" @@ -34,47 +34,24 @@ using namespace fields; using namespace constitutive; using namespace rateAndStateKernels; -QuasiDynamicEQRK32::QuasiDynamicEQRK32( const string & name, - Group * const parent ): - PhysicsSolverBase( name, parent ), - m_stressSolver( nullptr ), - m_stressSolverName( "SpringSlider" ), - m_shearImpedance( 0.0 ), +ExplicitQDRateAndState::ExplicitQDRateAndState( const string & name, + Group * const parent ): + QDRateAndStateBase( name, parent ), m_butcherTable( BogackiShampine32Table()), // TODO: The butcher table should be specified in the XML input. m_successfulStep( false ), - m_controller( PIDController( { 1.0/18.0, 1.0/9.0, 1.0/18.0 }, + m_stepUpdateFactor( 1.0 ), + m_controller( PIDController( { 0.6, -0.2, 0.0 }, 1.0e-6, 1.0e-6, 0.81 )) // TODO: The control parameters should be specified in the XML input -{ - this->registerWrapper( viewKeyStruct::shearImpedanceString(), &m_shearImpedance ). - setInputFlag( InputFlags::REQUIRED ). - setDescription( "Shear impedance." ); - - this->registerWrapper( viewKeyStruct::stressSolverNameString(), &m_stressSolverName ). - setInputFlag( InputFlags::OPTIONAL ). - setDescription( "Name of solver for computing stress. If empty, the spring-slider model is run." ); -} - -void QuasiDynamicEQRK32::postInputInitialization() -{ - - // Initialize member stress solver as specified in XML input - if( !m_stressSolverName.empty() ) - { - m_stressSolver = &this->getParent().getGroup< PhysicsSolverBase >( m_stressSolverName ); - } - - PhysicsSolverBase::postInputInitialization(); -} +{} -QuasiDynamicEQRK32::~QuasiDynamicEQRK32() +ExplicitQDRateAndState::~ExplicitQDRateAndState() { // TODO Auto-generated destructor stub } - -void QuasiDynamicEQRK32::registerDataOnMesh( Group & meshBodies ) +void ExplicitQDRateAndState::registerDataOnMesh( Group & meshBodies ) { - PhysicsSolverBase::registerDataOnMesh( meshBodies ); + QDRateAndStateBase::registerDataOnMesh( meshBodies ); forDiscretizationOnMeshTargets( meshBodies, [&] ( string const &, MeshLevel & mesh, @@ -86,79 +63,21 @@ void QuasiDynamicEQRK32::registerDataOnMesh( Group & meshBodies ) [&]( localIndex const, SurfaceElementSubRegion & subRegion ) { - // Scalar functions on fault - subRegion.registerField< rateAndState::stateVariable >( getName() ); - subRegion.registerField< rateAndState::stateVariable_n >( getName() ); - subRegion.registerField< rateAndState::slipRate >( getName() ); - - // Tangent (2-component) functions on fault - string const labels2Comp[2] = {"tangent1", "tangent2" }; - subRegion.registerField< rateAndState::slipVelocity >( getName() ). - setDimLabels( 1, labels2Comp ).reference().resizeDimension< 1 >( 2 ); - subRegion.registerField< rateAndState::slipVelocity_n >( getName() ). - setDimLabels( 1, labels2Comp ).reference().resizeDimension< 1 >( 2 ); - subRegion.registerField< rateAndState::deltaSlip >( getName() ). - setDimLabels( 1, labels2Comp ).reference().resizeDimension< 1 >( 2 ); - subRegion.registerField< rateAndState::deltaSlip_n >( getName() ). - setDimLabels( 1, labels2Comp ).reference().resizeDimension< 1 >( 2 ); - // Runge-Kutta stage rates and error integer const numRKComponents = 3; subRegion.registerField< rateAndState::rungeKuttaStageRates >( getName() ).reference().resizeDimension< 1, 2 >( m_butcherTable.numStages, numRKComponents ); subRegion.registerField< rateAndState::error >( getName() ).reference().resizeDimension< 1 >( numRKComponents ); - - - if( !subRegion.hasWrapper( contact::dispJump::key() )) - { - // 3-component functions on fault - string const labels3Comp[3] = { "normal", "tangent1", "tangent2" }; - subRegion.registerField< contact::dispJump >( getName() ). - setDimLabels( 1, labels3Comp ). - reference().resizeDimension< 1 >( 3 ); - subRegion.registerField< contact::dispJump_n >( getName() ). - setDimLabels( 1, labels3Comp ). - reference().resizeDimension< 1 >( 3 ); - subRegion.registerField< contact::traction >( getName() ). - setDimLabels( 1, labels3Comp ). - reference().resizeDimension< 1 >( 3 ); - subRegion.registerField< contact::traction_n >( getName() ). - setDimLabels( 1, labels3Comp ). - reference().resizeDimension< 1 >( 3 ); - - subRegion.registerWrapper< string >( viewKeyStruct::frictionLawNameString() ). - setPlotLevel( PlotLevel::NOPLOT ). - setRestartFlags( RestartFlags::NO_WRITE ). - setSizedFromParent( 0 ); - - string & frictionLawName = subRegion.getReference< string >( viewKeyStruct::frictionLawNameString() ); - frictionLawName = PhysicsSolverBase::getConstitutiveName< FrictionBase >( subRegion ); - GEOS_ERROR_IF( frictionLawName.empty(), GEOS_FMT( "{}: FrictionBase model not found on subregion {}", - getDataContext(), subRegion.getDataContext() ) ); - } } ); } ); } -real64 QuasiDynamicEQRK32::solverStep( real64 const & time_n, - real64 const & dt, - int const cycleNumber, - DomainPartition & domain ) +real64 ExplicitQDRateAndState::solverStep( real64 const & time_n, + real64 const & dt, + int const cycleNumber, + DomainPartition & domain ) { - if( cycleNumber == 0 ) - { - /// Apply initial conditions to the Fault - FieldSpecificationManager & fieldSpecificationManager = FieldSpecificationManager::getInstance(); - - forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &, - MeshLevel & mesh, - arrayView1d< string const > const & ) - - { - fieldSpecificationManager.applyInitialConditions( mesh ); - - } ); - saveState( domain ); - } + applyInitialConditionsToFault( cycleNumber, domain ); + saveState( domain ); real64 dtAdaptive = dt; @@ -184,31 +103,29 @@ real64 QuasiDynamicEQRK32::solverStep( real64 const & time_n, stepRateStateODEAndComputeError( dtAdaptive, domain ); // Update timestep based on the time step error - real64 const dtNext = setNextDt( dtAdaptive, domain ); - if( m_successfulStep ) // set in setNextDt + evalTimestep( domain ); + if( m_successfulStep ) // set in evalTimestep { - // Compute stresses, and slip velocity and save results at updated time, + // Compute stresses, and slip velocity and save results at time_n + dtAdapitve if( !m_butcherTable.FSAL ) { dtStress = updateStresses( time_n, dtAdaptive, cycleNumber, domain ); updateSlipVelocity( time_n, dtAdaptive, domain ); } saveState( domain ); - // update the time step and exit the adaptive time step loop - dtAdaptive = dtNext; break; } else { // Retry with updated time step - dtAdaptive = dtNext; + dtAdaptive = setNextDt( dtAdaptive, domain ); } } - // return time step size achieved by stress solver + // return last successful adaptive time step (passed along to setNextDt) return dtAdaptive; } -void QuasiDynamicEQRK32::stepRateStateODEInitialSubstage( real64 const dt, DomainPartition & domain ) const +void ExplicitQDRateAndState::stepRateStateODEInitialSubstage( real64 const dt, DomainPartition & domain ) const { forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &, @@ -247,9 +164,9 @@ void QuasiDynamicEQRK32::stepRateStateODEInitialSubstage( real64 const dt, Domai } ); } -void QuasiDynamicEQRK32::stepRateStateODESubstage( integer const stageIndex, - real64 const dt, - DomainPartition & domain ) const +void ExplicitQDRateAndState::stepRateStateODESubstage( integer const stageIndex, + real64 const dt, + DomainPartition & domain ) const { forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &, @@ -276,7 +193,7 @@ void QuasiDynamicEQRK32::stepRateStateODESubstage( integer const stageIndex, } ); } -void QuasiDynamicEQRK32::stepRateStateODEAndComputeError( real64 const dt, DomainPartition & domain ) const +void ExplicitQDRateAndState::stepRateStateODEAndComputeError( real64 const dt, DomainPartition & domain ) const { forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &, MeshLevel & mesh, @@ -316,64 +233,9 @@ void QuasiDynamicEQRK32::stepRateStateODEAndComputeError( real64 const dt, Domai } ); } -real64 QuasiDynamicEQRK32::updateStresses( real64 const & time_n, - real64 const & dt, - const int cycleNumber, - DomainPartition & domain ) const -{ - GEOS_LOG_LEVEL_RANK_0( 1, "Stress solver" ); - // Call member variable stress solver to update the stress state - if( m_stressSolver ) - { - // 1. Solve the momentum balance - real64 const dtStress = m_stressSolver->solverStep( time_n, dt, cycleNumber, domain ); - - return dtStress; - } - else - { - // Spring-slider shear traction computation - forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &, - MeshLevel & mesh, - arrayView1d< string const > const & regionNames ) - - { - mesh.getElemManager().forElementSubRegions< SurfaceElementSubRegion >( regionNames, - [&]( localIndex const, - SurfaceElementSubRegion & subRegion ) - { - - arrayView2d< real64 const > const deltaSlip = subRegion.getField< rateAndState::deltaSlip >(); - arrayView2d< real64 > const traction = subRegion.getField< fields::contact::traction >(); - arrayView2d< real64 const > const traction_n = subRegion.getField< fields::contact::traction_n >(); - - string const & fricitonLawName = subRegion.template getReference< string >( viewKeyStruct::frictionLawNameString() ); - RateAndStateFriction const & frictionLaw = getConstitutiveModel< RateAndStateFriction >( subRegion, fricitonLawName ); - - RateAndStateFriction::KernelWrapper frictionKernelWrapper = frictionLaw.createKernelUpdates(); - - forAll< parallelDevicePolicy<> >( subRegion.size(), [=] GEOS_HOST_DEVICE ( localIndex const k ) - { - SpringSliderParameters springSliderParameters = SpringSliderParameters( traction[k][0], - frictionKernelWrapper.getACoefficient( k ), - frictionKernelWrapper.getBCoefficient( k ), - frictionKernelWrapper.getDcCoefficient( k ) ); - - - traction[k][1] = traction_n[k][1] + springSliderParameters.tauRate * dt - - springSliderParameters.springStiffness * deltaSlip[k][0]; - traction[k][2] = traction_n[k][2] + springSliderParameters.tauRate * dt - - springSliderParameters.springStiffness * deltaSlip[k][1]; - } ); - } ); - } ); - return dt; - } -} - -void QuasiDynamicEQRK32::updateSlipVelocity( real64 const & time_n, - real64 const & dt, - DomainPartition & domain ) const +void ExplicitQDRateAndState::updateSlipVelocity( real64 const & time_n, + real64 const & dt, + DomainPartition & domain ) const { GEOS_LOG_LEVEL_RANK_0( 1, "Rate and State solver" ); integer const maxIterNewton = m_nonlinearSolverParameters.m_maxIterNewton; @@ -394,45 +256,8 @@ void QuasiDynamicEQRK32::updateSlipVelocity( real64 const & time_n, } ); } -void QuasiDynamicEQRK32::saveState( DomainPartition & domain ) const +void ExplicitQDRateAndState::evalTimestep( DomainPartition & domain ) { - forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &, - MeshLevel & mesh, - arrayView1d< string const > const & regionNames ) - - { - mesh.getElemManager().forElementSubRegions< SurfaceElementSubRegion >( regionNames, - [&]( localIndex const, - SurfaceElementSubRegion & subRegion ) - { - arrayView1d< real64 const > const stateVariable = subRegion.getField< rateAndState::stateVariable >(); - arrayView2d< real64 const > const slipVelocity = subRegion.getField< rateAndState::slipVelocity >(); - arrayView2d< real64 const > const deltaSlip = subRegion.getField< rateAndState::deltaSlip >(); - arrayView2d< real64 const > const dispJump = subRegion.getField< contact::dispJump >(); - arrayView2d< real64 const > const traction = subRegion.getField< contact::traction >(); - - arrayView1d< real64 > const stateVariable_n = subRegion.getField< rateAndState::stateVariable_n >(); - arrayView2d< real64 > const slipVelocity_n = subRegion.getField< rateAndState::slipVelocity_n >(); - arrayView2d< real64 > const deltaSlip_n = subRegion.getField< rateAndState::deltaSlip >(); - arrayView2d< real64 > const dispJump_n = subRegion.getField< contact::dispJump_n >(); - arrayView2d< real64 > const traction_n = subRegion.getField< contact::traction_n >(); - - forAll< parallelDevicePolicy<> >( subRegion.size(), [=] GEOS_HOST_DEVICE ( localIndex const k ) - { - stateVariable_n[k] = stateVariable[k]; - LvArray::tensorOps::copy< 2 >( deltaSlip_n[k], deltaSlip[k] ); - LvArray::tensorOps::copy< 2 >( slipVelocity_n[k], slipVelocity[k] ); - LvArray::tensorOps::copy< 3 >( dispJump_n[k], dispJump[k] ); - LvArray::tensorOps::copy< 3 >( traction_n[k], traction[k] ); - } ); - } ); - } ); -} - -real64 QuasiDynamicEQRK32::setNextDt( real64 const & currentDt, DomainPartition & domain ) -{ - - // Spring-slider shear traction computation forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &, MeshLevel const & mesh, arrayView1d< string const > const & regionNames ) @@ -455,24 +280,29 @@ real64 QuasiDynamicEQRK32::setNextDt( real64 const & currentDt, DomainPartition } ); // Compute update factor to currentDt using PID error controller + limiter - real64 const dtFactor = m_controller.computeUpdateFactor( m_butcherTable.algHighOrder, m_butcherTable.algLowOrder ); - real64 const nextDt = dtFactor*currentDt; + m_stepUpdateFactor = m_controller.computeUpdateFactor( m_butcherTable.algHighOrder, m_butcherTable.algLowOrder ); // Check if step was acceptable - m_successfulStep = (dtFactor >= m_controller.acceptSafety) ? true : false; + m_successfulStep = (m_stepUpdateFactor >= m_controller.acceptSafety) ? true : false; if( m_successfulStep ) { m_controller.errors[2] = m_controller.errors[1]; m_controller.errors[1] = m_controller.errors[0]; + } +} + +real64 ExplicitQDRateAndState::setNextDt( real64 const & currentDt, DomainPartition & domain ) +{ + GEOS_UNUSED_VAR( domain ); + real64 const nextDt = m_stepUpdateFactor*currentDt; + if( m_successfulStep ) + { GEOS_LOG_LEVEL_RANK_0( 1, GEOS_FMT( "Adaptive time step successful. The next dt will be {:.2e} s", nextDt )); } else { - GEOS_LOG_LEVEL_RANK_0( 1, GEOS_FMT( "Adaptive time step failed. The next dt will be {:.2e} s", nextDt )); + GEOS_LOG_LEVEL_RANK_0( 1, GEOS_FMT( "Adaptive time step failed. Retry step with dt {:.2e} s", nextDt )); } - return nextDt; } -REGISTER_CATALOG_ENTRY( PhysicsSolverBase, QuasiDynamicEQRK32, string const &, dataRepository::Group * const ) - } // namespace geos diff --git a/src/coreComponents/physicsSolvers/inducedSeismicity/QuasiDynamicEQRK32.hpp b/src/coreComponents/physicsSolvers/inducedSeismicity/ExplicitQDRateAndState.hpp similarity index 67% rename from src/coreComponents/physicsSolvers/inducedSeismicity/QuasiDynamicEQRK32.hpp rename to src/coreComponents/physicsSolvers/inducedSeismicity/ExplicitQDRateAndState.hpp index bcb3dd8a72a..69769af9381 100644 --- a/src/coreComponents/physicsSolvers/inducedSeismicity/QuasiDynamicEQRK32.hpp +++ b/src/coreComponents/physicsSolvers/inducedSeismicity/ExplicitQDRateAndState.hpp @@ -13,46 +13,35 @@ * ------------------------------------------------------------------------------------------------------------ */ -#ifndef GEOS_PHYSICSSOLVERS_INDUCED_QUASIDYNAMICEQRK32_HPP -#define GEOS_PHYSICSSOLVERS_INDUCED_QUASIDYNAMICEQRK32_HPP +#ifndef GEOS_PHYSICSSOLVERS_INDUCEDSEISMICITY_QUASIDYNAMICEQRK32_HPP +#define GEOS_PHYSICSSOLVERS_INDUCEDSEISMICITY_QUASIDYNAMICEQRK32_HPP -#include "physicsSolvers/PhysicsSolverBase.hpp" -#include "kernels/RateAndStateKernels.hpp" +#include "QDRateAndStateBase.hpp" +#include "kernels/ExplicitRateAndStateKernels.hpp" namespace geos { -class QuasiDynamicEQRK32 : public PhysicsSolverBase +class ExplicitQDRateAndState : public QDRateAndStateBase { public: /// The default nullary constructor is disabled to avoid compiler auto-generation: - QuasiDynamicEQRK32() = delete; + ExplicitQDRateAndState() = delete; /// The constructor needs a user-defined "name" and a parent Group (to place this instance in the tree structure of classes) - QuasiDynamicEQRK32( const string & name, - Group * const parent ); + ExplicitQDRateAndState( const string & name, + Group * const parent ); /// Destructor - virtual ~QuasiDynamicEQRK32() override; + virtual ~ExplicitQDRateAndState() override; - static string catalogName() { return "QuasiDynamicEQRK32"; } - - /** - * @return Get the final class Catalog name - */ - virtual string getCatalogName() const override { return catalogName(); } + static string derivedSolverPrefix() { return "Explicit";}; /// This method ties properties with their supporting mesh virtual void registerDataOnMesh( Group & meshBodies ) override; - struct viewKeyStruct : public PhysicsSolverBase::viewKeyStruct + struct viewKeyStruct : public QDRateAndStateBase::viewKeyStruct { - /// stress solver name - constexpr static char const * stressSolverNameString() { return "stressSolverName"; } - /// Friction law name string - constexpr static char const * frictionLawNameString() { return "frictionLawName"; } - /// Friction law name string - constexpr static char const * shearImpedanceString() { return "shearImpedance"; } /// target slip increment constexpr static char const * timeStepTol() { return "timeStepTol"; } }; @@ -66,6 +55,12 @@ class QuasiDynamicEQRK32 : public PhysicsSolverBase virtual real64 setNextDt( real64 const & currentDt, DomainPartition & domain ) override final; + /** + * @brief Evaluates whether an adaptive time step was successful + * @param domain + */ + void evalTimestep( DomainPartition & domain ); + /** * @brief Computes stage rates for the initial Runge-Kutta substage and updates slip and state * @param dt @@ -90,11 +85,6 @@ class QuasiDynamicEQRK32 : public PhysicsSolverBase */ void stepRateStateODEAndComputeError( real64 const dt, DomainPartition & domain ) const; - real64 updateStresses( real64 const & time_n, - real64 const & dt, - const int cycleNumber, - DomainPartition & domain ) const; - /** * @brief Updates rate-and-state slip velocity * @param domain @@ -103,25 +93,7 @@ class QuasiDynamicEQRK32 : public PhysicsSolverBase real64 const & dt, DomainPartition & domain ) const; - /** - * @brief save the current state - * @param domain - */ - void saveState( DomainPartition & domain ) const; - -private: - - virtual void postInputInitialization() override; - - - /// pointer to stress solver - PhysicsSolverBase * m_stressSolver; - - /// stress solver name - string m_stressSolverName; - - /// shear impedance - real64 m_shearImpedance; +protected: /// Runge-Kutta Butcher table (specifies the embedded RK method) // TODO: The specific type should not be hardcoded! @@ -130,6 +102,8 @@ class QuasiDynamicEQRK32 : public PhysicsSolverBase bool m_successfulStep; // Flag indicating if the adative time step was accepted + real64 m_stepUpdateFactor; // Factor to update timestep with + /** * @brief Proportional-integral-derivative controller used for updating time step * based error estimate in the current and previous time steps. @@ -195,41 +169,8 @@ class QuasiDynamicEQRK32 : public PhysicsSolverBase PIDController m_controller; - - class SpringSliderParameters - { -public: - - GEOS_HOST_DEVICE - SpringSliderParameters( real64 const normalTraction, real64 const a, real64 const b, real64 const Dc ): - tauRate( 1e-4 ), - springStiffness( 0.0 ) - { - real64 const criticalStiffness = normalTraction * (b - a) / Dc; - springStiffness = 0.9 * criticalStiffness; - } - - /// Default copy constructor - SpringSliderParameters( SpringSliderParameters const & ) = default; - - /// Default move constructor - SpringSliderParameters( SpringSliderParameters && ) = default; - - /// Deleted default constructor - SpringSliderParameters() = delete; - - /// Deleted copy assignment operator - SpringSliderParameters & operator=( SpringSliderParameters const & ) = delete; - - /// Deleted move assignment operator - SpringSliderParameters & operator=( SpringSliderParameters && ) = delete; - - real64 tauRate; - - real64 springStiffness; - }; }; } /* namespace geos */ -#endif /* GEOS_PHYSICSSOLVERS_INDUCED_QUASIDYNAMICEQRK32_HPP */ +#endif /* GEOS_PHYSICSSOLVERS_INDUCEDSEISMICITY_QUASIDYNAMICEQRK32_HPP */ diff --git a/src/coreComponents/physicsSolvers/inducedSeismicity/ImplicitQDRateAndState.cpp b/src/coreComponents/physicsSolvers/inducedSeismicity/ImplicitQDRateAndState.cpp new file mode 100644 index 00000000000..733fbe4ab7a --- /dev/null +++ b/src/coreComponents/physicsSolvers/inducedSeismicity/ImplicitQDRateAndState.cpp @@ -0,0 +1,142 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * 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 ImplicitQDRateAndState.cpp + */ + +#include "ImplicitQDRateAndState.hpp" + +#include "dataRepository/InputFlags.hpp" +#include "mesh/DomainPartition.hpp" +#include "kernels/ImplicitRateAndStateKernels.hpp" +#include "rateAndStateFields.hpp" +#include "physicsSolvers/contact/ContactFields.hpp" + + +namespace geos +{ + +using namespace dataRepository; +using namespace fields; +using namespace constitutive; + +ImplicitQDRateAndState::ImplicitQDRateAndState( const string & name, + Group * const parent ): + QDRateAndStateBase( name, parent ), + m_targetSlipIncrement( 1.0e-7 ) +{ + this->registerWrapper( viewKeyStruct::targetSlipIncrementString(), &m_targetSlipIncrement ). + setInputFlag( InputFlags::OPTIONAL ). + setApplyDefaultValue( 1.0e-7 ). + setDescription( "Target slip incrmeent for timestep size selction" ); +} + +ImplicitQDRateAndState::~ImplicitQDRateAndState() +{ + // TODO Auto-generated destructor stub +} + +void ImplicitQDRateAndState::solveRateAndStateEquations( real64 const time_n, + real64 const dt, + DomainPartition & domain ) const +{ + integer const maxNewtonIter = m_nonlinearSolverParameters.m_maxIterNewton; + real64 const newtonTol = m_nonlinearSolverParameters.m_newtonTol; + forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &, + MeshLevel & mesh, + arrayView1d< string const > const & regionNames ) + + { + mesh.getElemManager().forElementSubRegions< SurfaceElementSubRegion >( regionNames, + [&]( localIndex const, + SurfaceElementSubRegion & subRegion ) + { + // solve rate and state equations. + rateAndStateKernels::createAndLaunch< rateAndStateKernels::ImplicitFixedStressRateAndStateKernel, parallelDevicePolicy<> >( subRegion, + viewKeyStruct::frictionLawNameString(), + m_shearImpedance, + maxNewtonIter, newtonTol, + time_n, + dt ); // save + // old + // state + updateSlip( subRegion, dt ); + } ); + } ); +} + +real64 ImplicitQDRateAndState::solverStep( real64 const & time_n, + real64 const & dt, + int const cycleNumber, + DomainPartition & domain ) +{ + applyInitialConditionsToFault( cycleNumber, domain ); + GEOS_LOG_LEVEL_RANK_0( 1, "Stress solver" ); + updateStresses( time_n, dt, cycleNumber, domain ); + GEOS_LOG_LEVEL_RANK_0( 1, "Rate and state solver" ); + solveRateAndStateEquations( time_n, dt, domain ); + saveState( domain ); + return dt; +} + +void ImplicitQDRateAndState::updateSlip( ElementSubRegionBase & subRegion, real64 const dt ) const +{ + arrayView2d< real64 const > const slipVelocity = subRegion.getField< rateAndState::slipVelocity >(); + arrayView2d< real64 > const deltaSlip = subRegion.getField< contact::deltaSlip >(); + + forAll< parallelDevicePolicy<> >( subRegion.size(), [=] GEOS_HOST_DEVICE ( localIndex const k ) + { + deltaSlip[k][0] = slipVelocity[k][0] * dt; + deltaSlip[k][1] = slipVelocity[k][1] * dt; + } ); +} + +real64 ImplicitQDRateAndState::setNextDt( real64 const & currentDt, DomainPartition & domain ) +{ + GEOS_UNUSED_VAR( currentDt ); + + real64 maxSlipRate = 0.0; + forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &, + MeshLevel const & mesh, + arrayView1d< string const > const & regionNames ) + + { + real64 maxSlipRateOnThisRank = 0.0; + mesh.getElemManager().forElementSubRegions< SurfaceElementSubRegion >( regionNames, + [&]( localIndex const, + SurfaceElementSubRegion const & subRegion ) + { + arrayView1d< real64 const > const slipRate = subRegion.getField< rateAndState::slipRate >(); + + RAJA::ReduceMax< parallelDeviceReduce, real64 > maximumSlipRateOnThisRegion( 0.0 ); + forAll< parallelDevicePolicy<> >( subRegion.size(), [=] GEOS_HOST_DEVICE ( localIndex const k ) + { + maximumSlipRateOnThisRegion.max( slipRate[k] ); + } ); + if( maximumSlipRateOnThisRegion.get() > maxSlipRateOnThisRank ) + maxSlipRateOnThisRank = maximumSlipRateOnThisRegion.get(); + } ); + maxSlipRate = MpiWrapper::max( maxSlipRateOnThisRank ); + } ); + + real64 const nextDt = m_targetSlipIncrement / maxSlipRate; + + GEOS_LOG_LEVEL_RANK_0( 1, GEOS_FMT( "The next dt will be {:.2e} s", nextDt )); + + return nextDt; +} + +} // namespace geos diff --git a/src/coreComponents/physicsSolvers/inducedSeismicity/ImplicitQDRateAndState.hpp b/src/coreComponents/physicsSolvers/inducedSeismicity/ImplicitQDRateAndState.hpp new file mode 100644 index 00000000000..907612d0aef --- /dev/null +++ b/src/coreComponents/physicsSolvers/inducedSeismicity/ImplicitQDRateAndState.hpp @@ -0,0 +1,70 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * SPDX-License-Identifier: LGPL-2.1-only + * + * Copyright (c) 2016-2024 Lawrence Livermore National Security LLC + * Copyright (c) 2018-2024 TotalEnergies + * Copyright (c) 2018-2024 The Board of Trustees of the Leland Stanford Junior University + * Copyright (c) 2023-2024 Chevron + * Copyright (c) 2019- GEOS/GEOSX Contributors + * All rights reserved + * + * See top level LICENSE, COPYRIGHT, CONTRIBUTORS, NOTICE, and ACKNOWLEDGEMENTS files for details. + * ------------------------------------------------------------------------------------------------------------ + */ + +#ifndef GEOS_PHYSICSSOLVERS_INDUCEDSEISMICITY_IMPLICITQDRATEANDSTATE_HPP +#define GEOS_PHYSICSSOLVERS_INDUCEDSEISMICITY_IMPLICITQDRATEANDSTATE_HPP + +#include "QDRateAndStateBase.hpp" + +namespace geos +{ + +class ImplicitQDRateAndState : public QDRateAndStateBase +{ +public: + /// The default nullary constructor is disabled to avoid compiler auto-generation: + ImplicitQDRateAndState() = delete; + + /// The constructor needs a user-defined "name" and a parent Group (to place this instance in the tree structure of classes) + ImplicitQDRateAndState( const string & name, + Group * const parent ); + + /// Destructor + virtual ~ImplicitQDRateAndState() override; + + static string derivedSolverPrefix() { return "Implicit";}; + + struct viewKeyStruct : public QDRateAndStateBase::viewKeyStruct + { + /// target slip increment + constexpr static char const * targetSlipIncrementString() { return "targetSlipIncrement"; } + }; + + virtual real64 setNextDt( real64 const & currentDt, + DomainPartition & domain ) override final; + + /** + * @brief save the old state + * @param subRegion + */ + void updateSlip( ElementSubRegionBase & subRegion, real64 const dt ) const; + + virtual real64 solverStep( real64 const & time_n, + real64 const & dt, + integer const cycleNumber, + DomainPartition & domain ) override final; +protected: + + void solveRateAndStateEquations( real64 const time_n, + real64 const dt, + DomainPartition & domain ) const; + + /// target slip rate + real64 m_targetSlipIncrement; +}; + +} /* namespace geos */ + +#endif /* GEOS_PHYSICSSOLVERS_INDUCEDSEISMICITY_IMPLICITQDRATEANDSTATE_HPP */ diff --git a/src/coreComponents/physicsSolvers/inducedSeismicity/QDRateAndStateBase.cpp b/src/coreComponents/physicsSolvers/inducedSeismicity/QDRateAndStateBase.cpp new file mode 100644 index 00000000000..a515685cab9 --- /dev/null +++ b/src/coreComponents/physicsSolvers/inducedSeismicity/QDRateAndStateBase.cpp @@ -0,0 +1,237 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * 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 QDRateAndStateBase.cpp + */ + +#include "QDRateAndStateBase.hpp" + +#include "dataRepository/InputFlags.hpp" +#include "mesh/DomainPartition.hpp" +#include "rateAndStateFields.hpp" +#include "physicsSolvers/contact/ContactFields.hpp" +#include "fieldSpecification/FieldSpecificationManager.hpp" +#include "constitutive/contact/RateAndStateFriction.hpp" +#include "kernels/RateAndStateKernelsBase.hpp" + +namespace geos +{ + +using namespace dataRepository; +using namespace fields; +using namespace constitutive; + +QDRateAndStateBase::QDRateAndStateBase( const string & name, + Group * const parent ): + PhysicsSolverBase( name, parent ), + m_shearImpedance( 0.0 ) +{ + this->registerWrapper( viewKeyStruct::shearImpedanceString(), &m_shearImpedance ). + setInputFlag( InputFlags::REQUIRED ). + setDescription( "Shear impedance." ); +} + +QDRateAndStateBase::~QDRateAndStateBase() +{ + // TODO Auto-generated destructor stub +} + +void QDRateAndStateBase::registerDataOnMesh( Group & meshBodies ) +{ + PhysicsSolverBase::registerDataOnMesh( meshBodies ); + + forDiscretizationOnMeshTargets( meshBodies, [&] ( string const &, + MeshLevel & mesh, + arrayView1d< string const > const & regionNames ) + { + ElementRegionManager & elemManager = mesh.getElemManager(); + + elemManager.forElementSubRegions< SurfaceElementSubRegion >( regionNames, + [&]( localIndex const, + SurfaceElementSubRegion & subRegion ) + { + // Scalar functions on fault + subRegion.registerField< rateAndState::stateVariable >( getName() ); + subRegion.registerField< rateAndState::stateVariable_n >( getName() ); + subRegion.registerField< rateAndState::slipRate >( getName() ); + subRegion.registerField< rateAndState::slipRate_n >( getName() ); + + // Tangent (2-component) functions on fault + string const labels2Comp[2] = {"tangent1", "tangent2" }; + + subRegion.registerField< rateAndState::slipVelocity >( getName() ). + setDimLabels( 1, labels2Comp ).reference().resizeDimension< 1 >( 2 ); + subRegion.registerField< rateAndState::slipVelocity_n >( getName() ). + setDimLabels( 1, labels2Comp ).reference().resizeDimension< 1 >( 2 ); + + subRegion.registerField< rateAndState::shearTraction >( getName() ). + setDimLabels( 1, labels2Comp ).reference().resizeDimension< 1 >( 2 ); + subRegion.registerField< rateAndState::normalTraction >( getName() ); + + subRegion.registerField< rateAndState::shearTraction_n >( getName() ). + setDimLabels( 1, labels2Comp ).reference().resizeDimension< 1 >( 2 ); + subRegion.registerField< rateAndState::normalTraction_n >( getName() ); + + subRegion.registerField< rateAndState::backgroundShearStress >( getName() ). + setDimLabels( 1, labels2Comp ).reference().resizeDimension< 1 >( 2 ); + subRegion.registerField< rateAndState::backgroundNormalStress >( getName() ); + } ); + } ); +} + +void QDRateAndStateBase::enforceRateAndVelocityConsistency( SurfaceElementSubRegion & subRegion ) const +{ + arrayView2d< real64 > const slipVelocity = subRegion.getField< rateAndState::slipVelocity >(); + arrayView1d< real64 > const slipRate = subRegion.getField< rateAndState::slipRate >(); + arrayView1d< real64 const > const stateVariable = subRegion.getField< rateAndState::stateVariable >(); + + arrayView2d< real64 > const backgroundShearStress = subRegion.getField< rateAndState::backgroundShearStress >(); + arrayView1d< real64 > const backgroundNormalStress = subRegion.getField< rateAndState::backgroundNormalStress >(); + + real64 const shearImpedance = m_shearImpedance; + + string const & frictionaLawName = subRegion.getReference< string >( viewKeyStruct::frictionLawNameString() ); + constitutive::RateAndStateFriction const & frictionLaw = subRegion.getConstitutiveModel< constitutive::RateAndStateFriction >( frictionaLawName ); + + constitutive::RateAndStateFriction::KernelWrapper frictionLawKernelWrapper = frictionLaw.createKernelUpdates(); + + RAJA::ReduceMax< parallelDeviceReduce, int > negativeSlipRate( 0 ); + RAJA::ReduceMax< parallelDeviceReduce, int > bothNonZero( 0 ); + + forAll< parallelDevicePolicy<> >( subRegion.size(), [=] GEOS_HOST_DEVICE ( localIndex const k ) + { + if( slipRate[k] < 0.0 ) + { + negativeSlipRate.max( 1 ); + } + else if( LvArray::tensorOps::l2Norm< 2 >( slipVelocity[k] ) > 0.0 && slipRate[k] > 0.0 ) + { + bothNonZero.max( 1 ); + } + else if( LvArray::tensorOps::l2Norm< 2 >( slipVelocity[k] ) > 0.0 ) + { + slipRate[k] = LvArray::tensorOps::l2Norm< 2 >( slipVelocity[k] ); + } + else if( slipRate[k] > 0.0 ) + { + real64 const frictionCoefficient = frictionLawKernelWrapper.frictionCoefficient( k, slipRate[k], stateVariable[k] ); + rateAndStateKernels::projectSlipRateBase( k, + frictionCoefficient, + shearImpedance, + backgroundNormalStress, + backgroundShearStress, + slipRate, + slipVelocity ); + } + } ); + + + GEOS_ERROR_IF( negativeSlipRate.get() > 0, "SlipRate cannot be negative." ); + GEOS_ERROR_IF( bothNonZero.get() > 0, "Only one between slipRate and slipVelocity can be specified as i.c." ); + +} + +void QDRateAndStateBase::applyInitialConditionsToFault( int const cycleNumber, + DomainPartition & domain ) const +{ + if( cycleNumber == 0 ) + { + /// Apply initial conditions to the Fault + FieldSpecificationManager & fieldSpecificationManager = FieldSpecificationManager::getInstance(); + + forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &, + MeshLevel & mesh, + arrayView1d< string const > const & regionNames ) + + { + fieldSpecificationManager.applyInitialConditions( mesh ); + mesh.getElemManager().forElementSubRegions< SurfaceElementSubRegion >( regionNames, + [&]( localIndex const, + SurfaceElementSubRegion & subRegion ) + { + enforceRateAndVelocityConsistency( subRegion ); + + arrayView1d< real64 const > const slipRate = subRegion.getField< rateAndState::slipRate >(); + arrayView1d< real64 const > const stateVariable = subRegion.getField< rateAndState::stateVariable >(); + + arrayView1d< real64 > const normalTraction = subRegion.getField< rateAndState::normalTraction >(); + arrayView2d< real64 > const shearTraction = subRegion.getField< rateAndState::shearTraction >(); + + arrayView1d< real64 > const stateVariable_n = subRegion.getField< rateAndState::stateVariable_n >(); + arrayView1d< real64 > const slipRate_n = subRegion.getField< rateAndState::slipRate_n >(); + arrayView1d< real64 > const normalTraction_n = subRegion.getField< rateAndState::normalTraction_n >(); + arrayView2d< real64 > const shearTraction_n = subRegion.getField< rateAndState::shearTraction_n >(); + + arrayView2d< real64 const > const backgroundShearStress = subRegion.getField< rateAndState::backgroundShearStress >(); + arrayView1d< real64 const > const backgroundNormalStress = subRegion.getField< rateAndState::backgroundNormalStress >(); + + forAll< parallelDevicePolicy<> >( subRegion.size(), [=] GEOS_HOST_DEVICE ( localIndex const k ) + { + slipRate_n [k] = slipRate[k]; + stateVariable_n[k] = stateVariable[k]; + + normalTraction[k] = backgroundNormalStress[k]; + LvArray::tensorOps::copy< 2 >( shearTraction[k], backgroundShearStress[k] ); + + normalTraction_n[k] = normalTraction[k]; + LvArray::tensorOps::copy< 2 >( shearTraction_n[k], shearTraction[k] ); + } ); + } ); + } ); + } +} + +void QDRateAndStateBase::saveState( DomainPartition & domain ) const +{ + forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &, + MeshLevel & mesh, + arrayView1d< string const > const & regionNames ) + + { + mesh.getElemManager().forElementSubRegions< SurfaceElementSubRegion >( regionNames, + [&]( localIndex const, + SurfaceElementSubRegion & subRegion ) + { + arrayView1d< real64 const > const stateVariable = subRegion.getField< rateAndState::stateVariable >(); + arrayView2d< real64 const > const slipVelocity = subRegion.getField< rateAndState::slipVelocity >(); + arrayView2d< real64 const > const deltaSlip = subRegion.getField< contact::deltaSlip >(); + arrayView2d< real64 const > const dispJump = subRegion.getField< contact::dispJump >(); + arrayView2d< real64 const > const shearTraction = subRegion.getField< rateAndState::shearTraction >(); + arrayView1d< real64 const > const normalTraction = subRegion.getField< rateAndState::normalTraction >(); + + + arrayView1d< real64 > const stateVariable_n = subRegion.getField< rateAndState::stateVariable_n >(); + arrayView2d< real64 > const slipVelocity_n = subRegion.getField< rateAndState::slipVelocity_n >(); + arrayView2d< real64 > const deltaSlip_n = subRegion.getField< contact::deltaSlip >(); + arrayView2d< real64 > const dispJump_n = subRegion.getField< contact::dispJump_n >(); + arrayView2d< real64 > const shearTraction_n = subRegion.getField< rateAndState::shearTraction_n >(); + arrayView1d< real64 > const normalTraction_n = subRegion.getField< rateAndState::normalTraction_n >(); + + + forAll< parallelDevicePolicy<> >( subRegion.size(), [=] GEOS_HOST_DEVICE ( localIndex const k ) + { + stateVariable_n[k] = stateVariable[k]; + normalTraction_n[k] = normalTraction[k]; + LvArray::tensorOps::copy< 2 >( deltaSlip_n[k], deltaSlip[k] ); + LvArray::tensorOps::copy< 2 >( slipVelocity_n[k], slipVelocity[k] ); + LvArray::tensorOps::copy< 3 >( dispJump_n[k], dispJump[k] ); + LvArray::tensorOps::copy< 2 >( shearTraction_n[k], shearTraction[k] ); + } ); + } ); + } ); +} + +} // namespace geos diff --git a/src/coreComponents/physicsSolvers/inducedSeismicity/QDRateAndStateBase.hpp b/src/coreComponents/physicsSolvers/inducedSeismicity/QDRateAndStateBase.hpp new file mode 100644 index 00000000000..17b4ecb0685 --- /dev/null +++ b/src/coreComponents/physicsSolvers/inducedSeismicity/QDRateAndStateBase.hpp @@ -0,0 +1,89 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * 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_PHYSICSSOLVERS_INDUCEDSEISMICITY_QDRATEANDSTATEBASE_HPP +#define GEOS_PHYSICSSOLVERS_INDUCEDSEISMICITY_QDRATEANDSTATEBASE_HPP + +#include "physicsSolvers/PhysicsSolverBase.hpp" + +namespace geos +{ + +class QDRateAndStateBase : public PhysicsSolverBase +{ +public: + /// The default nullary constructor is disabled to avoid compiler auto-generation: + QDRateAndStateBase() = delete; + + /// The constructor needs a user-defined "name" and a parent Group (to place this instance in the tree structure of classes) + QDRateAndStateBase( const string & name, + Group * const parent ); + + /// Destructor + virtual ~QDRateAndStateBase() override; + + /// This method ties properties with their supporting mesh + virtual void registerDataOnMesh( Group & meshBodies ) override; + + struct viewKeyStruct : public PhysicsSolverBase::viewKeyStruct + { + /// Friction law name string + constexpr static char const * frictionLawNameString() { return "frictionLawName"; } + /// Friction law name string + constexpr static char const * shearImpedanceString() { return "shearImpedance"; } + }; + + /** + * @brief Save the current state of the solver fields + * @param domain the domain object + */ + void saveState( DomainPartition & domain ) const; + + /** + * @brief Check that only one of slip rate or slip velocity are specified as initial conditions + * and initialize the unspecified field + * @param subRegion the element subregion + */ + void enforceRateAndVelocityConsistency( SurfaceElementSubRegion & subRegion ) const; + + /** + * @brief Compute stresses and update tractions on the fault + * @param time_n the current time + * @param dt the time step + * @param cycleNumber the current cycle number + * @param domain the domain object + */ + virtual real64 updateStresses( real64 const & time_n, + real64 const & dt, + const int cycleNumber, + DomainPartition & domain ) const = 0; + + /** + * @brief Apply initial conditions to fields on the fault + * @param cycleNumber the current cycle number + * @param domain the domain object + */ + virtual void applyInitialConditionsToFault( int const cycleNumber, + DomainPartition & domain ) const; + +protected: + + /// shear impedance + real64 m_shearImpedance; +}; + +} /* namespace geos */ + +#endif /* GEOS_PHYSICSSOLVERS_INDUCEDSEISMICTY_QDRATEANDSTATEBASE_HPP */ diff --git a/src/coreComponents/physicsSolvers/inducedSeismicity/QuasiDynamicEQ.cpp b/src/coreComponents/physicsSolvers/inducedSeismicity/QuasiDynamicEQ.cpp deleted file mode 100644 index 3ddf5fc1494..00000000000 --- a/src/coreComponents/physicsSolvers/inducedSeismicity/QuasiDynamicEQ.cpp +++ /dev/null @@ -1,287 +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 QuasiDynamicEQ.cpp - */ - -#include "QuasiDynamicEQ.hpp" - -#include "dataRepository/InputFlags.hpp" -#include "mesh/DomainPartition.hpp" -#include "kernels/RateAndStateKernels.hpp" -#include "rateAndStateFields.hpp" -#include "physicsSolvers/contact/ContactFields.hpp" -#include "fieldSpecification/FieldSpecificationManager.hpp" - -namespace geos -{ - -using namespace dataRepository; -using namespace fields; -using namespace constitutive; -using namespace rateAndStateKernels; - -QuasiDynamicEQ::QuasiDynamicEQ( const string & name, - Group * const parent ): - PhysicsSolverBase( name, parent ), - m_stressSolver( nullptr ), - m_stressSolverName( "SpringSlider" ), - m_shearImpedance( 0.0 ), - m_targetSlipIncrement( 1.0e-7 ) -{ - this->registerWrapper( viewKeyStruct::shearImpedanceString(), &m_shearImpedance ). - setInputFlag( InputFlags::REQUIRED ). - setDescription( "Shear impedance." ); - - this->registerWrapper( viewKeyStruct::stressSolverNameString(), &m_stressSolverName ). - setInputFlag( InputFlags::OPTIONAL ). - setDescription( "Name of solver for computing stress. If empty, the spring-slider model is run." ); - - this->registerWrapper( viewKeyStruct::targetSlipIncrementString(), &m_targetSlipIncrement ). - setInputFlag( InputFlags::OPTIONAL ). - setApplyDefaultValue( 1.0e-7 ). - setDescription( "Target slip incrmeent for timestep size selction" ); -} - -void QuasiDynamicEQ::postInputInitialization() -{ - - // Initialize member stress solver as specified in XML input - if( !m_stressSolverName.empty() ) - { - m_stressSolver = &this->getParent().getGroup< PhysicsSolverBase >( m_stressSolverName ); - } - - PhysicsSolverBase::postInputInitialization(); -} - -QuasiDynamicEQ::~QuasiDynamicEQ() -{ - // TODO Auto-generated destructor stub -} - -void QuasiDynamicEQ::registerDataOnMesh( Group & meshBodies ) -{ - PhysicsSolverBase::registerDataOnMesh( meshBodies ); - - forDiscretizationOnMeshTargets( meshBodies, [&] ( string const &, - MeshLevel & mesh, - arrayView1d< string const > const & regionNames ) - { - ElementRegionManager & elemManager = mesh.getElemManager(); - - elemManager.forElementSubRegions< SurfaceElementSubRegion >( regionNames, - [&]( localIndex const, - SurfaceElementSubRegion & subRegion ) - { - // Scalar functions on fault - subRegion.registerField< rateAndState::stateVariable >( getName() ); - subRegion.registerField< rateAndState::stateVariable_n >( getName() ); - subRegion.registerField< rateAndState::slipRate >( getName() ); - - // Tangent (2-component) functions on fault - string const labels2Comp[2] = {"tangent1", "tangent2" }; - subRegion.registerField< rateAndState::slipVelocity >( getName() ). - setDimLabels( 1, labels2Comp ).reference().resizeDimension< 1 >( 2 ); - subRegion.registerField< rateAndState::deltaSlip >( getName() ). - setDimLabels( 1, labels2Comp ).reference().resizeDimension< 1 >( 2 ); - - if( !subRegion.hasWrapper( contact::dispJump::key() )) - { - // 3-component functions on fault - string const labels3Comp[3] = { "normal", "tangent1", "tangent2" }; - subRegion.registerField< contact::dispJump >( getName() ). - setDimLabels( 1, labels3Comp ). - reference().resizeDimension< 1 >( 3 ); - subRegion.registerField< contact::traction >( getName() ). - setDimLabels( 1, labels3Comp ). - reference().resizeDimension< 1 >( 3 ); - - subRegion.registerWrapper< string >( viewKeyStruct::frictionLawNameString() ). - setPlotLevel( PlotLevel::NOPLOT ). - setRestartFlags( RestartFlags::NO_WRITE ). - setSizedFromParent( 0 ); - - string & frictionLawName = subRegion.getReference< string >( viewKeyStruct::frictionLawNameString() ); - frictionLawName =PhysicsSolverBase::getConstitutiveName< FrictionBase >( subRegion ); - GEOS_ERROR_IF( frictionLawName.empty(), GEOS_FMT( "{}: FrictionBase model not found on subregion {}", - getDataContext(), subRegion.getDataContext() ) ); - } - } ); - } ); -} - -real64 QuasiDynamicEQ::solverStep( real64 const & time_n, - real64 const & dt, - int const cycleNumber, - DomainPartition & domain ) -{ - if( cycleNumber == 0 ) - { - /// Apply initial conditions to the Fault - FieldSpecificationManager & fieldSpecificationManager = FieldSpecificationManager::getInstance(); - - forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &, - MeshLevel & mesh, - arrayView1d< string const > const & ) - - { - fieldSpecificationManager.applyInitialConditions( mesh ); - } ); - } - - /// 1. Compute shear and normal tractions - GEOS_LOG_LEVEL_RANK_0( 1, "Stress solver" ); - - real64 const dtStress = updateStresses( time_n, dt, cycleNumber, domain ); - - /// 2. Solve for slip rate and state variable and, compute slip - GEOS_LOG_LEVEL_RANK_0( 1, "Rate and State solver" ); - integer const maxIterNewton = m_nonlinearSolverParameters.m_maxIterNewton; - real64 const newtonTol = m_nonlinearSolverParameters.m_newtonTol; - forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &, - MeshLevel & mesh, - arrayView1d< string const > const & regionNames ) - - { - mesh.getElemManager().forElementSubRegions< SurfaceElementSubRegion >( regionNames, - [&]( localIndex const, - SurfaceElementSubRegion & subRegion ) - { - // solve rate and state equations. - createAndLaunch< ImplicitFixedStressRateAndStateKernel, parallelDevicePolicy<> >( subRegion, viewKeyStruct::frictionLawNameString(), m_shearImpedance, maxIterNewton, newtonTol, time_n, - dtStress ); - // save old state - saveOldStateAndUpdateSlip( subRegion, dtStress ); - } ); - } ); - - // return time step size achieved by stress solver - return dtStress; -} - -real64 QuasiDynamicEQ::updateStresses( real64 const & time_n, - real64 const & dt, - const int cycleNumber, - DomainPartition & domain ) const -{ - // Call member variable stress solver to update the stress state - if( m_stressSolver ) - { - // 1. Solve the momentum balance - real64 const dtStress = m_stressSolver->solverStep( time_n, dt, cycleNumber, domain ); - - return dtStress; - } - else - { - // Spring-slider shear traction computation - forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &, - MeshLevel & mesh, - arrayView1d< string const > const & regionNames ) - - { - mesh.getElemManager().forElementSubRegions< SurfaceElementSubRegion >( regionNames, - [&]( localIndex const, - SurfaceElementSubRegion & subRegion ) - { - - arrayView2d< real64 const > const deltaSlip = subRegion.getField< rateAndState::deltaSlip >(); - arrayView2d< real64 > const traction = subRegion.getField< fields::contact::traction >(); - - string const & fricitonLawName = subRegion.template getReference< string >( viewKeyStruct::frictionLawNameString() ); - RateAndStateFriction const & frictionLaw = getConstitutiveModel< RateAndStateFriction >( subRegion, fricitonLawName ); - - RateAndStateFriction::KernelWrapper frictionKernelWrapper = frictionLaw.createKernelUpdates(); - - forAll< parallelDevicePolicy<> >( subRegion.size(), [=] GEOS_HOST_DEVICE ( localIndex const k ) - { - SpringSliderParameters springSliderParameters = SpringSliderParameters( traction[k][0], - frictionKernelWrapper.getACoefficient( k ), - frictionKernelWrapper.getBCoefficient( k ), - frictionKernelWrapper.getDcCoefficient( k ) ); - - - traction[k][1] = traction[k][1] + springSliderParameters.tauRate * dt - - springSliderParameters.springStiffness * deltaSlip[k][0]; - traction[k][2] = traction[k][2] + springSliderParameters.tauRate * dt - - springSliderParameters.springStiffness * deltaSlip[k][1]; - } ); - } ); - } ); - return dt; - } -} - -void QuasiDynamicEQ::saveOldStateAndUpdateSlip( ElementSubRegionBase & subRegion, real64 const dt ) const -{ - arrayView1d< real64 > const stateVariable = subRegion.getField< rateAndState::stateVariable >(); - arrayView1d< real64 > const stateVariable_n = subRegion.getField< rateAndState::stateVariable_n >(); - arrayView2d< real64 > const slipVelocity = subRegion.getField< rateAndState::slipVelocity >(); - arrayView2d< real64 > const deltaSlip = subRegion.getField< rateAndState::deltaSlip >(); - - arrayView2d< real64 > const dispJump = subRegion.getField< contact::dispJump >(); - - forAll< parallelDevicePolicy<> >( subRegion.size(), [=] GEOS_HOST_DEVICE ( localIndex const k ) - { - stateVariable_n[k] = stateVariable[k]; - deltaSlip[k][0] = slipVelocity[k][0] * dt; - deltaSlip[k][1] = slipVelocity[k][1] * dt; - // Update tangential components of the displacement jump - dispJump[k][1] = dispJump[k][1] + slipVelocity[k][0] * dt; - dispJump[k][2] = dispJump[k][2] + slipVelocity[k][1] * dt; - } ); -} - -real64 QuasiDynamicEQ::setNextDt( real64 const & currentDt, DomainPartition & domain ) -{ - GEOS_UNUSED_VAR( currentDt ); - - real64 maxSlipRate = 0.0; - // Spring-slider shear traction computation - forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &, - MeshLevel const & mesh, - arrayView1d< string const > const & regionNames ) - - { - real64 maxSlipRateOnThisRank = 0.0; - mesh.getElemManager().forElementSubRegions< SurfaceElementSubRegion >( regionNames, - [&]( localIndex const, - SurfaceElementSubRegion const & subRegion ) - { - arrayView1d< real64 const > const slipRate = subRegion.getField< rateAndState::slipRate >(); - - RAJA::ReduceMax< parallelDeviceReduce, real64 > maximumSlipRateOnThisRegion( 0.0 ); - forAll< parallelDevicePolicy<> >( subRegion.size(), [=] GEOS_HOST_DEVICE ( localIndex const k ) - { - maximumSlipRateOnThisRegion.max( slipRate[k] ); - } ); - if( maximumSlipRateOnThisRegion.get() > maxSlipRateOnThisRank ) - maxSlipRateOnThisRank = maximumSlipRateOnThisRegion.get(); - } ); - maxSlipRate = MpiWrapper::max( maxSlipRateOnThisRank ); - } ); - - real64 const nextDt = m_targetSlipIncrement / maxSlipRate; - - GEOS_LOG_LEVEL_RANK_0( 1, GEOS_FMT( "The next dt will be {:.2e} s", nextDt )); - - return nextDt; -} - -REGISTER_CATALOG_ENTRY( PhysicsSolverBase, QuasiDynamicEQ, string const &, dataRepository::Group * const ) - -} // namespace geos diff --git a/src/coreComponents/physicsSolvers/inducedSeismicity/QuasiDynamicEQ.hpp b/src/coreComponents/physicsSolvers/inducedSeismicity/QuasiDynamicEQ.hpp deleted file mode 100644 index 6d85175d3c7..00000000000 --- a/src/coreComponents/physicsSolvers/inducedSeismicity/QuasiDynamicEQ.hpp +++ /dev/null @@ -1,135 +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. - * ------------------------------------------------------------------------------------------------------------ - */ - -#ifndef GEOS_PHYSICSSOLVERS_INDUCED_QUASIDYNAMICEQ_HPP -#define GEOS_PHYSICSSOLVERS_INDUCED_QUASIDYNAMICEQ_HPP - -#include "physicsSolvers/PhysicsSolverBase.hpp" - -namespace geos -{ - -class QuasiDynamicEQ : public PhysicsSolverBase -{ -public: - /// The default nullary constructor is disabled to avoid compiler auto-generation: - QuasiDynamicEQ() = delete; - - /// The constructor needs a user-defined "name" and a parent Group (to place this instance in the tree structure of classes) - QuasiDynamicEQ( const string & name, - Group * const parent ); - - /// Destructor - virtual ~QuasiDynamicEQ() override; - - static string catalogName() { return "QuasiDynamicEQ"; } - - /** - * @return Get the final class Catalog name - */ - virtual string getCatalogName() const override { return catalogName(); } - - /// This method ties properties with their supporting mesh - virtual void registerDataOnMesh( Group & meshBodies ) override; - - struct viewKeyStruct : public PhysicsSolverBase::viewKeyStruct - { - /// stress solver name - constexpr static char const * stressSolverNameString() { return "stressSolverName"; } - /// Friction law name string - constexpr static char const * frictionLawNameString() { return "frictionLawName"; } - /// Friction law name string - constexpr static char const * shearImpedanceString() { return "shearImpedance"; } - /// target slip increment - constexpr static char const * targetSlipIncrementString() { return "targetSlipIncrement"; } - }; - - virtual real64 solverStep( real64 const & time_n, - real64 const & dt, - integer const cycleNumber, - DomainPartition & domain ) override final; - - virtual real64 setNextDt( real64 const & currentDt, - DomainPartition & domain ) override final; - - real64 updateStresses( real64 const & time_n, - real64 const & dt, - const int cycleNumber, - DomainPartition & domain ) const; - - /** - * @brief save the old state - * @param subRegion - */ - void saveOldStateAndUpdateSlip( ElementSubRegionBase & subRegion, real64 const dt ) const; - - -private: - - - - virtual void postInputInitialization() override; - - - - /// pointer to stress solver - PhysicsSolverBase * m_stressSolver; - - /// stress solver name - string m_stressSolverName; - - /// shear impedance - real64 m_shearImpedance; - - /// target slip rate - real64 m_targetSlipIncrement; - - class SpringSliderParameters - { -public: - - GEOS_HOST_DEVICE - SpringSliderParameters( real64 const normalTraction, real64 const a, real64 const b, real64 const Dc ): - tauRate( 1e-4 ), - springStiffness( 0.0 ) - { - real64 const criticalStiffness = normalTraction * (b - a) / Dc; - springStiffness = 0.9 * criticalStiffness; - } - - /// Default copy constructor - SpringSliderParameters( SpringSliderParameters const & ) = default; - - /// Default move constructor - SpringSliderParameters( SpringSliderParameters && ) = default; - - /// Deleted default constructor - SpringSliderParameters() = delete; - - /// Deleted copy assignment operator - SpringSliderParameters & operator=( SpringSliderParameters const & ) = delete; - - /// Deleted move assignment operator - SpringSliderParameters & operator=( SpringSliderParameters && ) = delete; - - real64 tauRate; - - real64 springStiffness; - }; -}; - -} /* namespace geos */ - -#endif /* GEOS_PHYSICSSOLVERS_INDUCED_QUASIDYNAMICEQ_HPP */ diff --git a/src/coreComponents/physicsSolvers/inducedSeismicity/QuasiDynamicEarthQuake.cpp b/src/coreComponents/physicsSolvers/inducedSeismicity/QuasiDynamicEarthQuake.cpp new file mode 100644 index 00000000000..7c45b81a7a1 --- /dev/null +++ b/src/coreComponents/physicsSolvers/inducedSeismicity/QuasiDynamicEarthQuake.cpp @@ -0,0 +1,145 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * 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 QuasiDynamicEarthQuake.cpp + */ + +#include "QuasiDynamicEarthQuake.hpp" + +#include "dataRepository/InputFlags.hpp" +#include "mesh/DomainPartition.hpp" +#include "rateAndStateFields.hpp" +#include "physicsSolvers/contact/ContactFields.hpp" +#include "fieldSpecification/FieldSpecificationManager.hpp" +#include "physicsSolvers/fluidFlow/FlowSolverBaseFields.hpp" + +#include "ExplicitQDRateAndState.hpp" + +namespace geos +{ + +using namespace dataRepository; +using namespace fields; +using namespace constitutive; +using namespace rateAndStateKernels; + +template< typename RSSOLVER_TYPE > +QuasiDynamicEarthQuake< RSSOLVER_TYPE >::QuasiDynamicEarthQuake( const string & name, + Group * const parent ): + RSSOLVER_TYPE( name, parent ), + m_stressSolverName(), + m_stressSolver( nullptr ) +{ + this->registerWrapper( viewKeyStruct::stressSolverNameString(), &m_stressSolverName ). + setInputFlag( InputFlags::REQUIRED ). + setDescription( "Name of solver for computing stress." ); +} + +template< typename RSSOLVER_TYPE > +void QuasiDynamicEarthQuake< RSSOLVER_TYPE >::postInputInitialization() +{ + + // Initialize member stress solver as specified in XML input + m_stressSolver = &this->getParent().template getGroup< PhysicsSolverBase >( m_stressSolverName ); + + PhysicsSolverBase::postInputInitialization(); +} + +template< typename RSSOLVER_TYPE > +QuasiDynamicEarthQuake< RSSOLVER_TYPE >::~QuasiDynamicEarthQuake() +{ + // TODO Auto-generated destructor stub +} + +template< typename RSSOLVER_TYPE > +real64 QuasiDynamicEarthQuake< RSSOLVER_TYPE >::updateStresses( real64 const & time_n, + real64 const & dt, + const int cycleNumber, + DomainPartition & domain ) const +{ + // 1. Setup variables for stress solver + setTargetDispJump( domain ); + + // 2. Solve the momentum balance + real64 const dtAccepted = m_stressSolver->solverStep( time_n, dt, cycleNumber, domain ); + + // 3. Add background stress and possible forcing. + this->forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &, + MeshLevel & mesh, + arrayView1d< string const > const & regionNames ) + { + mesh.getElemManager().forElementSubRegions< SurfaceElementSubRegion >( regionNames, + [&]( localIndex const, + SurfaceElementSubRegion & subRegion ) + { + arrayView2d< real64 const > const traction = subRegion.getField< contact::traction >(); + + arrayView2d< real64 > const shearTraction = subRegion.getField< rateAndState::shearTraction >(); + arrayView1d< real64 > const normalTraction = subRegion.getField< rateAndState::normalTraction >(); + + arrayView2d< real64 const > const backgroundShearStress = subRegion.getField< rateAndState::backgroundShearStress >(); + arrayView1d< real64 const > const backgroundNormalStress = subRegion.getField< rateAndState::backgroundNormalStress >(); + + forAll< parallelDevicePolicy<> >( subRegion.size(), [=] GEOS_HOST_DEVICE ( localIndex const k ) + { + normalTraction[k] = backgroundNormalStress[k] - traction[k][0]; // compressive traction is negative in geos + for( int i = 0; i < 2; ++i ) + { + shearTraction( k, i ) = backgroundShearStress( k, i ) + traction( k, i+1 ); + } + } ); + } ); + } ); + + return dtAccepted; +} + +template< typename RSSOLVER_TYPE > +void QuasiDynamicEarthQuake< RSSOLVER_TYPE >::setTargetDispJump( DomainPartition & domain ) const +{ + this->forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &, + MeshLevel & mesh, + arrayView1d< string const > const & regionNames ) + { + mesh.getElemManager().forElementSubRegions< SurfaceElementSubRegion >( regionNames, + [&]( localIndex const, + SurfaceElementSubRegion & subRegion ) + { + arrayView2d< real64 > const deltaSlip = subRegion.getField< contact::deltaSlip >(); + arrayView2d< real64 > const targetDispJump = subRegion.getField< contact::targetIncrementalJump >(); + + forAll< parallelDevicePolicy<> >( subRegion.size(), [=] GEOS_HOST_DEVICE ( localIndex const k ) + { + targetDispJump( k, 0 ) = 0.0; + targetDispJump( k, 1 ) = deltaSlip( k, 0 ); + targetDispJump( k, 2 ) = deltaSlip( k, 1 ); + } ); + } ); + } ); +} + +template class QuasiDynamicEarthQuake< ImplicitQDRateAndState >; +template class QuasiDynamicEarthQuake< ExplicitQDRateAndState >; + +namespace +{ +typedef QuasiDynamicEarthQuake< ImplicitQDRateAndState > ImplicitQuasiDynamicEarthQuake; +typedef QuasiDynamicEarthQuake< ExplicitQDRateAndState > ExplicitQuasiDynamicEarthQuake; +REGISTER_CATALOG_ENTRY( PhysicsSolverBase, ImplicitQuasiDynamicEarthQuake, string const &, dataRepository::Group * const ) +REGISTER_CATALOG_ENTRY( PhysicsSolverBase, ExplicitQuasiDynamicEarthQuake, string const &, dataRepository::Group * const ) +} + +} // namespace geos diff --git a/src/coreComponents/physicsSolvers/inducedSeismicity/QuasiDynamicEarthQuake.hpp b/src/coreComponents/physicsSolvers/inducedSeismicity/QuasiDynamicEarthQuake.hpp new file mode 100644 index 00000000000..728ec97ca3d --- /dev/null +++ b/src/coreComponents/physicsSolvers/inducedSeismicity/QuasiDynamicEarthQuake.hpp @@ -0,0 +1,72 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * 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_PHYSICSSOLVERS_INDUCED_QUASIDYNAMICEARTHQUAKE_HPP +#define GEOS_PHYSICSSOLVERS_INDUCED_QUASIDYNAMICEARTHQUAKE_HPP + +#include "ImplicitQDRateAndState.hpp" + +namespace geos +{ + +template< typename RSSOLVER_TYPE = ImplicitQDRateAndState > +class QuasiDynamicEarthQuake : public RSSOLVER_TYPE +{ +public: + + /// The default nullary constructor is disabled to avoid compiler auto-generation: + QuasiDynamicEarthQuake() = delete; + + /// The constructor needs a user-defined "name" and a parent Group (to place this instance in the tree structure of classes) + QuasiDynamicEarthQuake( const string & name, + dataRepository::Group * const parent ); + + /// Destructor + virtual ~QuasiDynamicEarthQuake() override; + + static string catalogName() { return RSSOLVER_TYPE::derivedSolverPrefix() + "QuasiDynamicEQ"; } + + /** + * @return Get the final class Catalog name + */ + virtual string getCatalogName() const override { return catalogName(); } + + struct viewKeyStruct : public RSSOLVER_TYPE::viewKeyStruct + { + /// stress solver name + static constexpr char const * stressSolverNameString() { return "stressSolverName"; } + }; + + void postInputInitialization() override final; + + void setTargetDispJump( DomainPartition & domain ) const; + + virtual real64 updateStresses( real64 const & time_n, + real64 const & dt, + const int cycleNumber, + DomainPartition & domain ) const override final; + +private: + + string m_stressSolverName; + + PhysicsSolverBase * m_stressSolver; + +}; + +} /* namespace geos */ + +#endif /* GEOS_PHYSICSSOLVERS_INDUCED_QUASIDYNAMICEQBASE_HPP */ diff --git a/src/coreComponents/physicsSolvers/inducedSeismicity/SpringSlider.cpp b/src/coreComponents/physicsSolvers/inducedSeismicity/SpringSlider.cpp new file mode 100644 index 00000000000..0c427304f9d --- /dev/null +++ b/src/coreComponents/physicsSolvers/inducedSeismicity/SpringSlider.cpp @@ -0,0 +1,158 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * 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 SpringSlider.cpp + */ + +#include "SpringSlider.hpp" + +#include "dataRepository/InputFlags.hpp" +#include "mesh/DomainPartition.hpp" +#include "rateAndStateFields.hpp" +#include "physicsSolvers/contact/ContactFields.hpp" +#include "fieldSpecification/FieldSpecificationManager.hpp" +#include "constitutive/contact/RateAndStateFriction.hpp" +#include "ExplicitQDRateAndState.hpp" + +namespace geos +{ + +using namespace dataRepository; +using namespace fields; +using namespace constitutive; + +template< typename RSSOLVER_TYPE > +SpringSlider< RSSOLVER_TYPE >::SpringSlider( const string & name, + Group * const parent ): + RSSOLVER_TYPE( name, parent ) +{} + +template< typename RSSOLVER_TYPE > +SpringSlider< RSSOLVER_TYPE >::~SpringSlider() +{ + // TODO Auto-generated destructor stub +} + +template< typename RSSOLVER_TYPE > +void SpringSlider< RSSOLVER_TYPE >::registerDataOnMesh( Group & meshBodies ) +{ + RSSOLVER_TYPE::registerDataOnMesh( meshBodies ); + + this->forDiscretizationOnMeshTargets( meshBodies, [&] ( string const &, + MeshLevel & mesh, + arrayView1d< string const > const & regionNames ) + { + ElementRegionManager & elemManager = mesh.getElemManager(); + + elemManager.forElementSubRegions< SurfaceElementSubRegion >( regionNames, + [&]( localIndex const, + SurfaceElementSubRegion & subRegion ) + { + // 3-component functions on fault + string const labels3Comp[3] = { "normal", "tangent1", "tangent2" }; + subRegion.registerField< contact::dispJump >( this->getName() ). + setDimLabels( 1, labels3Comp ). + reference().template resizeDimension< 1 >( 3 ); + + subRegion.registerField< contact::dispJump_n >( this->getName() ). + setDimLabels( 1, labels3Comp ). + reference().template resizeDimension< 1 >( 3 ); + + string const labels2Comp[2] = { "tangent1", "tangent2" }; + + subRegion.registerField< contact::deltaSlip >( this->getName() ). + setDimLabels( 1, labels2Comp ). + reference().template resizeDimension< 1 >( 2 ); + + subRegion.registerField< contact::deltaSlip_n >( this->getName() ). + setDimLabels( 1, labels2Comp ). + reference().template resizeDimension< 1 >( 2 ); + + subRegion.registerWrapper< string >( viewKeyStruct::frictionLawNameString() ). + setPlotLevel( PlotLevel::NOPLOT ). + setRestartFlags( RestartFlags::NO_WRITE ). + setSizedFromParent( 0 ); + + string & frictionLawName = subRegion.getReference< string >( viewKeyStruct::frictionLawNameString() ); + frictionLawName =PhysicsSolverBase::getConstitutiveName< FrictionBase >( subRegion ); + GEOS_ERROR_IF( frictionLawName.empty(), GEOS_FMT( "{}: FrictionBase model not found on subregion {}", + this->getDataContext(), subRegion.getDataContext() ) ); + } ); + } ); +} + +template< typename RSSOLVER_TYPE > +real64 SpringSlider< RSSOLVER_TYPE >::updateStresses( real64 const & time_n, + real64 const & dt, + const int cycleNumber, + DomainPartition & domain ) const + +{ + GEOS_UNUSED_VAR( cycleNumber, time_n ); + // Spring-slider shear traction computation + this->forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &, + MeshLevel & mesh, + arrayView1d< string const > const & regionNames ) + + { + mesh.getElemManager().forElementSubRegions< SurfaceElementSubRegion >( regionNames, + [&]( localIndex const, + SurfaceElementSubRegion & subRegion ) + { + + arrayView2d< real64 const > const deltaSlip = subRegion.getField< fields::contact::deltaSlip >(); + arrayView2d< real64 > const shearTraction = subRegion.getField< fields::rateAndState::shearTraction >(); + arrayView2d< real64 > const shearTraction_n = subRegion.getField< fields::rateAndState::shearTraction_n >(); + + arrayView1d< real64 > const normalTraction = subRegion.getField< fields::rateAndState::normalTraction >(); + + + string const & fricitonLawName = subRegion.template getReference< string >( viewKeyStruct::frictionLawNameString() ); + RateAndStateFriction const & frictionLaw = this->template getConstitutiveModel< RateAndStateFriction >( subRegion, fricitonLawName ); + + RateAndStateFriction::KernelWrapper frictionKernelWrapper = frictionLaw.createKernelUpdates(); + + forAll< parallelDevicePolicy<> >( subRegion.size(), [=] GEOS_HOST_DEVICE ( localIndex const k ) + { + SpringSliderParameters springSliderParameters = SpringSliderParameters( normalTraction[k], + frictionKernelWrapper.getACoefficient( k ), + frictionKernelWrapper.getBCoefficient( k ), + frictionKernelWrapper.getDcCoefficient( k ) ); + + + + shearTraction[k][0] = shearTraction_n[k][0] + springSliderParameters.tauRate * dt + - springSliderParameters.springStiffness * deltaSlip[k][0]; + shearTraction[k][1] = shearTraction_n[k][1] + springSliderParameters.tauRate * dt + - springSliderParameters.springStiffness * deltaSlip[k][1]; + } ); + } ); + } ); + return dt; +} + +template class SpringSlider< ImplicitQDRateAndState >; +template class SpringSlider< ExplicitQDRateAndState >; + +namespace +{ +typedef SpringSlider< ImplicitQDRateAndState > ImplicitSpringSlider; +typedef SpringSlider< ExplicitQDRateAndState > ExplicitSpringSlider; +REGISTER_CATALOG_ENTRY( PhysicsSolverBase, ImplicitSpringSlider, string const &, dataRepository::Group * const ) +REGISTER_CATALOG_ENTRY( PhysicsSolverBase, ExplicitSpringSlider, string const &, dataRepository::Group * const ) +} + +} // namespace geos diff --git a/src/coreComponents/physicsSolvers/inducedSeismicity/SpringSlider.hpp b/src/coreComponents/physicsSolvers/inducedSeismicity/SpringSlider.hpp new file mode 100644 index 00000000000..08c9a829c7e --- /dev/null +++ b/src/coreComponents/physicsSolvers/inducedSeismicity/SpringSlider.hpp @@ -0,0 +1,91 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * 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_PHYSICSSOLVERS_INDUCEDSEISMICITY_SPRINGSLIDER_HPP +#define GEOS_PHYSICSSOLVERS_INDUCEDSEISMICITY_SPRINGSLIDER_HPP + +#include "physicsSolvers/inducedSeismicity/ImplicitQDRateAndState.hpp" + +namespace geos +{ + +template< typename RSSOLVER_TYPE = ImplicitQDRateAndState > +class SpringSlider : public RSSOLVER_TYPE +{ +public: + + SpringSlider() = delete; + + SpringSlider( const string & name, + dataRepository::Group * const parent ); + + /// Destructor + virtual ~SpringSlider() override; + + static string catalogName() { return RSSOLVER_TYPE::derivedSolverPrefix() + "SpringSlider"; } + + virtual string getCatalogName() const override { return catalogName(); } + + virtual void registerDataOnMesh( dataRepository::Group & meshBodies ) override; + + struct viewKeyStruct : public RSSOLVER_TYPE::viewKeyStruct + {}; + + virtual real64 updateStresses( real64 const & time_n, + real64 const & dt, + const int cycleNumber, + DomainPartition & domain ) const override final; + +private: + + class SpringSliderParameters + { +public: + + GEOS_HOST_DEVICE + SpringSliderParameters( real64 const normalTraction, real64 const a, real64 const b, real64 const Dc ): + tauRate( 1e-4 ), + springStiffness( 0.0 ) + { + real64 const criticalStiffness = normalTraction * (b - a) / Dc; + springStiffness = 0.9 * criticalStiffness; + } + + /// Default copy constructor + SpringSliderParameters( SpringSliderParameters const & ) = default; + + /// Default move constructor + SpringSliderParameters( SpringSliderParameters && ) = default; + + /// Deleted default constructor + SpringSliderParameters() = delete; + + /// Deleted copy assignment operator + SpringSliderParameters & operator=( SpringSliderParameters const & ) = delete; + + /// Deleted move assignment operator + SpringSliderParameters & operator=( SpringSliderParameters && ) = delete; + + real64 tauRate; + + real64 springStiffness; + }; +}; + +} /* namespace geos */ + +#endif /* GEOS_PHYSICSSOLVERS_INDUCEDSEISMICITY_SPRINGSLIDER_HPP */ diff --git a/src/coreComponents/physicsSolvers/inducedSeismicity/kernels/RateAndStateKernels.hpp b/src/coreComponents/physicsSolvers/inducedSeismicity/kernels/ExplicitRateAndStateKernels.hpp similarity index 65% rename from src/coreComponents/physicsSolvers/inducedSeismicity/kernels/RateAndStateKernels.hpp rename to src/coreComponents/physicsSolvers/inducedSeismicity/kernels/ExplicitRateAndStateKernels.hpp index d91dff75ef6..baf0ee6b06b 100644 --- a/src/coreComponents/physicsSolvers/inducedSeismicity/kernels/RateAndStateKernels.hpp +++ b/src/coreComponents/physicsSolvers/inducedSeismicity/kernels/ExplicitRateAndStateKernels.hpp @@ -13,13 +13,10 @@ * ------------------------------------------------------------------------------------------------------------ */ -#ifndef GEOS_PHYSICSSOLVERS_RATEANDSTATEKERNELS_HPP_ -#define GEOS_PHYSICSSOLVERS_RATEANDSTATEKERNELS_HPP_ +#ifndef GEOS_PHYSICSSOLVERS_INDUCEDSEISMICITY_KERNELS_EXPLICITRATEANDSTATEKERNELS_HPP_ +#define GEOS_PHYSICSSOLVERS_INDUCEDSEISMICITY_KERNELS_EXPLICITRATEANDSTATEKERNELS_HPP_ -#include "common/DataTypes.hpp" -#include "common/GEOS_RAJA_Interface.hpp" -#include "constitutive/contact/RateAndStateFriction.hpp" -#include "physicsSolvers/inducedSeismicity/rateAndStateFields.hpp" +#include "RateAndStateKernelsBase.hpp" #include "denseLinearAlgebra/denseLASolvers.hpp" namespace geos @@ -28,137 +25,6 @@ namespace geos namespace rateAndStateKernels { -// TBD: Pass the kernel and add getters for relevant fields to make this function general purpose and avoid -// wrappers? -GEOS_HOST_DEVICE -static void projectSlipRateBase( localIndex const k, - real64 const frictionCoefficient, - real64 const shearImpedance, - arrayView2d< real64 const > const traction, - arrayView1d< real64 const > const slipRate, - arrayView2d< real64 > const slipVelocity ) -{ - // Project slip rate onto shear traction to get slip velocity components - real64 const frictionForce = traction[k][0] * frictionCoefficient; - real64 const projectionScaling = 1.0 / ( shearImpedance + frictionForce / slipRate[k] ); - slipVelocity[k][0] = projectionScaling * traction[k][1]; - slipVelocity[k][1] = projectionScaling * traction[k][2]; -} - -/** - * @class ImplicitFixedStressRateAndStateKernel - * - * @brief - * - * @details - */ -class ImplicitFixedStressRateAndStateKernel -{ -public: - - ImplicitFixedStressRateAndStateKernel( SurfaceElementSubRegion & subRegion, - constitutive::RateAndStateFriction const & frictionLaw, - real64 const shearImpedance ): - m_slipRate( subRegion.getField< fields::rateAndState::slipRate >() ), - m_stateVariable( subRegion.getField< fields::rateAndState::stateVariable >() ), - m_stateVariable_n( subRegion.getField< fields::rateAndState::stateVariable_n >() ), - m_traction( subRegion.getField< fields::contact::traction >() ), - m_slipVelocity( subRegion.getField< fields::rateAndState::slipVelocity >() ), - m_shearImpedance( shearImpedance ), - m_frictionLaw( frictionLaw.createKernelUpdates() ) - {} - - /** - * @struct StackVariables - * @brief Kernel variables located on the stack - */ - struct StackVariables - { -public: - - GEOS_HOST_DEVICE - StackVariables( ) - {} - - real64 jacobian[2][2]{}; - - real64 rhs[2]{}; - - }; - - GEOS_HOST_DEVICE - void setup( localIndex const k, - real64 const dt, - StackVariables & stack ) const - { - real64 const normalTraction = m_traction[k][0]; - real64 const shearTractionMagnitude = LvArray::math::sqrt( m_traction[k][1] * m_traction[k][1] + m_traction[k][2] * m_traction[k][2] ); - // Eq 1: Scalar force balance for slipRate and shear traction magnitude - stack.rhs[0] = shearTractionMagnitude - m_shearImpedance * m_slipRate[k] - - normalTraction * m_frictionLaw.frictionCoefficient( k, m_slipRate[k], m_stateVariable[k] ); - real64 const dFriction[2] = { -normalTraction * m_frictionLaw.dFrictionCoefficient_dStateVariable( k, m_slipRate[k], m_stateVariable[k] ), - -m_shearImpedance - normalTraction * m_frictionLaw.dFrictionCoefficient_dSlipRate( k, m_slipRate[k], m_stateVariable[k] ) }; - - // Eq 2: slip law - stack.rhs[1] = (m_stateVariable[k] - m_stateVariable_n[k]) / dt - m_frictionLaw.stateEvolution( k, m_slipRate[k], m_stateVariable[k] ); - real64 const dStateEvolutionLaw[2] = { 1 / dt - m_frictionLaw.dStateEvolution_dStateVariable( k, m_slipRate[k], m_stateVariable[k] ), - -m_frictionLaw.dStateEvolution_dSlipRate( k, m_slipRate[k], m_stateVariable[k] ) }; - - // Assemble Jacobian matrix - stack.jacobian[0][0] = dFriction[0]; // derivative of Eq 1 w.r.t. stateVariable - stack.jacobian[0][1] = dFriction[1]; // derivative of Eq 1 w.r.t. slipRate - stack.jacobian[1][0] = dStateEvolutionLaw[0]; // derivative of Eq 2 w.r.t. stateVariable - stack.jacobian[1][1] = dStateEvolutionLaw[1]; // derivative of Eq 2 w.r.t. slipRate - } - - GEOS_HOST_DEVICE - void solve( localIndex const k, - StackVariables & stack ) const - { - /// Solve 2x2 system - real64 solution[2] = {0.0, 0.0}; - denseLinearAlgebra::solve< 2 >( stack.jacobian, stack.rhs, solution ); - - // Update variables - m_stateVariable[k] -= solution[0]; - m_slipRate[k] -= solution[1]; - } - - GEOS_HOST_DEVICE - void projectSlipRate( localIndex const k ) const - { - real64 const frictionCoefficient = m_frictionLaw.frictionCoefficient( k, m_slipRate[k], m_stateVariable[k] ); - projectSlipRateBase( k, frictionCoefficient, m_shearImpedance, m_traction, m_slipRate, m_slipVelocity ); - } - - GEOS_HOST_DEVICE - camp::tuple< int, real64 > checkConvergence( StackVariables const & stack, - real64 const tol ) const - { - real64 const residualNorm = LvArray::tensorOps::l2Norm< 2 >( stack.rhs ); - int const converged = residualNorm < tol ? 1 : 0; - camp::tuple< int, real64 > result { converged, residualNorm }; - return result; - } - -private: - - arrayView1d< real64 > const m_slipRate; - - arrayView1d< real64 > const m_stateVariable; - - arrayView1d< real64 const > const m_stateVariable_n; - - arrayView2d< real64 const > const m_traction; - - arrayView2d< real64 > const m_slipVelocity; - - real64 const m_shearImpedance; - - constitutive::RateAndStateFriction::KernelWrapper m_frictionLaw; - -}; - /** * @class ExplicitRateAndStateKernel * @@ -175,7 +41,8 @@ class ExplicitRateAndStateKernel real64 const shearImpedance ): m_slipRate( subRegion.getField< fields::rateAndState::slipRate >() ), m_stateVariable( subRegion.getField< fields::rateAndState::stateVariable >() ), - m_traction( subRegion.getField< fields::contact::traction >() ), + m_normalTraction( subRegion.getField< fields::rateAndState::normalTraction >() ), + m_shearTraction( subRegion.getField< fields::rateAndState::shearTraction >() ), m_slipVelocity( subRegion.getField< fields::rateAndState::slipVelocity >() ), m_shearImpedance( shearImpedance ), m_frictionLaw( frictionLaw.createKernelUpdates() ) @@ -189,12 +56,10 @@ class ExplicitRateAndStateKernel { public: - GEOS_HOST_DEVICE - StackVariables( ) - {} + StackVariables() = default; - real64 jacobian; - real64 rhs; + real64 jacobian{}; + real64 rhs{}; }; @@ -204,8 +69,8 @@ class ExplicitRateAndStateKernel StackVariables & stack ) const { GEOS_UNUSED_VAR( dt ); - real64 const normalTraction = m_traction[k][0]; - real64 const shearTractionMagnitude = LvArray::math::sqrt( m_traction[k][1] * m_traction[k][1] + m_traction[k][2] * m_traction[k][2] ); + real64 const normalTraction = m_normalTraction[k]; + real64 const shearTractionMagnitude = LvArray::tensorOps::l2Norm< 2 >( m_shearTraction[k] ); // Slip rate is bracketed between [0, shear traction magnitude / shear impedance] // If slip rate is outside the bracket, re-initialize to the middle value @@ -224,7 +89,7 @@ class ExplicitRateAndStateKernel // Slip rate is bracketed between [0, shear traction magnitude / shear impedance] // Check that the update did not end outside of the bracket. - real64 const shearTractionMagnitude = LvArray::math::sqrt( m_traction[k][1] * m_traction[k][1] + m_traction[k][2] * m_traction[k][2] ); + real64 const shearTractionMagnitude = LvArray::tensorOps::l2Norm< 2 >( m_shearTraction[k] ); real64 const upperBound = shearTractionMagnitude/m_shearImpedance; if( m_slipRate[k] > upperBound ) m_slipRate[k] = 0.5*upperBound; @@ -245,7 +110,43 @@ class ExplicitRateAndStateKernel void projectSlipRate( localIndex const k ) const { real64 const frictionCoefficient = m_frictionLaw.frictionCoefficient( k, m_slipRate[k], m_stateVariable[k] ); - projectSlipRateBase( k, frictionCoefficient, m_shearImpedance, m_traction, m_slipRate, m_slipVelocity ); + projectSlipRateBase( k, frictionCoefficient, m_shearImpedance, m_normalTraction, m_shearTraction, m_slipRate, m_slipVelocity ); + } + + GEOS_HOST_DEVICE + void udpateVariables( localIndex const k ) const + { + projectSlipRate( k ); + } + + GEOS_HOST_DEVICE + void resetState( localIndex const k ) const + { + GEOS_UNUSED_VAR( k ); + } + + /** + * @brief Performs the kernel launch + * @tparam KernelType The Rate-and-state kernel to launch + * @tparam POLICY the policy used in the RAJA kernels + */ + template< typename POLICY > + static real64 + solveRateAndStateEquation( SurfaceElementSubRegion & subRegion, + ExplicitRateAndStateKernel & kernel, + real64 dt, + integer const maxNewtonIter, + real64 const newtonTol ) + { + GEOS_MARK_FUNCTION; + + newtonSolve< POLICY >( subRegion, kernel, dt, maxNewtonIter, newtonTol ); + + forAll< POLICY >( subRegion.size(), [=] GEOS_HOST_DEVICE ( localIndex const k ) + { + kernel.projectSlipRate( k ); + } ); + return dt; } private: @@ -254,7 +155,9 @@ class ExplicitRateAndStateKernel arrayView1d< real64 > const m_stateVariable; - arrayView2d< real64 const > const m_traction; + arrayView1d< real64 const > const m_normalTraction; + + arrayView2d< real64 const > const m_shearTraction; arrayView2d< real64 > const m_slipVelocity; @@ -264,65 +167,6 @@ class ExplicitRateAndStateKernel }; - -/** - * @brief Performs the kernel launch - * @tparam KernelType The Rate-and-state kernel to launch - * @tparam POLICY the policy used in the RAJA kernels - */ -template< typename KernelType, typename POLICY > -static void -createAndLaunch( SurfaceElementSubRegion & subRegion, - string const & frictionLawNameKey, - real64 const shearImpedance, - integer const maxIterNewton, - real64 const newtonTol, - real64 const time_n, - real64 const dt ) -{ - GEOS_MARK_FUNCTION; - - GEOS_UNUSED_VAR( time_n ); - - string const & frictionaLawName = subRegion.getReference< string >( frictionLawNameKey ); - constitutive::RateAndStateFriction const & frictionLaw = subRegion.getConstitutiveModel< constitutive::RateAndStateFriction >( frictionaLawName ); - KernelType kernel( subRegion, frictionLaw, shearImpedance ); - - // Newton loop (outside of the kernel launch) - bool allConverged = false; - for( integer iter = 0; iter < maxIterNewton; iter++ ) - { - RAJA::ReduceMin< parallelDeviceReduce, int > converged( 1 ); - RAJA::ReduceMax< parallelDeviceReduce, real64 > residualNorm( 0.0 ); - forAll< POLICY >( subRegion.size(), [=] GEOS_HOST_DEVICE ( localIndex const k ) - { - typename KernelType::StackVariables stack; - kernel.setup( k, dt, stack ); - kernel.solve( k, stack ); - auto const [elementConverged, elementResidualNorm] = kernel.checkConvergence( stack, newtonTol ); - converged.min( elementConverged ); - residualNorm.max( elementResidualNorm ); - } ); - - real64 const maxResidualNorm = MpiWrapper::max( residualNorm.get() ); - GEOS_LOG_RANK_0( GEOS_FMT( "-----iter {} : residual = {:.10e} ", iter, maxResidualNorm ) ); - - if( converged.get() ) - { - allConverged = true; - break; - } - } - if( !allConverged ) - { - GEOS_ERROR( " Failed to converge" ); - } - forAll< POLICY >( subRegion.size(), [=] GEOS_HOST_DEVICE ( localIndex const k ) - { - kernel.projectSlipRate( k ); - } ); -} - /** * @brief Butcher table for embedded RK3(2) method using Kuttas third order * method for the high-order update, and an explicit trapezoidal rule @@ -366,7 +210,8 @@ struct BogackiShampine32Table * * @tparam Butcher table defining the Runge-Kutta method. */ -template< typename TABLE_TYPE > class EmbeddedRungeKuttaKernel +template< typename TABLE_TYPE > +class EmbeddedRungeKuttaKernel { public: @@ -378,8 +223,8 @@ template< typename TABLE_TYPE > class EmbeddedRungeKuttaKernel m_slipRate( subRegion.getField< fields::rateAndState::slipRate >() ), m_slipVelocity( subRegion.getField< fields::rateAndState::slipVelocity >() ), m_slipVelocity_n( subRegion.getField< fields::rateAndState::slipVelocity_n >() ), - m_deltaSlip( subRegion.getField< fields::rateAndState::deltaSlip >() ), - m_deltaSlip_n( subRegion.getField< fields::rateAndState::deltaSlip_n >() ), + m_deltaSlip( subRegion.getField< fields::contact::deltaSlip >() ), + m_deltaSlip_n( subRegion.getField< fields::contact::deltaSlip_n >() ), m_dispJump( subRegion.getField< fields::contact::dispJump >() ), m_dispJump_n( subRegion.getField< fields::contact::dispJump_n >() ), m_error( subRegion.getField< fields::rateAndState::error >() ), @@ -579,4 +424,4 @@ template< typename TABLE_TYPE > class EmbeddedRungeKuttaKernel } /* namespace geos */ -#endif /* GEOS_PHYSICSSOLVERS_RATEANDSTATEKERNELS_HPP_ */ +#endif /* GEOS_PHYSICSSOLVERS_INDUCEDSEISMICITY_KERNELS_EXPLICITRATEANDSTATEKERNELS_HPP_ */ diff --git a/src/coreComponents/physicsSolvers/inducedSeismicity/kernels/ImplicitRateAndStateKernels.hpp b/src/coreComponents/physicsSolvers/inducedSeismicity/kernels/ImplicitRateAndStateKernels.hpp new file mode 100644 index 00000000000..8ff3951f299 --- /dev/null +++ b/src/coreComponents/physicsSolvers/inducedSeismicity/kernels/ImplicitRateAndStateKernels.hpp @@ -0,0 +1,225 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * 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) 2018-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_PHYSICSSOLVERS_INDUCEDSEISMICITY_KERNELS_IMPLICITRATEANDSTATEKERNELS_HPP_ +#define GEOS_PHYSICSSOLVERS_INDUCEDSEISMICITY_KERNELS_IMPLICITRATEANDSTATEKERNELS_HPP_ + +#include "RateAndStateKernelsBase.hpp" +#include "denseLinearAlgebra/denseLASolvers.hpp" + +namespace geos +{ + +namespace rateAndStateKernels +{ + +/** + * @class ImplicitFixedStressRateAndStateKernel + * + * @brief + * + * @details + */ +class ImplicitFixedStressRateAndStateKernel +{ +public: + + ImplicitFixedStressRateAndStateKernel( SurfaceElementSubRegion & subRegion, + constitutive::RateAndStateFriction const & frictionLaw, + real64 const shearImpedance ): + m_slipRate( subRegion.getField< fields::rateAndState::slipRate >() ), + m_stateVariable( subRegion.getField< fields::rateAndState::stateVariable >() ), + m_stateVariable_n( subRegion.getField< fields::rateAndState::stateVariable_n >() ), + m_slipRate_n( subRegion.getField< fields::rateAndState::slipRate_n >() ), + m_normalTraction( subRegion.getField< fields::rateAndState::normalTraction >() ), + m_shearTraction( subRegion.getField< fields::rateAndState::shearTraction >() ), + m_slipVelocity( subRegion.getField< fields::rateAndState::slipVelocity >() ), + m_shearImpedance( shearImpedance ), + m_frictionLaw( frictionLaw.createKernelUpdates() ) + {} + + /** + * @struct StackVariables + * @brief Kernel variables located on the stack + */ + struct StackVariables + { +public: + + StackVariables( ) = default; + + real64 jacobian[2][2]{}; + + real64 rhs[2]{}; + + }; + + GEOS_HOST_DEVICE + void setup( localIndex const k, + real64 const dt, + StackVariables & stack ) const + { + real64 const normalTraction = m_normalTraction[k]; + real64 const shearTractionMagnitude = LvArray::tensorOps::l2Norm< 2 >( m_shearTraction[k] ); + + // Eq 1: Scalar force balance for slipRate and shear traction magnitude + stack.rhs[0] = shearTractionMagnitude - m_shearImpedance * m_slipRate[k] + - normalTraction * m_frictionLaw.frictionCoefficient( k, m_slipRate[k], m_stateVariable[k] ); + real64 const dFriction[2] = { -normalTraction * m_frictionLaw.dFrictionCoefficient_dStateVariable( k, m_slipRate[k], m_stateVariable[k] ), + -m_shearImpedance - normalTraction * m_frictionLaw.dFrictionCoefficient_dSlipRate( k, m_slipRate[k], m_stateVariable[k] ) }; + + // Eq 2: slip law + stack.rhs[1] = (m_stateVariable[k] - m_stateVariable_n[k]) / dt - m_frictionLaw.stateEvolution( k, m_slipRate[k], m_stateVariable[k] ); + real64 const dStateEvolutionLaw[2] = { 1.0 / dt - m_frictionLaw.dStateEvolution_dStateVariable( k, m_slipRate[k], m_stateVariable[k] ), + -m_frictionLaw.dStateEvolution_dSlipRate( k, m_slipRate[k], m_stateVariable[k] ) }; + + // Assemble Jacobian matrix + stack.jacobian[0][0] = dFriction[0]; // derivative of Eq 1 w.r.t. stateVariable + stack.jacobian[0][1] = dFriction[1]; // derivative of Eq 1 w.r.t. slipRate + stack.jacobian[1][0] = dStateEvolutionLaw[0]; // derivative of Eq 2 w.r.t. stateVariable + stack.jacobian[1][1] = dStateEvolutionLaw[1]; // derivative of Eq 2 w.r.t. slipRate + } + + GEOS_HOST_DEVICE + void solve( localIndex const k, + StackVariables & stack ) const + { + /// Solve 2x2 system + real64 solution[2] = {0.0, 0.0}; + LvArray::tensorOps::scale< 2 >( stack.rhs, -1.0 ); + denseLinearAlgebra::solve< 2 >( stack.jacobian, stack.rhs, solution ); + + // Slip rate is bracketed between [0, shear traction magnitude / shear impedance] + // Check that the update did not end outside of the bracket. + real64 const shearTractionMagnitude = LvArray::tensorOps::l2Norm< 2 >( m_shearTraction[k] ); + real64 const upperBound = shearTractionMagnitude / m_shearImpedance - m_slipRate[k]; + real64 const lowerBound = -m_slipRate[k]; + + real64 scalingFactor = 1.0; + if( solution[1] > upperBound ) + { + scalingFactor = 0.5 * upperBound / solution[1]; + } + else if( solution[1] < lowerBound ) + { + scalingFactor = 0.5 * lowerBound / solution[1]; + } + + LvArray::tensorOps::scale< 2 >( solution, scalingFactor ); + + // Update variables + m_stateVariable[k] += solution[0]; + m_slipRate[k] += solution[1]; + } + + GEOS_HOST_DEVICE + void projectSlipRate( localIndex const k ) const + { + real64 const frictionCoefficient = m_frictionLaw.frictionCoefficient( k, m_slipRate[k], m_stateVariable[k] ); + projectSlipRateBase( k, frictionCoefficient, m_shearImpedance, m_normalTraction, m_shearTraction, m_slipRate, m_slipVelocity ); + } + + GEOS_HOST_DEVICE + void udpateVariables( localIndex const k ) const + { + projectSlipRate( k ); + m_stateVariable_n[k] = m_stateVariable[k]; + m_slipRate_n[k] = m_slipRate[k]; + } + + GEOS_HOST_DEVICE + camp::tuple< int, real64 > checkConvergence( StackVariables const & stack, + real64 const tol ) const + { + real64 const residualNorm = LvArray::tensorOps::l2Norm< 2 >( stack.rhs ); + int const converged = residualNorm < tol ? 1 : 0; + camp::tuple< int, real64 > result { converged, residualNorm }; + return result; + } + + GEOS_HOST_DEVICE + void resetState( localIndex const k ) const + { + m_stateVariable[k] = m_stateVariable_n[k]; + m_slipRate[k] = m_slipRate_n[k]; + } + + template< typename POLICY > + static real64 solveRateAndStateEquation( SurfaceElementSubRegion & subRegion, + ImplicitFixedStressRateAndStateKernel & kernel, + real64 dt, + integer const maxNewtonIter, + real64 const newtonTol ) + { + bool converged = false; + for( integer attempt = 0; attempt < 5; attempt++ ) + { + if( attempt > 0 ) + { + forAll< POLICY >( subRegion.size(), [=] GEOS_HOST_DEVICE ( localIndex const k ) + { + kernel.resetState( k ); + } ); + } + GEOS_LOG_RANK_0( GEOS_FMT( " Attempt {} ", attempt ) ); + converged = newtonSolve< POLICY >( subRegion, kernel, dt, maxNewtonIter, newtonTol ); + if( converged ) + { + forAll< POLICY >( subRegion.size(), [=] GEOS_HOST_DEVICE ( localIndex const k ) + { + kernel.udpateVariables( k ); + } ); + return dt; + } + else + { + GEOS_LOG_RANK_0( GEOS_FMT( " Attempt {} failed. Halving dt and retrying.", attempt ) ); + dt *= 0.5; + } + } + if( !converged ) + { + GEOS_ERROR( "Maximum number of attempts reached without convergence." ); + } + return dt; + } + +private: + + arrayView1d< real64 > const m_slipRate; + + arrayView1d< real64 > const m_stateVariable; + + arrayView1d< real64 > const m_stateVariable_n; + + arrayView1d< real64 > const m_slipRate_n; + + arrayView1d< real64 > const m_normalTraction; + + arrayView2d< real64 > const m_shearTraction; + + arrayView2d< real64 > const m_slipVelocity; + + real64 const m_shearImpedance; + + constitutive::RateAndStateFriction::KernelWrapper m_frictionLaw; + +}; + +} /* namespace rateAndStateKernels */ + +} /* namespace geos */ + +#endif /* GEOS_PHYSICSSOLVERS_INDUCEDSEISMICITY_KERNELS_IMPLICITRATEANDSTATEKERNELS_HPP_ */ diff --git a/src/coreComponents/physicsSolvers/inducedSeismicity/kernels/RateAndStateKernelsBase.hpp b/src/coreComponents/physicsSolvers/inducedSeismicity/kernels/RateAndStateKernelsBase.hpp new file mode 100644 index 00000000000..c889bb94723 --- /dev/null +++ b/src/coreComponents/physicsSolvers/inducedSeismicity/kernels/RateAndStateKernelsBase.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) 2018-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_PHYSICSSOLVERS_INDUCEDSEISMICITY_KERNELS_RATEANDSTATEKERNELSBASE_HPP_ +#define GEOS_PHYSICSSOLVERS_INDUCEDSEISMICITY_KERNELS_RATEANDSTATEKERNELSBASE_HPP_ + +#include "common/DataTypes.hpp" +#include "common/GEOS_RAJA_Interface.hpp" +#include "constitutive/contact/RateAndStateFriction.hpp" +#include "physicsSolvers/inducedSeismicity/rateAndStateFields.hpp" + +namespace geos +{ + +namespace rateAndStateKernels +{ + +// TBD: Pass the kernel and add getters for relevant fields to make this function general purpose and avoid +// wrappers? +GEOS_HOST_DEVICE +static void projectSlipRateBase( localIndex const k, + real64 const frictionCoefficient, + real64 const shearImpedance, + arrayView1d< real64 const > const normalTraction, + arrayView2d< real64 const > const shearTraction, + arrayView1d< real64 const > const slipRate, + arrayView2d< real64 > const slipVelocity ) +{ + // Project slip rate onto shear traction to get slip velocity components + real64 const frictionForce = normalTraction[k] * frictionCoefficient; + real64 const projectionScaling = 1.0 / ( shearImpedance + frictionForce / slipRate[k] ); + slipVelocity[k][0] = projectionScaling * shearTraction[k][0]; + slipVelocity[k][1] = projectionScaling * shearTraction[k][1]; +} + +template< typename POLICY, typename KERNEL_TYPE > +static bool newtonSolve( SurfaceElementSubRegion & subRegion, + KERNEL_TYPE & kernel, + real64 const dt, + integer const maxNewtonIter, + real64 const newtonTol ) +{ + bool allConverged = false; + for( integer iter = 0; iter < maxNewtonIter; iter++ ) + { + RAJA::ReduceMin< parallelDeviceReduce, int > converged( 1 ); + RAJA::ReduceMax< parallelDeviceReduce, real64 > residualNorm( 0.0 ); + forAll< POLICY >( subRegion.size(), [=] GEOS_HOST_DEVICE ( localIndex const k ) + { + typename KERNEL_TYPE::StackVariables stack; + kernel.setup( k, dt, stack ); + kernel.solve( k, stack ); + auto const [elementConverged, elementResidualNorm] = kernel.checkConvergence( stack, newtonTol ); + converged.min( elementConverged ); + residualNorm.max( elementResidualNorm ); + } ); + + real64 const maxResidualNorm = MpiWrapper::max( residualNorm.get() ); + GEOS_LOG_RANK_0( GEOS_FMT( " Newton iter {} : residual = {:.10e} ", iter, maxResidualNorm ) ); + + if( converged.get() ) + { + allConverged = true; + break; + } + } + return allConverged; +} + +/** + * @brief Performs the kernel launch + * @tparam POLICY the policy used in the RAJA kernels + */ +template< typename KERNEL_TYPE, typename POLICY > +static void +createAndLaunch( SurfaceElementSubRegion & subRegion, + string const & frictionLawNameKey, + real64 const shearImpedance, + integer const maxNewtonIter, + real64 const newtonTol, + real64 const time_n, + real64 const totalDt ) +{ + GEOS_MARK_FUNCTION; + + GEOS_UNUSED_VAR( time_n ); + + string const & frictionaLawName = subRegion.getReference< string >( frictionLawNameKey ); + constitutive::RateAndStateFriction const & frictionLaw = subRegion.getConstitutiveModel< constitutive::RateAndStateFriction >( frictionaLawName ); + KERNEL_TYPE kernel( subRegion, frictionLaw, shearImpedance ); + + real64 dtRemaining = totalDt; + real64 dt = totalDt; + for( integer subStep = 0; subStep < 5 && dtRemaining > 0.0; ++subStep ) + { + real64 dtAccepted = KERNEL_TYPE::template solveRateAndStateEquation< POLICY >( subRegion, kernel, dt, maxNewtonIter, newtonTol ); + dtRemaining -= dtAccepted; + + if( dtRemaining > 0.0 ) + { + dt = dtAccepted; + } + GEOS_LOG_RANK_0( GEOS_FMT( " sub-step = {} completed, dt = {}, remaining dt = {}", subStep, dt, dtRemaining ) ); + } +} + +} /* namespace rateAndStateKernels */ + +} /* namespace geos */ + +#endif /* GEOS_PHYSICSSOLVERS_INDUCEDSEISMICITY_KERNELS_RATEANDSTATEKERNELSBASE_HPP_ */ diff --git a/src/coreComponents/physicsSolvers/inducedSeismicity/rateAndStateFields.hpp b/src/coreComponents/physicsSolvers/inducedSeismicity/rateAndStateFields.hpp index e060dceb28a..599a6f0e0c4 100644 --- a/src/coreComponents/physicsSolvers/inducedSeismicity/rateAndStateFields.hpp +++ b/src/coreComponents/physicsSolvers/inducedSeismicity/rateAndStateFields.hpp @@ -35,15 +35,23 @@ namespace rateAndState DECLARE_FIELD( slipRate, "slipRate", array1d< real64 >, - 1.0e-6, - NOPLOT, + 0.0, + LEVEL_0, WRITE_AND_READ, "Slip rate" ); +DECLARE_FIELD( slipRate_n, + "slipRate_n", + array1d< real64 >, + 0.0, + NOPLOT, + WRITE_AND_READ, + "Slip rate at timestep n." ); + DECLARE_FIELD( slipVelocity, "slipVelocity", array2d< real64 >, - 0.70710678118e-6, + 0.0, LEVEL_0, WRITE_AND_READ, "Slip velocity" ); @@ -51,7 +59,7 @@ DECLARE_FIELD( slipVelocity, DECLARE_FIELD( slipVelocity_n, "slipVelocity_n", array2d< real64 >, - 0.70710678118e-6, + 0.0, NOPLOT, WRITE_AND_READ, "Slip velocity at previous time step" ); @@ -72,23 +80,53 @@ DECLARE_FIELD( stateVariable_n, WRITE_AND_READ, "Initial rate- and state-dependent friction state variable at this time step" ); +DECLARE_FIELD( normalTraction, + "normalTraction", + array1d< real64 >, + 0.0, + LEVEL_0, + WRITE_AND_READ, + "Normal traction" ); -DECLARE_FIELD( deltaSlip, - "deltaSlip", +DECLARE_FIELD( shearTraction, + "shearTraction", array2d< real64 >, 0.0, LEVEL_0, WRITE_AND_READ, - "Slip increment" ); + "Shear traction" ); + +DECLARE_FIELD( normalTraction_n, + "normalTraction_n", + array1d< real64 >, + 0.0, + LEVEL_0, + WRITE_AND_READ, + "Normal traction at previous timestep n." ); -DECLARE_FIELD( deltaSlip_n, - "deltaSlip_n", +DECLARE_FIELD( shearTraction_n, + "shearTraction_n", array2d< real64 >, 0.0, - NOPLOT, + LEVEL_0, WRITE_AND_READ, - "Initial slip increment at this time step" ); + "Shear traction at previous timestep n." ); +DECLARE_FIELD( backgroundNormalStress, + "backgroundNormalStress", + array1d< real64 >, + 0.0, + LEVEL_0, + WRITE_AND_READ, + "Background Normal Stress" ); + +DECLARE_FIELD( backgroundShearStress, + "backgroundShearStress", + array2d< real64 >, + 0.0, + LEVEL_0, + WRITE_AND_READ, + "Background Shear Stress" ); DECLARE_FIELD( rungeKuttaStageRates, "rungeKuttaStageRates", diff --git a/src/coreComponents/physicsSolvers/multiphysics/CMakeLists.txt b/src/coreComponents/physicsSolvers/multiphysics/CMakeLists.txt index 2332116db38..08bf8b159c8 100644 --- a/src/coreComponents/physicsSolvers/multiphysics/CMakeLists.txt +++ b/src/coreComponents/physicsSolvers/multiphysics/CMakeLists.txt @@ -9,6 +9,7 @@ set( physicsSolvers_headers multiphysics/HydrofractureSolver.hpp multiphysics/HydrofractureSolverKernels.hpp multiphysics/MultiphasePoromechanics.hpp + multiphysics/OneWayCoupledFractureFlowContactMechanics.hpp multiphysics/MultiphasePoromechanicsConformingFractures.hpp multiphysics/PhaseFieldFractureSolver.hpp multiphysics/PoromechanicsInitialization.hpp @@ -48,6 +49,7 @@ set( physicsSolvers_sources multiphysics/FlowProppantTransportSolver.cpp multiphysics/HydrofractureSolver.cpp multiphysics/MultiphasePoromechanics.cpp + multiphysics/OneWayCoupledFractureFlowContactMechanics.cpp multiphysics/MultiphasePoromechanicsConformingFractures.cpp multiphysics/PhaseFieldFractureSolver.cpp multiphysics/PoromechanicsInitialization.cpp diff --git a/src/coreComponents/physicsSolvers/multiphysics/OneWayCoupledFractureFlowContactMechanics.cpp b/src/coreComponents/physicsSolvers/multiphysics/OneWayCoupledFractureFlowContactMechanics.cpp new file mode 100644 index 00000000000..6f5c291f1ce --- /dev/null +++ b/src/coreComponents/physicsSolvers/multiphysics/OneWayCoupledFractureFlowContactMechanics.cpp @@ -0,0 +1,88 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * SPDX-License-Identifier: LGPL-2.1-only + * + * Copyright (c) 2016-2024 Lawrence Livermore National Security LLC + * Copyright (c) 2018-2024 TotalEnergies + * Copyright (c) 2018-2024 The Board of Trustees of the Leland Stanford Junior University + * Copyright (c) 2023-2024 Chevron + * Copyright (c) 2019- GEOS/GEOSX Contributors + * All rights reserved + * + * See top level LICENSE, COPYRIGHT, CONTRIBUTORS, NOTICE, and ACKNOWLEDGEMENTS files for details. + * ------------------------------------------------------------------------------------------------------------ + */ + +/** + * @file OneWayCoupledFractureFlowContactMechanics.cpp + */ + +#include "OneWayCoupledFractureFlowContactMechanics.hpp" +#include "physicsSolvers/contact/ContactFields.hpp" +#include "physicsSolvers/fluidFlow/FlowSolverBaseFields.hpp" +#include "mesh/DomainPartition.hpp" + + +namespace geos +{ +using namespace dataRepository; + +template< typename FLOW_SOLVER > +OneWayCoupledFractureFlowContactMechanics< FLOW_SOLVER >::OneWayCoupledFractureFlowContactMechanics( const string & name, + Group * const parent ) + : Base( name, parent ) +{} + +template< typename FLOW_SOLVER > +void OneWayCoupledFractureFlowContactMechanics< FLOW_SOLVER >::postInputInitialization() +{ + bool const isSequential = this->getNonlinearSolverParameters().couplingType() == NonlinearSolverParameters::CouplingType::Sequential; + GEOS_THROW_IF( !isSequential, + "Only sequential coupling is allowed for this solver.", + InputError ); + + Base::postInputInitialization(); +} + + +template< typename FLOW_SOLVER > +real64 OneWayCoupledFractureFlowContactMechanics< FLOW_SOLVER >::sequentiallyCoupledSolverStep( real64 const & time_n, + real64 const & dt, + int const cycleNumber, + DomainPartition & domain ) +{ + forEachArgInTuple( m_solvers, [&]( auto & solver, auto ) + { + solver->solverStep( time_n, dt, cycleNumber, domain ); + } ); + + this->forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &, + MeshLevel & mesh, + arrayView1d< string const > const & regionNames ) + { + mesh.getElemManager().forElementSubRegions< SurfaceElementSubRegion >( regionNames, + [&]( localIndex const, + SurfaceElementSubRegion & subRegion ) + { + arrayView2d< real64 > const traction = subRegion.getField< fields::contact::traction >(); + arrayView1d< real64 > const pressure = subRegion.getField< fields::flow::pressure >(); + + forAll< parallelDevicePolicy<> >( subRegion.size(), [=] GEOS_HOST_DEVICE ( localIndex const k ) + { + traction( k, 0 ) = traction( k, 0 ) + pressure[k]; + } ); + } ); + } ); + + return dt; +} + +template class OneWayCoupledFractureFlowContactMechanics< SinglePhaseBase >; + +namespace +{ +typedef OneWayCoupledFractureFlowContactMechanics< SinglePhaseBase > OneWayCoupledFractureFlowContactMechanicsSinglePhase; +REGISTER_CATALOG_ENTRY( PhysicsSolverBase, OneWayCoupledFractureFlowContactMechanicsSinglePhase, string const &, dataRepository::Group * const ) +} + +} /* namespace geos */ diff --git a/src/coreComponents/physicsSolvers/multiphysics/OneWayCoupledFractureFlowContactMechanics.hpp b/src/coreComponents/physicsSolvers/multiphysics/OneWayCoupledFractureFlowContactMechanics.hpp new file mode 100644 index 00000000000..03b6b1a6f4b --- /dev/null +++ b/src/coreComponents/physicsSolvers/multiphysics/OneWayCoupledFractureFlowContactMechanics.hpp @@ -0,0 +1,93 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * 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 OneWayCoupledFractureFlowContactMechanics.hpp + */ + +#ifndef GEOS_PHYSICSSOLVERS_MULTIPHYSICS_ONEWAYCOUPLEDFRACTUREFLOWCONTACTMECHANICS_HPP_ +#define GEOS_PHYSICSSOLVERS_MULTIPHYSICS_ONEWAYCOUPLEDFRACTUREFLOWCONTACTMECHANICS_HPP_ + +#include "physicsSolvers/multiphysics/CoupledSolver.hpp" +#include "physicsSolvers/contact/SolidMechanicsLagrangeContactBubbleStab.hpp" +#include "physicsSolvers/fluidFlow/SinglePhaseBase.hpp" +#include "dataRepository/Group.hpp" + +namespace geos +{ + +template< typename FLOW_SOLVER = SinglePhaseBase > +class OneWayCoupledFractureFlowContactMechanics : public CoupledSolver< FLOW_SOLVER, SolidMechanicsLagrangeContactBubbleStab > +{ +public: + + using Base = CoupledSolver< FLOW_SOLVER, SolidMechanicsLagrangeContactBubbleStab >; + using Base::m_solvers; + using Base::m_dofManager; + using Base::m_localMatrix; + using Base::m_rhs; + using Base::m_solution; + + enum class SolverType : integer + { + Flow = 0, + SolidMechanics = 1 + }; + + /** + * @brief main constructor for OneWayCoupledFractureFlowContactMechanics objects + * @param name the name of this instantiation of OneWayCoupledFractureFlowContactMechanics in the repository + * @param parent the parent group of this instantiation of OneWayCoupledFractureFlowContactMechanics + */ + OneWayCoupledFractureFlowContactMechanics( const string & name, + dataRepository::Group * const parent ); + + /// Destructor for the class + ~OneWayCoupledFractureFlowContactMechanics() override {} + + /** + * @brief name of the node manager in the object catalog + * @return string that contains the catalog name to generate a new OneWayCoupledFractureFlowContactMechanics object through the object + * catalog. + */ + static string catalogName() + { + return "OneWayCoupledFractureFlowContactMechanics"; + } + + /** + * @copydoc PhysicsSolverBase::getCatalogName() + */ + string getCatalogName() const override { return catalogName(); } + + virtual real64 sequentiallyCoupledSolverStep( real64 const & time_n, + real64 const & dt, + int const cycleNumber, + DomainPartition & domain ) override final; + + virtual void postInputInitialization() override final; + + /**@}*/ + +private: + + struct viewKeyStruct : public Base::viewKeyStruct + {}; + +}; + +} /* namespace geos */ + +#endif /* GEOS_PHYSICSSOLVERS_MULTIPHYSICS_ONEWAYCOUPLEDFRACTUREFLOWCONTACTMECHANICS_HPP_ */ diff --git a/src/coreComponents/physicsSolvers/solidMechanics/SolidMechanicsStatistics.cpp b/src/coreComponents/physicsSolvers/solidMechanics/SolidMechanicsStatistics.cpp index 5e266c0258c..d16dd1ea519 100644 --- a/src/coreComponents/physicsSolvers/solidMechanics/SolidMechanicsStatistics.cpp +++ b/src/coreComponents/physicsSolvers/solidMechanics/SolidMechanicsStatistics.cpp @@ -26,6 +26,9 @@ #include "fileIO/Outputs/OutputBase.hpp" #include "mesh/DomainPartition.hpp" #include "physicsSolvers/solidMechanics/LogLevelsInfo.hpp" +#include "common/format/table/TableData.hpp" +#include "common/format/table/TableFormatter.hpp" +#include "common/format/table/TableLayout.hpp" namespace geos { @@ -155,13 +158,17 @@ void SolidMechanicsStatistics::computeNodeStatistics( MeshLevel & mesh, real64 c MpiWrapper::getMpiOp( MpiWrapper::Reduction::Min ), MPI_COMM_GEOS ); - GEOS_LOG_LEVEL_INFO_RANK_0( logInfo::Statistics, GEOS_FMT( "{} (time {} s): Min displacement (X, Y, Z): {}, {}, {} m", - getName(), time, nodeStatistics.minDisplacement[0], - nodeStatistics.minDisplacement[1], nodeStatistics.minDisplacement[2] ) ); - GEOS_LOG_LEVEL_INFO_RANK_0( logInfo::Statistics, GEOS_FMT( "{} (time {} s): Max displacement (X, Y, Z): {}, {}, {} m", - getName(), time, nodeStatistics.maxDisplacement[0], - nodeStatistics.maxDisplacement[1], nodeStatistics.maxDisplacement[2] ) ); + TableData mechanicsData; + mechanicsData.addRow( "min", GEOS_FMT( "[{},{},{}]", nodeStatistics.minDisplacement[0], + nodeStatistics.minDisplacement[1], nodeStatistics.minDisplacement[2] )); + mechanicsData.addRow( "max", GEOS_FMT( "[{},{},{}]", nodeStatistics.maxDisplacement[0], + nodeStatistics.maxDisplacement[1], nodeStatistics.maxDisplacement[2] )); + string const title = GEOS_FMT( "{}, (time {} s):", getName(), time ); + TableLayout mechanicsLayout( title, { " ", "Displacement (X, Y, Z)"} ); + + TableTextFormatter mechanicsFormatter( mechanicsLayout ); + GEOS_LOG_RANK_0( mechanicsFormatter.toString( mechanicsData )); if( m_writeCSV > 0 && MpiWrapper::commRank() == 0 ) { std::ofstream outputFile( m_outputDir + "/" + mesh.getName() + "_node_statistics" + ".csv", std::ios_base::app ); diff --git a/src/coreComponents/physicsSolvers/surfaceGeneration/SurfaceGenerator.cpp b/src/coreComponents/physicsSolvers/surfaceGeneration/SurfaceGenerator.cpp index 2e16f5bfeea..aaf9430df2b 100644 --- a/src/coreComponents/physicsSolvers/surfaceGeneration/SurfaceGenerator.cpp +++ b/src/coreComponents/physicsSolvers/surfaceGeneration/SurfaceGenerator.cpp @@ -180,15 +180,27 @@ SurfaceGenerator::SurfaceGenerator( const string & name, // m_maxTurnAngle(91.0), m_nodeBasedSIF( 1 ), m_isPoroelastic( 0 ), - m_rockToughness( 1.0e99 ), + m_initialRockToughness( 1.0e99 ), + m_toughnessScalingFactor( 0.0 ), + m_fractureOrigin( { 0.0, 0.0, 0.0 } ), m_mpiCommOrder( 0 ) { this->registerWrapper( viewKeyStruct::failCriterionString(), &this->m_failCriterion ); - registerWrapper( viewKeyStruct::rockToughnessString(), &m_rockToughness ). + registerWrapper( viewKeyStruct::initialRockToughnessString(), &m_initialRockToughness ). setInputFlag( InputFlags::REQUIRED ). - setDescription( "Rock toughness of the solid material" ); + setDescription( "Initial rock toughness of the solid material" ); + + registerWrapper( viewKeyStruct::toughnessScalingFactorString(), &m_toughnessScalingFactor ). + setApplyDefaultValue( 0.0 ). + setInputFlag( InputFlags::OPTIONAL ). + setDescription( "Scaling factor for the rock toughness of the solid material" ); + + registerWrapper( viewKeyStruct::fractureOriginString(), &m_fractureOrigin ). + setDefaultValue( m_fractureOrigin ). + setInputFlag( InputFlags::OPTIONAL ). + setDescription( "Coordinate of fracture origin" ); registerWrapper( viewKeyStruct::nodeBasedSIFString(), &m_nodeBasedSIF ). setInputFlag( InputFlags::OPTIONAL ). @@ -372,17 +384,18 @@ void SurfaceGenerator::initializePostInitialConditionsPreSubGroups() FaceManager & faceManager = meshLevel.getFaceManager(); ElementRegionManager & elementManager = meshLevel.getElemManager(); arrayView2d< real64 const > const & faceNormals = faceManager.faceNormal(); + arrayView2d< real64 const > const & faceCenters = faceManager.faceCenter(); //TODO: roughness to KIC should be made a material constitutive relationship. arrayView2d< real64 > const & KIC = faceManager.getField< surfaceGeneration::K_IC >(); for( localIndex kf=0; kf= 0 ) + if( m_initialRockToughness >= 0 ) { - KIC[kf][0] = m_rockToughness; - KIC[kf][1] = m_rockToughness; - KIC[kf][2] = m_rockToughness; + KIC[kf][0] = m_initialRockToughness; + KIC[kf][1] = m_initialRockToughness; + KIC[kf][2] = m_initialRockToughness; } else { @@ -390,6 +403,10 @@ void SurfaceGenerator::initializePostInitialConditionsPreSubGroups() arrayView2d< localIndex const > const & faceToSubRegionMap = faceManager.elementSubRegionList(); arrayView2d< localIndex const > const & faceToElementMap = faceManager.elementList(); + KIC[kf][0] = 1e99; + KIC[kf][1] = 1e99; + KIC[kf][2] = 1e99; + for( localIndex k=0; k 0.0 ) + { + real64 faceCenter[3]; + faceCenter[0] = faceCenters[kf][0]; + faceCenter[1] = faceCenters[kf][1]; + faceCenter[2] = faceCenters[kf][2]; + + for( localIndex dim=0; dim<3; ++dim ) + { + real64 const initialRockToughness = KIC[kf][dim]; + + real64 const scaledToughness = scalingToughness( m_fractureOrigin, + faceCenter, + initialRockToughness, + m_toughnessScalingFactor ); + + KIC[kf][dim] = scaledToughness; + } + } } } ); } @@ -558,7 +594,6 @@ real64 SurfaceGenerator::solverStep( real64 const & time_n, PermeabilityBase & permModel = getConstitutiveModel< PermeabilityBase >( fractureSubRegion, permModelName ); permModel.initializeState(); } - } ); return rval; @@ -4562,6 +4597,20 @@ SurfaceGenerator::calculateRuptureRate( SurfaceElementRegion & faceElementRegion return globalMaxRuptureRate; } +real64 SurfaceGenerator::scalingToughness( R1Tensor const fractureOrigin, + real64 const (&faceCenter)[3], + real64 const initialRockToughness, + real64 const toughnessScalingFactor ) +{ + real64 const distance = sqrt( (fractureOrigin[0] - faceCenter[0])*(fractureOrigin[0] - faceCenter[0]) + + (fractureOrigin[1] - faceCenter[1])*(fractureOrigin[1] - faceCenter[1]) + + (fractureOrigin[2] - faceCenter[2])*(fractureOrigin[2] - faceCenter[2]) ); + + real64 scaledToughness = initialRockToughness*( 1 + toughnessScalingFactor*sqrt( distance ) ); + + return scaledToughness; +} + REGISTER_CATALOG_ENTRY( PhysicsSolverBase, diff --git a/src/coreComponents/physicsSolvers/surfaceGeneration/SurfaceGenerator.hpp b/src/coreComponents/physicsSolvers/surfaceGeneration/SurfaceGenerator.hpp index 4e435f9b566..084b63c5fe6 100644 --- a/src/coreComponents/physicsSolvers/surfaceGeneration/SurfaceGenerator.hpp +++ b/src/coreComponents/physicsSolvers/surfaceGeneration/SurfaceGenerator.hpp @@ -503,6 +503,11 @@ class SurfaceGenerator : public PhysicsSolverBase real64 calculateRuptureRate( SurfaceElementRegion & faceElementRegion ); + real64 scalingToughness( R1Tensor const fractureOrigin, + real64 const (&faceCenter)[3], + real64 const initialRockToughness, + real64 const toughnessScalingFactor ); + /** * @struct viewKeyStruct holds char strings and viewKeys for fast lookup */ @@ -521,7 +526,10 @@ class SurfaceGenerator : public PhysicsSolverBase //TODO: rock toughness should be a material parameter, and we need to make rock toughness to KIC a constitutive // relation. - constexpr static char const * rockToughnessString() { return "rockToughness"; } + constexpr static char const * initialRockToughnessString() { return "initialRockToughness"; } + constexpr static char const * toughnessScalingFactorString() { return "toughnessScalingFactor"; } + //TODO: fracture origin can be obtained from the initial fracture geometry + constexpr static char const * fractureOriginString() { return "fractureOrigin"; } // //TODO: Once the node-based SIF criterion becomes mature and robust, remove the edge-based criterion. constexpr static char const * nodeBasedSIFString() { return "nodeBasedSIF"; } @@ -541,7 +549,11 @@ class SurfaceGenerator : public PhysicsSolverBase int m_isPoroelastic; - real64 m_rockToughness; + real64 m_initialRockToughness; + + real64 m_toughnessScalingFactor; + + R1Tensor m_fractureOrigin; // Flag for consistent communication ordering int m_mpiCommOrder; diff --git a/src/coreComponents/schema/schema.xsd b/src/coreComponents/schema/schema.xsd index 27cb9f836a9..87f70867a1e 100644 --- a/src/coreComponents/schema/schema.xsd +++ b/src/coreComponents/schema/schema.xsd @@ -363,6 +363,14 @@ + + + + + + + + @@ -371,6 +379,14 @@ + + + + + + + + @@ -383,6 +399,10 @@ + + + + @@ -395,14 +415,6 @@ - - - - - - - - @@ -2282,16 +2294,19 @@ Level 0 outputs no specific information for this solver. Higher levels require m + + + + + - - @@ -3176,6 +3191,74 @@ Level 0 outputs no specific information for this solver. Higher levels require m + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -3273,6 +3356,78 @@ Local- Add jump stabilization on interior of macro elements--> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -3395,6 +3550,40 @@ Level 0 outputs no specific information for this solver. Higher levels require m + + + + + + + + + + + + + + + + + + + + + + @@ -3535,78 +3724,6 @@ Level 0 outputs no specific information for this solver. Higher levels require m - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -4634,10 +4751,14 @@ Level 0 outputs no specific information for this solver. Higher levels require m + + + + - - + + @@ -4947,10 +5068,14 @@ Level 0 outputs no specific information for this solver. Higher levels require m - + diff --git a/src/coreComponents/schema/schema.xsd.other b/src/coreComponents/schema/schema.xsd.other index 64384a62a01..bdfbfbec836 100644 --- a/src/coreComponents/schema/schema.xsd.other +++ b/src/coreComponents/schema/schema.xsd.other @@ -529,16 +529,19 @@ + + + + + - - @@ -917,6 +920,28 @@ + + + + + + + + + + + + + + + + + + + + + + @@ -945,7 +970,7 @@ - + @@ -956,22 +981,33 @@ - + - - + + - + + + + + + + + + + + + @@ -982,20 +1018,25 @@ + + + - + + + - + @@ -1008,7 +1049,7 @@ - + @@ -1019,18 +1060,20 @@ - + + + - + diff --git a/src/coreComponents/unitTests/fluidFlowTests/testFlowStatistics.cpp b/src/coreComponents/unitTests/fluidFlowTests/testFlowStatistics.cpp index 9258009135d..fc4bf169852 100644 --- a/src/coreComponents/unitTests/fluidFlowTests/testFlowStatistics.cpp +++ b/src/coreComponents/unitTests/fluidFlowTests/testFlowStatistics.cpp @@ -481,10 +481,12 @@ TestSet getTestSet() + fluxNames="{*}" + logLevel="3" /> + logLevel="3" /> + fluxNames="{*}" + logLevel="1" /> + fluxNames="{*}" + logLevel="1" />