From 6b292333250ce99ebc761589ed37e29f45a1d1a6 Mon Sep 17 00:00:00 2001 From: Bernd Flemisch <bernd@iws.uni-stuttgart.de> Date: Tue, 27 Nov 2012 16:39:49 +0000 Subject: [PATCH] implicit branch: change directory structure. Add dumux/implicit directory. Copy models from boxmodels/model to implicit/model. Copy boxmodels/common to implicit/box. Copy ccmodels/common (from devel) to implicit/cellcentered. Copy boxmodels/common/box*.hh to implicit/common/implicit*.hh. Move test/boxmodels to test/implicit. Adapt configure.ac and Makefile.ams. Everything still builds. git-svn-id: svn://svn.iws.uni-stuttgart.de/DUMUX/dumux/branches/implicit@9679 2fb0f335-1f38-0410-981e-8018bf24f1b0 --- configure.ac | 48 +- dumux/Makefile.am | 2 +- dumux/implicit/1p/1pindices.hh | 45 + dumux/implicit/1p/1plocalresidual.hh | 169 ++++ dumux/implicit/1p/1pmodel.hh | 125 +++ dumux/implicit/1p/1pproperties.hh | 65 ++ dumux/implicit/1p/1ppropertydefaults.hh | 109 ++ dumux/implicit/1p/1pvolumevariables.hh | 203 ++++ dumux/implicit/1p/Makefile.am | 4 + dumux/implicit/1p2c/1p2cfluxvariables.hh | 494 +++++++++ dumux/implicit/1p2c/1p2cindices.hh | 63 ++ dumux/implicit/1p2c/1p2clocalresidual.hh | 297 ++++++ dumux/implicit/1p2c/1p2cmodel.hh | 326 ++++++ dumux/implicit/1p2c/1p2cproperties.hh | 68 ++ dumux/implicit/1p2c/1p2cpropertydefaults.hh | 96 ++ dumux/implicit/1p2c/1p2cvolumevariables.hh | 284 ++++++ dumux/implicit/1p2c/Makefile.am | 4 + dumux/implicit/2p/2pindices.hh | 110 ++ dumux/implicit/2p/2plocalresidual.hh | 212 ++++ dumux/implicit/2p/2pmodel.hh | 366 +++++++ dumux/implicit/2p/2pproperties.hh | 76 ++ dumux/implicit/2p/2ppropertydefaults.hh | 148 +++ dumux/implicit/2p/2pvolumevariables.hh | 279 +++++ dumux/implicit/2p/Makefile.am | 5 + dumux/implicit/2p2c/2p2cfluxvariables.hh | 246 +++++ dumux/implicit/2p2c/2p2cindices.hh | 129 +++ dumux/implicit/2p2c/2p2clocalresidual.hh | 383 +++++++ dumux/implicit/2p2c/2p2cmodel.hh | 825 +++++++++++++++ dumux/implicit/2p2c/2p2cnewtoncontroller.hh | 110 ++ dumux/implicit/2p2c/2p2cproperties.hh | 70 ++ dumux/implicit/2p2c/2p2cpropertydefaults.hh | 160 +++ dumux/implicit/2p2c/2p2cvolumevariables.hh | 455 +++++++++ dumux/implicit/2p2c/Makefile.am | 4 + dumux/implicit/2p2cni/2p2cnifluxvariables.hh | 146 +++ dumux/implicit/2p2cni/2p2cniindices.hh | 51 + dumux/implicit/2p2cni/2p2cnilocalresidual.hh | 175 ++++ dumux/implicit/2p2cni/2p2cnimodel.hh | 98 ++ dumux/implicit/2p2cni/2p2cniproperties.hh | 47 + .../implicit/2p2cni/2p2cnipropertydefaults.hh | 76 ++ .../implicit/2p2cni/2p2cnivolumevariables.hh | 144 +++ dumux/implicit/2p2cni/Makefile.am | 4 + dumux/implicit/2pdfm/2pdfmfluxvariables.hh | 234 +++++ dumux/implicit/2pdfm/2pdfmindices.hh | 64 ++ dumux/implicit/2pdfm/2pdfmlocalresidual.hh | 335 ++++++ dumux/implicit/2pdfm/2pdfmmodel.hh | 390 +++++++ dumux/implicit/2pdfm/2pdfmproblem.hh | 79 ++ dumux/implicit/2pdfm/2pdfmproperties.hh | 69 ++ dumux/implicit/2pdfm/2pdfmpropertydefaults.hh | 144 +++ dumux/implicit/2pdfm/2pdfmvolumevariables.hh | 386 +++++++ dumux/implicit/2pdfm/Makefile.am | 4 + dumux/implicit/2pni/2pnifluxvariables.hh | 127 +++ dumux/implicit/2pni/2pniindices.hh | 48 + dumux/implicit/2pni/2pnilocalresidual.hh | 182 ++++ dumux/implicit/2pni/2pnimodel.hh | 93 ++ dumux/implicit/2pni/2pniproperties.hh | 47 + dumux/implicit/2pni/2pnipropertydefaults.hh | 72 ++ dumux/implicit/2pni/2pnivolumevariables.hh | 124 +++ dumux/implicit/2pni/Makefile.am | 4 + dumux/implicit/3p3c/3p3cfluxvariables.hh | 335 ++++++ dumux/implicit/3p3c/3p3cindices.hh | 85 ++ dumux/implicit/3p3c/3p3clocalresidual.hh | 264 +++++ dumux/implicit/3p3c/3p3cmodel.hh | 952 ++++++++++++++++++ dumux/implicit/3p3c/3p3cnewtoncontroller.hh | 85 ++ dumux/implicit/3p3c/3p3cproperties.hh | 66 ++ dumux/implicit/3p3c/3p3cpropertydefaults.hh | 143 +++ dumux/implicit/3p3c/3p3cvolumevariables.hh | 732 ++++++++++++++ dumux/implicit/3p3c/Makefile.am | 4 + dumux/implicit/3p3cni/3p3cnifluxvariables.hh | 129 +++ dumux/implicit/3p3cni/3p3cniindices.hh | 51 + dumux/implicit/3p3cni/3p3cnilocalresidual.hh | 193 ++++ dumux/implicit/3p3cni/3p3cnimodel.hh | 108 ++ dumux/implicit/3p3cni/3p3cniproperties.hh | 47 + .../implicit/3p3cni/3p3cnipropertydefaults.hh | 71 ++ .../implicit/3p3cni/3p3cnivolumevariables.hh | 157 +++ dumux/implicit/3p3cni/Makefile.am | 4 + dumux/implicit/Makefile.am | 5 + dumux/implicit/box/Makefile.am | 4 + dumux/implicit/box/boxassembler.hh | 858 ++++++++++++++++ dumux/implicit/box/boxdarcyfluxvariables.hh | 313 ++++++ dumux/implicit/box/boxelementboundarytypes.hh | 148 +++ .../implicit/box/boxelementvolumevariables.hh | 138 +++ .../box/boxforchheimerfluxvariables.hh | 358 +++++++ dumux/implicit/box/boxfvelementgeometry.hh | 933 +++++++++++++++++ dumux/implicit/box/boxlocaljacobian.hh | 534 ++++++++++ dumux/implicit/box/boxlocalresidual.hh | 742 ++++++++++++++ dumux/implicit/box/boxmodel.hh | 927 +++++++++++++++++ dumux/implicit/box/boxproblem.hh | 835 +++++++++++++++ dumux/implicit/box/boxproperties.hh | 142 +++ dumux/implicit/box/boxpropertydefaults.hh | 207 ++++ dumux/implicit/box/boxvolumevariables.hh | 190 ++++ dumux/implicit/box/intersectiontovertexbc.hh | 112 +++ dumux/implicit/box/porousmediaboxproblem.hh | 197 ++++ dumux/implicit/cellcentered/Makefile.am | 4 + dumux/implicit/cellcentered/ccassembler.hh | 683 +++++++++++++ .../cellcentered/ccelementboundarytypes.hh | 150 +++ .../cellcentered/ccelementvolumevariables.hh | 107 ++ .../cellcentered/ccfvelementgeometry.hh | 219 ++++ .../implicit/cellcentered/cclocaljacobian.hh | 541 ++++++++++ .../implicit/cellcentered/cclocalresidual.hh | 710 +++++++++++++ dumux/implicit/cellcentered/ccmodel.hh | 755 ++++++++++++++ dumux/implicit/cellcentered/ccproblem.hh | 849 ++++++++++++++++ dumux/implicit/cellcentered/ccproperties.hh | 142 +++ .../cellcentered/ccpropertydefaults.hh | 209 ++++ .../cellcentered/porousmediaccproblem.hh | 192 ++++ dumux/implicit/co2/Makefile.am | 4 + dumux/implicit/co2/co2model.hh | 283 ++++++ dumux/implicit/co2/co2volumevariables.hh | 404 ++++++++ dumux/implicit/co2ni/Makefile.am | 4 + dumux/implicit/co2ni/co2nimodel.hh | 56 ++ dumux/implicit/co2ni/co2nivolumevariables.hh | 139 +++ dumux/implicit/common/Makefile.am | 4 + dumux/implicit/common/implicitassembler.hh | 858 ++++++++++++++++ .../common/implicitdarcyfluxvariables.hh | 313 ++++++ .../common/implicitelementboundarytypes.hh | 148 +++ .../common/implicitelementvolumevariables.hh | 138 +++ .../implicitforchheimerfluxvariables.hh | 358 +++++++ .../common/implicitfvelementgeometry.hh | 933 +++++++++++++++++ .../implicit/common/implicitlocaljacobian.hh | 534 ++++++++++ .../implicit/common/implicitlocalresidual.hh | 742 ++++++++++++++ dumux/implicit/common/implicitmodel.hh | 927 +++++++++++++++++ dumux/implicit/common/implicitproblem.hh | 835 +++++++++++++++ dumux/implicit/common/implicitproperties.hh | 142 +++ .../common/implicitpropertydefaults.hh | 207 ++++ .../common/implicitvolumevariables.hh | 190 ++++ .../common/porousmediaimplicitproblem.hh | 197 ++++ dumux/implicit/mpnc/Makefile.am | 6 + dumux/implicit/mpnc/diffusion/Makefile.am | 4 + dumux/implicit/mpnc/diffusion/diffusion.hh | 150 +++ .../implicit/mpnc/diffusion/fluxvariables.hh | 221 ++++ .../mpnc/diffusion/volumevariables.hh | 164 +++ dumux/implicit/mpnc/energy/Makefile.am | 4 + .../mpnc/energy/mpncfluxvariablesenergy.hh | 205 ++++ .../implicit/mpnc/energy/mpncindicesenergy.hh | 75 ++ .../mpnc/energy/mpnclocalresidualenergy.hh | 233 +++++ .../mpnc/energy/mpncvolumevariablesenergy.hh | 207 ++++ .../mpnc/energy/mpncvtkwriterenergy.hh | 218 ++++ dumux/implicit/mpnc/mass/Makefile.am | 4 + dumux/implicit/mpnc/mass/mpncindicesmass.hh | 84 ++ .../mpnc/mass/mpnclocalresidualmass.hh | 369 +++++++ .../mpnc/mass/mpncvolumevariablesmass.hh | 131 +++ dumux/implicit/mpnc/mass/mpncvtkwritermass.hh | 118 +++ dumux/implicit/mpnc/mpncfluxvariables.hh | 174 ++++ dumux/implicit/mpnc/mpncindices.hh | 103 ++ dumux/implicit/mpnc/mpnclocalresidual.hh | 247 +++++ dumux/implicit/mpnc/mpncmodel.hh | 188 ++++ dumux/implicit/mpnc/mpncnewtoncontroller.hh | 266 +++++ dumux/implicit/mpnc/mpncproperties.hh | 133 +++ dumux/implicit/mpnc/mpncpropertydefaults.hh | 279 +++++ dumux/implicit/mpnc/mpncvolumevariables.hh | 340 +++++++ dumux/implicit/mpnc/mpncvolumevariablesia.hh | 85 ++ dumux/implicit/mpnc/mpncvtkwriter.hh | 126 +++ dumux/implicit/mpnc/mpncvtkwritercommon.hh | 275 +++++ dumux/implicit/mpnc/mpncvtkwritermodule.hh | 256 +++++ dumux/implicit/richards/Makefile.am | 4 + dumux/implicit/richards/richardsindices.hh | 67 ++ .../richards/richardslocalresidual.hh | 158 +++ dumux/implicit/richards/richardsmodel.hh | 208 ++++ .../richards/richardsnewtoncontroller.hh | 125 +++ dumux/implicit/richards/richardsproblem.hh | 92 ++ dumux/implicit/richards/richardsproperties.hh | 70 ++ .../richards/richardspropertydefaults.hh | 168 ++++ .../richards/richardsvolumevariables.hh | 280 ++++++ .../{artmeshreader.hh => artgridcreator.hh} | 594 ++++++----- test/Makefile.am | 2 +- test/boxmodels/2pdfm/test_2pdfm.cc | 136 --- .../1p/1ptest-reference.vtu | 0 .../1p/1ptestproblem.hh | 0 .../1p/1ptestspatialparams.hh | 0 .../{boxmodels => implicit}/1p/CMakeLists.txt | 0 test/{boxmodels => implicit}/1p/Makefile.am | 0 .../1p/grids/test_1p_1d.dgf | 0 .../1p/grids/test_1p_2d.dgf | 0 .../1p/grids/test_1p_3d.dgf | 0 test/{boxmodels => implicit}/1p/test_1p.cc | 0 test/{boxmodels => implicit}/1p/test_1p.input | 0 .../1p2c/1p2coutflowproblem.hh | 0 .../1p2c/1p2coutflowspatialparams.hh | 0 .../1p2c/CMakeLists.txt | 0 test/{boxmodels => implicit}/1p2c/Makefile.am | 0 .../1p2c/grids/test_1p2c.dgf | 0 .../1p2c/outflow-reference.vtu | 0 .../{boxmodels => implicit}/1p2c/test_1p2c.cc | 0 .../1p2c/test_1p2c.input | 0 .../{boxmodels => implicit}/2p/CMakeLists.txt | 0 test/{boxmodels => implicit}/2p/Makefile.am | 0 .../2p/grids/test_2p.dgf | 0 .../2p/lens-reference.vtu | 0 .../{boxmodels => implicit}/2p/lensproblem.hh | 0 .../2p/lensspatialparams.hh | 0 test/{boxmodels => implicit}/2p/test_2p.cc | 0 test/{boxmodels => implicit}/2p/test_2p.input | 0 .../2p2c/CMakeLists.txt | 0 test/{boxmodels => implicit}/2p2c/Makefile.am | 0 .../2p2c/grids/test_2p2c.dgf | 0 .../2p2c/injection-reference.vtu | 0 .../2p2c/injectionproblem.hh | 0 .../2p2c/injectionspatialparams.hh | 0 .../{boxmodels => implicit}/2p2c/test_2p2c.cc | 0 .../2p2c/test_2p2c.input | 0 .../2p2cni/CMakeLists.txt | 0 .../2p2cni/Makefile.am | 0 .../2p2cni/grids/test_2p2cni.dgf | 0 .../2p2cni/test_2p2cni.cc | 0 .../2p2cni/test_2p2cni.input | 0 .../2p2cni/waterair-reference.vtu | 0 .../2p2cni/waterairproblem.hh | 0 .../2p2cni/waterairspatialparams.hh | 0 .../2pdfm/2pdfmspatialparams.hh | 117 +-- .../2pdfm/2pdfmtestproblem.hh | 65 +- .../{boxmodels => implicit}/2pdfm/Makefile.am | 0 .../2pdfm/grids/2pdfmartmesh.net | 0 test/implicit/2pdfm/test_2pdfm.cc | 62 ++ test/implicit/2pdfm/test_2pdfm.input | 28 + .../2pni/CMakeLists.txt | 0 test/{boxmodels => implicit}/2pni/Makefile.am | 0 .../2pni/grids/test_2pni.dgf | 0 .../2pni/injection2pni-reference.vtu | 0 .../2pni/injectionproblem2pni.hh | 0 .../{boxmodels => implicit}/2pni/test_2pni.cc | 0 .../2pni/test_2pni.input | 0 .../3p3c/CMakeLists.txt | 0 test/{boxmodels => implicit}/3p3c/Makefile.am | 0 .../3p3c/grids/test_3p3c.dgf | 0 .../3p3c/grids/test_3p3c_coarse.dgf | 0 .../3p3c/infiltration-reference.vtu | 0 .../3p3c/infiltrationproblem.hh | 0 .../3p3c/infiltrationspatialparameters.hh | 0 .../{boxmodels => implicit}/3p3c/test_3p3c.cc | 0 .../3p3c/test_3p3c.input | 0 .../3p3cni/CMakeLists.txt | 0 .../3p3cni/Makefile.am | 0 .../3p3cni/column3p3cni-reference.vtu | 0 .../3p3cni/columnxylolproblem.hh | 0 .../3p3cni/columnxylolspatialparams.hh | 0 .../3p3cni/grids/column.dgf | 0 .../3p3cni/grids/kuev_3p3cni.dgf | 0 .../3p3cni/grids/kuev_3p3cni_coarse.dgf | 0 .../3p3cni/kuevetteproblem.hh | 0 .../3p3cni/kuevettespatialparams.hh | 0 .../3p3cni/test_3p3cni.cc | 0 .../3p3cni/test_3p3cni.input | 0 test/{boxmodels => implicit}/CMakeLists.txt | 0 test/{boxmodels => implicit}/Makefile.am | 0 .../co2/CMakeLists.txt | 0 test/{boxmodels => implicit}/co2/Makefile.am | 0 .../co2/co2-reference.vtu | 0 .../{boxmodels => implicit}/co2/co2values.inc | 0 .../co2/grids/heterogeneousSmall.dgf | 0 .../co2/heterogeneousco2tables.hh | 0 .../co2/heterogeneousproblem.hh | 0 .../co2/heterogeneousspatialparameters.hh | 0 test/{boxmodels => implicit}/co2/test_co2.cc | 0 .../co2/test_co2.input | 0 .../co2ni/CMakeLists.txt | 0 .../{boxmodels => implicit}/co2ni/Makefile.am | 0 .../co2ni/co2ni-reference.vtu | 0 .../co2ni/co2values.inc | 0 .../co2ni/grids/heterogeneousSmall.dgf | 0 .../co2ni/heterogeneousco2tables.hh | 0 .../co2ni/heterogeneousproblemni.hh | 0 .../co2ni/heterogeneousspatialparametersni.hh | 0 .../co2ni/test_co2ni.cc | 0 .../co2ni/test_co2ni.input | 0 .../mpnc/CMakeLists.txt | 0 test/{boxmodels => implicit}/mpnc/Makefile.am | 0 .../mpnc/forchheimer1p-reference.vtp | 0 .../mpnc/forchheimer1pproblem.hh | 0 .../mpnc/forchheimer2p-reference.vtu | 0 .../mpnc/forchheimer2pproblem.hh | 0 .../mpnc/forchheimerspatialparams.hh | 0 .../mpnc/grids/forchheimer1d.dgf | 0 .../mpnc/grids/obstacle_24x16.dgf | 0 .../mpnc/grids/obstacle_48x32.dgf | 0 .../mpnc/obstacle-reference.vtu | 0 .../mpnc/obstacleproblem.hh | 0 .../mpnc/obstaclespatialparams.hh | 0 .../mpnc/test_forchheimer1p.cc | 0 .../mpnc/test_forchheimer1p.input | 0 .../mpnc/test_forchheimer2p.cc | 0 .../mpnc/test_forchheimer2p.input | 0 .../{boxmodels => implicit}/mpnc/test_mpnc.cc | 0 .../mpnc/test_mpnc.input | 0 .../richards/CMakeLists.txt | 0 .../richards/Makefile.am | 0 .../richards/grids/richardslens-24x16.dgf | 0 .../richards/grids/richardslens-48x32.dgf | 0 .../richards/grids/richardslens-96x64.dgf | 0 .../richardslens-reference-parallel.vtu | 0 .../richards/richardslens-reference.vtu | 0 .../richards/richardslensproblem.hh | 0 .../richards/richardslensspatialparams.hh | 0 .../richards/test_richards.cc | 0 .../richards/test_richards.input | 0 293 files changed, 37408 insertions(+), 602 deletions(-) create mode 100644 dumux/implicit/1p/1pindices.hh create mode 100644 dumux/implicit/1p/1plocalresidual.hh create mode 100644 dumux/implicit/1p/1pmodel.hh create mode 100644 dumux/implicit/1p/1pproperties.hh create mode 100644 dumux/implicit/1p/1ppropertydefaults.hh create mode 100644 dumux/implicit/1p/1pvolumevariables.hh create mode 100644 dumux/implicit/1p/Makefile.am create mode 100644 dumux/implicit/1p2c/1p2cfluxvariables.hh create mode 100644 dumux/implicit/1p2c/1p2cindices.hh create mode 100644 dumux/implicit/1p2c/1p2clocalresidual.hh create mode 100644 dumux/implicit/1p2c/1p2cmodel.hh create mode 100644 dumux/implicit/1p2c/1p2cproperties.hh create mode 100644 dumux/implicit/1p2c/1p2cpropertydefaults.hh create mode 100644 dumux/implicit/1p2c/1p2cvolumevariables.hh create mode 100644 dumux/implicit/1p2c/Makefile.am create mode 100644 dumux/implicit/2p/2pindices.hh create mode 100644 dumux/implicit/2p/2plocalresidual.hh create mode 100644 dumux/implicit/2p/2pmodel.hh create mode 100644 dumux/implicit/2p/2pproperties.hh create mode 100644 dumux/implicit/2p/2ppropertydefaults.hh create mode 100644 dumux/implicit/2p/2pvolumevariables.hh create mode 100644 dumux/implicit/2p/Makefile.am create mode 100644 dumux/implicit/2p2c/2p2cfluxvariables.hh create mode 100644 dumux/implicit/2p2c/2p2cindices.hh create mode 100644 dumux/implicit/2p2c/2p2clocalresidual.hh create mode 100644 dumux/implicit/2p2c/2p2cmodel.hh create mode 100644 dumux/implicit/2p2c/2p2cnewtoncontroller.hh create mode 100644 dumux/implicit/2p2c/2p2cproperties.hh create mode 100644 dumux/implicit/2p2c/2p2cpropertydefaults.hh create mode 100644 dumux/implicit/2p2c/2p2cvolumevariables.hh create mode 100644 dumux/implicit/2p2c/Makefile.am create mode 100644 dumux/implicit/2p2cni/2p2cnifluxvariables.hh create mode 100644 dumux/implicit/2p2cni/2p2cniindices.hh create mode 100644 dumux/implicit/2p2cni/2p2cnilocalresidual.hh create mode 100644 dumux/implicit/2p2cni/2p2cnimodel.hh create mode 100644 dumux/implicit/2p2cni/2p2cniproperties.hh create mode 100644 dumux/implicit/2p2cni/2p2cnipropertydefaults.hh create mode 100644 dumux/implicit/2p2cni/2p2cnivolumevariables.hh create mode 100644 dumux/implicit/2p2cni/Makefile.am create mode 100644 dumux/implicit/2pdfm/2pdfmfluxvariables.hh create mode 100644 dumux/implicit/2pdfm/2pdfmindices.hh create mode 100644 dumux/implicit/2pdfm/2pdfmlocalresidual.hh create mode 100644 dumux/implicit/2pdfm/2pdfmmodel.hh create mode 100644 dumux/implicit/2pdfm/2pdfmproblem.hh create mode 100644 dumux/implicit/2pdfm/2pdfmproperties.hh create mode 100644 dumux/implicit/2pdfm/2pdfmpropertydefaults.hh create mode 100644 dumux/implicit/2pdfm/2pdfmvolumevariables.hh create mode 100644 dumux/implicit/2pdfm/Makefile.am create mode 100644 dumux/implicit/2pni/2pnifluxvariables.hh create mode 100644 dumux/implicit/2pni/2pniindices.hh create mode 100644 dumux/implicit/2pni/2pnilocalresidual.hh create mode 100644 dumux/implicit/2pni/2pnimodel.hh create mode 100644 dumux/implicit/2pni/2pniproperties.hh create mode 100644 dumux/implicit/2pni/2pnipropertydefaults.hh create mode 100644 dumux/implicit/2pni/2pnivolumevariables.hh create mode 100644 dumux/implicit/2pni/Makefile.am create mode 100644 dumux/implicit/3p3c/3p3cfluxvariables.hh create mode 100644 dumux/implicit/3p3c/3p3cindices.hh create mode 100644 dumux/implicit/3p3c/3p3clocalresidual.hh create mode 100644 dumux/implicit/3p3c/3p3cmodel.hh create mode 100644 dumux/implicit/3p3c/3p3cnewtoncontroller.hh create mode 100644 dumux/implicit/3p3c/3p3cproperties.hh create mode 100644 dumux/implicit/3p3c/3p3cpropertydefaults.hh create mode 100644 dumux/implicit/3p3c/3p3cvolumevariables.hh create mode 100644 dumux/implicit/3p3c/Makefile.am create mode 100644 dumux/implicit/3p3cni/3p3cnifluxvariables.hh create mode 100644 dumux/implicit/3p3cni/3p3cniindices.hh create mode 100644 dumux/implicit/3p3cni/3p3cnilocalresidual.hh create mode 100644 dumux/implicit/3p3cni/3p3cnimodel.hh create mode 100644 dumux/implicit/3p3cni/3p3cniproperties.hh create mode 100644 dumux/implicit/3p3cni/3p3cnipropertydefaults.hh create mode 100644 dumux/implicit/3p3cni/3p3cnivolumevariables.hh create mode 100644 dumux/implicit/3p3cni/Makefile.am create mode 100644 dumux/implicit/Makefile.am create mode 100644 dumux/implicit/box/Makefile.am create mode 100644 dumux/implicit/box/boxassembler.hh create mode 100644 dumux/implicit/box/boxdarcyfluxvariables.hh create mode 100644 dumux/implicit/box/boxelementboundarytypes.hh create mode 100644 dumux/implicit/box/boxelementvolumevariables.hh create mode 100644 dumux/implicit/box/boxforchheimerfluxvariables.hh create mode 100644 dumux/implicit/box/boxfvelementgeometry.hh create mode 100644 dumux/implicit/box/boxlocaljacobian.hh create mode 100644 dumux/implicit/box/boxlocalresidual.hh create mode 100644 dumux/implicit/box/boxmodel.hh create mode 100644 dumux/implicit/box/boxproblem.hh create mode 100644 dumux/implicit/box/boxproperties.hh create mode 100644 dumux/implicit/box/boxpropertydefaults.hh create mode 100644 dumux/implicit/box/boxvolumevariables.hh create mode 100644 dumux/implicit/box/intersectiontovertexbc.hh create mode 100644 dumux/implicit/box/porousmediaboxproblem.hh create mode 100644 dumux/implicit/cellcentered/Makefile.am create mode 100644 dumux/implicit/cellcentered/ccassembler.hh create mode 100644 dumux/implicit/cellcentered/ccelementboundarytypes.hh create mode 100644 dumux/implicit/cellcentered/ccelementvolumevariables.hh create mode 100644 dumux/implicit/cellcentered/ccfvelementgeometry.hh create mode 100644 dumux/implicit/cellcentered/cclocaljacobian.hh create mode 100644 dumux/implicit/cellcentered/cclocalresidual.hh create mode 100644 dumux/implicit/cellcentered/ccmodel.hh create mode 100644 dumux/implicit/cellcentered/ccproblem.hh create mode 100644 dumux/implicit/cellcentered/ccproperties.hh create mode 100644 dumux/implicit/cellcentered/ccpropertydefaults.hh create mode 100644 dumux/implicit/cellcentered/porousmediaccproblem.hh create mode 100644 dumux/implicit/co2/Makefile.am create mode 100644 dumux/implicit/co2/co2model.hh create mode 100644 dumux/implicit/co2/co2volumevariables.hh create mode 100644 dumux/implicit/co2ni/Makefile.am create mode 100644 dumux/implicit/co2ni/co2nimodel.hh create mode 100644 dumux/implicit/co2ni/co2nivolumevariables.hh create mode 100644 dumux/implicit/common/Makefile.am create mode 100644 dumux/implicit/common/implicitassembler.hh create mode 100644 dumux/implicit/common/implicitdarcyfluxvariables.hh create mode 100644 dumux/implicit/common/implicitelementboundarytypes.hh create mode 100644 dumux/implicit/common/implicitelementvolumevariables.hh create mode 100644 dumux/implicit/common/implicitforchheimerfluxvariables.hh create mode 100644 dumux/implicit/common/implicitfvelementgeometry.hh create mode 100644 dumux/implicit/common/implicitlocaljacobian.hh create mode 100644 dumux/implicit/common/implicitlocalresidual.hh create mode 100644 dumux/implicit/common/implicitmodel.hh create mode 100644 dumux/implicit/common/implicitproblem.hh create mode 100644 dumux/implicit/common/implicitproperties.hh create mode 100644 dumux/implicit/common/implicitpropertydefaults.hh create mode 100644 dumux/implicit/common/implicitvolumevariables.hh create mode 100644 dumux/implicit/common/porousmediaimplicitproblem.hh create mode 100644 dumux/implicit/mpnc/Makefile.am create mode 100644 dumux/implicit/mpnc/diffusion/Makefile.am create mode 100644 dumux/implicit/mpnc/diffusion/diffusion.hh create mode 100644 dumux/implicit/mpnc/diffusion/fluxvariables.hh create mode 100644 dumux/implicit/mpnc/diffusion/volumevariables.hh create mode 100644 dumux/implicit/mpnc/energy/Makefile.am create mode 100644 dumux/implicit/mpnc/energy/mpncfluxvariablesenergy.hh create mode 100644 dumux/implicit/mpnc/energy/mpncindicesenergy.hh create mode 100644 dumux/implicit/mpnc/energy/mpnclocalresidualenergy.hh create mode 100644 dumux/implicit/mpnc/energy/mpncvolumevariablesenergy.hh create mode 100644 dumux/implicit/mpnc/energy/mpncvtkwriterenergy.hh create mode 100644 dumux/implicit/mpnc/mass/Makefile.am create mode 100644 dumux/implicit/mpnc/mass/mpncindicesmass.hh create mode 100644 dumux/implicit/mpnc/mass/mpnclocalresidualmass.hh create mode 100644 dumux/implicit/mpnc/mass/mpncvolumevariablesmass.hh create mode 100644 dumux/implicit/mpnc/mass/mpncvtkwritermass.hh create mode 100644 dumux/implicit/mpnc/mpncfluxvariables.hh create mode 100644 dumux/implicit/mpnc/mpncindices.hh create mode 100644 dumux/implicit/mpnc/mpnclocalresidual.hh create mode 100644 dumux/implicit/mpnc/mpncmodel.hh create mode 100644 dumux/implicit/mpnc/mpncnewtoncontroller.hh create mode 100644 dumux/implicit/mpnc/mpncproperties.hh create mode 100644 dumux/implicit/mpnc/mpncpropertydefaults.hh create mode 100644 dumux/implicit/mpnc/mpncvolumevariables.hh create mode 100644 dumux/implicit/mpnc/mpncvolumevariablesia.hh create mode 100644 dumux/implicit/mpnc/mpncvtkwriter.hh create mode 100644 dumux/implicit/mpnc/mpncvtkwritercommon.hh create mode 100644 dumux/implicit/mpnc/mpncvtkwritermodule.hh create mode 100644 dumux/implicit/richards/Makefile.am create mode 100644 dumux/implicit/richards/richardsindices.hh create mode 100644 dumux/implicit/richards/richardslocalresidual.hh create mode 100644 dumux/implicit/richards/richardsmodel.hh create mode 100644 dumux/implicit/richards/richardsnewtoncontroller.hh create mode 100644 dumux/implicit/richards/richardsproblem.hh create mode 100644 dumux/implicit/richards/richardsproperties.hh create mode 100644 dumux/implicit/richards/richardspropertydefaults.hh create mode 100644 dumux/implicit/richards/richardsvolumevariables.hh rename dumux/io/{artmeshreader.hh => artgridcreator.hh} (51%) delete mode 100644 test/boxmodels/2pdfm/test_2pdfm.cc rename test/{boxmodels => implicit}/1p/1ptest-reference.vtu (100%) rename test/{boxmodels => implicit}/1p/1ptestproblem.hh (100%) rename test/{boxmodels => implicit}/1p/1ptestspatialparams.hh (100%) rename test/{boxmodels => implicit}/1p/CMakeLists.txt (100%) rename test/{boxmodels => implicit}/1p/Makefile.am (100%) rename test/{boxmodels => implicit}/1p/grids/test_1p_1d.dgf (100%) rename test/{boxmodels => implicit}/1p/grids/test_1p_2d.dgf (100%) rename test/{boxmodels => implicit}/1p/grids/test_1p_3d.dgf (100%) rename test/{boxmodels => implicit}/1p/test_1p.cc (100%) rename test/{boxmodels => implicit}/1p/test_1p.input (100%) rename test/{boxmodels => implicit}/1p2c/1p2coutflowproblem.hh (100%) rename test/{boxmodels => implicit}/1p2c/1p2coutflowspatialparams.hh (100%) rename test/{boxmodels => implicit}/1p2c/CMakeLists.txt (100%) rename test/{boxmodels => implicit}/1p2c/Makefile.am (100%) rename test/{boxmodels => implicit}/1p2c/grids/test_1p2c.dgf (100%) rename test/{boxmodels => implicit}/1p2c/outflow-reference.vtu (100%) rename test/{boxmodels => implicit}/1p2c/test_1p2c.cc (100%) rename test/{boxmodels => implicit}/1p2c/test_1p2c.input (100%) rename test/{boxmodels => implicit}/2p/CMakeLists.txt (100%) rename test/{boxmodels => implicit}/2p/Makefile.am (100%) rename test/{boxmodels => implicit}/2p/grids/test_2p.dgf (100%) rename test/{boxmodels => implicit}/2p/lens-reference.vtu (100%) rename test/{boxmodels => implicit}/2p/lensproblem.hh (100%) rename test/{boxmodels => implicit}/2p/lensspatialparams.hh (100%) rename test/{boxmodels => implicit}/2p/test_2p.cc (100%) rename test/{boxmodels => implicit}/2p/test_2p.input (100%) rename test/{boxmodels => implicit}/2p2c/CMakeLists.txt (100%) rename test/{boxmodels => implicit}/2p2c/Makefile.am (100%) rename test/{boxmodels => implicit}/2p2c/grids/test_2p2c.dgf (100%) rename test/{boxmodels => implicit}/2p2c/injection-reference.vtu (100%) rename test/{boxmodels => implicit}/2p2c/injectionproblem.hh (100%) rename test/{boxmodels => implicit}/2p2c/injectionspatialparams.hh (100%) rename test/{boxmodels => implicit}/2p2c/test_2p2c.cc (100%) rename test/{boxmodels => implicit}/2p2c/test_2p2c.input (100%) rename test/{boxmodels => implicit}/2p2cni/CMakeLists.txt (100%) rename test/{boxmodels => implicit}/2p2cni/Makefile.am (100%) rename test/{boxmodels => implicit}/2p2cni/grids/test_2p2cni.dgf (100%) rename test/{boxmodels => implicit}/2p2cni/test_2p2cni.cc (100%) rename test/{boxmodels => implicit}/2p2cni/test_2p2cni.input (100%) rename test/{boxmodels => implicit}/2p2cni/waterair-reference.vtu (100%) rename test/{boxmodels => implicit}/2p2cni/waterairproblem.hh (100%) rename test/{boxmodels => implicit}/2p2cni/waterairspatialparams.hh (100%) rename test/{boxmodels => implicit}/2pdfm/2pdfmspatialparams.hh (74%) rename test/{boxmodels => implicit}/2pdfm/2pdfmtestproblem.hh (83%) rename test/{boxmodels => implicit}/2pdfm/Makefile.am (100%) rename test/{boxmodels => implicit}/2pdfm/grids/2pdfmartmesh.net (100%) create mode 100644 test/implicit/2pdfm/test_2pdfm.cc create mode 100644 test/implicit/2pdfm/test_2pdfm.input rename test/{boxmodels => implicit}/2pni/CMakeLists.txt (100%) rename test/{boxmodels => implicit}/2pni/Makefile.am (100%) rename test/{boxmodels => implicit}/2pni/grids/test_2pni.dgf (100%) rename test/{boxmodels => implicit}/2pni/injection2pni-reference.vtu (100%) rename test/{boxmodels => implicit}/2pni/injectionproblem2pni.hh (100%) rename test/{boxmodels => implicit}/2pni/test_2pni.cc (100%) rename test/{boxmodels => implicit}/2pni/test_2pni.input (100%) rename test/{boxmodels => implicit}/3p3c/CMakeLists.txt (100%) rename test/{boxmodels => implicit}/3p3c/Makefile.am (100%) rename test/{boxmodels => implicit}/3p3c/grids/test_3p3c.dgf (100%) rename test/{boxmodels => implicit}/3p3c/grids/test_3p3c_coarse.dgf (100%) rename test/{boxmodels => implicit}/3p3c/infiltration-reference.vtu (100%) rename test/{boxmodels => implicit}/3p3c/infiltrationproblem.hh (100%) rename test/{boxmodels => implicit}/3p3c/infiltrationspatialparameters.hh (100%) rename test/{boxmodels => implicit}/3p3c/test_3p3c.cc (100%) rename test/{boxmodels => implicit}/3p3c/test_3p3c.input (100%) rename test/{boxmodels => implicit}/3p3cni/CMakeLists.txt (100%) rename test/{boxmodels => implicit}/3p3cni/Makefile.am (100%) rename test/{boxmodels => implicit}/3p3cni/column3p3cni-reference.vtu (100%) rename test/{boxmodels => implicit}/3p3cni/columnxylolproblem.hh (100%) rename test/{boxmodels => implicit}/3p3cni/columnxylolspatialparams.hh (100%) rename test/{boxmodels => implicit}/3p3cni/grids/column.dgf (100%) rename test/{boxmodels => implicit}/3p3cni/grids/kuev_3p3cni.dgf (100%) rename test/{boxmodels => implicit}/3p3cni/grids/kuev_3p3cni_coarse.dgf (100%) rename test/{boxmodels => implicit}/3p3cni/kuevetteproblem.hh (100%) rename test/{boxmodels => implicit}/3p3cni/kuevettespatialparams.hh (100%) rename test/{boxmodels => implicit}/3p3cni/test_3p3cni.cc (100%) rename test/{boxmodels => implicit}/3p3cni/test_3p3cni.input (100%) rename test/{boxmodels => implicit}/CMakeLists.txt (100%) rename test/{boxmodels => implicit}/Makefile.am (100%) rename test/{boxmodels => implicit}/co2/CMakeLists.txt (100%) rename test/{boxmodels => implicit}/co2/Makefile.am (100%) rename test/{boxmodels => implicit}/co2/co2-reference.vtu (100%) rename test/{boxmodels => implicit}/co2/co2values.inc (100%) rename test/{boxmodels => implicit}/co2/grids/heterogeneousSmall.dgf (100%) rename test/{boxmodels => implicit}/co2/heterogeneousco2tables.hh (100%) rename test/{boxmodels => implicit}/co2/heterogeneousproblem.hh (100%) rename test/{boxmodels => implicit}/co2/heterogeneousspatialparameters.hh (100%) rename test/{boxmodels => implicit}/co2/test_co2.cc (100%) rename test/{boxmodels => implicit}/co2/test_co2.input (100%) rename test/{boxmodels => implicit}/co2ni/CMakeLists.txt (100%) rename test/{boxmodels => implicit}/co2ni/Makefile.am (100%) rename test/{boxmodels => implicit}/co2ni/co2ni-reference.vtu (100%) rename test/{boxmodels => implicit}/co2ni/co2values.inc (100%) rename test/{boxmodels => implicit}/co2ni/grids/heterogeneousSmall.dgf (100%) rename test/{boxmodels => implicit}/co2ni/heterogeneousco2tables.hh (100%) rename test/{boxmodels => implicit}/co2ni/heterogeneousproblemni.hh (100%) rename test/{boxmodels => implicit}/co2ni/heterogeneousspatialparametersni.hh (100%) rename test/{boxmodels => implicit}/co2ni/test_co2ni.cc (100%) rename test/{boxmodels => implicit}/co2ni/test_co2ni.input (100%) rename test/{boxmodels => implicit}/mpnc/CMakeLists.txt (100%) rename test/{boxmodels => implicit}/mpnc/Makefile.am (100%) rename test/{boxmodels => implicit}/mpnc/forchheimer1p-reference.vtp (100%) rename test/{boxmodels => implicit}/mpnc/forchheimer1pproblem.hh (100%) rename test/{boxmodels => implicit}/mpnc/forchheimer2p-reference.vtu (100%) rename test/{boxmodels => implicit}/mpnc/forchheimer2pproblem.hh (100%) rename test/{boxmodels => implicit}/mpnc/forchheimerspatialparams.hh (100%) rename test/{boxmodels => implicit}/mpnc/grids/forchheimer1d.dgf (100%) rename test/{boxmodels => implicit}/mpnc/grids/obstacle_24x16.dgf (100%) rename test/{boxmodels => implicit}/mpnc/grids/obstacle_48x32.dgf (100%) rename test/{boxmodels => implicit}/mpnc/obstacle-reference.vtu (100%) rename test/{boxmodels => implicit}/mpnc/obstacleproblem.hh (100%) rename test/{boxmodels => implicit}/mpnc/obstaclespatialparams.hh (100%) rename test/{boxmodels => implicit}/mpnc/test_forchheimer1p.cc (100%) rename test/{boxmodels => implicit}/mpnc/test_forchheimer1p.input (100%) rename test/{boxmodels => implicit}/mpnc/test_forchheimer2p.cc (100%) rename test/{boxmodels => implicit}/mpnc/test_forchheimer2p.input (100%) rename test/{boxmodels => implicit}/mpnc/test_mpnc.cc (100%) rename test/{boxmodels => implicit}/mpnc/test_mpnc.input (100%) rename test/{boxmodels => implicit}/richards/CMakeLists.txt (100%) rename test/{boxmodels => implicit}/richards/Makefile.am (100%) rename test/{boxmodels => implicit}/richards/grids/richardslens-24x16.dgf (100%) rename test/{boxmodels => implicit}/richards/grids/richardslens-48x32.dgf (100%) rename test/{boxmodels => implicit}/richards/grids/richardslens-96x64.dgf (100%) rename test/{boxmodels => implicit}/richards/richardslens-reference-parallel.vtu (100%) rename test/{boxmodels => implicit}/richards/richardslens-reference.vtu (100%) rename test/{boxmodels => implicit}/richards/richardslensproblem.hh (100%) rename test/{boxmodels => implicit}/richards/richardslensspatialparams.hh (100%) rename test/{boxmodels => implicit}/richards/test_richards.cc (100%) rename test/{boxmodels => implicit}/richards/test_richards.input (100%) diff --git a/configure.ac b/configure.ac index 2dbe007b34..663287b549 100644 --- a/configure.ac +++ b/configure.ac @@ -56,6 +56,26 @@ AC_CONFIG_FILES([dumux.pc dumux/freeflow/stokes/Makefile dumux/freeflow/stokes2c/Makefile dumux/freeflow/stokes2cni/Makefile + dumux/implicit/Makefile + dumux/implicit/1p/Makefile + dumux/implicit/1p2c/Makefile + dumux/implicit/2p/Makefile + dumux/implicit/2p2c/Makefile + dumux/implicit/2p2cni/Makefile + dumux/implicit/2pni/Makefile + dumux/implicit/2pdfm/Makefile + dumux/implicit/3p3c/Makefile + dumux/implicit/3p3cni/Makefile + dumux/implicit/box/Makefile + dumux/implicit/cellcentered/Makefile + dumux/implicit/common/Makefile + dumux/implicit/co2/Makefile + dumux/implicit/co2ni/Makefile + dumux/implicit/mpnc/Makefile + dumux/implicit/mpnc/diffusion/Makefile + dumux/implicit/mpnc/energy/Makefile + dumux/implicit/mpnc/mass/Makefile + dumux/implicit/richards/Makefile dumux/io/Makefile dumux/linear/Makefile dumux/material/Makefile @@ -75,20 +95,20 @@ AC_CONFIG_FILES([dumux.pc dumux/parallel/Makefile m4/Makefile test/Makefile - test/boxmodels/Makefile - test/boxmodels/1p/Makefile - test/boxmodels/2p/Makefile - test/boxmodels/2pni/Makefile - test/boxmodels/1p2c/Makefile - test/boxmodels/2p2c/Makefile - test/boxmodels/2p2cni/Makefile - test/boxmodels/2pdfm/Makefile - test/boxmodels/3p3c/Makefile - test/boxmodels/3p3cni/Makefile - test/boxmodels/co2/Makefile - test/boxmodels/co2ni/Makefile - test/boxmodels/mpnc/Makefile - test/boxmodels/richards/Makefile + test/implicit/Makefile + test/implicit/1p/Makefile + test/implicit/2p/Makefile + test/implicit/2pni/Makefile + test/implicit/1p2c/Makefile + test/implicit/2p2c/Makefile + test/implicit/2p2cni/Makefile + test/implicit/2pdfm/Makefile + test/implicit/3p3c/Makefile + test/implicit/3p3cni/Makefile + test/implicit/co2/Makefile + test/implicit/co2ni/Makefile + test/implicit/mpnc/Makefile + test/implicit/richards/Makefile test/common/Makefile test/common/generalproblem/Makefile test/common/propertysystem/Makefile diff --git a/dumux/Makefile.am b/dumux/Makefile.am index d5aa78cec3..f5093738d6 100644 --- a/dumux/Makefile.am +++ b/dumux/Makefile.am @@ -1,4 +1,4 @@ -SUBDIRS = boxmodels common decoupled freeflow io linear material nonlinear parallel +SUBDIRS = boxmodels common decoupled freeflow implicit io linear material nonlinear parallel dumuxdir = $(includedir)/dumux diff --git a/dumux/implicit/1p/1pindices.hh b/dumux/implicit/1p/1pindices.hh new file mode 100644 index 0000000000..237d2a79d7 --- /dev/null +++ b/dumux/implicit/1p/1pindices.hh @@ -0,0 +1,45 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \file + * + * \brief Defines the indices for the one-phase box model. + */ +#ifndef DUMUX_1P_INDICES_HH +#define DUMUX_1P_INDICES_HH + +namespace Dumux +{ +// \{ + +/*! + * \ingroup OnePBoxModel + * \ingroup BoxIndices + * \brief Indices for the one-phase model. + */ +struct OnePIndices +{ + static const int conti0EqIdx = 0; //index for the mass balance + static const int pressureIdx = 0; //index of the primary variable +}; + +// \} +} // end namepace + +#endif diff --git a/dumux/implicit/1p/1plocalresidual.hh b/dumux/implicit/1p/1plocalresidual.hh new file mode 100644 index 0000000000..0666c4459a --- /dev/null +++ b/dumux/implicit/1p/1plocalresidual.hh @@ -0,0 +1,169 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \file + * + * \brief Element-wise calculation of the Jacobian matrix for problems + * using the one-phase box model. + */ +#ifndef DUMUX_1P_LOCAL_RESIDUAL_HH +#define DUMUX_1P_LOCAL_RESIDUAL_HH + +#include <dumux/boxmodels/common/boxlocalresidual.hh> + +#include "1pvolumevariables.hh" +#include "1pproperties.hh" + +namespace Dumux +{ +/*! + * \ingroup OnePBoxModel + * \ingroup BoxLocalResidual + * \brief Element-wise calculation of the Jacobian matrix for problems + * using the one-phase box model. + */ +template<class TypeTag> +class OnePLocalResidual : public GET_PROP_TYPE(TypeTag, BaseLocalResidual) +{ + typedef OnePLocalResidual<TypeTag> ThisType; + + typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; + typedef typename GET_PROP_TYPE(TypeTag, PrimaryVariables) PrimaryVariables; + typedef typename GET_PROP_TYPE(TypeTag, VolumeVariables) VolumeVariables; + typedef typename GET_PROP_TYPE(TypeTag, FluxVariables) FluxVariables; + typedef typename GET_PROP_TYPE(TypeTag, ElementVolumeVariables) ElementVolumeVariables; + + typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; + enum { dim = GridView::dimension }; + typedef Dune::FieldVector<Scalar, dim> DimVector; + + typedef typename GET_PROP_TYPE(TypeTag, Indices) Indices; + //index of the mass balance equation + enum { + conti0EqIdx = Indices::conti0EqIdx //index for the mass balance + }; + //index of the primary variable + enum{ + pressureIdx = Indices::pressureIdx //index for the primary variable + }; + +public: + + /*! + * \brief Constructor. Sets the upwind weight. + */ + OnePLocalResidual() + { + // retrieve the upwind weight for the mass conservation equations. Use the value + // specified via the property system as default, and overwrite + // it by the run-time parameter from the Dune::ParameterTree + upwindWeight_ = GET_PARAM_FROM_GROUP(TypeTag, Scalar, Implicit, MassUpwindWeight); + }; + + /*! + * \brief Evaluate the rate of change of all conservation + * quantites (e.g. phase mass) within a sub-control + * volume of a finite volume element for the OneP + * model. + * + * This function should not include the source and sink terms. + * \param storage The phase mass within the sub-control volume + * \param scvIdx The SCV (sub-control-volume) index + * \param usePrevSol Evaluate function with solution of current or previous time step + */ + void computeStorage(PrimaryVariables &storage, const int scvIdx, const bool usePrevSol) const + { + // if flag usePrevSol is set, the solution from the previous + // time step is used, otherwise the current solution is + // used. The secondary variables are used accordingly. This + // is required to compute the derivative of the storage term + // using the implicit euler method. + const ElementVolumeVariables &elemVolVars = usePrevSol ? this->prevVolVars_() : this->curVolVars_(); + const VolumeVariables &volVars = elemVolVars[scvIdx]; + + // partial time derivative of the wetting phase mass + storage[conti0EqIdx] = volVars.density() * volVars.porosity(); + } + + + /*! + * \brief Evaluate the mass flux over a face of a sub-control + * volume. + * + * \param flux The flux over the SCV (sub-control-volume) face + * \param faceIdx The index of the SCV face + * \param onBoundary A boolean variable to specify whether the flux variables + * are calculated for interior SCV faces or boundary faces, default=false + */ + void computeFlux(PrimaryVariables &flux, const int faceIdx, const bool onBoundary=false) const + { + FluxVariables fluxVars(this->problem_(), + this->element_(), + this->fvGeometry_(), + faceIdx, + this->curVolVars_(), + onBoundary); + + const VolumeVariables &up = this->curVolVars_(fluxVars.upstreamIdx(/*phaseIdx=*/0)); + const VolumeVariables &dn = this->curVolVars_(fluxVars.downstreamIdx(/*phaseIdx=*/0)); + flux[conti0EqIdx] = + (( upwindWeight_)*up.density() + + + (1 - upwindWeight_)*dn.density()) + * + fluxVars.volumeFlux(/*phaseIdx=*/0); + } + + /*! + * \brief Calculate the source term of the equation. + * + * \param source The source/sink in the SCV + * \param scvIdx The index of the SCV + * + */ + void computeSource(PrimaryVariables &source, const int scvIdx) + { + this->problem_().boxSDSource(source, + this->element_(), + this->fvGeometry_(), + scvIdx, + this->curVolVars_()); + } + + /*! + * \brief Return the temperature given the solution vector of a + * finite volume. + */ + template <class PrimaryVariables> + Scalar temperature(const PrimaryVariables &priVars) + { return this->problem_.temperature(); /* constant temperature */ } + +private: + ThisType &asImp_() + { return *static_cast<ThisType *>(this); } + + const ThisType &asImp_() const + { return *static_cast<const ThisType *>(this); } + + Scalar upwindWeight_; +}; + +} + +#endif diff --git a/dumux/implicit/1p/1pmodel.hh b/dumux/implicit/1p/1pmodel.hh new file mode 100644 index 0000000000..40afc03848 --- /dev/null +++ b/dumux/implicit/1p/1pmodel.hh @@ -0,0 +1,125 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \file + * + * \brief Base class for all models which use the one-phase, + * box model. + * Adaption of the BOX scheme to the one-phase flow model. + */ + +#ifndef DUMUX_1P_MODEL_HH +#define DUMUX_1P_MODEL_HH + +#include <dumux/boxmodels/common/boxmodel.hh> + +#include "1plocalresidual.hh" + +namespace Dumux +{ +/*! + * \ingroup OnePBoxModel + * \brief A single-phase, isothermal flow model using the box scheme. + * + * Single-phase, isothermal flow model, which solves the mass + * continuity equation + * \f[ + \phi \frac{\partial \varrho}{\partial t} + \text{div} (- \varrho \frac{\textbf K}{\mu} ( \textbf{grad}\, p -\varrho {\textbf g})) = q, + * \f] + * discretized using a vertex-centered finite volume (box) scheme as + * spatial and the implicit Euler method as time discretization. The + * model supports compressible as well as incompressible fluids. + */ +template<class TypeTag > +class OnePBoxModel : public GET_PROP_TYPE(TypeTag, BaseModel) +{ + typedef typename GET_PROP_TYPE(TypeTag, FVElementGeometry) FVElementGeometry; + typedef typename GET_PROP_TYPE(TypeTag, VolumeVariables) VolumeVariables; + typedef typename GET_PROP_TYPE(TypeTag, SpatialParams) SpatialParams; + typedef typename GET_PROP_TYPE(TypeTag, ElementBoundaryTypes) ElementBoundaryTypes; + typedef typename GET_PROP_TYPE(TypeTag, SolutionVector) SolutionVector; + + typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; + typedef typename GridView::template Codim<0>::Iterator ElementIterator; + enum { dim = GridView::dimension }; + +public: + /*! + * \brief \copybrief Dumux::BoxModel::addOutputVtkFields + * + * Specialization for the OnePBoxModel, adding the pressure and + * the process rank to the VTK writer. + */ + template<class MultiWriter> + void addOutputVtkFields(const SolutionVector &sol, + MultiWriter &writer) + { + typedef Dune::BlockVector<Dune::FieldVector<double, 1> > ScalarField; + + // create the required scalar fields + unsigned numVertices = this->problem_().gridView().size(dim); + ScalarField *p = writer.allocateManagedBuffer(numVertices); + ScalarField *K = writer.allocateManagedBuffer(numVertices); + + unsigned numElements = this->gridView_().size(0); + ScalarField *rank = writer.allocateManagedBuffer(numElements); + + FVElementGeometry fvGeometry; + VolumeVariables volVars; + ElementBoundaryTypes elemBcTypes; + + ElementIterator elemIt = this->gridView_().template begin<0>(); + ElementIterator elemEndIt = this->gridView_().template end<0>(); + for (; elemIt != elemEndIt; ++elemIt) + { + int idx = this->problem_().model().elementMapper().map(*elemIt); + (*rank)[idx] = this->gridView_().comm().rank(); + + fvGeometry.update(this->gridView_(), *elemIt); + elemBcTypes.update(this->problem_(), *elemIt, fvGeometry); + + int numVerts = elemIt->template count<dim> (); + for (int i = 0; i < numVerts; ++i) + { + int globalIdx = this->vertexMapper().map(*elemIt, i, dim); + volVars.update(sol[globalIdx], + this->problem_(), + *elemIt, + fvGeometry, + i, + false); + const SpatialParams &spatialParams = this->problem_().spatialParams(); + + (*p)[globalIdx] = volVars.pressure(); + (*K)[globalIdx]= spatialParams.intrinsicPermeability(*elemIt, + fvGeometry, + i); + } + } + + writer.attachVertexData(*p, "p"); + writer.attachVertexData(*K, "K"); + writer.attachCellData(*rank, "process rank"); + } +}; +} + +#include "1ppropertydefaults.hh" + +#endif diff --git a/dumux/implicit/1p/1pproperties.hh b/dumux/implicit/1p/1pproperties.hh new file mode 100644 index 0000000000..87112a4110 --- /dev/null +++ b/dumux/implicit/1p/1pproperties.hh @@ -0,0 +1,65 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \ingroup Properties + * \ingroup BoxProperties + * \ingroup OnePBoxModel + * \file + * + * \brief Defines the properties required for the one-phase BOX model. + */ +#ifndef DUMUX_1P_PROPERTIES_DATA_HH +#define DUMUX_1P_PROPERTIES_DATA_HH + +#include <dumux/boxmodels/common/boxproperties.hh> + +namespace Dumux +{ +// \{ +/////////////////////////////////////////////////////////////////////////// +// properties for the isothermal single phase model +/////////////////////////////////////////////////////////////////////////// +namespace Properties { + +////////////////////////////////////////////////////////////////// +// Type tags +////////////////////////////////////////////////////////////////// + +//! The type tag for the isothermal single phase problems +NEW_TYPE_TAG(BoxOneP, INHERITS_FROM(BoxModel)); + +////////////////////////////////////////////////////////////////// +// Property tags +////////////////////////////////////////////////////////////////// + +NEW_PROP_TAG(NumPhases); //!< Number of fluid phases in the system +NEW_PROP_TAG(Indices); //!< Enumerations for the model +NEW_PROP_TAG(SpatialParams); //!< The type of the spatial parameters object +NEW_PROP_TAG(FluidSystem); //!< The type of the fluid system to use +NEW_PROP_TAG(Fluid); //!< The fluid used for the default fluid system +NEW_PROP_TAG(ProblemEnableGravity); //!< Returns whether gravity is considered in the problem +NEW_PROP_TAG(ImplicitMassUpwindWeight); //!< Returns weight of the upwind cell when calculating fluxes +NEW_PROP_TAG(ImplicitMobilityUpwindWeight); //!< Weight for the upwind mobility in the velocity calculation +NEW_PROP_TAG(SpatialParamsForchCoeff); //!< Property for the forchheimer coefficient +// \} +} + +} // end namepace + +#endif diff --git a/dumux/implicit/1p/1ppropertydefaults.hh b/dumux/implicit/1p/1ppropertydefaults.hh new file mode 100644 index 0000000000..27ef59c662 --- /dev/null +++ b/dumux/implicit/1p/1ppropertydefaults.hh @@ -0,0 +1,109 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \ingroup Properties + * \ingroup BoxProperties + * \ingroup OnePBoxModel + * \file + * + * \brief Defines the properties required for the one-phase BOX model. + */ +#ifndef DUMUX_1P_PROPERTY_DEFAULTS_HH +#define DUMUX_1P_PROPERTY_DEFAULTS_HH + +#include <dumux/boxmodels/common/boxproperties.hh> + +#include "1pmodel.hh" +#include "1plocalresidual.hh" +#include "1pvolumevariables.hh" +#include <dumux/boxmodels/common/boxdarcyfluxvariables.hh> +#include "1pindices.hh" + +#include <dumux/material/fluidsystems/gasphase.hh> +#include <dumux/material/fluidsystems/liquidphase.hh> +#include <dumux/material/components/nullcomponent.hh> +#include <dumux/material/fluidsystems/1pfluidsystem.hh> +#include <dumux/material/spatialparams/boxspatialparams1p.hh> + +namespace Dumux +{ +// \{ + +/////////////////////////////////////////////////////////////////////////// +// default property values for the isothermal single phase model +/////////////////////////////////////////////////////////////////////////// +namespace Properties { +SET_INT_PROP(BoxOneP, NumEq, 1); //!< set the number of equations to 1 +SET_INT_PROP(BoxOneP, NumPhases, 1); //!< The number of phases in the 1p model is 1 + +//! The local residual function +SET_TYPE_PROP(BoxOneP, + LocalResidual, + OnePLocalResidual<TypeTag>); + +//! the Model property +SET_TYPE_PROP(BoxOneP, Model, OnePBoxModel<TypeTag>); + +//! the VolumeVariables property +SET_TYPE_PROP(BoxOneP, VolumeVariables, OnePVolumeVariables<TypeTag>); + +//! the FluxVariables property +SET_TYPE_PROP(BoxOneP, FluxVariables, BoxDarcyFluxVariables<TypeTag>); + +//! The indices required by the isothermal single-phase model +SET_TYPE_PROP(BoxOneP, Indices, OnePIndices); + +//! The spatial parameters to be employed. +//! Use BoxSpatialParamsOneP by default. +SET_TYPE_PROP(BoxOneP, SpatialParams, BoxSpatialParamsOneP<TypeTag>); + +//! The weight of the upwind control volume when calculating +//! fluxes. Use central differences by default. +SET_SCALAR_PROP(BoxOneP, ImplicitMassUpwindWeight, 0.5); + +//! weight for the upwind mobility in the velocity calculation +//! fluxes. Use central differences by default. +SET_SCALAR_PROP(BoxOneP, ImplicitMobilityUpwindWeight, 0.5); + +//! The fluid system to use by default +SET_TYPE_PROP(BoxOneP, FluidSystem, Dumux::FluidSystems::OneP<typename GET_PROP_TYPE(TypeTag, Scalar), typename GET_PROP_TYPE(TypeTag, Fluid)>); + +SET_PROP(BoxOneP, Fluid) +{ private: + typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; +public: + typedef Dumux::LiquidPhase<Scalar, Dumux::NullComponent<Scalar> > type; +}; + +// enable gravity by default +SET_BOOL_PROP(BoxOneP, ProblemEnableGravity, true); + +//! default value for the forchheimer coefficient +// Source: Ward, J.C. 1964 Turbulent flow in porous media. ASCE J. Hydraul. Div 90. +// Actually the Forchheimer coefficient is also a function of the dimensions of the +// porous medium. Taking it as a constant is only a first approximation +// (Nield, Bejan, Convection in porous media, 2006, p. 10) +SET_SCALAR_PROP(BoxModel, SpatialParamsForchCoeff, 0.55); + +// \} +} // end namepace Properties + +} // end namepace Dumux + +#endif diff --git a/dumux/implicit/1p/1pvolumevariables.hh b/dumux/implicit/1p/1pvolumevariables.hh new file mode 100644 index 0000000000..b262b85b77 --- /dev/null +++ b/dumux/implicit/1p/1pvolumevariables.hh @@ -0,0 +1,203 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \file + * + * \brief Quantities required by the one-phase box model defined on a vertex. + */ +#ifndef DUMUX_1P_VOLUME_VARIABLES_HH +#define DUMUX_1P_VOLUME_VARIABLES_HH + +#include "1pproperties.hh" +#include <dumux/boxmodels/common/boxvolumevariables.hh> + +#include <dumux/material/fluidstates/immisciblefluidstate.hh> + +namespace Dumux +{ + +/*! + * \ingroup OnePBoxModel + * \ingroup BoxVolumeVariables + * \brief Contains the quantities which are constant within a + * finite volume in the one-phase model. + */ +template <class TypeTag> +class OnePVolumeVariables : public BoxVolumeVariables<TypeTag> +{ + typedef BoxVolumeVariables<TypeTag> ParentType; + + typedef typename GET_PROP_TYPE(TypeTag, VolumeVariables) Implementation; + typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; + typedef typename GET_PROP_TYPE(TypeTag, Problem) Problem; + typedef typename GET_PROP_TYPE(TypeTag, FVElementGeometry) FVElementGeometry; + typedef typename GET_PROP_TYPE(TypeTag, PrimaryVariables) PrimaryVariables; + typedef typename GET_PROP_TYPE(TypeTag, Indices) Indices; + typedef typename GET_PROP_TYPE(TypeTag, FluidSystem) FluidSystem; + + typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; + typedef typename GridView::template Codim<0>::Entity Element; + +public: + //! Type of the fluid state + typedef Dumux::ImmiscibleFluidState<Scalar, FluidSystem> FluidState; + + /*! + * \copydoc BoxVolumeVariables::update + */ + void update(const PrimaryVariables &priVars, + const Problem &problem, + const Element &element, + const FVElementGeometry &fvGeometry, + const int scvIdx, + const bool isOldSol) + { + ParentType::update(priVars, problem, element, fvGeometry, scvIdx, isOldSol); + + completeFluidState(priVars, problem, element, fvGeometry, scvIdx, fluidState_); + // porosity + porosity_ = problem.spatialParams().porosity(element, + fvGeometry, + scvIdx); + + // energy related quantities not contained in the fluid state + asImp_().updateEnergy_(priVars, problem, element, fvGeometry, scvIdx, isOldSol); + }; + + /*! + * \copydoc BoxModel::completeFluidState + */ + static void completeFluidState(const PrimaryVariables& priVars, + const Problem& problem, + const Element& element, + const FVElementGeometry& fvGeometry, + const int scvIdx, + FluidState& fluidState) + { + Scalar t = Implementation::temperature_(priVars, problem, element, + fvGeometry, scvIdx); + fluidState.setTemperature(t); + + fluidState.setPressure(/*phaseIdx=*/0, priVars[Indices::pressureIdx]); + + // saturation in a single phase is always 1 and thus redundant + // to set. But since we use the fluid state shared by the + // immiscible multi-phase models, so we have to set it here... + fluidState.setSaturation(/*phaseIdx=*/0, 1.0); + + typename FluidSystem::ParameterCache paramCache; + paramCache.updatePhase(fluidState, /*phaseIdx=*/0); + + Scalar value = FluidSystem::density(fluidState, paramCache, /*phaseIdx=*/0); + fluidState.setDensity(/*phaseIdx=*/0, value); + + value = FluidSystem::viscosity(fluidState, paramCache, /*phaseIdx=*/0); + fluidState.setViscosity(/*phaseIdx=*/0, value); + } + + /*! + * \brief Return temperature \f$\mathrm{[K]}\f$ inside the sub-control volume. + * + * Note that we assume thermodynamic equilibrium, i.e. the + * temperatures of the rock matrix and of all fluid phases are + * identical. + */ + Scalar temperature() const + { return fluidState_.temperature(); } + + /*! + * \brief Return the effective pressure \f$\mathrm{[Pa]}\f$ of a given phase within + * the control volume. + */ + Scalar pressure() const + { return fluidState_.pressure(/*phaseIdx=*/0); } + + /*! + * \brief Return the mass density \f$\mathrm{[kg/m^3]}\f$ of a given phase within the + * control volume. + */ + Scalar density() const + { return fluidState_.density(/*phaseIdx=*/0); } + + /*! + * \brief Return the dynamic viscosity \f$\mathrm{[Pa s]}\f$ of the fluid within the + * control volume. + */ + Scalar viscosity() const + { return fluidState_.viscosity(/*phaseIdx=*/0); } + + /*! + * \brief Returns the mobility. + * + * This function enables the use of BoxDarcyFluxVariables + * with the 1p box model, ALTHOUGH the term mobility is + * usually not employed in the one phase context. + * + * \param phaseIdx The phase index + */ + Scalar mobility(int phaseIdx = 0) const + { return 1.0/fluidState_.viscosity(phaseIdx); } + + /*! + * \brief Return the average porosity \f$\mathrm{[-]}\f$ within the control volume. + */ + Scalar porosity() const + { return porosity_; } + + /*! + * \brief Return the fluid state of the control volume. + */ + const FluidState &fluidState() const + { return fluidState_; } + +protected: + static Scalar temperature_(const PrimaryVariables &priVars, + const Problem& problem, + const Element &element, + const FVElementGeometry &fvGeometry, + const int scvIdx) + { + return problem.boxTemperature(element, fvGeometry, scvIdx); + } + + /*! + * \brief Called by update() to compute the energy related quantities. + */ + void updateEnergy_(const PrimaryVariables &sol, + const Problem &problem, + const Element &element, + const FVElementGeometry &fvGeometry, + const int scvIdx, + const bool isOldSol) + { } + + FluidState fluidState_; + Scalar porosity_; + +private: + Implementation &asImp_() + { return *static_cast<Implementation*>(this); } + + const Implementation &asImp_() const + { return *static_cast<const Implementation*>(this); } +}; + +} + +#endif diff --git a/dumux/implicit/1p/Makefile.am b/dumux/implicit/1p/Makefile.am new file mode 100644 index 0000000000..2dca3ed04d --- /dev/null +++ b/dumux/implicit/1p/Makefile.am @@ -0,0 +1,4 @@ +1pdir = $(includedir)/dumux/boxmodels/1p +1p_HEADERS = *.hh + +include $(top_srcdir)/am/global-rules diff --git a/dumux/implicit/1p2c/1p2cfluxvariables.hh b/dumux/implicit/1p2c/1p2cfluxvariables.hh new file mode 100644 index 0000000000..81a09a6098 --- /dev/null +++ b/dumux/implicit/1p2c/1p2cfluxvariables.hh @@ -0,0 +1,494 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \file + * + * \brief This file contains the data which is required to calculate + * all fluxes of fluid phases over a face of a finite volume. + * + * This means pressure and mole-fraction gradients, phase densities at + * the integration point, etc. + * + */ +#ifndef DUMUX_1P2C_FLUX_VARIABLES_HH +#define DUMUX_1P2C_FLUX_VARIABLES_HH + +#include "1p2cproperties.hh" + +#include <dumux/common/math.hh> +#include <dumux/common/valgrind.hh> + +namespace Dumux +{ + +/*! + * \ingroup OnePTwoCBoxModel + * \ingroup BoxFluxVariables + * \brief This template class contains the data which is required to + * calculate the fluxes of the fluid phases over a face of a + * finite volume for the one-phase, two-component model. + * + * This means pressure and mole-fraction gradients, phase densities at + * the intergration point, etc. + */ +template <class TypeTag> +class OnePTwoCFluxVariables +{ + typedef typename GET_PROP_TYPE(TypeTag, Problem) Problem; + typedef typename GET_PROP_TYPE(TypeTag, VolumeVariables) VolumeVariables; + typedef typename GET_PROP_TYPE(TypeTag, ElementVolumeVariables) ElementVolumeVariables; + typedef typename GET_PROP_TYPE(TypeTag, SpatialParams) SpatialParams; + + typedef typename GET_PROP_TYPE(TypeTag, Indices) Indices; + enum { + transportCompIdx = Indices::transportCompIdx + }; + + typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; + typedef typename GridView::template Codim<0>::Entity Element; + enum { + dim = GridView::dimension, + dimWorld = GridView::dimensionworld + }; + + typedef typename GridView::ctype CoordScalar; + typedef Dune::FieldVector<CoordScalar, dimWorld> GlobalPosition; + typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; + typedef Dune::FieldVector<Scalar, dim> DimVector; + typedef Dune::FieldMatrix<Scalar, dim, dim> DimMatrix; + + typedef typename GET_PROP_TYPE(TypeTag, FVElementGeometry) FVElementGeometry; + typedef typename FVElementGeometry::SubControlVolumeFace SCVFace; + +public: + /* + * \brief The constructor + * + * \param problem The problem + * \param element The finite element + * \param fvGeometry The finite-volume geometry in the box scheme + * \param scvfIdx The local index of the SCV (sub-control-volume) face + * \param elemVolVars The volume variables of the current element + * \param onBoundary A boolean variable to specify whether the flux variables + * are calculated for interior SCV faces or boundary faces, default=false + */ + OnePTwoCFluxVariables(const Problem &problem, + const Element &element, + const FVElementGeometry &fvGeometry, + const int faceIdx, + const ElementVolumeVariables &elemVolVars, + const bool onBoundary = false) + : fvGeometry_(fvGeometry), faceIdx_(faceIdx), onBoundary_(onBoundary) + { + viscosity_ = Scalar(0); + molarDensity_ = Scalar(0); + density_ = Scalar(0); + potentialGrad_ = Scalar(0); + moleFractionGrad_ = Scalar(0); + + calculateGradients_(problem, element, elemVolVars); + calculateK_(problem, element, elemVolVars); + calculateVelocities_(problem, element, elemVolVars); + calculatePorousDiffCoeff_(problem, element, elemVolVars); + calculateDispersionTensor_(problem, element, elemVolVars); + }; + +public: + /*! + * \brief Return the pressure potential multiplied with the + * intrinsic permeability and the face normal which + * goes from vertex i to vertex j. + * + * Note that the length of the face's normal is the area of the + * phase, so this is not the actual velocity but the integral of + * the velocity over the face's area. Also note that the phase + * mobility is not yet included here since this would require a + * decision on the upwinding approach (which is done in the + * actual model). + */ + Scalar KmvpNormal() const + { return KmvpNormal_; } + + /*! + * \brief Return the pressure potential multiplied with the + * intrinsic permeability as vector (for velocity output). + */ + DimVector Kmvp() const + { return Kmvp_; } + + /*! + * \brief The face of the current sub-control volume. This may be either + * an inner sub-control-volume face or a SCV face on the boundary. + */ + const SCVFace &face() const + { + if (onBoundary_) + return fvGeometry_.boundaryFace[faceIdx_]; + else + return fvGeometry_.subContVolFace[faceIdx_]; + } + + /*! + * \brief Return the intrinsic permeability tensor \f$\mathrm{[m^2]}\f$. + */ + const DimMatrix &intrinsicPermeability() const + { return K_; } + + /*! + * \brief Return the dispersion tensor \f$\mathrm{[m^2/s]}\f$. + */ + const DimMatrix &dispersionTensor() const + { return dispersionTensor_; } + + /*! + * \brief Return the pressure potential gradient \f$\mathrm{[Pa/m]}\f$. + */ + const DimVector &potentialGrad() const + { return potentialGrad_; } + + + /*! + * \brief Return the mole-fraction gradient of a component in a phase \f$\mathrm{[mol/mol/m)]}\f$. + * + * \param compIdx The index of the considered component + */ + const DimVector &moleFractionGrad(int compIdx) const + { + if (compIdx != 1) + { DUNE_THROW(Dune::InvalidStateException, + "The 1p2c model is supposed to need " + "only the concentration gradient of " + "the second component!"); } + return moleFractionGrad_; + }; + + /*! + * \brief The binary diffusion coefficient for each fluid phase in the porous medium \f$\mathrm{[m^2/s]}\f$. + */ + Scalar porousDiffCoeff() const + { + // TODO: tensorial porousDiffCoeff_usion coefficients + return porousDiffCoeff_; + }; + + /*! + * \brief Return viscosity \f$\mathrm{[Pa s]}\f$ of a phase at the integration + * point. + */ + Scalar viscosity() const + { return viscosity_;} + + /*! + * \brief Return molar density \f$\mathrm{[mol/m^3]}\f$ of a phase at the integration + * point. + */ + Scalar molarDensity() const + { return molarDensity_; } + + /*! + * \brief Return density \f$\mathrm{[kg/m^3]}\f$ of a phase at the integration + * point. + */ + Scalar density() const + { return density_; } + + /*! + * \brief Given the intrinsic permeability times the pressure + * potential gradient and SCV face normal for a phase, + * return the local index of the upstream control volume + * for a given phase. + * + * \param normalFlux The flux over a face of the sub-control volume + */ + int upstreamIdx(Scalar normalFlux) const + { return (normalFlux >= 0)?face().i:face().j; } + + /*! + * \brief Given the intrinsic permeability times the pressure + * potential gradient and SCV face normal for a phase, + * return the local index of the downstream control volume + * for a given phase. + * + * \param normalFlux The flux over a face of the sub-control volume + */ + int downstreamIdx(Scalar normalFlux) const + { return (normalFlux > 0)?face().j:face().i; } + + /*! + * \brief Return the local index of the upstream control volume + * for a given phase. + */ + int upstreamIdx() const + { return upstreamIdx_; } + + /*! + * \brief Return the local index of the downstream control volume + * for a given phase. + */ + int downstreamIdx() const + { return downstreamIdx_; } + +protected: + + /*! + * \brief Calculation of the pressure and mole-/mass-fraction gradients. + * + * \param problem The considered problem file + * \param element The considered element of the grid + * \param elemVolVars The parameters stored in the considered element + */ + void calculateGradients_(const Problem &problem, + const Element &element, + const ElementVolumeVariables &elemVolVars) + { + const VolumeVariables &volVarsI = elemVolVars[face().i]; + const VolumeVariables &volVarsJ = elemVolVars[face().j]; + + DimVector tmp; + //The decision of the if-statement depends on the function useTwoPointGradient(const Element &element, + //int vertexI,int vertexJ) defined in test/tissue_tumor_spatialparameters.hh + if (!problem.spatialParams().useTwoPointGradient(element, face().i, face().j)) { + // use finite-element gradients + tmp = 0.0; + for (int idx = 0; + idx < fvGeometry_.numFAP; + idx++) // loop over adjacent vertices + { + // FE gradient at vertex idx + const DimVector &feGrad = face().grad[idx]; + + // index for the element volume variables + int volVarsIdx = face().fapIndices[idx]; + + // the pressure gradient + tmp = feGrad; + tmp *= elemVolVars[volVarsIdx].pressure(); + potentialGrad_ += tmp; + + // the mole-fraction gradient + tmp = feGrad; + tmp *= elemVolVars[volVarsIdx].moleFraction(transportCompIdx); + moleFractionGrad_ += tmp; + + // phase viscosity + viscosity_ += elemVolVars[volVarsIdx].viscosity()*face().shapeValue[idx]; + + //phase molar density + molarDensity_ += elemVolVars[volVarsIdx].molarDensity()*face().shapeValue[idx]; + + //phase density + density_ += elemVolVars[volVarsIdx].density()*face().shapeValue[idx]; + } + } + else { + // use two-point gradients + const GlobalPosition &globalPosI = element.geometry().corner(face().i); + const GlobalPosition &globalPosJ = element.geometry().corner(face().j); + tmp = globalPosI; + tmp -= globalPosJ; + Scalar dist = tmp.two_norm(); + + tmp = face().normal; + tmp /= face().normal.two_norm()*dist; + + potentialGrad_ = tmp; + potentialGrad_ *= volVarsJ.pressure() - volVarsI.pressure(); + moleFractionGrad_ = tmp; + moleFractionGrad_ *= volVarsJ.moleFraction(transportCompIdx) - volVarsI.moleFraction(transportCompIdx); + } + + /////////////// + // correct the pressure gradients by the gravitational acceleration + /////////////// + if (GET_PARAM_FROM_GROUP(TypeTag, bool, Problem, EnableGravity)) { + // calculate the phase density at the integration point. we + // only do this if the wetting phase is present in both cells + Scalar rhoI = elemVolVars[face().i].density(); + Scalar rhoJ = elemVolVars[face().j].density(); + Scalar density = (rhoI + rhoJ)/2; + + // estimate the gravitational acceleration at a given SCV face + // using the arithmetic mean + DimVector f(problem.boxGravity(element, fvGeometry_, face().i)); + f += problem.boxGravity(element, fvGeometry_, face().j); + f /= 2; + + // make it a force + f *= density; + + // calculate the final potential gradient + potentialGrad_ -= f; + } + } + + /*! + * \brief Calculation of the harmonic mean of the intrinsic permeability + * uses the meanK function in the boxspatialparameters.hh file in the folder + * material/spatialparameters + * + * \param problem The considered problem file + * \param element The considered element of the grid + * \param elemVolVars The parameters stored in the considered element + */ + void calculateK_(const Problem &problem, + const Element &element, + const ElementVolumeVariables &elemVolVars) + { + const SpatialParams &sp = problem.spatialParams(); + sp.meanK(K_, + sp.intrinsicPermeability(element, + fvGeometry_, + face().i), + sp.intrinsicPermeability(element, + fvGeometry_, + face().j)); + + } + + /*! + * \brief Calculation of the velocity normal to face using Darcy's law. + * Tensorial permeability is multiplied with the potential gradient and the face normal. + * Identify upstream node of face. + * + * \param problem The considered problem file + * \param element The considered element of the grid + * \param elemVolVars The parameters stored in the considered element + */ + void calculateVelocities_(const Problem &problem, + const Element &element, + const ElementVolumeVariables &elemVolVars) + { + K_.mv(potentialGrad_, Kmvp_); + KmvpNormal_ = -(Kmvp_*face().normal); + + // set the upstream and downstream vertices + upstreamIdx_ = face().i; + downstreamIdx_ = face().j; + + if (KmvpNormal_ < 0) + { + std::swap(upstreamIdx_, + downstreamIdx_); + } + } + /*! + * \brief Calculation of the effective diffusion coefficient. + * + * \param problem The considered problem file + * \param element The considered element of the grid + * \param elemVolVars The parameters stored in the considered element + */ + void calculatePorousDiffCoeff_(const Problem &problem, + const Element &element, + const ElementVolumeVariables &elemVolVars) + { + const VolumeVariables &volVarsI = elemVolVars[face().i]; + const VolumeVariables &volVarsJ = elemVolVars[face().j]; + + // Diffusion coefficient in the porous medium + porousDiffCoeff_ + = harmonicMean(volVarsI.porosity() * volVarsI.tortuosity() * volVarsI.diffCoeff(), + volVarsJ.porosity() * volVarsJ.tortuosity() * volVarsJ.diffCoeff()); +// = 1./2*(volVarsI.porosity() * volVarsI.tortuosity() * volVarsI.diffCoeff() + +// volVarsJ.porosity() * volVarsJ.tortuosity() * volVarsJ.diffCoeff()); + } + + /*! + * \brief Calculation of the dispersion. + * + * \param problem The considered problem file + * \param element The considered element of the grid + * \param elemVolVars The parameters stored in the considered element + */ + void calculateDispersionTensor_(const Problem &problem, + const Element &element, + const ElementVolumeVariables &elemVolVars) + { + const VolumeVariables &volVarsI = elemVolVars[face().i]; + const VolumeVariables &volVarsJ = elemVolVars[face().j]; + + //calculate dispersivity at the interface: [0]: alphaL = longitudinal disp. [m], [1] alphaT = transverse disp. [m] + Scalar dispersivity[2]; + dispersivity[0] = 0.5 * (volVarsI.dispersivity()[0] + volVarsJ.dispersivity()[0]); + dispersivity[1] = 0.5 * (volVarsI.dispersivity()[1] + volVarsJ.dispersivity()[1]); + + //calculate velocity at interface: v = -1/mu * vDarcy = -1/mu * K * grad(p) + DimVector velocity; + Valgrind::CheckDefined(potentialGrad()); + Valgrind::CheckDefined(K_); + K_.mv(potentialGrad(), velocity); + velocity /= - 0.5 * (volVarsI.viscosity() + volVarsJ.viscosity()); + + //matrix multiplication of the velocity at the interface: vv^T + dispersionTensor_ = 0; + for (int i=0; i<dim; i++) + for (int j = 0; j<dim; j++) + dispersionTensor_[i][j]=velocity[i]*velocity[j]; + + //normalize velocity product --> vv^T/||v||, [m/s] + Scalar vNorm = velocity.two_norm(); + + dispersionTensor_ /= vNorm; + if (vNorm < 1e-20) + dispersionTensor_ = 0; + + //multiply with dispersivity difference: vv^T/||v||*(alphaL - alphaT), [m^2/s] --> alphaL = longitudinal disp., alphaT = transverse disp. + dispersionTensor_ *= (dispersivity[0] - dispersivity[1]); + + //add ||v||*alphaT to the main diagonal:vv^T/||v||*(alphaL - alphaT) + ||v||*alphaT, [m^2/s] + for (int i = 0; i<dim; i++) + dispersionTensor_[i][i] += vNorm*dispersivity[1]; + } + + const FVElementGeometry &fvGeometry_; + const int faceIdx_; + const bool onBoundary_; + + //! pressure potential gradient + DimVector potentialGrad_; + //! mole-fraction gradient + DimVector moleFractionGrad_; + //! the effective diffusion coefficent in the porous medium + Scalar porousDiffCoeff_; + + //! the dispersion tensor in the porous medium + DimMatrix dispersionTensor_; + + //! the intrinsic permeability tensor + DimMatrix K_; + // intrinsic permeability times pressure potential gradient + DimVector Kmvp_; + // projected on the face normal + Scalar KmvpNormal_; + + // local index of the upwind vertex for each phase + int upstreamIdx_; + // local index of the downwind vertex for each phase + int downstreamIdx_; + + //! viscosity of the fluid at the integration point + Scalar viscosity_; + + //! molar densities of the fluid at the integration point + Scalar molarDensity_, density_; +}; + +} // end namepace + +#endif diff --git a/dumux/implicit/1p2c/1p2cindices.hh b/dumux/implicit/1p2c/1p2cindices.hh new file mode 100644 index 0000000000..90a3d14f79 --- /dev/null +++ b/dumux/implicit/1p2c/1p2cindices.hh @@ -0,0 +1,63 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \file + * + * \brief Defines the primary variable and equation indices used by + * the 1p2c model + */ + +#ifndef DUMUX_1P2C_INDICES_HH +#define DUMUX_1P2C_INDICES_HH + +#include "1p2cproperties.hh" + +namespace Dumux +{ +// \{ + +/*! + * \ingroup OnePTwoCBoxModel + * \ingroup BoxIndices + * \brief The indices for the isothermal single-phase, two-component model. + */ +template <class TypeTag, int PVOffset = 0> +struct OnePTwoCIndices +{ + + //! Set the default phase used by the fluid system to the first one + static const int phaseIdx = GET_PROP_VALUE(TypeTag, PhaseIdx); + + //! Component indices + static const int phaseCompIdx = phaseIdx;//!< The index of the main component of the considered phase + static const int transportCompIdx = (unsigned int)(1-phaseIdx); //!< The index of the transported (minor) component; ASSUMES phase indices of 0 and 1 + + // Equation indices + static const int conti0EqIdx = PVOffset + 0; //!< continuity equation index + static const int transportEqIdx = PVOffset + 1; //!< transport equation index + + // primary variable indices + static const int pressureIdx = PVOffset + 0; //!< pressure + static const int massOrMoleFracIdx = PVOffset + 1; //!< mole fraction of the second component +}; + +// \} +} + +#endif diff --git a/dumux/implicit/1p2c/1p2clocalresidual.hh b/dumux/implicit/1p2c/1p2clocalresidual.hh new file mode 100644 index 0000000000..6d34e380d7 --- /dev/null +++ b/dumux/implicit/1p2c/1p2clocalresidual.hh @@ -0,0 +1,297 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \file + * + * \brief Element-wise calculation the local Jacobian for the single-phase, + * two-component model in the BOX scheme. + */ + +#ifndef DUMUX_ONEP_TWOC_LOCAL_RESIDUAL_HH +#define DUMUX_ONEP_TWOC_LOCAL_RESIDUAL_HH +#define VELOCITY_OUTPUT 1 //1 turns velocity output on, 0 turns it off + +#include <dumux/boxmodels/common/boxmodel.hh> + +#include <dumux/boxmodels/1p2c/1p2cproperties.hh> +#include <dumux/boxmodels/1p2c/1p2cvolumevariables.hh> +#include <dumux/boxmodels/1p2c/1p2cfluxvariables.hh> + +#include <dune/common/collectivecommunication.hh> +#include <vector> +#include <iostream> + +namespace Dumux +{ +/*! + * + * \ingroup OnePTwoCBoxModel + * \ingroup BoxLocalResidual + * \brief Calculate the local Jacobian for the single-phase, + * two-component model in the BOX scheme. + * + * This class is used to fill the gaps in BoxLocalResidual for the 1p2c flow and transport. + */ +template<class TypeTag> +class OnePTwoCLocalResidual : public GET_PROP_TYPE(TypeTag, BaseLocalResidual) +{ +protected: + typedef typename GET_PROP_TYPE(TypeTag, LocalResidual) Implementation; + + typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; + typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; + typedef typename GridView::IntersectionIterator IntersectionIterator; + + enum { dim = GridView::dimension }; + typedef Dune::FieldVector<Scalar, dim> DimVector; + + typedef typename GET_PROP_TYPE(TypeTag, VolumeVariables) VolumeVariables; + typedef typename GET_PROP_TYPE(TypeTag, FluxVariables) FluxVariables; + typedef typename GET_PROP_TYPE(TypeTag, ElementVolumeVariables) ElementVolumeVariables; + typedef typename GET_PROP_TYPE(TypeTag, PrimaryVariables) PrimaryVariables; + typedef typename GET_PROP_TYPE(TypeTag, BoundaryTypes) BoundaryTypes; + typedef typename GET_PROP_TYPE(TypeTag, FluidSystem) FluidSystem; + typedef typename GET_PROP_TYPE(TypeTag, Indices) Indices; + + enum { + numEq = GET_PROP_VALUE(TypeTag, NumEq), + + //phase index + phaseIdx = Indices::phaseIdx, + transportCompIdx = Indices::transportCompIdx + }; + // indices of the primary variables + enum { + pressuerIdx = Indices::pressureIdx, + massOrMoleFracIdx = Indices::massOrMoleFracIdx + }; + // indices of the equations + enum { + conti0EqIdx = Indices::conti0EqIdx, + transportEqIdx = Indices::transportEqIdx + }; + + //! property that defines whether mole or mass fractions are used + static const bool useMoles = GET_PROP_VALUE(TypeTag, UseMoles); + + + +public: + /*! + * \brief Constructor. Sets the upwind weight. + */ + OnePTwoCLocalResidual() + { + // retrieve the upwind weight for the mass conservation equations. Use the value + // specified via the property system as default, and overwrite + // it by the run-time parameter from the Dune::ParameterTree + upwindWeight_ = GET_PARAM_FROM_GROUP(TypeTag, Scalar, Implicit, MassUpwindWeight); + }; + + /*! + * \brief Evaluate the amount of all conservation quantities + * (e.g. phase mass) within a finite volume. + * + * \param storage The mass of the component within the sub-control volume + * \param scvIdx The index of the considered face of the sub-control volume + * \param usePrevSol Evaluate function with solution of current or previous time step + */ + void computeStorage(PrimaryVariables &storage, const int scvIdx, const bool usePrevSol) const + { + // if flag usePrevSol is set, the solution from the previous + // time step is used, otherwise the current solution is + // used. The secondary variables are used accordingly. This + // is required to compute the derivative of the storage term + // using the implicit euler method. + const ElementVolumeVariables &elemVolVars = usePrevSol ? this->prevVolVars_() : this->curVolVars_(); + const VolumeVariables &volVars = elemVolVars[scvIdx]; + + storage = 0; + if(!useMoles) + { + // storage term of continuity equation - massfractions + storage[conti0EqIdx] += + volVars.fluidState().density(phaseIdx)*volVars.porosity(); + //storage term of the transport equation - massfractions + storage[transportEqIdx] += + volVars.fluidState().density(phaseIdx) * volVars.fluidState().massFraction(phaseIdx, transportCompIdx) * volVars.porosity(); + } + else + { + // storage term of continuity equation- molefractions + //careful: molarDensity changes with moleFrac! + storage[conti0EqIdx] += volVars.molarDensity()*volVars.porosity(); + // storage term of the transport equation - molefractions + storage[transportEqIdx] += + volVars.fluidState().molarDensity(phaseIdx)*volVars.fluidState().moleFraction(phaseIdx, transportCompIdx) * + volVars.porosity(); + } + + } + + /*! + * \brief Evaluate the mass flux over a face of a sub-control + * volume. + * + * \param flux The flux over the SCV (sub-control-volume) face for each component + * \param faceIdx The index of the considered face of the sub control volume + * \param onBoundary A boolean variable to specify whether the flux variables + * are calculated for interior SCV faces or boundary faces, default=false + */ + void computeFlux(PrimaryVariables &flux, const int faceIdx, const bool onBoundary=false) const + { + flux = 0; + FluxVariables fluxVars(this->problem_(), + this->element_(), + this->fvGeometry_(), + faceIdx, + this->curVolVars_(), + onBoundary); + + asImp_()->computeAdvectiveFlux(flux, fluxVars); + asImp_()->computeDiffusiveFlux(flux, fluxVars); + } + + /*! + * \brief Evaluate the advective mass flux of all components over + * a face of a sub-control volume. + * + * \param flux The advective flux over the sub-control-volume face for each component + * \param fluxVars The flux variables at the current SCV + */ + void computeAdvectiveFlux(PrimaryVariables &flux, const FluxVariables &fluxVars) const + { + //////// + // advective fluxes of all components in all phases + //////// + + // data attached to upstream and the downstream vertices + // of the current phase + const VolumeVariables &up = + this->curVolVars_(fluxVars.upstreamIdx()); + const VolumeVariables &dn = + this->curVolVars_(fluxVars.downstreamIdx()); + + if(!useMoles) + { + // total mass flux - massfraction + //KmvpNormal is the Darcy velocity multiplied with the normal vector, calculated in 1p2cfluxvariables.hh + flux[conti0EqIdx] += + fluxVars.KmvpNormal() * + (( upwindWeight_)*up.density()/up.viscosity() + + + ((1 - upwindWeight_)*dn.density()/dn.viscosity())); + + // advective flux of the second component - massfraction + flux[transportEqIdx] += + fluxVars.KmvpNormal() * + (( upwindWeight_)*up.fluidState().density(phaseIdx) * up.fluidState().massFraction(phaseIdx, transportCompIdx)/up.viscosity() + + + (1 - upwindWeight_)*dn.fluidState().density(phaseIdx)*dn.fluidState().massFraction(phaseIdx, transportCompIdx)/dn.viscosity()); + } + else + { + // total mass flux - molefraction + //KmvpNormal is the Darcy velocity multiplied with the normal vector, calculated in 1p2cfluxvariables.hh + flux[conti0EqIdx] += + fluxVars.KmvpNormal() * + (( upwindWeight_)*up.molarDensity()/up.viscosity() + + + ((1 - upwindWeight_)*dn.molarDensity()/dn.viscosity())); + + // advective flux of the second component -molefraction + flux[transportEqIdx] += + fluxVars.KmvpNormal() * + (( upwindWeight_)*up.molarDensity() * up.fluidState().moleFraction(phaseIdx, transportCompIdx)/up.viscosity() + + + (1 - upwindWeight_)*dn.molarDensity() * dn.fluidState().moleFraction(phaseIdx, transportCompIdx)/dn.viscosity()); + } + + } + + /*! + * \brief Adds the diffusive mass flux of all components over + * a face of a sub-control volume. + * + * \param flux The diffusive flux over the sub-control-volume face for each component + * \param fluxVars The flux variables at the current SCV + */ + void computeDiffusiveFlux(PrimaryVariables &flux, const FluxVariables &fluxVars) const + { + Scalar tmp(0); + + // diffusive flux of second component + if(!useMoles) + { + // diffusive flux of the second component - massfraction + tmp = -(fluxVars.moleFractionGrad(transportCompIdx)*fluxVars.face().normal); + tmp *= fluxVars.porousDiffCoeff() * fluxVars.molarDensity(); + + // dispersive flux of second component - massfraction + DimVector normalDisp; + fluxVars.dispersionTensor().mv(fluxVars.face().normal, normalDisp); + tmp -= fluxVars.molarDensity()* + (normalDisp * fluxVars.moleFractionGrad(transportCompIdx)); + + // convert it to a mass flux and add it + flux[transportEqIdx] += tmp * FluidSystem::molarMass(transportCompIdx); + } + else + { + // diffusive flux of the second component - molefraction + tmp = -(fluxVars.moleFractionGrad(transportCompIdx)*fluxVars.face().normal); + tmp *= fluxVars.porousDiffCoeff() * fluxVars.molarDensity(); + + // dispersive flux of second component - molefraction + DimVector normalDisp; + fluxVars.dispersionTensor().mv(fluxVars.face().normal, normalDisp); + tmp -= fluxVars.molarDensity()* + (normalDisp * fluxVars.moleFractionGrad(transportCompIdx)); + + flux[transportEqIdx] += tmp; + } + } + + /*! + * \brief Calculate the source term of the equation + * \param source The source/sink in the SCV for each component + * \param scvIdx The index of the vertex of the sub control volume + * + */ + void computeSource(PrimaryVariables &source, const int scvIdx) + { + this->problem_().boxSDSource(source, + this->element_(), + this->fvGeometry_(), + scvIdx, + this->curVolVars_()); + } + + Implementation *asImp_() + { return static_cast<Implementation *> (this); } + const Implementation *asImp_() const + { return static_cast<const Implementation *> (this); } + +private: + Scalar upwindWeight_; +}; + +} + +#endif diff --git a/dumux/implicit/1p2c/1p2cmodel.hh b/dumux/implicit/1p2c/1p2cmodel.hh new file mode 100644 index 0000000000..3b70302f85 --- /dev/null +++ b/dumux/implicit/1p2c/1p2cmodel.hh @@ -0,0 +1,326 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \file + * + * \brief Base class for all models which use the single-phase, + * two-component box model. + * Adaption of the BOX scheme to the one-phase two-component flow model. + */ + +#ifndef DUMUX_ONEP_TWOC_MODEL_HH +#define DUMUX_ONEP_TWOC_MODEL_HH + +#include "1p2cproperties.hh" +#include "1p2clocalresidual.hh" + +#include <dumux/boxmodels/common/boxmodel.hh> + +namespace Dumux +{ + +/*! + * \ingroup OnePTwoCBoxModel + * \brief Adaption of the BOX scheme to the one-phase two-component flow model. + * + * This model implements a one-phase flow of a compressible fluid, that consists of two components, + * using a standard Darcy + * approach as the equation for the conservation of momentum: + \f[ + v_{D} = - \frac{\textbf K}{\mu} + \left(\text{grad} p - \varrho {\textbf g} \right) + \f] + * + * Gravity can be enabled or disabled via the property system. + * By inserting this into the continuity equation, one gets + \f[ + \Phi \frac{\partial \varrho}{\partial t} - \text{div} \left\{ + \varrho \frac{\textbf K}{\mu} \left(\text{grad}\, p - \varrho {\textbf g} \right) + \right\} = q \;, + \f] + * + * The transport of the components is described by the following equation: + \f[ + \Phi \frac{ \partial \varrho x}{\partial t} - \text{div} \left( \varrho \frac{{\textbf K} x}{\mu} \left( \text{grad}\, p - + \varrho {\textbf g} \right) + \varrho \tau \Phi D \text{grad} x \right) = q. + \f] + * + * All equations are discretized using a fully-coupled vertex-centered + * finite volume (box) scheme as spatial and + * the implicit Euler method as time discretization. + * + * The primary variables are the pressure \f$p\f$ and the mole or mass fraction of dissolved component \f$x\f$. + */ + +template<class TypeTag > +class OnePTwoCBoxModel : public GET_PROP_TYPE(TypeTag, BaseModel) +{ + typedef typename GET_PROP_TYPE(TypeTag, FVElementGeometry) FVElementGeometry; + typedef typename GET_PROP_TYPE(TypeTag, VolumeVariables) VolumeVariables; + typedef typename GET_PROP_TYPE(TypeTag, FluxVariables) FluxVariables; + typedef typename GET_PROP_TYPE(TypeTag, FluidSystem) FluidSystem; + typedef typename GET_PROP_TYPE(TypeTag, ElementVolumeVariables) ElementVolumeVariables; + typedef typename GET_PROP_TYPE(TypeTag, ElementBoundaryTypes) ElementBoundaryTypes; + typedef typename GET_PROP_TYPE(TypeTag, SolutionVector) SolutionVector; + + typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; + enum { + dim = GridView::dimension, + dimWorld = GridView::dimensionworld + }; + typedef typename GridView::template Codim<0>::Iterator ElementIterator; + typedef typename GridView::template Codim<dim>::Iterator VertexIterator; + + typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; + typedef Dune::FieldVector<Scalar, dim> DimVector; + +public: + /*! + * \brief Constructor. Sets the upwind weight. + */ + OnePTwoCBoxModel() + { + // retrieve the upwind weight for the mass conservation equations. Use the value + // specified via the property system as default, and overwrite + // it by the run-time parameter from the Dune::ParameterTree + upwindWeight_ = GET_PARAM_FROM_GROUP(TypeTag, Scalar, Implicit, MassUpwindWeight); + } + + /*! + * \brief \copybrief Dumux::BoxModel::addOutputVtkFields + * + * Specialization for the OnePTwoCBoxModel, adding pressure, + * mass and mole fractions, and the process rank to the VTK writer. + */ + template<class MultiWriter> + void addOutputVtkFields(const SolutionVector &sol, + MultiWriter &writer) + { + typedef Dune::BlockVector<Dune::FieldVector<double, 1> > ScalarField; + + // create the required scalar fields + unsigned numVertices = this->problem_().gridView().size(dim); + ScalarField &pressure = *writer.allocateManagedBuffer(numVertices); + ScalarField &delp = *writer.allocateManagedBuffer(numVertices); + ScalarField &moleFraction0 = *writer.allocateManagedBuffer(numVertices); + ScalarField &moleFraction1 = *writer.allocateManagedBuffer(numVertices); + ScalarField &massFraction0 = *writer.allocateManagedBuffer(numVertices); + ScalarField &massFraction1 = *writer.allocateManagedBuffer(numVertices); + ScalarField &rho = *writer.allocateManagedBuffer(numVertices); + ScalarField &mu = *writer.allocateManagedBuffer(numVertices); +#ifdef VELOCITY_OUTPUT // check if velocity output is demanded + ScalarField &velocityX = *writer.allocateManagedBuffer(numVertices); + ScalarField &velocityY = *writer.allocateManagedBuffer(numVertices); + ScalarField &velocityZ = *writer.allocateManagedBuffer(numVertices); + //use vertical faces for vx and horizontal faces for vy calculation + std::vector<DimVector> boxSurface(numVertices); + // initialize velocity fields + for (unsigned int i = 0; i < numVertices; ++i) + { + + velocityX[i] = 0; + if (dim > 1) + { + velocityY[i] = 0; + } + if (dim > 2) + { + velocityZ[i] = 0; + } + boxSurface[i] = Scalar(0.0); // initialize the boundary surface of the fv-boxes + } +#endif + unsigned numElements = this->gridView_().size(0); + ScalarField &rank = + *writer.allocateManagedBuffer(numElements); + + FVElementGeometry fvGeometry; + VolumeVariables volVars; + ElementBoundaryTypes elemBcTypes; + + ElementIterator elemIt = this->gridView_().template begin<0>(); + ElementIterator elemEndIt = this->gridView_().template end<0>(); + for (; elemIt != elemEndIt; ++elemIt) + { + int idx = this->problem_().model().elementMapper().map(*elemIt); + rank[idx] = this->gridView_().comm().rank(); + + fvGeometry.update(this->gridView_(), *elemIt); + elemBcTypes.update(this->problem_(), *elemIt, fvGeometry); + + int numVerts = elemIt->template count<dim> (); + for (int i = 0; i < numVerts; ++i) + { + int globalIdx = this->vertexMapper().map(*elemIt, i, dim); + volVars.update(sol[globalIdx], + this->problem_(), + *elemIt, + fvGeometry, + i, + false); + + pressure[globalIdx] = volVars.pressure(); + delp[globalIdx] = volVars.pressure() - 1e5; + moleFraction0[globalIdx] = volVars.moleFraction(0); + moleFraction1[globalIdx] = volVars.moleFraction(1); + massFraction0[globalIdx] = volVars.massFraction(0); + massFraction1[globalIdx] = volVars.massFraction(1); + rho[globalIdx] = volVars.density(); + mu[globalIdx] = volVars.viscosity(); + } + +#ifdef VELOCITY_OUTPUT // check if velocity output is demanded + // In the box method, the velocity is evaluated on the FE-Grid. However, to get an + // average apparent velocity at the vertex, all contributing velocities have to be interpolated. + DimVector velocity; + + ElementVolumeVariables elemVolVars; + elemVolVars.update(this->problem_(), + *elemIt, + fvGeometry, + false /* isOldSol? */); + // loop over the phases + for (int faceIdx = 0; faceIdx < fvGeometry.numEdges; faceIdx++) + { + velocity = 0.0; + //prepare the flux calculations (set up and prepare geometry, FE gradients) + FluxVariables fluxVars(this->problem_(), + *elemIt, + fvGeometry, + faceIdx, + elemVolVars); + + //use vertical faces for vx and horizontal faces for vy calculation + DimVector xVector(0), yVector(0); + xVector[0] = 1; yVector[1] = 1; + + Dune::SeqScalarProduct<DimVector> sp; + + Scalar xDir = std::abs(sp.dot(fluxVars.face().normal, xVector)); + Scalar yDir = std::abs(sp.dot(fluxVars.face().normal, yVector)); + + // up+downstream node + const VolumeVariables &up = + elemVolVars[fluxVars.upstreamIdx()]; + const VolumeVariables &dn = + elemVolVars[fluxVars.downstreamIdx()]; + + //get surface area to weight velocity at the IP with the surface area + Scalar scvfArea = fluxVars.face().normal.two_norm(); + + int vertIIdx = this->problem_().vertexMapper().map( + *elemIt, fluxVars.face().i, dim); + int vertJIdx = this->problem_().vertexMapper().map( + *elemIt, fluxVars.face().j, dim); + + //use vertical faces (horizontal noraml vector) to calculate vx + //in case of heterogeneities it seams to be better to define intrinisc permeability elementwise + if(xDir > yDir)//(fluxVars.face().normal[0] > 1e-10 || fluxVars.face().normal[0] < -1e-10)// (xDir > yDir) + { + // get darcy velocity + //calculate (v n) n/A + Scalar tmp = fluxVars.KmvpNormal(); + velocity = fluxVars.face().normal; + velocity *= tmp; + velocity /= scvfArea; + velocity *= (upwindWeight_ / up.viscosity() + + (1 - upwindWeight_)/ dn.viscosity()); + + // add surface area for weighting purposes + boxSurface[vertIIdx][0] += scvfArea; + boxSurface[vertJIdx][0] += scvfArea; + + velocityX[vertJIdx] += velocity[0]; + velocityX[vertIIdx] += velocity[0]; + + } + if (yDir > xDir)//(fluxVars.face().normal[1] > 1e-10 || fluxVars.face().normal[1] < -1e-10)// (yDir > xDir) + { + // get darcy velocity + //calculate (v n) n/A + Scalar tmp = fluxVars.KmvpNormal(); + velocity = fluxVars.face().normal; + velocity *= tmp; + velocity /= scvfArea; + velocity *= (upwindWeight_ / up.viscosity() + + (1 - upwindWeight_)/ dn.viscosity()); + + // add surface area for weighting purposes + boxSurface[vertIIdx][1] += scvfArea; + boxSurface[vertJIdx][1] += scvfArea; + + velocityY[vertJIdx] += velocity[1]; + velocityY[vertIIdx] += velocity[1]; + } + } +#endif + } +#ifdef VELOCITY_OUTPUT + // normalize the velocities at the vertices + // calculate the bounding box of the grid view + VertexIterator vIt = this->gridView_().template begin<dim>(); + const VertexIterator vEndIt = this->gridView_().template end<dim>(); + for (; vIt!=vEndIt; ++vIt) + { + int i = this->problem_().vertexMapper().map(*vIt); + + //use vertical faces for vx and horizontal faces for vy calculation + velocityX[i] /= boxSurface[i][0]; + if (dim >= 2) + { + velocityY[i] /= boxSurface[i][1]; + } + if (dim == 3) + { + velocityZ[i] /= boxSurface[i][2]; + } + } +#endif + writer.attachVertexData(pressure, "P"); + writer.attachVertexData(delp, "delp"); +#ifdef VELOCITY_OUTPUT // check if velocity output is demanded + writer.attachVertexData(velocityX, "Vx"); + writer.attachVertexData(velocityY, "Vy"); + if (dim > 2) + writer.attachVertexData(velocityZ, "Vz"); +#endif + char nameMoleFraction0[42], nameMoleFraction1[42]; + snprintf(nameMoleFraction0, 42, "x_%s", FluidSystem::componentName(0)); + snprintf(nameMoleFraction1, 42, "x_%s", FluidSystem::componentName(1)); + writer.attachVertexData(moleFraction0, nameMoleFraction0); + writer.attachVertexData(moleFraction1, nameMoleFraction1); + + char nameMassFraction0[42], nameMassFraction1[42]; + snprintf(nameMassFraction0, 42, "X_%s", FluidSystem::componentName(0)); + snprintf(nameMassFraction1, 42, "X_%s", FluidSystem::componentName(1)); + writer.attachVertexData(massFraction0, nameMassFraction0); + writer.attachVertexData(massFraction1, nameMassFraction1); + writer.attachVertexData(rho, "rho"); + writer.attachVertexData(mu, "mu"); + writer.attachCellData(rank, "process rank"); + } + +private: + Scalar upwindWeight_; +}; +} + +#include "1p2cpropertydefaults.hh" + +#endif diff --git a/dumux/implicit/1p2c/1p2cproperties.hh b/dumux/implicit/1p2c/1p2cproperties.hh new file mode 100644 index 0000000000..38d03eba34 --- /dev/null +++ b/dumux/implicit/1p2c/1p2cproperties.hh @@ -0,0 +1,68 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \ingroup Properties + * \ingroup BoxProperties + * \ingroup OnePTwoCBoxModel + * \file + * + * \brief Defines the properties required for the single-phase, + * two-component BOX model. + */ + +#ifndef DUMUX_1P2C_PROPERTIES_HH +#define DUMUX_1P2C_PROPERTIES_HH + + +#include<dumux/boxmodels/common/boxproperties.hh> + +namespace Dumux +{ +// \{ +namespace Properties +{ + +////////////////////////////////////////////////////////////////// +// Type tags +////////////////////////////////////////////////////////////////// + +//! The type tag for the isothermal single-phase, two-component problems +NEW_TYPE_TAG(BoxOnePTwoC, INHERITS_FROM(BoxModel)); + +////////////////////////////////////////////////////////////////// +// Property tags +////////////////////////////////////////////////////////////////// + +NEW_PROP_TAG(NumPhases); //!< Number of fluid phases in the system +NEW_PROP_TAG(PhaseIdx); //!< A phase index in to allow that a two-phase fluidsystem is used +NEW_PROP_TAG(NumComponents); //!< Number of fluid components in the system +NEW_PROP_TAG(Indices); //!< Enumerations for the model +NEW_PROP_TAG(SpatialParams); //!< The type of the spatial parameters +NEW_PROP_TAG(FluidSystem); //!< Type of the multi-component relations +NEW_PROP_TAG(ImplicitMassUpwindWeight); //!< The default value of the upwind weight +NEW_PROP_TAG(ProblemEnableGravity); //!< Returns whether gravity is considered in the problem +NEW_PROP_TAG(UseMoles); //!Defines whether mole (true) or mass (false) fractions are used +NEW_PROP_TAG(Scaling); //!Defines Scaling of the model +NEW_PROP_TAG(SpatialParamsForchCoeff); //!< Property for the forchheimer coefficient +} +// \} +} + +#endif + diff --git a/dumux/implicit/1p2c/1p2cpropertydefaults.hh b/dumux/implicit/1p2c/1p2cpropertydefaults.hh new file mode 100644 index 0000000000..79cd1e11f3 --- /dev/null +++ b/dumux/implicit/1p2c/1p2cpropertydefaults.hh @@ -0,0 +1,96 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \ingroup Properties + * \ingroup BoxProperties + * \ingroup OnePTwoCBoxModel + * \file + * + * \brief Defines some default values for the properties of the the + * single-phase, two-component BOX model. + */ + +#ifndef DUMUX_1P2C_PROPERTY_DEFAULTS_HH +#define DUMUX_1P2C_PROPERTY_DEFAULTS_HH + +#include "1p2cproperties.hh" + +#include "1p2cmodel.hh" +#include "1p2clocalresidual.hh" +#include "1p2cvolumevariables.hh" +#include "1p2cfluxvariables.hh" +#include "1p2cindices.hh" + +#include <dumux/material/spatialparams/boxspatialparams1p.hh> + +namespace Dumux +{ +// \{ +namespace Properties +{ +////////////////////////////////////////////////////////////////// +// Property values +////////////////////////////////////////////////////////////////// + +SET_INT_PROP(BoxOnePTwoC, NumEq, 2); //!< set the number of equations to 2 +SET_INT_PROP(BoxOnePTwoC, NumPhases, 1); //!< The number of phases in the 1p2c model is 1 +SET_INT_PROP(BoxOnePTwoC, NumComponents, 2); //!< The number of components in the 1p2c model is 2 +SET_SCALAR_PROP(BoxOnePTwoC, Scaling, 1); //!< Scaling of the model is set to 1 by default +SET_BOOL_PROP(BoxOnePTwoC, UseMoles, false); //!< Define that mass fractions are used in the balance equations + +//! Use the 1p2c local residual function for the 1p2c model +SET_TYPE_PROP(BoxOnePTwoC, LocalResidual, OnePTwoCLocalResidual<TypeTag>); + +//! define the model +SET_TYPE_PROP(BoxOnePTwoC, Model, OnePTwoCBoxModel<TypeTag>); + +//! define the VolumeVariables +SET_TYPE_PROP(BoxOnePTwoC, VolumeVariables, OnePTwoCVolumeVariables<TypeTag>); + +//! define the FluxVariables +SET_TYPE_PROP(BoxOnePTwoC, FluxVariables, OnePTwoCFluxVariables<TypeTag>); + +//! set default upwind weight to 1.0, i.e. fully upwind +SET_SCALAR_PROP(BoxOnePTwoC, ImplicitMassUpwindWeight, 1.0); + +//! Set the indices used by the 1p2c model +SET_TYPE_PROP(BoxOnePTwoC, Indices, Dumux::OnePTwoCIndices<TypeTag, 0>); + +//! The spatial parameters to be employed. +//! Use BoxSpatialParamsOneP by default. +SET_TYPE_PROP(BoxOnePTwoC, SpatialParams, BoxSpatialParamsOneP<TypeTag>); + +//! Set the phaseIndex per default to zero (important for two-phase fluidsystems). +SET_INT_PROP(BoxOnePTwoC, PhaseIdx, 0); + +// enable gravity by default +SET_BOOL_PROP(BoxOnePTwoC, ProblemEnableGravity, true); + +//! default value for the forchheimer coefficient +// Source: Ward, J.C. 1964 Turbulent flow in porous media. ASCE J. Hydraul. Div 90. +// Actually the Forchheimer coefficient is also a function of the dimensions of the +// porous medium. Taking it as a constant is only a first approximation +// (Nield, Bejan, Convection in porous media, 2006, p. 10) +SET_SCALAR_PROP(BoxModel, SpatialParamsForchCoeff, 0.55); +} +// \} +} + +#endif + diff --git a/dumux/implicit/1p2c/1p2cvolumevariables.hh b/dumux/implicit/1p2c/1p2cvolumevariables.hh new file mode 100644 index 0000000000..5e1620dc9b --- /dev/null +++ b/dumux/implicit/1p2c/1p2cvolumevariables.hh @@ -0,0 +1,284 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \file + * \brief Quantities required by the single-phase, two-component box + * model defined on a vertex. + */ +#ifndef DUMUX_1P2C_VOLUME_VARIABLES_HH +#define DUMUX_1P2C_VOLUME_VARIABLES_HH + +#include <dumux/boxmodels/common/boxvolumevariables.hh> +#include <dumux/material/fluidstates/compositionalfluidstate.hh> + +#include "1p2cproperties.hh" + +namespace Dumux +{ + +/*! + * \ingroup OnePTwoCBoxModel + * \ingroup BoxVolumeVariables + * \brief Contains the quantities which are constant within a + * finite volume in the single-phase, two-component model. + */ +template <class TypeTag> +class OnePTwoCVolumeVariables : public BoxVolumeVariables<TypeTag> +{ + typedef BoxVolumeVariables<TypeTag> ParentType; + + typedef typename GET_PROP_TYPE(TypeTag, Problem) Problem; + typedef typename GET_PROP_TYPE(TypeTag, VolumeVariables) Implementation; + typedef typename GET_PROP_TYPE(TypeTag, FVElementGeometry) FVElementGeometry; + typedef typename GET_PROP_TYPE(TypeTag, PrimaryVariables) PrimaryVariables; + typedef typename GET_PROP_TYPE(TypeTag, FluidSystem) FluidSystem; + static const bool useMoles = GET_PROP_VALUE(TypeTag, UseMoles); + + typedef typename GET_PROP_TYPE(TypeTag, Indices) Indices; + enum { + phaseIdx = Indices::phaseIdx, + phaseCompIdx = Indices::phaseCompIdx, + transportCompIdx = Indices::transportCompIdx + }; + //indices of primary variables + enum{ + pressureIdx = Indices::pressureIdx, + massOrMoleFracIdx = Indices::massOrMoleFracIdx + }; + + typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; + typedef typename GridView::template Codim<0>::Entity Element; + enum { dim = GridView::dimension }; + + typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; + typedef Dune::FieldVector<Scalar,dim> DimVector; + +public: + //! The type returned by the fluidState() method + typedef Dumux::CompositionalFluidState<Scalar, FluidSystem> FluidState; + + /*! + * \copydoc BoxVolumeVariables::update + */ + void update(const PrimaryVariables &priVars, + const Problem &problem, + const Element &element, + const FVElementGeometry &fvGeometry, + const int scvIdx, + const bool isOldSol) + { + ParentType::update(priVars, problem, element, fvGeometry, scvIdx, isOldSol); + + //calculate all secondary variables from the primary variables and store results in fluidstate + completeFluidState(priVars, problem, element, fvGeometry, scvIdx, fluidState_); + + porosity_ = problem.spatialParams().porosity(element, fvGeometry, scvIdx); + tortuosity_ = problem.spatialParams().tortuosity(element, fvGeometry, scvIdx); + dispersivity_ = problem.spatialParams().dispersivity(element, fvGeometry, scvIdx); + + // Second instance of a parameter cache. + // Could be avoided if diffusion coefficients also + // became part of the fluid state. + typename FluidSystem::ParameterCache paramCache; + paramCache.updatePhase(fluidState_, phaseIdx); + + diffCoeff_ = FluidSystem::binaryDiffusionCoefficient(fluidState_, + paramCache, + phaseIdx, + phaseCompIdx, + transportCompIdx); + + Valgrind::CheckDefined(porosity_); + Valgrind::CheckDefined(tortuosity_); + Valgrind::CheckDefined(dispersivity_); + Valgrind::CheckDefined(diffCoeff_); + + // energy related quantities not contained in the fluid state + asImp_().updateEnergy_(priVars, problem, element, fvGeometry, scvIdx, isOldSol); + } + + /*! + * \copydoc BoxModel::completeFluidState + */ + static void completeFluidState(const PrimaryVariables& priVars, + const Problem& problem, + const Element& element, + const FVElementGeometry& fvGeometry, + const int scvIdx, + FluidState& fluidState) + { + Scalar T = Implementation::temperature_(priVars, problem, element, + fvGeometry, scvIdx); + fluidState.setTemperature(T); + + fluidState.setPressure(phaseIdx, priVars[pressureIdx]); + + Scalar x1 = priVars[massOrMoleFracIdx]; //mole or mass fraction of component 1 + if(!useMoles) //mass-fraction formulation + { + // convert mass to mole fractions + Scalar M0 = FluidSystem::molarMass(phaseCompIdx); + Scalar M1 = FluidSystem::molarMass(transportCompIdx); + //meanMolarMass if x1_ is a massfraction + Scalar meanMolarMass = M0*M1/(M1 + x1*(M0 - M1)); + + x1 *= meanMolarMass/M1; + } + fluidState.setMoleFraction(phaseIdx, phaseCompIdx, 1 - x1); + fluidState.setMoleFraction(phaseIdx, transportCompIdx, x1); + + typename FluidSystem::ParameterCache paramCache; + paramCache.updatePhase(fluidState, phaseIdx); + + Scalar value; + value = FluidSystem::density(fluidState, paramCache, phaseIdx); + fluidState.setDensity(phaseIdx, value); + value = FluidSystem::viscosity(fluidState, paramCache, phaseIdx); + fluidState.setViscosity(phaseIdx, value); + } + + /*! + * \brief Return the fluid configuration at the given primary + * variables + */ + const FluidState &fluidState() const + { return fluidState_; } + + /*! + * \brief Return density \f$\mathrm{[kg/m^3]}\f$ the of the fluid phase. + */ + Scalar density() const + { return fluidState_.density(phaseIdx); } + + /*! + * \brief Return molar density \f$\mathrm{[mol/m^3]}\f$ the of the fluid phase. + */ + Scalar molarDensity() const + { return fluidState_.molarDensity(phaseIdx);} + + /*! + * \brief Return mole fraction \f$\mathrm{[mol/mol]}\f$ of a component in the phase. + * + * \param compIdx The index of the component + */ + Scalar moleFraction(int compIdx) const + { return fluidState_.moleFraction(phaseIdx, (compIdx==0)?phaseCompIdx:transportCompIdx); } + + /*! + * \brief Return mass fraction \f$\mathrm{[kg/kg]}\f$ of a component in the phase. + * + * \param compIdx The index of the component + */ + Scalar massFraction(int compIdx) const + { return fluidState_.massFraction(phaseIdx, (compIdx==0)?phaseCompIdx:transportCompIdx); } + + /*! + * \brief Return concentration \f$\mathrm{[mol/m^3]}\f$ of a component in the phase. + * + * \param compIdx The index of the component + */ + Scalar molarity(int compIdx) const + { return fluidState_.molarity(phaseIdx, (compIdx==0)?phaseCompIdx:transportCompIdx); } + + /*! + * \brief Return the effective pressure \f$\mathrm{[Pa]}\f$ of a given phase within + * the control volume. + */ + Scalar pressure() const + { return fluidState_.pressure(phaseIdx); } + + /*! + * \brief Return the binary diffusion coefficient \f$\mathrm{[m^2/s]}\f$ in the fluid. + */ + Scalar diffCoeff() const + { return diffCoeff_; } + + /*! + * \brief Return the tortuosity \f$\mathrm{[-]}\f$ of the streamlines of the fluid. + */ + Scalar tortuosity() const + { return tortuosity_; } + + /*! + * \brief Returns the dispersivity of the fluid's streamlines. + */ + const DimVector &dispersivity() const + { return dispersivity_; } + + /*! + * \brief Return temperature \f$\mathrm{[K]}\f$ inside the sub-control volume. + * + * Note that we assume thermodynamic equilibrium, i.e. the + * temperature of the rock matrix and of all fluid phases are + * identical. + */ + Scalar temperature() const + { return fluidState_.temperature(phaseIdx); } + + /*! + * \brief Return the dynamic viscosity \f$\mathrm{[Pa*s]}\f$ of a given phase + * within the control volume. + */ + Scalar viscosity() const + { return fluidState_.viscosity(phaseIdx); } + + /*! + * \brief Return the average porosity \f$\mathrm{[-]}\f$ within the control volume. + */ + Scalar porosity() const + { return porosity_; } + +protected: + static Scalar temperature_(const PrimaryVariables &priVars, + const Problem& problem, + const Element &element, + const FVElementGeometry &fvGeometry, + const int scvIdx) + { + return problem.boxTemperature(element, fvGeometry, scvIdx); + } + + /*! + * \brief Called by update() to compute the energy related quantities. + */ + void updateEnergy_(const PrimaryVariables &priVars, + const Problem &problem, + const Element &element, + const FVElementGeometry &fvGeometry, + const int scvIdx, + const bool isOldSol) + { } + + Scalar porosity_; //!< Effective porosity within the control volume + Scalar tortuosity_; + DimVector dispersivity_; + Scalar diffCoeff_; + FluidState fluidState_; + +private: + Implementation &asImp_() + { return *static_cast<Implementation*>(this); } + + const Implementation &asImp_() const + { return *static_cast<const Implementation*>(this); } +}; + +}// end namepace + +#endif diff --git a/dumux/implicit/1p2c/Makefile.am b/dumux/implicit/1p2c/Makefile.am new file mode 100644 index 0000000000..8a96579124 --- /dev/null +++ b/dumux/implicit/1p2c/Makefile.am @@ -0,0 +1,4 @@ +1p2cdir = $(includedir)/dumux/boxmodels/1p2c +1p2c_HEADERS = *.hh + +include $(top_srcdir)/am/global-rules diff --git a/dumux/implicit/2p/2pindices.hh b/dumux/implicit/2p/2pindices.hh new file mode 100644 index 0000000000..a36d15fdd2 --- /dev/null +++ b/dumux/implicit/2p/2pindices.hh @@ -0,0 +1,110 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ + +/*! + * \file + * + * \brief Defines the indices required for the two-phase box model. + */ +#ifndef DUMUX_BOX_2P_INDICES_HH +#define DUMUX_BOX_2P_INDICES_HH + +#include "2pproperties.hh" + +namespace Dumux +{ +// \{ + +/*! + * \ingroup TwoPBoxModel + * \ingroup BoxIndices + * \brief The common indices for the isothermal two-phase model. + */ + +struct TwoPFormulation +{ + static const int pwSn = 0; //!< Pw and Sn as primary variables + static const int pnSw = 1; //!< Pn and Sw as primary variables +}; + +template <class TypeTag> +struct TwoPCommonIndices +{ + typedef typename GET_PROP_TYPE(TypeTag, FluidSystem) FluidSystem; + + // Phase indices + static const int wPhaseIdx = FluidSystem::wPhaseIdx; //!< Index of the wetting phase + static const int nPhaseIdx = FluidSystem::nPhaseIdx; //!< Index of the non-wetting phase +}; + +/*! + * \brief The indices for the \f$p_w-S_n\f$ formulation of the + * isothermal two-phase model. + * + * \tparam TypeTag The problem type tag + * \tparam formulation The formulation, either pwSn or pnSw + * \tparam PVOffset The first index in a primary variable vector. + */ +template <class TypeTag, + int formulation = TwoPFormulation::pwSn, + int PVOffset = 0> +struct TwoPIndices +: public TwoPCommonIndices<TypeTag>, TwoPFormulation +{ + // Primary variable indices + static const int pressureIdx = PVOffset + 0; //!< Index for wetting/non-wetting phase pressure (depending on formulation) in a solution vector + static const int saturationIdx = PVOffset + 1; //!< Index of the saturation of the non-wetting/wetting phase + + // indices of the primary variables + static const int pwIdx = PVOffset + 0; //!< Pressure index of the wetting phase + static const int SnIdx = PVOffset + 1; //!< Saturation index of the wetting phase + + // indices of the equations + static const int contiWEqIdx = PVOffset + 0; //!< Index of the continuity equation of the wetting phase + static const int contiNEqIdx = PVOffset + 1; //!< Index of the continuity equation of the non-wetting phase +}; + +/*! + * \brief The indices for the \f$p_w-S_n\f$ formulation of the + * isothermal two-phase model. + * + * \tparam PVOffset The first index in a primary variable vector. + */ +template <class TypeTag, int PVOffset> +struct TwoPIndices<TypeTag, TwoPFormulation::pnSw, PVOffset> +: public TwoPCommonIndices<TypeTag>, TwoPFormulation +{ + // Primary variable indices + static const int pressureIdx = PVOffset + 0; //!< Index for wetting/non-wetting phase pressure (depending on formulation) in a solution vector + static const int saturationIdx = PVOffset + 1; //!< Index of the saturation of the non-wetting/wetting phase + + // indices of the primary variables + static const int pnIdx = PVOffset + 0; //!< Pressure index of the wetting phase + static const int SwIdx = PVOffset + 1; //!< Saturation index of the wetting phase + + // indices of the equations + static const int contiNEqIdx = PVOffset + 0; //!< Index of the continuity equation of the non-wetting phase + static const int contiWEqIdx = PVOffset + 1; //!< Index of the continuity equation of the wetting phase +}; + +// \} +} // namespace Dumux + + +#endif diff --git a/dumux/implicit/2p/2plocalresidual.hh b/dumux/implicit/2p/2plocalresidual.hh new file mode 100644 index 0000000000..c72e5046c8 --- /dev/null +++ b/dumux/implicit/2p/2plocalresidual.hh @@ -0,0 +1,212 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \file + * + * \brief Element-wise calculation of the residual for the two-phase box model. + */ +#ifndef DUMUX_TWOP_LOCAL_RESIDUAL_BASE_HH +#define DUMUX_TWOP_LOCAL_RESIDUAL_BASE_HH + +#include <dumux/boxmodels/common/boxmodel.hh> + +#include "2pproperties.hh" + +namespace Dumux +{ +/*! + * \ingroup TwoPBoxModel + * \ingroup BoxLocalResidual + * \brief Element-wise calculation of the Jacobian matrix for problems + * using the two-phase box model. + * + * This class is also used for the non-isothermal model, which means + * that it uses static polymorphism. + */ +template<class TypeTag> +class TwoPLocalResidual : public GET_PROP_TYPE(TypeTag, BaseLocalResidual) +{ +protected: + typedef typename GET_PROP_TYPE(TypeTag, LocalResidual) Implementation; + typedef typename GET_PROP_TYPE(TypeTag, PrimaryVariables) PrimaryVariables; + typedef typename GET_PROP_TYPE(TypeTag, VolumeVariables) VolumeVariables; + typedef typename GET_PROP_TYPE(TypeTag, FluxVariables) FluxVariables; + typedef typename GET_PROP_TYPE(TypeTag, ElementVolumeVariables) ElementVolumeVariables; + + typedef typename GET_PROP_TYPE(TypeTag, Indices) Indices; + enum + { + contiWEqIdx = Indices::contiWEqIdx, + contiNEqIdx = Indices::contiNEqIdx, + wPhaseIdx = Indices::wPhaseIdx, + nPhaseIdx = Indices::nPhaseIdx, + numPhases = GET_PROP_VALUE(TypeTag, NumPhases) + }; + + typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; + enum { dimWorld = GridView::dimensionworld }; + + typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; + typedef Dune::FieldVector<Scalar, dimWorld> Vector; + +public: + /*! + * \brief Constructor. Sets the upwind weight. + */ + TwoPLocalResidual() + { + // retrieve the upwind weight for the mass conservation equations. Use the value + // specified via the property system as default, and overwrite + // it by the run-time parameter from the Dune::ParameterTree + massUpwindWeight_ = GET_PARAM_FROM_GROUP(TypeTag, Scalar, Implicit, MassUpwindWeight); + }; + + /*! + * \brief Evaluate the amount all conservation quantities + * (e.g. phase mass) within a finite sub-control volume. + * + * \param storage The phase mass within the sub-control volume + * \param scvIdx The SCV (sub-control-volume) index + * \param usePrevSol Evaluate function with solution of current or previous time step + */ + void computeStorage(PrimaryVariables &storage, int scvIdx, bool usePrevSol) const + { + // if flag usePrevSol is set, the solution from the previous + // time step is used, otherwise the current solution is + // used. The secondary variables are used accordingly. This + // is required to compute the derivative of the storage term + // using the implicit Euler method. + const ElementVolumeVariables &elemVolVars = usePrevSol ? this->prevVolVars_() : this->curVolVars_(); + const VolumeVariables &volVars = elemVolVars[scvIdx]; + + // wetting phase mass + storage[contiWEqIdx] = volVars.density(wPhaseIdx) * volVars.porosity() + * volVars.saturation(wPhaseIdx); + + // non-wetting phase mass + storage[contiNEqIdx] = volVars.density(nPhaseIdx) * volVars.porosity() + * volVars.saturation(nPhaseIdx); + } + + /*! + * \brief Evaluates the mass flux over a face of a sub-control + * volume. + * + * \param flux The flux over the SCV (sub-control-volume) face for each phase + * \param faceIdx The index of the SCV face + * \param onBoundary A boolean variable to specify whether the flux variables + * are calculated for interior SCV faces or boundary faces, default=false + */ + void computeFlux(PrimaryVariables &flux, int faceIdx, const bool onBoundary=false) const + { + FluxVariables fluxVars(this->problem_(), + this->element_(), + this->fvGeometry_(), + faceIdx, + this->curVolVars_(), + onBoundary); + flux = 0; + asImp_()->computeAdvectiveFlux(flux, fluxVars); + asImp_()->computeDiffusiveFlux(flux, fluxVars); + } + + /*! + * \brief Evaluates the advective mass flux of all components over + * a face of a sub-control volume. + * + * \param flux The advective flux over the sub-control-volume face for each phase + * \param fluxVars The flux variables at the current SCV + * + * This method is called by compute flux and is mainly there for + * derived models to ease adding equations selectively. + */ + void computeAdvectiveFlux(PrimaryVariables &flux, const FluxVariables &fluxVars) const + { + // loop over all phases + for (int phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) + { + // data attached to upstream and the downstream vertices + // of the current phase + const VolumeVariables &up = this->curVolVars_(fluxVars.upstreamIdx(phaseIdx)); + const VolumeVariables &dn = this->curVolVars_(fluxVars.downstreamIdx(phaseIdx)); + + // add advective flux of current phase + int eqIdx = (phaseIdx == wPhaseIdx) ? contiWEqIdx : contiNEqIdx; + flux[eqIdx] += + fluxVars.volumeFlux(phaseIdx) + * + (( massUpwindWeight_)*up.density(phaseIdx) + + + (1 - massUpwindWeight_)*dn.density(phaseIdx)); + } + } + + /*! + * \brief Adds the diffusive flux to the flux vector over + * the face of a sub-control volume. + * + * \param flux The diffusive flux over the sub-control-volume face for each phase + * \param fluxData The flux variables at the current SCV + * + * It doesn't do anything in two-phase model but is used by the + * non-isothermal two-phase models to calculate diffusive heat + * fluxes + */ + void computeDiffusiveFlux(PrimaryVariables &flux, const FluxVariables &fluxData) const + { + // diffusive fluxes + flux += 0.0; + } + + /*! + * \brief Calculate the source term of the equation + * + * \param q The source/sink in the SCV for each phase + * \param scvIdx The index of the SCV + * + */ + void computeSource(PrimaryVariables &q, int scvIdx) const + { + // retrieve the source term intrinsic to the problem + this->problem_().boxSDSource(q, + this->element_(), + this->fvGeometry_(), + scvIdx, + this->curVolVars_()); + } + + +protected: + Implementation *asImp_() + { + return static_cast<Implementation *> (this); + } + const Implementation *asImp_() const + { + return static_cast<const Implementation *> (this); + } + +private: + Scalar massUpwindWeight_; + +}; + +} + +#endif diff --git a/dumux/implicit/2p/2pmodel.hh b/dumux/implicit/2p/2pmodel.hh new file mode 100644 index 0000000000..5a46820d2c --- /dev/null +++ b/dumux/implicit/2p/2pmodel.hh @@ -0,0 +1,366 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ + +/*! +* \file +* +* \brief Adaption of the box scheme to the two-phase flow model. +*/ + +#ifndef DUMUX_TWOP_MODEL_HH +#define DUMUX_TWOP_MODEL_HH + +#include "2pproperties.hh" +#include "2plocalresidual.hh" + +namespace Dumux +{ + +/*! + * \ingroup TwoPBoxModel + * \brief A two-phase, isothermal flow model using the box scheme. + * + * This model implements two-phase flow of two immiscible fluids + * \f$\alpha \in \{ w, n \}\f$ using a standard multiphase Darcy + * approach as the equation for the conservation of momentum, i.e. + \f[ + v_\alpha = - \frac{k_{r\alpha}}{\mu_\alpha} \textbf{K} + \left(\textbf{grad}\, p_\alpha - \varrho_{\alpha} {\textbf g} \right) + \f] + * + * By inserting this into the equation for the conservation of the + * phase mass, one gets + \f[ + \phi \frac{\partial \varrho_\alpha S_\alpha}{\partial t} + - + \text{div} \left\{ + \varrho_\alpha \frac{k_{r\alpha}}{\mu_\alpha} \mbox{\bf K} \left(\textbf{grad}\, p_\alpha - \varrho_{\alpha} \mbox{\bf g} \right) + \right\} - q_\alpha = 0 \;, + \f] + * + * These equations are discretized by a fully-coupled vertex centered finite volume + * (box) scheme as spatial and the implicit Euler method as time + * discretization. + * + * By using constitutive relations for the capillary pressure \f$p_c = + * p_n - p_w\f$ and relative permeability \f$k_{r\alpha}\f$ and taking + * advantage of the fact that \f$S_w + S_n = 1\f$, the number of + * unknowns can be reduced to two. Currently the model supports + * choosing either \f$p_w\f$ and \f$S_n\f$ or \f$p_n\f$ and \f$S_w\f$ + * as primary variables. The formulation which ought to be used can be + * specified by setting the <tt>Formulation</tt> property to either + * <tt>TwoPCommonIndices::pWsN</tt> or <tt>TwoPCommonIndices::pNsW</tt>. By + * default, the model uses \f$p_w\f$ and \f$S_n\f$. + */ +template<class TypeTag > +class TwoPModel : public GET_PROP_TYPE(TypeTag, BaseModel) +{ + typedef typename GET_PROP_TYPE(TypeTag, FVElementGeometry) FVElementGeometry; + typedef typename GET_PROP_TYPE(TypeTag, VolumeVariables) VolumeVariables; + typedef typename GET_PROP_TYPE(TypeTag, SolutionVector) SolutionVector; + typedef typename GET_PROP_TYPE(TypeTag, FluxVariables) FluxVariables; + typedef typename GET_PROP_TYPE(TypeTag, ElementVolumeVariables) ElementVolumeVariables; + + typedef typename GET_PROP_TYPE(TypeTag, Indices) Indices; + enum { + nPhaseIdx = Indices::nPhaseIdx, + wPhaseIdx = Indices::wPhaseIdx, + pressureIdx = Indices::pressureIdx, + numPhases = GET_PROP_VALUE(TypeTag, NumPhases) + }; + + typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; + typedef typename GridView::template Codim<0>::Entity Element; + typedef typename GridView::template Codim<0>::Iterator ElementIterator; + typedef typename GridView::ctype CoordScalar; + enum { + dim = GridView::dimension, + dimWorld = GridView::dimensionworld + }; + + typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; + typedef Dune::FieldVector<Scalar, numPhases> PhasesVector; + typedef Dune::FieldVector<Scalar, dimWorld> GlobalPosition; + +public: + /*! + * \brief Append all quantities of interest which can be derived + * from the solution of the current time step to the VTK + * writer. + * + * \param sol The global solution vector + * \param writer The writer for multi-file VTK datasets + */ + template<class MultiWriter> + void addOutputVtkFields(const SolutionVector &sol, + MultiWriter &writer) + { + bool velocityOutput = GET_PARAM_FROM_GROUP(TypeTag, bool, Vtk, AddVelocity); + typedef Dune::BlockVector<Dune::FieldVector<double, 1> > ScalarField; + typedef Dune::BlockVector<Dune::FieldVector<double, dim> > VectorField; + + // get the number of degrees of freedom and the number of vertices + unsigned numDofs = this->numDofs(); + unsigned numVertices = this->problem_().gridView().size(dim); + + // velocity output currently only works for vertex data + if (numDofs != numVertices) + velocityOutput = false; + + // create the required scalar fields + ScalarField *pW = writer.allocateManagedBuffer(numDofs); + ScalarField *pN = writer.allocateManagedBuffer(numDofs); + ScalarField *pC = writer.allocateManagedBuffer(numDofs); + ScalarField *Sw = writer.allocateManagedBuffer(numDofs); + ScalarField *Sn = writer.allocateManagedBuffer(numDofs); + ScalarField *rhoW = writer.allocateManagedBuffer(numDofs); + ScalarField *rhoN = writer.allocateManagedBuffer(numDofs); + ScalarField *mobW = writer.allocateManagedBuffer(numDofs); + ScalarField *mobN = writer.allocateManagedBuffer(numDofs); + ScalarField *poro = writer.allocateManagedBuffer(numDofs); + ScalarField *Te = writer.allocateManagedBuffer(numDofs); + ScalarField *cellNum =writer.allocateManagedBuffer (numDofs); + VectorField *velocityN = writer.template allocateManagedBuffer<double, dim>(numDofs); + VectorField *velocityW = writer.template allocateManagedBuffer<double, dim>(numDofs); + + if(velocityOutput) // check if velocity output is demanded + { + // initialize velocity fields + for (unsigned int i = 0; i < numVertices; ++i) + { + (*velocityN)[i] = Scalar(0); + (*velocityW)[i] = Scalar(0); + (*cellNum)[i] = Scalar(0.0); + } + } + unsigned numElements = this->gridView_().size(0); + ScalarField *rank = writer.allocateManagedBuffer(numElements); + + FVElementGeometry fvGeometry; + VolumeVariables volVars; + ElementVolumeVariables elemVolVars; + + ElementIterator elemIt = this->gridView_().template begin<0>(); + ElementIterator elemEndIt = this->gridView_().template end<0>(); + for (; elemIt != elemEndIt; ++elemIt) + { + if(velocityOutput && !elemIt->geometry().type().isCube()){ + DUNE_THROW(Dune::InvalidStateException, + "Currently, velocity output only works for cubes. " + "Please set the EnableVelocityOutput property to false!"); + } + int idx = this->elementMapper().map(*elemIt); + (*rank)[idx] = this->gridView_().comm().rank(); + + fvGeometry.update(this->gridView_(), *elemIt); + + if (numDofs == numElements) // element data + { + volVars.update(sol[idx], + this->problem_(), + *elemIt, + fvGeometry, + /*scvIdx=*/0, + false); + + (*pW)[idx] = volVars.pressure(wPhaseIdx); + (*pN)[idx] = volVars.pressure(nPhaseIdx); + (*pC)[idx] = volVars.capillaryPressure(); + (*Sw)[idx] = volVars.saturation(wPhaseIdx); + (*Sn)[idx] = volVars.saturation(nPhaseIdx); + (*rhoW)[idx] = volVars.density(wPhaseIdx); + (*rhoN)[idx] = volVars.density(nPhaseIdx); + (*mobW)[idx] = volVars.mobility(wPhaseIdx); + (*mobN)[idx] = volVars.mobility(nPhaseIdx); + (*poro)[idx] = volVars.porosity(); + (*Te)[idx] = volVars.temperature(); + } + else // vertex data + { + int numVerts = elemIt->template count<dim> (); + for (int scvIdx = 0; scvIdx < numVerts; ++scvIdx) + { + int globalIdx = this->vertexMapper().map(*elemIt, scvIdx, dim); + volVars.update(sol[globalIdx], + this->problem_(), + *elemIt, + fvGeometry, + scvIdx, + false); + + (*pW)[globalIdx] = volVars.pressure(wPhaseIdx); + (*pN)[globalIdx] = volVars.pressure(nPhaseIdx); + (*pC)[globalIdx] = volVars.capillaryPressure(); + (*Sw)[globalIdx] = volVars.saturation(wPhaseIdx); + (*Sn)[globalIdx] = volVars.saturation(nPhaseIdx); + (*rhoW)[globalIdx] = volVars.density(wPhaseIdx); + (*rhoN)[globalIdx] = volVars.density(nPhaseIdx); + (*mobW)[globalIdx] = volVars.mobility(wPhaseIdx); + (*mobN)[globalIdx] = volVars.mobility(nPhaseIdx); + (*poro)[globalIdx] = volVars.porosity(); + (*Te)[globalIdx] = volVars.temperature(); + if(velocityOutput) + { + (*cellNum)[globalIdx] += 1; + } + } + + if(velocityOutput) + { + // calculate vertex velocities + GlobalPosition tmpVelocity[numPhases]; + + for(int phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) + { + tmpVelocity[phaseIdx] = Scalar(0.0); + } + + typedef Dune::BlockVector<Dune::FieldVector<Scalar, dim> > SCVVelocities; + SCVVelocities scvVelocityW(8), scvVelocityN(8); + + scvVelocityW = 0; + scvVelocityN = 0; + + ElementVolumeVariables elemVolVars; + + elemVolVars.update(this->problem_(), + *elemIt, + fvGeometry, + false /* oldSol? */); + + for (int faceIdx = 0; faceIdx < fvGeometry.numEdges; faceIdx++) + { + + FluxVariables fluxVars(this->problem_(), + *elemIt, + fvGeometry, + faceIdx, + elemVolVars); + + for (int phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) + { + // local position of integration point + const Dune::FieldVector<Scalar, dim>& localPosIP = fvGeometry.subContVolFace[faceIdx].ipLocal; + + // Transformation of the global normal vector to normal vector in the reference element + const typename Element::Geometry::JacobianTransposed& jacobianT1 = + elemIt->geometry().jacobianTransposed(localPosIP); + + const GlobalPosition globalNormal = fluxVars.face().normal; + GlobalPosition localNormal; + jacobianT1.mv(globalNormal, localNormal); + // note only works for cubes + const Scalar localArea = pow(2,-(dim-1)); + + localNormal /= localNormal.two_norm(); + + // Get the Darcy velocities. The Darcy velocities are divided by the area of the subcontrolvolumeface + // in the reference element. + PhasesVector q; + q[phaseIdx] = fluxVars.volumeFlux(phaseIdx) / localArea; + + // transform the normal Darcy velocity into a vector + tmpVelocity[phaseIdx] = localNormal; + tmpVelocity[phaseIdx] *= q[phaseIdx]; + + if(phaseIdx == wPhaseIdx){ + scvVelocityW[fluxVars.face().i] += tmpVelocity[phaseIdx]; + scvVelocityW[fluxVars.face().j] += tmpVelocity[phaseIdx]; + } + else if(phaseIdx == nPhaseIdx){ + scvVelocityN[fluxVars.face().i] += tmpVelocity[phaseIdx]; + scvVelocityN[fluxVars.face().j] += tmpVelocity[phaseIdx]; + } + } + } + typedef Dune::GenericReferenceElements<Scalar, dim> ReferenceElements; + const Dune::FieldVector<Scalar, dim> &localPos + = ReferenceElements::general(elemIt->geometry().type()).position(0, 0); + + // get the transposed Jacobian of the element mapping + const typename Element::Geometry::JacobianTransposed& jacobianT2 + = elemIt->geometry().jacobianTransposed(localPos); + + // transform vertex velocities from local to global coordinates + for (int i = 0; i < numVerts; ++i) + { + int globalIdx = this->vertexMapper().map(*elemIt, i, dim); + // calculate the subcontrolvolume velocity by the Piola transformation + Dune::FieldVector<CoordScalar, dim> scvVelocity(0); + + jacobianT2.mtv(scvVelocityW[i], scvVelocity); + scvVelocity /= elemIt->geometry().integrationElement(localPos); + // add up the wetting phase subcontrolvolume velocities for each vertex + (*velocityW)[globalIdx] += scvVelocity; + + jacobianT2.mtv(scvVelocityN[i], scvVelocity); + scvVelocity /= elemIt->geometry().integrationElement(localPos); + // add up the nonwetting phase subcontrolvolume velocities for each vertex + (*velocityN)[globalIdx] += scvVelocity; + } + } + } + } + + if (numDofs == numElements) // element data + { + writer.attachCellData(*Sn, "Sn"); + writer.attachCellData(*Sw, "Sw"); + writer.attachCellData(*pN, "pn"); + writer.attachCellData(*pW, "pw"); + writer.attachCellData(*pC, "pc"); + writer.attachCellData(*rhoW, "rhoW"); + writer.attachCellData(*rhoN, "rhoN"); + writer.attachCellData(*mobW, "mobW"); + writer.attachCellData(*mobN, "mobN"); + writer.attachCellData(*poro, "porosity"); + writer.attachCellData(*Te, "temperature"); + } + else // vertex data + { + writer.attachVertexData(*Sn, "Sn"); + writer.attachVertexData(*Sw, "Sw"); + writer.attachVertexData(*pN, "pn"); + writer.attachVertexData(*pW, "pw"); + writer.attachVertexData(*pC, "pc"); + writer.attachVertexData(*rhoW, "rhoW"); + writer.attachVertexData(*rhoN, "rhoN"); + writer.attachVertexData(*mobW, "mobW"); + writer.attachVertexData(*mobN, "mobN"); + writer.attachVertexData(*poro, "porosity"); + writer.attachVertexData(*Te, "temperature"); + if(velocityOutput) // check if velocity output is demanded + { + // divide the vertex velocities by the number of adjacent scvs i.e. cells + for(unsigned int globalIdx = 0; globalIdx < numVertices; ++globalIdx){ + (*velocityW)[globalIdx] /= (*cellNum)[globalIdx]; + (*velocityN)[globalIdx] /= (*cellNum)[globalIdx]; + } + writer.attachVertexData(*velocityW, "velocityW", dim); + writer.attachVertexData(*velocityN, "velocityN", dim); + } + } + writer.attachCellData(*rank, "process rank"); + } +}; +} + +#include "2ppropertydefaults.hh" + +#endif diff --git a/dumux/implicit/2p/2pproperties.hh b/dumux/implicit/2p/2pproperties.hh new file mode 100644 index 0000000000..c86ab53d93 --- /dev/null +++ b/dumux/implicit/2p/2pproperties.hh @@ -0,0 +1,76 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \ingroup Properties + * \ingroup BoxProperties + * \ingroup TwoPBoxModel + */ +/*! + * \file + * + * \brief Defines the properties required for the twophase BOX model. + */ + +#ifndef DUMUX_2P_PROPERTIES_HH +#define DUMUX_2P_PROPERTIES_HH + +#include <dumux/boxmodels/common/boxproperties.hh> + +namespace Dumux +{ + + + +//////////////////////////////// +// properties +//////////////////////////////// +namespace Properties +{ + +////////////////////////////////////////////////////////////////// +// Type tags +////////////////////////////////////////////////////////////////// + +//! The type tag for the two-phase problems +NEW_TYPE_TAG(BoxTwoP, INHERITS_FROM(BoxModel)); + +////////////////////////////////////////////////////////////////// +// Property tags +////////////////////////////////////////////////////////////////// + +NEW_PROP_TAG(NumPhases); //!< Number of fluid phases in the system +NEW_PROP_TAG(ProblemEnableGravity); //!< Returns whether gravity is considered in the problem +NEW_PROP_TAG(ImplicitMassUpwindWeight); //!< The value of the weight of the upwind direction in the mass conservation equations +NEW_PROP_TAG(ImplicitMobilityUpwindWeight); //!< Weight for the upwind mobility in the velocity calculation +NEW_PROP_TAG(Formulation); //!< The formulation of the model +NEW_PROP_TAG(Indices); //!< Enumerations for the model +NEW_PROP_TAG(SpatialParams); //!< The type of the spatial parameters +NEW_PROP_TAG(MaterialLaw); //!< The material law which ought to be used (extracted from the spatial parameters) +NEW_PROP_TAG(MaterialLawParams); //!< The context material law (extracted from the spatial parameters) +NEW_PROP_TAG(WettingPhase); //!< The wetting phase for two-phase models +NEW_PROP_TAG(NonwettingPhase); //!< The non-wetting phase for two-phase models +NEW_PROP_TAG(FluidSystem); //!<The fluid systems including the information about the phases +NEW_PROP_TAG(FluidState); //!<The phases state +NEW_PROP_TAG(VtkAddVelocity); //!< Returns whether velocity vectors are written into the vtk output +NEW_PROP_TAG(SpatialParamsForchCoeff); //!< Property for the forchheimer coefficient +} + +} + +#endif diff --git a/dumux/implicit/2p/2ppropertydefaults.hh b/dumux/implicit/2p/2ppropertydefaults.hh new file mode 100644 index 0000000000..2cd202b430 --- /dev/null +++ b/dumux/implicit/2p/2ppropertydefaults.hh @@ -0,0 +1,148 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \ingroup TwoPBoxModel + * \ingroup BoxProperties + * \ingroup Properties + * \file + * + * \brief Defines default values for the properties required by the + * twophase box model. + */ +#ifndef DUMUX_2P_PROPERTY_DEFAULTS_HH +#define DUMUX_2P_PROPERTY_DEFAULTS_HH + +#include "2pmodel.hh" +#include "2pindices.hh" +#include <dumux/boxmodels/common/boxdarcyfluxvariables.hh> +#include "2pvolumevariables.hh" +#include "2pproperties.hh" + +#include <dumux/material/fluidsystems/gasphase.hh> +#include <dumux/material/fluidsystems/liquidphase.hh> +#include <dumux/material/components/nullcomponent.hh> +#include <dumux/material/fluidsystems/2pimmisciblefluidsystem.hh> +#include <dumux/material/fluidstates/immisciblefluidstate.hh> +#include <dumux/material/spatialparams/boxspatialparams.hh> + +namespace Dumux +{ +namespace Properties +{ +////////////////////////////////////////////////////////////////// +// Property defaults +////////////////////////////////////////////////////////////////// +SET_INT_PROP(BoxTwoP, NumEq, 2); //!< set the number of equations to 2 +SET_INT_PROP(BoxTwoP, NumPhases, 2); //!< The number of phases in the 2p model is 2 + +//! Set the default formulation to pWsN +SET_INT_PROP(BoxTwoP, + Formulation, + TwoPFormulation::pwSn); + +//! Use the 2p local jacobian operator for the 2p model +SET_TYPE_PROP(BoxTwoP, + LocalResidual, + TwoPLocalResidual<TypeTag>); + +//! the Model property +SET_TYPE_PROP(BoxTwoP, Model, TwoPModel<TypeTag>); + +//! the VolumeVariables property +SET_TYPE_PROP(BoxTwoP, VolumeVariables, TwoPVolumeVariables<TypeTag>); + +//! the FluxVariables property +SET_TYPE_PROP(BoxTwoP, FluxVariables, BoxDarcyFluxVariables<TypeTag>); + +//! the upwind weight for the mass conservation equations. +SET_SCALAR_PROP(BoxTwoP, ImplicitMassUpwindWeight, 1.0); + +//! weight for the upwind mobility in the velocity calculation +SET_SCALAR_PROP(BoxTwoP, ImplicitMobilityUpwindWeight, 1.0); + +//! The indices required by the isothermal 2p model +SET_TYPE_PROP(BoxTwoP, + Indices, + TwoPIndices<TypeTag, GET_PROP_VALUE(TypeTag, Formulation), 0>); + +//! The spatial parameters to be employed. +//! Use BoxSpatialParams by default. +SET_TYPE_PROP(BoxTwoP, SpatialParams, BoxSpatialParams<TypeTag>); + +/*! + * \brief Set the property for the material parameters by extracting + * it from the material law. + */ +SET_TYPE_PROP(BoxTwoP, + MaterialLawParams, + typename GET_PROP_TYPE(TypeTag, MaterialLaw)::Params); + +SET_PROP(BoxTwoP, WettingPhase) +{ private: + typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; +public: + typedef Dumux::LiquidPhase<Scalar, Dumux::NullComponent<Scalar> > type; +}; + +SET_PROP(BoxTwoP, NonwettingPhase) +{ private: + typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; +public: + typedef Dumux::LiquidPhase<Scalar, Dumux::NullComponent<Scalar> > type; +}; + +SET_PROP(BoxTwoP, FluidSystem) +{ private: + typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; + typedef typename GET_PROP_TYPE(TypeTag, WettingPhase) WettingPhase; + typedef typename GET_PROP_TYPE(TypeTag, NonwettingPhase) NonwettingPhase; + +public: + typedef Dumux::FluidSystems::TwoPImmiscible<Scalar, + WettingPhase, + NonwettingPhase> type; +}; + +SET_PROP(BoxTwoP, FluidState) +{ +private: + typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; + typedef typename GET_PROP_TYPE(TypeTag, FluidSystem) FluidSystem; +public: + typedef ImmiscibleFluidState<Scalar, FluidSystem> type; +}; + +// disable velocity output by default +SET_BOOL_PROP(BoxTwoP, VtkAddVelocity, false); + +// enable gravity by default +SET_BOOL_PROP(BoxTwoP, ProblemEnableGravity, true); + +//! default value for the forchheimer coefficient +// Source: Ward, J.C. 1964 Turbulent flow in porous media. ASCE J. Hydraul. Div 90. +// Actually the Forchheimer coefficient is also a function of the dimensions of the +// porous medium. Taking it as a constant is only a first approximation +// (Nield, Bejan, Convection in porous media, 2006, p. 10) +SET_SCALAR_PROP(BoxModel, SpatialParamsForchCoeff, 0.55); +} +// + +} + +#endif diff --git a/dumux/implicit/2p/2pvolumevariables.hh b/dumux/implicit/2p/2pvolumevariables.hh new file mode 100644 index 0000000000..24e953f6a7 --- /dev/null +++ b/dumux/implicit/2p/2pvolumevariables.hh @@ -0,0 +1,279 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \file + * + * \brief Contains the quantities which are constant within a + * finite volume in the two-phase model. + */ +#ifndef DUMUX_2P_VOLUME_VARIABLES_HH +#define DUMUX_2P_VOLUME_VARIABLES_HH + +#include "2pproperties.hh" + +#include <dumux/boxmodels/common/boxvolumevariables.hh> + +#include <dune/common/fvector.hh> + +namespace Dumux +{ +/*! + * \ingroup TwoPBoxModel + * \ingroup BoxVolumeVariables + * \brief Contains the quantities which are are constant within a + * finite volume in the two-phase model. + */ +template <class TypeTag> +class TwoPVolumeVariables : public BoxVolumeVariables<TypeTag> +{ + typedef BoxVolumeVariables<TypeTag> ParentType; + + typedef typename GET_PROP_TYPE(TypeTag, VolumeVariables) Implementation; + typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; + typedef typename GET_PROP_TYPE(TypeTag, Problem) Problem; + typedef typename GET_PROP_TYPE(TypeTag, FluidSystem) FluidSystem; + typedef typename GET_PROP_TYPE(TypeTag, FluidState) FluidState; + typedef typename GET_PROP_TYPE(TypeTag, MaterialLaw) MaterialLaw; + typedef typename GET_PROP_TYPE(TypeTag, MaterialLawParams) MaterialLawParams; + typedef typename GET_PROP_TYPE(TypeTag, FVElementGeometry) FVElementGeometry; + typedef typename GET_PROP_TYPE(TypeTag, PrimaryVariables) PrimaryVariables; + + typedef typename GET_PROP_TYPE(TypeTag, Indices) Indices; + enum { + pwSn = Indices::pwSn, + pnSw = Indices::pnSw, + pressureIdx = Indices::pressureIdx, + saturationIdx = Indices::saturationIdx, + wPhaseIdx = Indices::wPhaseIdx, + nPhaseIdx = Indices::nPhaseIdx, + numPhases = GET_PROP_VALUE(TypeTag, NumPhases), + formulation = GET_PROP_VALUE(TypeTag, Formulation) + }; + + typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; + typedef typename GridView::template Codim<0>::Entity Element; + +public: + /*! + * \copydoc BoxVolumeVariables::update + */ + void update(const PrimaryVariables &priVars, + const Problem &problem, + const Element &element, + const FVElementGeometry &fvGeometry, + int scvIdx, + bool isOldSol) + { + ParentType::update(priVars, + problem, + element, + fvGeometry, + scvIdx, + isOldSol); + + completeFluidState(priVars, problem, element, fvGeometry, scvIdx, fluidState_); + + const MaterialLawParams &materialParams = + problem.spatialParams().materialLawParams(element, fvGeometry, scvIdx); + + mobility_[wPhaseIdx] = + MaterialLaw::krw(materialParams, fluidState_.saturation(wPhaseIdx)) + / fluidState_.viscosity(wPhaseIdx); + + mobility_[nPhaseIdx] = + MaterialLaw::krn(materialParams, fluidState_.saturation(wPhaseIdx)) + / fluidState_.viscosity(nPhaseIdx); + + // porosity + porosity_ = problem.spatialParams().porosity(element, + fvGeometry, + scvIdx); + + // energy related quantities not belonging to the fluid state + asImp_().updateEnergy_(priVars, problem, element, fvGeometry, scvIdx, isOldSol); + } + + /*! + * \copydoc BoxModel::completeFluidState + */ + static void completeFluidState(const PrimaryVariables& priVars, + const Problem& problem, + const Element& element, + const FVElementGeometry& fvGeometry, + int scvIdx, + FluidState& fluidState) + { + Scalar t = Implementation::temperature_(priVars, problem, element, + fvGeometry, scvIdx); + fluidState.setTemperature(t); + + // material law parameters + typedef typename GET_PROP_TYPE(TypeTag, MaterialLaw) MaterialLaw; + const typename MaterialLaw::Params &materialParams = + problem.spatialParams().materialLawParams(element, fvGeometry, scvIdx); + + + if (int(formulation) == pwSn) { + Scalar Sn = priVars[saturationIdx]; + fluidState.setSaturation(nPhaseIdx, Sn); + fluidState.setSaturation(wPhaseIdx, 1 - Sn); + + Scalar pW = priVars[pressureIdx]; + fluidState.setPressure(wPhaseIdx, pW); + fluidState.setPressure(nPhaseIdx, + pW + MaterialLaw::pC(materialParams, 1 - Sn)); + } + else if (int(formulation) == pnSw) { + Scalar Sw = priVars[saturationIdx]; + fluidState.setSaturation(wPhaseIdx, Sw); + fluidState.setSaturation(nPhaseIdx, 1 - Sw); + + Scalar pN = priVars[pressureIdx]; + fluidState.setPressure(nPhaseIdx, pN); + fluidState.setPressure(wPhaseIdx, + pN - MaterialLaw::pC(materialParams, Sw)); + } + + typedef typename GET_PROP_TYPE(TypeTag, FluidSystem) FluidSystem; + typename FluidSystem::ParameterCache paramCache; + paramCache.updateAll(fluidState); + + for (int phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) { + // compute and set the viscosity + Scalar mu = FluidSystem::viscosity(fluidState, paramCache, phaseIdx); + fluidState.setViscosity(phaseIdx, mu); + + // compute and set the density + Scalar rho = FluidSystem::density(fluidState, paramCache, phaseIdx); + fluidState.setDensity(phaseIdx, rho); + + // compute and set the enthalpy + Scalar h = Implementation::enthalpy_(fluidState, paramCache, phaseIdx); + fluidState.setEnthalpy(phaseIdx, h); + } + } + + /*! + * \brief Returns the phase state for the control-volume. + */ + const FluidState &fluidState() const + { return fluidState_; } + + /*! + * \brief Returns the effective saturation of a given phase within + * the control volume. + * + * \param phaseIdx The phase index + */ + Scalar saturation(int phaseIdx) const + { return fluidState_.saturation(phaseIdx); } + + /*! + * \brief Returns the mass density of a given phase within the + * control volume. + * + * \param phaseIdx The phase index + */ + Scalar density(int phaseIdx) const + { return fluidState_.density(phaseIdx); } + + /*! + * \brief Returns the effective pressure of a given phase within + * the control volume. + * + * \param phaseIdx The phase index + */ + Scalar pressure(int phaseIdx) const + { return fluidState_.pressure(phaseIdx); } + + /*! + * \brief Returns the capillary pressure within the control volume [Pa]. + */ + Scalar capillaryPressure() const + { return fluidState_.pressure(nPhaseIdx) - fluidState_.pressure(wPhaseIdx); } + + /*! + * \brief Returns temperature inside the sub-control volume. + * + * Note that we assume thermodynamic equilibrium, i.e. the + * temperature of the rock matrix and of all fluid phases are + * identical. + */ + Scalar temperature() const + { return fluidState_.temperature(/*phaseIdx=*/0); } + + /*! + * \brief Returns the effective mobility of a given phase within + * the control volume. + * + * \param phaseIdx The phase index + */ + Scalar mobility(int phaseIdx) const + { return mobility_[phaseIdx]; } + + /*! + * \brief Returns the average porosity within the control volume. + */ + Scalar porosity() const + { return porosity_; } + +protected: + static Scalar temperature_(const PrimaryVariables &priVars, + const Problem& problem, + const Element &element, + const FVElementGeometry &fvGeometry, + int scvIdx) + { + return problem.boxTemperature(element, fvGeometry, scvIdx); + } + + template<class ParameterCache> + static Scalar enthalpy_(const FluidState& fluidState, + const ParameterCache& paramCache, + int phaseIdx) + { + return 0; + } + + /*! + * \brief Called by update() to compute the energy related quantities + */ + void updateEnergy_(const PrimaryVariables &sol, + const Problem &problem, + const Element &element, + const FVElementGeometry &fvGeometry, + int vertIdx, + bool isOldSol) + { } + + FluidState fluidState_; + Scalar porosity_; + Scalar mobility_[numPhases]; + +private: + Implementation &asImp_() + { return *static_cast<Implementation*>(this); } + + const Implementation &asImp_() const + { return *static_cast<const Implementation*>(this); } +}; + +} + +#endif diff --git a/dumux/implicit/2p/Makefile.am b/dumux/implicit/2p/Makefile.am new file mode 100644 index 0000000000..27994b7a07 --- /dev/null +++ b/dumux/implicit/2p/Makefile.am @@ -0,0 +1,5 @@ +2pdir = $(includedir)/dumux/boxmodels/2p +2p_HEADERS = *.hh + +include $(top_srcdir)/am/global-rules + diff --git a/dumux/implicit/2p2c/2p2cfluxvariables.hh b/dumux/implicit/2p2c/2p2cfluxvariables.hh new file mode 100644 index 0000000000..f93b15a39b --- /dev/null +++ b/dumux/implicit/2p2c/2p2cfluxvariables.hh @@ -0,0 +1,246 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \file + * + * \brief This file contains the data which is required to calculate + * all fluxes of components over a face of a finite volume for + * the two-phase, two-component model. + */ +#ifndef DUMUX_2P2C_FLUX_VARIABLES_HH +#define DUMUX_2P2C_FLUX_VARIABLES_HH + +#include <dumux/common/math.hh> +#include <dumux/common/spline.hh> + +#include "2p2cproperties.hh" + +namespace Dumux +{ + +/*! + * \ingroup TwoPTwoCModel + * \ingroup BoxFluxVariables + * \brief This template class contains the data which is required to + * calculate all fluxes of components over a face of a finite + * volume for the two-phase, two-component model. + * + * This means pressure and concentration gradients, phase densities at + * the integration point, etc. + */ +template <class TypeTag> +class TwoPTwoCFluxVariables : public GET_PROP_TYPE(TypeTag, BaseFluxVariables) +{ + typedef typename GET_PROP_TYPE(TypeTag, BaseFluxVariables) BaseFluxVariables; + typedef typename GET_PROP_TYPE(TypeTag, Problem) Problem; + typedef typename GET_PROP_TYPE(TypeTag, VolumeVariables) VolumeVariables; + typedef typename GET_PROP_TYPE(TypeTag, ElementVolumeVariables) ElementVolumeVariables; + typedef typename GET_PROP_TYPE(TypeTag, SpatialParams) SpatialParams; + enum { numPhases = GET_PROP_VALUE(TypeTag, NumPhases) }; + + typedef typename GET_PROP_TYPE(TypeTag, Indices) Indices; + enum { + wPhaseIdx = Indices::wPhaseIdx, + nPhaseIdx = Indices::nPhaseIdx, + wCompIdx = Indices::wCompIdx, + nCompIdx = Indices::nCompIdx + }; + + typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; + typedef typename GridView::template Codim<0>::Entity Element; + enum { dim = GridView::dimension }; + + typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; + typedef Dune::FieldVector<Scalar, dim> DimVector; + typedef Dune::FieldMatrix<Scalar, dim, dim> DimMatrix; + + typedef typename GET_PROP_TYPE(TypeTag, FVElementGeometry) FVElementGeometry; + typedef typename FVElementGeometry::SubControlVolumeFace SCVFace; + +public: + /* + * \brief The constructor + * + * \param problem The problem + * \param element The finite element + * \param fvGeometry The finite-volume geometry in the box scheme + * \param faceIdx The local index of the SCV (sub-control-volume) face + * \param elemVolVars The volume variables of the current element + * \param onBoundary Distinguishes if we are on a SCV face or on a boundary face + */ + TwoPTwoCFluxVariables(const Problem &problem, + const Element &element, + const FVElementGeometry &fvGeometry, + const int faceIdx, + const ElementVolumeVariables &elemVolVars, + const bool onBoundary = false) + : BaseFluxVariables(problem, element, fvGeometry, faceIdx, elemVolVars, onBoundary) + { + for (int phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) { + density_[phaseIdx] = Scalar(0); + molarDensity_[phaseIdx] = Scalar(0); + moleFractionGrad_[phaseIdx] = Scalar(0); + } + + calculateValues_(problem, element, elemVolVars); + } + +protected: + void calculateValues_(const Problem &problem, + const Element &element, + const ElementVolumeVariables &elemVolVars) + { + // calculate densities at the integration points of the face + DimVector tmp(0.0); + for (int idx = 0; + idx < this->fvGeometry_.numFAP; + idx++) // loop over adjacent vertices + { + // index for the element volume variables + int volVarsIdx = this->face().fapIndices[idx]; + + for (int phaseIdx = 0; phaseIdx < numPhases; phaseIdx++) + { + density_[phaseIdx] += elemVolVars[volVarsIdx].density(phaseIdx)* + this->face().shapeValue[idx]; + molarDensity_[phaseIdx] += elemVolVars[volVarsIdx].molarDensity(phaseIdx)* + this->face().shapeValue[idx]; + } + } + + calculateGradients_(problem, element, elemVolVars); + calculatePorousDiffCoeff_(problem, element, elemVolVars); + } + + void calculateGradients_(const Problem &problem, + const Element &element, + const ElementVolumeVariables &elemVolVars) + { + // calculate gradients + DimVector tmp(0.0); + for (int idx = 0; + idx < this->fvGeometry_.numFAP; + idx++) // loop over adjacent vertices + { + // FE gradient at vertex idx + const DimVector &feGrad = this->face().grad[idx]; + + // index for the element volume variables + int volVarsIdx = this->face().fapIndices[idx]; + + // the mole fraction gradient of the wetting phase + tmp = feGrad; + tmp *= elemVolVars[volVarsIdx].fluidState().moleFraction(wPhaseIdx, nCompIdx); + moleFractionGrad_[wPhaseIdx] += tmp; + + // the mole fraction gradient of the non-wetting phase + tmp = feGrad; + tmp *= elemVolVars[volVarsIdx].fluidState().moleFraction(nPhaseIdx, wCompIdx); + moleFractionGrad_[nPhaseIdx] += tmp; + } + } + + Scalar rhoFactor_(int phaseIdx, int scvIdx, const ElementVolumeVariables &vDat) + { + static const Scalar eps = 1e-2; + const Scalar sat = vDat[scvIdx].density(phaseIdx); + if (sat > eps) + return 0.5; + if (sat <= 0) + return 0; + + static const Dumux::Spline<Scalar> sp(0, eps, // x0, x1 + 0, 0.5, // y0, y1 + 0, 0); // m0, m1 + return sp.eval(sat); + } + + void calculatePorousDiffCoeff_(const Problem &problem, + const Element &element, + const ElementVolumeVariables &elemVolVars) + { + const VolumeVariables &volVarsI = elemVolVars[this->face().i]; + const VolumeVariables &volVarsJ = elemVolVars[this->face().j]; + + for (int phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) + { + // make sure to only calculate diffusion coefficients + // for phases which exist in both finite volumes + if (volVarsI.saturation(phaseIdx) <= 0 || + volVarsJ.saturation(phaseIdx) <= 0) + { + porousDiffCoeff_[phaseIdx] = 0.0; + continue; + } + + // calculate tortuosity at the nodes i and j needed + // for porous media diffusion coefficient + Scalar tauI = + 1.0/(volVarsI.porosity() * volVarsI.porosity()) * + pow(volVarsI.porosity() * volVarsI.saturation(phaseIdx), 7.0/3); + Scalar tauJ = + 1.0/(volVarsJ.porosity() * volVarsJ.porosity()) * + pow(volVarsJ.porosity() * volVarsJ.saturation(phaseIdx), 7.0/3); + // Diffusion coefficient in the porous medium + + // -> harmonic mean + porousDiffCoeff_[phaseIdx] = harmonicMean(volVarsI.porosity() * volVarsI.saturation(phaseIdx) * tauI * volVarsI.diffCoeff(phaseIdx), + volVarsJ.porosity() * volVarsJ.saturation(phaseIdx) * tauJ * volVarsJ.diffCoeff(phaseIdx)); + } + } + +public: + /*! + * \brief The binary diffusion coefficient for each fluid phase. + */ + Scalar porousDiffCoeff(int phaseIdx) const + { return porousDiffCoeff_[phaseIdx]; }; + + /*! + * \brief Return density \f$\mathrm{[kg/m^3]}\f$ of a phase. + */ + Scalar density(int phaseIdx) const + { return density_[phaseIdx]; } + + /*! + * \brief Return molar density \f$\mathrm{[mol/m^3]}\f$ of a phase. + */ + Scalar molarDensity(int phaseIdx) const + { return molarDensity_[phaseIdx]; } + + /*! + * \brief The mole fraction gradient of the dissolved component in a phase. + */ + const DimVector &moleFractionGrad(int phaseIdx) const + { return moleFractionGrad_[phaseIdx]; }; + +protected: + // mole fraction gradients + DimVector moleFractionGrad_[numPhases]; + + // density of each face at the integration point + Scalar density_[numPhases], molarDensity_[numPhases]; + + // the diffusion coefficient for the porous medium + Scalar porousDiffCoeff_[numPhases]; +}; + +} // end namespace + +#endif diff --git a/dumux/implicit/2p2c/2p2cindices.hh b/dumux/implicit/2p2c/2p2cindices.hh new file mode 100644 index 0000000000..1f00b2540e --- /dev/null +++ b/dumux/implicit/2p2c/2p2cindices.hh @@ -0,0 +1,129 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ + +/*! + * \file + * + * \brief Defines the indices required for the 2p2c BOX model. + */ +#ifndef DUMUX_2P2C_INDICES_HH +#define DUMUX_2P2C_INDICES_HH + +#include "2p2cproperties.hh" + +namespace Dumux +{ +// \{ + +/*! + * \ingroup TwoPTwoCModel + * \ingroup BoxIndices + * \brief Enumerates the formulations which the 2p2c model accepts. + */ +struct TwoPTwoCFormulation +{ + enum { + pwSn, + pnSw + }; +}; + +/*! + * \brief The indices for the isothermal TwoPTwoC model. + * + * \tparam formulation The formulation, either pwSn or pnSw. + * \tparam PVOffset The first index in a primary variable vector. + */ +template <class TypeTag, + int formulation = TwoPTwoCFormulation::pwSn, + int PVOffset = 0> +class TwoPTwoCIndices +{ + typedef typename GET_PROP_TYPE(TypeTag, FluidSystem) FluidSystem; + +public: + // Phase indices + static const int wPhaseIdx = FluidSystem::wPhaseIdx; //!< Index of the wetting phase + static const int nPhaseIdx = FluidSystem::nPhaseIdx; //!< Index of the non-wetting phase + + // Component indices + static const int wCompIdx = FluidSystem::wCompIdx; //!< Index of the primary component of the wetting phase + static const int nCompIdx = FluidSystem::nCompIdx; //!< Index of the primary component of the non-wetting phase + + // present phases (-> 'pseudo' primary variable) + static const int wPhaseOnly = 1; //!< Only the wetting phase is present + static const int nPhaseOnly = 0; //!< Only the non-wetting phase is present + static const int bothPhases = 2; //!< Both phases are present + + // Primary variable indices + static const int pressureIdx = PVOffset + 0; //!< Index for wetting/non-wetting phase pressure (depending on formulation) in a solution vector + static const int switchIdx = PVOffset + 1; //!< Index of the either the saturation or the mass fraction of the non-wetting/wetting phase + + static const int pwIdx = pressureIdx; //!< Index for wetting phase pressure in a solution vector + static const int SnOrXIdx = switchIdx; //!< Index of the either the saturation of the non-wetting phase or the mass fraction secondary component in the only phase + + // equation indices + static const int conti0EqIdx = PVOffset; //!< Index of the mass conservation equation for the first component + static const int contiWEqIdx = conti0EqIdx + wCompIdx; //!< Index of the mass conservation equation for the liquid's primary component + static const int contiNEqIdx = conti0EqIdx + nCompIdx; //!< Index of the mass conservation equation for the gas' primary component +}; + +/*! + * \brief The indices for the isothermal TwoPTwoC model in the pn-Sw + * formulation. + * + * \tparam PVOffset The first index in a primary variable vector. + */ +template <class TypeTag, int PVOffset> +class TwoPTwoCIndices<TypeTag, TwoPTwoCFormulation::pnSw, PVOffset> +{ + typedef typename GET_PROP_TYPE(TypeTag, FluidSystem) FluidSystem; + +public: + // Phase indices + static const int wPhaseIdx = FluidSystem::wPhaseIdx; //!< Index of the wetting phase + static const int nPhaseIdx = FluidSystem::nPhaseIdx; //!< Index of the non-wetting phase + + // Component indices + static const int wCompIdx = FluidSystem::wCompIdx; //!< Index of the primary component of the wetting phase + static const int nCompIdx = FluidSystem::nCompIdx; //!< Index of the primary component of the non-wetting phase + + // present phases (-> 'pseudo' primary variable) + static const int wPhaseOnly = 1; //!< Only the wetting phase is present + static const int nPhaseOnly = 2; //!< Only the non-wetting phase is present + static const int bothPhases = 3; //!< Both phases are present + + // Primary variable indices + static const int pressureIdx = PVOffset + 0; //!< Index for wetting/non-wetting phase pressure (depending on formulation) in a solution vector + static const int switchIdx = PVOffset + 1; //!< Index of the either the saturation or the mass fraction of the non-wetting/wetting phase + + static const int pnIdx = pressureIdx; //!< Index for non-wetting phase pressure in a solution vector + static const int SwOrXIdx = switchIdx; //!< Index of the either the saturation of the liquid phase or the mass fraction of the secondary component in the only phase + + // Equation indices + static const int conti0EqIdx = PVOffset; //!< Index of the mass conservation equation for the first component + static const int contiWEqIdx = conti0EqIdx + wCompIdx; //!< Index of the mass conservation equation for the liquid's primary component + static const int contiNEqIdx = conti0EqIdx + nCompIdx; //!< Index of the mass conservation equation for the gas' primary component +}; + +// \} + +} + +#endif diff --git a/dumux/implicit/2p2c/2p2clocalresidual.hh b/dumux/implicit/2p2c/2p2clocalresidual.hh new file mode 100644 index 0000000000..d36494883f --- /dev/null +++ b/dumux/implicit/2p2c/2p2clocalresidual.hh @@ -0,0 +1,383 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \file + * + * \brief Element-wise calculation of the Jacobian matrix for problems + * using the two-phase two-component box model. + */ + +#ifndef DUMUX_2P2C_LOCAL_RESIDUAL_BASE_HH +#define DUMUX_2P2C_LOCAL_RESIDUAL_BASE_HH + +#include <dumux/boxmodels/common/boxmodel.hh> +#include <dumux/common/math.hh> + +#include "2p2cproperties.hh" +#include "2p2cvolumevariables.hh" +#include "2p2cfluxvariables.hh" +#include "2p2cnewtoncontroller.hh" + +#include <iostream> +#include <vector> + +namespace Dumux +{ +/*! + * \ingroup TwoPTwoCModel + * \ingroup BoxLocalResidual + * \brief Element-wise calculation of the Jacobian matrix for problems + * using the two-phase two-component box model. + * + * This class is used to fill the gaps in BoxLocalResidual for the 2P-2C flow. + */ +template<class TypeTag> +class TwoPTwoCLocalResidual: public GET_PROP_TYPE(TypeTag, BaseLocalResidual) +{ + protected: + typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; + typedef typename GET_PROP_TYPE(TypeTag, LocalResidual) Implementation; + typedef typename GET_PROP_TYPE(TypeTag, FluidSystem) FluidSystem; + typedef typename GET_PROP_TYPE(TypeTag, PrimaryVariables) PrimaryVariables; + typedef typename GET_PROP_TYPE(TypeTag, FVElementGeometry) FVElementGeometry; + typedef typename GET_PROP_TYPE(TypeTag, VolumeVariables) VolumeVariables; + typedef typename GET_PROP_TYPE(TypeTag, ElementVolumeVariables) ElementVolumeVariables; + typedef typename GET_PROP_TYPE(TypeTag, BoundaryTypes) BoundaryTypes; + typedef typename GET_PROP_TYPE(TypeTag, ElementBoundaryTypes) ElementBoundaryTypes; + typedef typename GET_PROP_TYPE(TypeTag, FluxVariables) FluxVariables; + enum + { + numEq = GET_PROP_VALUE(TypeTag, NumEq), + numPhases = GET_PROP_VALUE(TypeTag, NumPhases), + numComponents = GET_PROP_VALUE(TypeTag, NumComponents) + }; + + typedef typename GET_PROP_TYPE(TypeTag, Indices) Indices; + enum + { + contiWEqIdx = Indices::contiWEqIdx, + contiNEqIdx = Indices::contiNEqIdx, + wPhaseIdx = Indices::wPhaseIdx, + nPhaseIdx = Indices::nPhaseIdx, + wCompIdx = Indices::wCompIdx, + nCompIdx = Indices::nCompIdx + }; + + typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; + typedef typename GridView::template Codim<0>::Entity Element; + typedef typename GridView::IntersectionIterator IntersectionIterator; + + static constexpr unsigned int replaceCompEqIdx = + GET_PROP_VALUE(TypeTag, ReplaceCompEqIdx); + + public: + /*! + * \brief Constructor. Sets the upwind weight. + */ + TwoPTwoCLocalResidual() + { + // retrieve the upwind weight for the mass conservation equations. Use the value + // specified via the property system as default, and overwrite + // it by the run-time parameter from the Dune::ParameterTree + massUpwindWeight_ = GET_PARAM_FROM_GROUP(TypeTag, Scalar, Implicit, MassUpwindWeight); + }; + + /*! + * \brief Evaluate the storage term of the current solution in a + * single phase. + * + * \param element The element + * \param phaseIdx The index of the fluid phase + */ + void evalPhaseStorage(const Element &element, const int phaseIdx) + { + FVElementGeometry fvGeometry; + fvGeometry.update(this->gridView_(), element); + ElementBoundaryTypes bcTypes; + bcTypes.update(this->problem_(), element, fvGeometry); + ElementVolumeVariables elemVolVars; + elemVolVars.update(this->problem_(), element, fvGeometry, false); + + this->storageTerm_.resize(fvGeometry.numSCV); + this->storageTerm_ = 0; + + this->elemPtr_ = &element; + this->fvElemGeomPtr_ = &fvGeometry; + this->bcTypesPtr_ = &bcTypes; + this->prevVolVarsPtr_ = 0; + this->curVolVarsPtr_ = &elemVolVars; + evalPhaseStorage_(phaseIdx); + } + + /*! + * \brief Evaluate the amount all conservation quantities + * (e.g. phase mass) within a sub-control volume. + * + * The result should be averaged over the volume (e.g. phase mass + * inside a sub-control volume divided by the volume) + * + * \param storage The mass of the component within the sub-control volume + * \param scvIdx The SCV (sub-control-volume) index + * \param usePrevSol Evaluate function with solution of current or previous time step + */ + void computeStorage(PrimaryVariables &storage, const int scvIdx, bool usePrevSol) const + { + // if flag usePrevSol is set, the solution from the previous + // time step is used, otherwise the current solution is + // used. The secondary variables are used accordingly. This + // is required to compute the derivative of the storage term + // using the implicit euler method. + const ElementVolumeVariables &elemVolVars = usePrevSol ? this->prevVolVars_() + : this->curVolVars_(); + const VolumeVariables &volVars = elemVolVars[scvIdx]; + + // compute storage term of all components within all phases + storage = 0; + + for (unsigned int phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) + { + for (unsigned int compIdx = contiCompIdx1_(); compIdx <= contiCompIdx2_(); ++compIdx) + { + unsigned int eqIdx = (compIdx == wCompIdx) ? contiWEqIdx : contiNEqIdx; + storage[eqIdx] += volVars.density(phaseIdx) + * volVars.saturation(phaseIdx) + * volVars.fluidState().massFraction(phaseIdx, compIdx); + } + // this is only processed, if one component mass balance equation + // is replaced by the total mass balance equation + if (replaceCompEqIdx < numComponents) + storage[replaceCompEqIdx] += + volVars.density(phaseIdx) + * volVars.saturation(phaseIdx); + } + storage *= volVars.porosity(); + } + + /*! + * \brief Evaluates the total flux of all conservation quantities + * over a face of a sub-control volume. + * + * \param flux The flux over the SCV (sub-control-volume) face for each component + * \param faceIdx The index of the SCV face + * \param onBoundary Evaluate flux at inner SCV face or on a boundary face + */ + void computeFlux(PrimaryVariables &flux, const int faceIdx, bool onBoundary=false) const + { + FluxVariables fluxVars(this->problem_(), + this->element_(), + this->fvGeometry_(), + faceIdx, + this->curVolVars_(), + onBoundary); + + flux = 0; + asImp_()->computeAdvectiveFlux(flux, fluxVars); + Valgrind::CheckDefined(flux); + asImp_()->computeDiffusiveFlux(flux, fluxVars); + Valgrind::CheckDefined(flux); + } + + /*! + * \brief Evaluates the advective mass flux of all components over + * a face of a sub-control volume. + * + * \param flux The advective flux over the sub-control-volume face for each component + * \param fluxVars The flux variables at the current SCV face + */ + void computeAdvectiveFlux(PrimaryVariables &flux, const FluxVariables &fluxVars) const + { + //////// + // advective fluxes of all components in all phases + //////// + for (unsigned int phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) + { + // data attached to upstream and the downstream vertices + // of the current phase + const VolumeVariables &up = + this->curVolVars_(fluxVars.upstreamIdx(phaseIdx)); + const VolumeVariables &dn = + this->curVolVars_(fluxVars.downstreamIdx(phaseIdx)); + + for (unsigned int compIdx = contiCompIdx1_(); compIdx <= contiCompIdx2_(); ++compIdx) + { + unsigned int eqIdx = (compIdx == wCompIdx) ? contiWEqIdx : contiNEqIdx; + // add advective flux of current component in current + // phase + if (massUpwindWeight_ > 0.0) + // upstream vertex + flux[eqIdx] += + fluxVars.volumeFlux(phaseIdx) + * massUpwindWeight_ + * up.density(phaseIdx) + * up.fluidState().massFraction(phaseIdx, compIdx); + if (massUpwindWeight_ < 1.0) + // downstream vertex + flux[eqIdx] += + fluxVars.volumeFlux(phaseIdx) + * (1 - massUpwindWeight_) + * dn.density(phaseIdx) + * dn.fluidState().massFraction(phaseIdx, compIdx); + + Valgrind::CheckDefined(fluxVars.volumeFlux(phaseIdx)); + Valgrind::CheckDefined(up.density(phaseIdx)); + Valgrind::CheckDefined(up.fluidState().massFraction(phaseIdx, compIdx)); + Valgrind::CheckDefined(dn.density(phaseIdx)); + Valgrind::CheckDefined(dn.fluidState().massFraction(phaseIdx, compIdx)); + } + // flux of the total mass balance; + // this is only processed, if one component mass balance equation + // is replaced by a total mass balance equation + if (replaceCompEqIdx < numComponents) + { + // upstream vertex + if (massUpwindWeight_ > 0.0) + flux[replaceCompEqIdx] += + fluxVars.volumeFlux(phaseIdx) + * massUpwindWeight_ + * up.density(phaseIdx); + // downstream vertex + if (massUpwindWeight_ < 1.0) + flux[replaceCompEqIdx] += + fluxVars.volumeFlux(phaseIdx) + * (1 - massUpwindWeight_) + * dn.density(phaseIdx); + Valgrind::CheckDefined(fluxVars.volumeFlux(phaseIdx)); + Valgrind::CheckDefined(up.density(phaseIdx)); + Valgrind::CheckDefined(dn.density(phaseIdx)); + + } + + } + } + + /*! + * \brief Adds the diffusive mass flux of all components over + * a face of a sub-control volume. + * + * \param flux The diffusive flux over the sub-control-volume face for each component + * \param fluxVars The flux variables at the current sub control volume face + */ + void computeDiffusiveFlux(PrimaryVariables &flux, const FluxVariables &fluxVars) const + { + // add diffusive flux of gas component in liquid phase + Scalar tmp = fluxVars.moleFractionGrad(wPhaseIdx)*fluxVars.face().normal; + tmp *= -1; + tmp *= + fluxVars.porousDiffCoeff(wPhaseIdx) * + fluxVars.molarDensity(wPhaseIdx); + // add the diffusive fluxes only to the component mass balance + if (replaceCompEqIdx != contiNEqIdx) + flux[contiNEqIdx] += tmp * FluidSystem::molarMass(nCompIdx); + if (replaceCompEqIdx != contiWEqIdx) + flux[contiWEqIdx] -= tmp * FluidSystem::molarMass(wCompIdx); + + // add diffusive flux of liquid component in non-wetting phase + tmp = fluxVars.moleFractionGrad(nPhaseIdx)*fluxVars.face().normal; + tmp *= -1; + tmp *= + fluxVars.porousDiffCoeff(nPhaseIdx) * + fluxVars.molarDensity(nPhaseIdx); + // add the diffusive fluxes only to the component mass balance + if (replaceCompEqIdx != contiWEqIdx) + flux[contiWEqIdx] += tmp * FluidSystem::molarMass(wCompIdx); + if (replaceCompEqIdx != contiNEqIdx) + flux[contiNEqIdx] -= tmp * FluidSystem::molarMass(nCompIdx); + } + + /*! + * \brief Calculate the source term of the equation + * + * \param source The source/sink in the sub-control volume for each component + * \param scvIdx The index of the sub-control volume + */ + void computeSource(PrimaryVariables& source, const int scvIdx) + { + this->problem_().boxSDSource(source, + this->element_(), + this->fvGeometry_(), + scvIdx, + this->curVolVars_()); + } + + protected: + void evalPhaseStorage_(const int phaseIdx) + { + // evaluate the storage terms of a single phase + for (int i=0; i < this->fvGeometry_().numSCV; i++) { + PrimaryVariables &storage = this->storageTerm_[i]; + const ElementVolumeVariables &elemVolVars = this->curVolVars_(); + const VolumeVariables &volVars = elemVolVars[i]; + + // compute storage term of all components within all phases + storage = 0; + for (int compIdx = 0; compIdx < numComponents; ++compIdx) + { + int eqIdx = (compIdx == wCompIdx) ? contiWEqIdx : contiNEqIdx; + storage[eqIdx] += volVars.density(phaseIdx) + * volVars.saturation(phaseIdx) + * volVars.fluidState().massFraction(phaseIdx, compIdx); + } + + storage *= volVars.porosity(); + storage *= this->fvGeometry_().subContVol[i].volume; + } + } + + /*! + * \brief Return the equation index of the first mass-balance equation + * of the component (used for loops); if one component mass balance + * is replaced by the total mass balance, this is the index + * of the remaining component mass-balance equation. + */ + unsigned int contiCompIdx1_() const { + switch (replaceCompEqIdx) + { + case contiWEqIdx: return contiNEqIdx; + case contiNEqIdx: return contiWEqIdx; + default: return 0; + } + } + + /*! + * \brief Return the equation index of the second mass balance + * of the component (used for loops); + * if one component mass balance is replaced by the total mass balance + * (replaceCompEqIdx < 2), this index is the same as contiCompIdx1(). + */ + unsigned int contiCompIdx2_() const { + switch (replaceCompEqIdx) + { + case contiWEqIdx: return contiNEqIdx; + case contiNEqIdx: return contiWEqIdx; + default: return numComponents-1; + } + } + + Implementation *asImp_() + { return static_cast<Implementation *> (this); } + const Implementation *asImp_() const + { return static_cast<const Implementation *> (this); } + + private: + Scalar massUpwindWeight_; +}; + +} // end namespace + +#endif diff --git a/dumux/implicit/2p2c/2p2cmodel.hh b/dumux/implicit/2p2c/2p2cmodel.hh new file mode 100644 index 0000000000..b3b395a54d --- /dev/null +++ b/dumux/implicit/2p2c/2p2cmodel.hh @@ -0,0 +1,825 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \file + * + * \brief Adaption of the BOX scheme to the two-phase two-component flow model. + */ +#ifndef DUMUX_2P2C_MODEL_HH +#define DUMUX_2P2C_MODEL_HH + +#include "2p2cproperties.hh" +#include "2p2clocalresidual.hh" + +namespace Dumux +{ +/*! + * \ingroup TwoPTwoCModel + * \brief Adaption of the BOX scheme to the two-phase two-component flow model. + * + * This model implements two-phase two-component flow of two compressible and + * partially miscible fluids \f$\alpha \in \{ w, n \}\f$ composed of the two components + * \f$\kappa \in \{ w, a \}\f$. The standard multiphase Darcy + * approach is used as the equation for the conservation of momentum: + * \f[ + v_\alpha = - \frac{k_{r\alpha}}{\mu_\alpha} \mbox{\bf K} + \left(\text{grad}\, p_\alpha - \varrho_{\alpha} \mbox{\bf g} \right) + * \f] + * + * By inserting this into the equations for the conservation of the + * components, one gets one transport equation for each component + * \f{eqnarray*} + && \phi \frac{\partial (\sum_\alpha \varrho_\alpha X_\alpha^\kappa S_\alpha )} + {\partial t} + - \sum_\alpha \text{div} \left\{ \varrho_\alpha X_\alpha^\kappa + \frac{k_{r\alpha}}{\mu_\alpha} \mbox{\bf K} + (\text{grad}\, p_\alpha - \varrho_{\alpha} \mbox{\bf g}) \right\} + \nonumber \\ \nonumber \\ + &-& \sum_\alpha \text{div} \left\{{\bf D}_{\alpha, pm}^\kappa \varrho_{\alpha} \text{grad}\, X^\kappa_{\alpha} \right\} + - \sum_\alpha q_\alpha^\kappa = 0 \qquad \kappa \in \{w, a\} \, , + \alpha \in \{w, g\} + \f} + * + * This is discretized using a fully-coupled vertex + * centered finite volume (box) scheme as spatial and + * the implicit Euler method as temporal discretization. + * + * By using constitutive relations for the capillary pressure \f$p_c = + * p_n - p_w\f$ and relative permeability \f$k_{r\alpha}\f$ and taking + * advantage of the fact that \f$S_w + S_n = 1\f$ and \f$X^\kappa_w + X^\kappa_n = 1\f$, the number of + * unknowns can be reduced to two. + * The used primary variables are, like in the two-phase model, either \f$p_w\f$ and \f$S_n\f$ + * or \f$p_n\f$ and \f$S_w\f$. The formulation which ought to be used can be + * specified by setting the <tt>Formulation</tt> property to either + * TwoPTwoCIndices::pWsN or TwoPTwoCIndices::pNsW. By + * default, the model uses \f$p_w\f$ and \f$S_n\f$. + * Moreover, the second primary variable depends on the phase state, since a + * primary variable switch is included. The phase state is stored for all nodes + * of the system. Following cases can be distinguished: + * <ul> + * <li> Both phases are present: The saturation is used (either \f$S_n\f$ or \f$S_w\f$, dependent on the chosen <tt>Formulation</tt>), + * as long as \f$ 0 < S_\alpha < 1\f$</li>. + * <li> Only wetting phase is present: The mass fraction of, e.g., air in the wetting phase \f$X^a_w\f$ is used, + * as long as the maximum mass fraction is not exceeded \f$(X^a_w<X^a_{w,max})\f$</li> + * <li> Only non-wetting phase is present: The mass fraction of, e.g., water in the non-wetting phase, \f$X^w_n\f$, is used, + * as long as the maximum mass fraction is not exceeded \f$(X^w_n<X^w_{n,max})\f$</li> + * </ul> + */ + +template<class TypeTag> +class TwoPTwoCModel: public GET_PROP_TYPE(TypeTag, BaseModel) +{ + typedef typename GET_PROP_TYPE(TypeTag, BaseModel) ParentType; + + typedef typename GET_PROP_TYPE(TypeTag, Problem) Problem; + typedef typename GET_PROP_TYPE(TypeTag, FluidSystem) FluidSystem; + typedef typename GET_PROP_TYPE(TypeTag, FVElementGeometry) FVElementGeometry; + typedef typename GET_PROP_TYPE(TypeTag, PrimaryVariables) PrimaryVariables; + typedef typename GET_PROP_TYPE(TypeTag, VolumeVariables) VolumeVariables; + typedef typename GET_PROP_TYPE(TypeTag, ElementVolumeVariables) ElementVolumeVariables; + typedef typename GET_PROP_TYPE(TypeTag, FluxVariables) FluxVariables; + typedef typename GET_PROP_TYPE(TypeTag, SolutionVector) SolutionVector; + enum { + numPhases = GET_PROP_VALUE(TypeTag, NumPhases), + numComponents = GET_PROP_VALUE(TypeTag, NumComponents) + }; + + typedef typename GET_PROP_TYPE(TypeTag, Indices) Indices; + enum { + pressureIdx = Indices::pressureIdx, + switchIdx = Indices::switchIdx, + + wPhaseIdx = Indices::wPhaseIdx, + nPhaseIdx = Indices::nPhaseIdx, + wCompIdx = Indices::wCompIdx, + nCompIdx = Indices::nCompIdx, + + wPhaseOnly = Indices::wPhaseOnly, + nPhaseOnly = Indices::nPhaseOnly, + bothPhases = Indices::bothPhases, + + pwSn = TwoPTwoCFormulation::pwSn, + pnSw = TwoPTwoCFormulation::pnSw, + formulation = GET_PROP_VALUE(TypeTag, Formulation) + }; + + typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; + typedef typename GridView::ctype CoordScalar; + typedef typename GridView::template Codim<0>::Entity Element; + typedef typename GridView::template Codim<0>::Iterator ElementIterator; + enum { + dim = GridView::dimension, + dimWorld = GridView::dimensionworld + }; + typedef typename GridView::template Codim<dim>::Entity Vertex; + typedef typename GridView::template Codim<dim>::Iterator VertexIterator; + + typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; + typedef Dune::FieldVector<Scalar, numPhases> PhasesVector; + typedef Dune::FieldVector<Scalar, dimWorld> GlobalPosition; + +public: + /*! + * \brief Initialize the static data with the initial solution. + * + * \param problem The problem to be solved + */ + void init(Problem &problem) + { + ParentType::init(problem); + + unsigned numDofs = this->numDofs(); + unsigned numVertices = this->problem_().gridView().size(dim); + + staticVertexDat_.resize(numDofs); + + setSwitched_(false); + + // check, if velocity output can be used (works only for cubes so far) + velocityOutput_ = GET_PARAM_FROM_GROUP(TypeTag, bool, Vtk, AddVelocity); + ElementIterator eIt = this->gridView_().template begin<0>(); + ElementIterator eEndIt = this->gridView_().template end<0>(); + for (; eIt != eEndIt; ++eIt) + { + if(eIt->geometry().type().isCube() == false){ + velocityOutput_ = false; + } + + if (numDofs != numVertices) // i.e. cell-centered discretization + { + velocityOutput_ = false; + + int globalIdx = this->dofMapper().map(*eIt); + const GlobalPosition &globalPos = eIt->geometry().center(); + + // initialize phase presence + staticVertexDat_[globalIdx].phasePresence + = this->problem_().initialPhasePresence(*(this->gridView_().template begin<dim>()), + globalIdx, globalPos); + staticVertexDat_[globalIdx].wasSwitched = false; + + staticVertexDat_[globalIdx].oldPhasePresence + = staticVertexDat_[globalIdx].phasePresence; + } + } + + if (velocityOutput_ != GET_PARAM_FROM_GROUP(TypeTag, bool, Vtk, AddVelocity)) + std::cout << "ATTENTION: Velocity output only works for cubes and is set to false for simplices\n"; + + if (numDofs == numVertices) // i.e. vertex-centered discretization + { + VertexIterator vIt = this->gridView_().template begin<dim> (); + const VertexIterator &vEndIt = this->gridView_().template end<dim> (); + for (; vIt != vEndIt; ++vIt) + { + int globalIdx = this->dofMapper().map(*vIt); + const GlobalPosition &globalPos = vIt->geometry().corner(0); + + // initialize phase presence + staticVertexDat_[globalIdx].phasePresence + = this->problem_().initialPhasePresence(*vIt, globalIdx, + globalPos); + staticVertexDat_[globalIdx].wasSwitched = false; + + staticVertexDat_[globalIdx].oldPhasePresence + = staticVertexDat_[globalIdx].phasePresence; + } + } + } + + /*! + * \brief Compute the total storage inside one phase of all + * conservation quantities. + * + * \param storage Contains the storage of each component for one phase + * \param phaseIdx The phase index + */ + void globalPhaseStorage(PrimaryVariables &storage, const int phaseIdx) + { + storage = 0; + + ElementIterator eIt = this->gridView_().template begin<0>(); + const ElementIterator eEndIt = this->gridView_().template end<0>(); + for (; eIt != eEndIt; ++eIt) { + this->localResidual().evalPhaseStorage(*eIt, phaseIdx); + + for (unsigned int i = 0; i < this->localResidual().storageTerm().size(); ++i) + storage += this->localResidual().storageTerm()[i]; + } + + if (this->gridView_().comm().size() > 1) + storage = this->gridView_().comm().sum(storage); + } + + /*! + * \brief Called by the update() method if applying the newton + * method was unsuccessful. + */ + void updateFailed() + { + ParentType::updateFailed(); + + setSwitched_(false); + resetPhasePresence_(); + }; + + /*! + * \brief Called by the problem if a time integration was + * successful, post processing of the solution is done and the + * result has been written to disk. + * + * This should prepare the model for the next time integration. + */ + void advanceTimeLevel() + { + ParentType::advanceTimeLevel(); + + // update the phase state + updateOldPhasePresence_(); + setSwitched_(false); + } + + /*! + * \brief Return true if the primary variables were switched for + * at least one vertex after the last timestep. + */ + bool switched() const + { + return switchFlag_; + } + + /*! + * \brief Returns the phase presence of the current or the old solution of a vertex. + * + * \param globalIdx The global vertex index + * \param oldSol Evaluate function with solution of current or previous time step + */ + int phasePresence(int globalIdx, bool oldSol) const + { + return oldSol ? staticVertexDat_[globalIdx].oldPhasePresence + : staticVertexDat_[globalIdx].phasePresence; + } + + /*! + * \brief Append all quantities of interest which can be derived + * from the solution of the current time step to the VTK + * writer. + * + * \param sol The solution vector + * \param writer The writer for multi-file VTK datasets + */ + template<class MultiWriter> + void addOutputVtkFields(const SolutionVector &sol, + MultiWriter &writer) + { + typedef Dune::BlockVector<Dune::FieldVector<double, 1> > ScalarField; + typedef Dune::BlockVector<Dune::FieldVector<double, dim> > VectorField; + + // create the required scalar fields + unsigned numDofs = this->numDofs(); + unsigned numVertices = this->problem_().gridView().size(dim); + + // velocity output currently only works for vertex data + if (numDofs != numVertices) + velocityOutput_ = false; + + ScalarField *sN = writer.allocateManagedBuffer(numDofs); + ScalarField *sW = writer.allocateManagedBuffer(numDofs); + ScalarField *pN = writer.allocateManagedBuffer(numDofs); + ScalarField *pW = writer.allocateManagedBuffer(numDofs); + ScalarField *pC = writer.allocateManagedBuffer(numDofs); + ScalarField *rhoW = writer.allocateManagedBuffer(numDofs); + ScalarField *rhoN = writer.allocateManagedBuffer(numDofs); + ScalarField *mobW = writer.allocateManagedBuffer(numDofs); + ScalarField *mobN = writer.allocateManagedBuffer(numDofs); + ScalarField *phasePresence = writer.allocateManagedBuffer(numDofs); + ScalarField *massFrac[numPhases][numComponents]; + for (int phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) + for (int compIdx = 0; compIdx < numComponents; ++compIdx) + massFrac[phaseIdx][compIdx] = writer.allocateManagedBuffer(numDofs); + ScalarField *temperature = writer.allocateManagedBuffer(numDofs); + ScalarField *poro = writer.allocateManagedBuffer(numDofs); + ScalarField *cellNum =writer.allocateManagedBuffer (numDofs); + VectorField *velocityN = writer.template allocateManagedBuffer<double, dim>(numDofs); + VectorField *velocityW = writer.template allocateManagedBuffer<double, dim>(numDofs); + + if(velocityOutput_) // check if velocity output is demanded + { + // initialize velocity fields + for (unsigned int i = 0; i < numDofs; ++i) + { + (*velocityN)[i] = Scalar(0); + (*velocityW)[i] = Scalar(0); + (*cellNum)[i] = Scalar(0.0); + } + } + + unsigned numElements = this->gridView_().size(0); + ScalarField *rank = writer.allocateManagedBuffer(numElements); + + FVElementGeometry fvGeometry; + VolumeVariables volVars; + + ElementIterator eIt = this->gridView_().template begin<0>(); + ElementIterator eEndIt = this->gridView_().template end<0>(); + for (; eIt != eEndIt; ++eIt) + { + int idx = this->elementMapper().map(*eIt); + (*rank)[idx] = this->gridView_().comm().rank(); + fvGeometry.update(this->gridView_(), *eIt); + + for (int scvIdx = 0; scvIdx < fvGeometry.numSCV; ++scvIdx) + { + int globalIdx; + if (numDofs == numElements) // element data + globalIdx = idx; + else + globalIdx = this->vertexMapper().map(*eIt, scvIdx, dim); + + volVars.update(sol[globalIdx], + this->problem_(), + *eIt, + fvGeometry, + scvIdx, + false); + (*sN)[globalIdx] = volVars.saturation(nPhaseIdx); + (*sW)[globalIdx] = volVars.saturation(wPhaseIdx); + (*pN)[globalIdx] = volVars.pressure(nPhaseIdx); + (*pW)[globalIdx] = volVars.pressure(wPhaseIdx); + (*pC)[globalIdx] = volVars.capillaryPressure(); + (*rhoW)[globalIdx] = volVars.fluidState().density(wPhaseIdx); + (*rhoN)[globalIdx] = volVars.fluidState().density(nPhaseIdx); + (*mobW)[globalIdx] = volVars.mobility(wPhaseIdx); + (*mobN)[globalIdx] = volVars.mobility(nPhaseIdx); + for (int phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) + for (int compIdx = 0; compIdx < numComponents; ++compIdx) + { + (*massFrac[phaseIdx][compIdx])[globalIdx] + = volVars.fluidState().massFraction(phaseIdx, compIdx); + + Valgrind::CheckDefined((*massFrac[phaseIdx][compIdx])[globalIdx][0]); + } + (*poro)[globalIdx] = volVars.porosity(); + (*temperature)[globalIdx] = volVars.temperature(); + (*phasePresence)[globalIdx] + = staticVertexDat_[globalIdx].phasePresence; + if(velocityOutput_) + { + (*cellNum)[globalIdx] += 1; + } + } + + if(velocityOutput_) + { + // calculate vertex velocities + GlobalPosition tmpVelocity[numPhases]; + + for(int phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) + { + tmpVelocity[phaseIdx] = Scalar(0.0); + } + + typedef Dune::BlockVector<Dune::FieldVector<Scalar, dim> > SCVVelocities; + SCVVelocities scvVelocityW(8), scvVelocityN(8); + + scvVelocityW = 0; + scvVelocityN = 0; + + ElementVolumeVariables elemVolVars; + + elemVolVars.update(this->problem_(), + *eIt, + fvGeometry, + false /* oldSol? */); + + for (int faceIdx = 0; faceIdx < fvGeometry.numEdges; faceIdx++) + { + + FluxVariables fluxVars(this->problem_(), + *eIt, + fvGeometry, + faceIdx, + elemVolVars); + + for (int phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) + { + // local position of integration point + const Dune::FieldVector<Scalar, dim>& localPosIP = fvGeometry.subContVolFace[faceIdx].ipLocal; + + // Transformation of the global normal vector to normal vector in the reference element + const typename Element::Geometry::JacobianTransposed jacobianT1 = + eIt->geometry().jacobianTransposed(localPosIP); + const GlobalPosition globalNormal = fluxVars.face().normal; + + GlobalPosition localNormal(0); + jacobianT1.mv(globalNormal, localNormal); + // note only works for cubes + const Scalar localArea = pow(2,-(dim-1)); + + localNormal /= localNormal.two_norm(); + + // Get the Darcy velocities. The Darcy velocities are divided by the area of the subcontrolvolume + // face in the reference element. + PhasesVector flux; + flux[phaseIdx] = fluxVars.volumeFlux(phaseIdx) / localArea; + + // transform the normal Darcy velocity into a vector + tmpVelocity[phaseIdx] = localNormal; + tmpVelocity[phaseIdx] *= flux[phaseIdx]; + + if(phaseIdx == wPhaseIdx){ + scvVelocityW[fluxVars.face().i] += tmpVelocity[phaseIdx]; + scvVelocityW[fluxVars.face().j] += tmpVelocity[phaseIdx]; + } + else if(phaseIdx == nPhaseIdx){ + scvVelocityN[fluxVars.face().i] += tmpVelocity[phaseIdx]; + scvVelocityN[fluxVars.face().j] += tmpVelocity[phaseIdx]; + } + } + } + + typedef Dune::GenericReferenceElements<Scalar, dim> ReferenceElements; + const Dune::FieldVector<Scalar, dim>& localPos = + ReferenceElements::general(eIt->geometry().type()).position(0, 0); + + // get the transposed Jacobian of the element mapping + const typename Element::Geometry::JacobianTransposed jacobianT2 = + eIt->geometry().jacobianTransposed(localPos); + + // transform vertex velocities from local to global coordinates + for (int scvIdx = 0; scvIdx < fvGeometry.numSCV; ++scvIdx) + { + int globalIdx = this->vertexMapper().map(*eIt, scvIdx, dim); + // calculate the subcontrolvolume velocity by the Piola transformation + Dune::FieldVector<CoordScalar, dim> scvVelocity(0); + + jacobianT2.mtv(scvVelocityW[scvIdx], scvVelocity); + scvVelocity /= eIt->geometry().integrationElement(localPos); + // add up the wetting phase subcontrolvolume velocities for each vertex + (*velocityW)[globalIdx] += scvVelocity; + + jacobianT2.mtv(scvVelocityN[scvIdx], scvVelocity); + scvVelocity /= eIt->geometry().integrationElement(localPos); + // add up the nonwetting phase subcontrolvolume velocities for each vertex + (*velocityN)[globalIdx] += scvVelocity; + } + } // velocity output + } // loop over elements + + if(velocityOutput_) + { + // divide the vertex velocities by the number of adjacent scvs i.e. cells + for(unsigned int globalIdx = 0; globalIdx < numDofs; ++globalIdx){ + (*velocityW)[globalIdx] /= (*cellNum)[globalIdx]; + (*velocityN)[globalIdx] /= (*cellNum)[globalIdx]; + } + } + + if (numDofs == numElements) // element data + { + writer.attachCellData(*sN, "Sn"); + writer.attachCellData(*sW, "Sw"); + writer.attachCellData(*pN, "pN"); + writer.attachCellData(*pW, "pW"); + writer.attachCellData(*pC, "pC"); + writer.attachCellData(*rhoW, "rhoW"); + writer.attachCellData(*rhoN, "rhoN"); + writer.attachCellData(*mobW, "mobW"); + writer.attachCellData(*mobN, "mobN"); + for (int phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) + { + for (int compIdx = 0; compIdx < numComponents; ++compIdx) + { + std::ostringstream oss; + oss << "X_" << FluidSystem::phaseName(phaseIdx) << "^" << FluidSystem::componentName(compIdx); + writer.attachCellData(*massFrac[phaseIdx][compIdx], oss.str()); + } + } + writer.attachCellData(*poro, "porosity"); + writer.attachCellData(*temperature, "temperature"); + writer.attachCellData(*phasePresence, "phase presence"); + } + else + { + writer.attachVertexData(*sN, "Sn"); + writer.attachVertexData(*sW, "Sw"); + writer.attachVertexData(*pN, "pN"); + writer.attachVertexData(*pW, "pW"); + writer.attachVertexData(*pC, "pC"); + writer.attachVertexData(*rhoW, "rhoW"); + writer.attachVertexData(*rhoN, "rhoN"); + writer.attachVertexData(*mobW, "mobW"); + writer.attachVertexData(*mobN, "mobN"); + for (int phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) + { + for (int compIdx = 0; compIdx < numComponents; ++compIdx) + { + std::ostringstream oss; + oss << "X_" << FluidSystem::phaseName(phaseIdx) << "^" << FluidSystem::componentName(compIdx); + writer.attachVertexData(*massFrac[phaseIdx][compIdx], oss.str()); + } + } + writer.attachVertexData(*poro, "porosity"); + writer.attachVertexData(*temperature, "temperature"); + writer.attachVertexData(*phasePresence, "phase presence"); + + if(velocityOutput_) // check if velocity output is demanded + { + writer.attachVertexData(*velocityW, "velocityW", dim); + writer.attachVertexData(*velocityN, "velocityN", dim); + } + } + + writer.attachCellData(*rank, "process rank"); + } + + /*! + * \brief Write the current solution to a restart file. + * + * \param outStream The output stream of one vertex for the restart file + * \param entity The entity, either a vertex or an element + */ + template<class Entity> + void serializeEntity(std::ostream &outStream, const Entity &entity) + { + // write primary variables + ParentType::serializeEntity(outStream, entity); + + int globalIdx = this->dofMapper().map(entity); + if (!outStream.good()) + DUNE_THROW(Dune::IOError, "Could not serialize entity " << globalIdx); + + outStream << staticVertexDat_[globalIdx].phasePresence << " "; + } + + /*! + * \brief Reads the current solution from a restart file. + * + * \param inStream The input stream of one vertex from the restart file + * \param entity The entity, either a vertex or an element + */ + template<class Entity> + void deserializeEntity(std::istream &inStream, const Entity &entity) + { + // read primary variables + ParentType::deserializeEntity(inStream, entity); + + // read phase presence + int globalIdx = this->dofMapper().map(entity); + if (!inStream.good()) + DUNE_THROW(Dune::IOError, + "Could not deserialize entity " << globalIdx); + + inStream >> staticVertexDat_[globalIdx].phasePresence; + staticVertexDat_[globalIdx].oldPhasePresence + = staticVertexDat_[globalIdx].phasePresence; + + } + + /*! + * \brief Update the static data of all vertices in the grid. + * + * \param curGlobalSol The current global solution + * \param oldGlobalSol The previous global solution + */ + void updateStaticData(SolutionVector &curGlobalSol, + const SolutionVector &oldGlobalSol) + { + bool wasSwitched = false; + + for (unsigned i = 0; i < staticVertexDat_.size(); ++i) + staticVertexDat_[i].visited = false; + + unsigned numDofs = this->numDofs(); + unsigned numVertices = this->problem_().gridView().size(dim); + + FVElementGeometry fvGeometry; + static VolumeVariables volVars; + ElementIterator eIt = this->gridView_().template begin<0> (); + const ElementIterator &eEndIt = this->gridView_().template end<0> (); + for (; eIt != eEndIt; ++eIt) + { + fvGeometry.update(this->gridView_(), *eIt); + for (int scvIdx = 0; scvIdx < fvGeometry.numSCV; ++scvIdx) + { + int globalIdx; + + if (numDofs != numVertices) + globalIdx = this->elementMapper().map(*eIt); + else + globalIdx = this->vertexMapper().map(*eIt, scvIdx, dim); + + if (staticVertexDat_[globalIdx].visited) + continue; + + staticVertexDat_[globalIdx].visited = true; + volVars.update(curGlobalSol[globalIdx], + this->problem_(), + *eIt, + fvGeometry, + scvIdx, + false); + const GlobalPosition &globalPos = eIt->geometry().corner(scvIdx); + if (primaryVarSwitch_(curGlobalSol, + volVars, + globalIdx, + globalPos)) + { + this->jacobianAssembler().markVertexRed(globalIdx); + wasSwitched = true; + } + } + } + + // make sure that if there was a variable switch in an + // other partition we will also set the switch flag + // for our partition. + if (this->gridView_().comm().size() > 1) + wasSwitched = this->gridView_().comm().max(wasSwitched); + + setSwitched_(wasSwitched); + } + +protected: + /*! + * \brief Data which is attached to each vertex and is not only + * stored locally. + */ + struct StaticVars + { + int phasePresence; + bool wasSwitched; + + int oldPhasePresence; + bool visited; + }; + + /*! + * \brief Reset the current phase presence of all vertices to the old one. + * + * This is done after an update failed. + */ + void resetPhasePresence_() + { + for (unsigned int idx = 0; idx < staticVertexDat_.size(); ++idx) + { + staticVertexDat_[idx].phasePresence + = staticVertexDat_[idx].oldPhasePresence; + staticVertexDat_[idx].wasSwitched = false; + } + } + + /*! + * \brief Set the old phase of all verts state to the current one. + */ + void updateOldPhasePresence_() + { + for (unsigned int idx = 0; idx < staticVertexDat_.size(); ++idx) + { + staticVertexDat_[idx].oldPhasePresence + = staticVertexDat_[idx].phasePresence; + staticVertexDat_[idx].wasSwitched = false; + } + } + + /*! + * \brief Set whether there was a primary variable switch after in + * the last timestep. + */ + void setSwitched_(bool yesno) + { + switchFlag_ = yesno; + } + + // perform variable switch at a vertex; Returns true if a + // variable switch was performed. + bool primaryVarSwitch_(SolutionVector &globalSol, + const VolumeVariables &volVars, int globalIdx, + const GlobalPosition &globalPos) + { + // evaluate primary variable switch + bool wouldSwitch = false; + int phasePresence = staticVertexDat_[globalIdx].phasePresence; + int newPhasePresence = phasePresence; + + // check if a primary var switch is necessary + if (phasePresence == nPhaseOnly) + { + // calculate mole fraction in the hypothetic wetting phase + Scalar xww = volVars.fluidState().moleFraction(wPhaseIdx, wCompIdx); + Scalar xwn = volVars.fluidState().moleFraction(wPhaseIdx, nCompIdx); + + Scalar xwMax = 1.0; + if (xww + xwn > xwMax) + wouldSwitch = true; + if (staticVertexDat_[globalIdx].wasSwitched) + xwMax *= 1.02; + + // if the sum of the mole fractions would be larger than + // 100%, wetting phase appears + if (xww + xwn > xwMax) + { + // wetting phase appears + std::cout << "wetting phase appears at vertex " << globalIdx + << ", coordinates: " << globalPos << ", xww + xwn: " + << xww + xwn << std::endl; + newPhasePresence = bothPhases; + if (formulation == pnSw) + globalSol[globalIdx][switchIdx] = 0.0; + else if (formulation == pwSn) + globalSol[globalIdx][switchIdx] = 1.0; + } + } + else if (phasePresence == wPhaseOnly) + { + // calculate fractions of the partial pressures in the + // hypothetic nonwetting phase + Scalar xnw = volVars.fluidState().moleFraction(nPhaseIdx, wCompIdx); + Scalar xnn = volVars.fluidState().moleFraction(nPhaseIdx, nCompIdx); + + Scalar xgMax = 1.0; + if (xnw + xnn > xgMax) + wouldSwitch = true; + if (staticVertexDat_[globalIdx].wasSwitched) + xgMax *= 1.02; + + // if the sum of the mole fractions would be larger than + // 100%, nonwetting phase appears + if (xnw + xnn > xgMax) + { + // nonwetting phase appears + std::cout << "nonwetting phase appears at vertex " << globalIdx + << ", coordinates: " << globalPos << ", xnw + xnn: " + << xnw + xnn << std::endl; + newPhasePresence = bothPhases; + if (formulation == pnSw) + globalSol[globalIdx][switchIdx] = 0.999; + else if (formulation == pwSn) + globalSol[globalIdx][switchIdx] = 0.001; + } + } + else if (phasePresence == bothPhases) + { + Scalar Smin = 0.0; + if (staticVertexDat_[globalIdx].wasSwitched) + Smin = -0.01; + + if (volVars.saturation(nPhaseIdx) <= Smin) + { + wouldSwitch = true; + // nonwetting phase disappears + std::cout << "Nonwetting phase disappears at vertex " << globalIdx + << ", coordinates: " << globalPos << ", Sn: " + << volVars.saturation(nPhaseIdx) << std::endl; + newPhasePresence = wPhaseOnly; + + globalSol[globalIdx][switchIdx] + = volVars.fluidState().massFraction(wPhaseIdx, nCompIdx); + } + else if (volVars.saturation(wPhaseIdx) <= Smin) + { + wouldSwitch = true; + // wetting phase disappears + std::cout << "Wetting phase disappears at vertex " << globalIdx + << ", coordinates: " << globalPos << ", Sw: " + << volVars.saturation(wPhaseIdx) << std::endl; + newPhasePresence = nPhaseOnly; + + globalSol[globalIdx][switchIdx] + = volVars.fluidState().massFraction(nPhaseIdx, wCompIdx); + } + } + + staticVertexDat_[globalIdx].phasePresence = newPhasePresence; + staticVertexDat_[globalIdx].wasSwitched = wouldSwitch; + return phasePresence != newPhasePresence; + } + +protected: + // parameters given in constructor + std::vector<StaticVars> staticVertexDat_; + bool switchFlag_; + bool velocityOutput_; +}; + +} + +#include "2p2cpropertydefaults.hh" + +#endif diff --git a/dumux/implicit/2p2c/2p2cnewtoncontroller.hh b/dumux/implicit/2p2c/2p2cnewtoncontroller.hh new file mode 100644 index 0000000000..374ccf5931 --- /dev/null +++ b/dumux/implicit/2p2c/2p2cnewtoncontroller.hh @@ -0,0 +1,110 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \file + * \brief A 2p2c specific controller for the newton solver. + * + * This controller 'knows' what a 'physically meaningful' solution is + * which allows the newton method to abort quicker if the solution is + * way out of bounds. + */ +#ifndef DUMUX_2P2C_NEWTON_CONTROLLER_HH +#define DUMUX_2P2C_NEWTON_CONTROLLER_HH + +#include "2p2cproperties.hh" + +#include <dumux/nonlinear/newtoncontroller.hh> + +namespace Dumux { + +/*! + * \ingroup Newton + * \ingroup TwoPTwoCModel + * \brief A 2p2c specific controller for the newton solver. + * + * This controller 'knows' what a 'physically meaningful' solution is + * which allows the newton method to abort quicker if the solution is + * way out of bounds. + */ +template <class TypeTag> +class TwoPTwoCNewtonController : public NewtonController<TypeTag> +{ + typedef NewtonController<TypeTag> ParentType; + typedef typename GET_PROP_TYPE(TypeTag, Problem) Problem; + typedef typename GET_PROP_TYPE(TypeTag, SolutionVector) SolutionVector; + +public: + TwoPTwoCNewtonController(const Problem &problem) + : ParentType(problem) + {} + + /*! + * \brief + * Suggest a new time step size based either on the number of newton + * iterations required or on the variable switch + * + * \param uCurrentIter The current global solution vector + * \param uLastIter The previous global solution vector + * + */ + void newtonEndStep(SolutionVector &uCurrentIter, + const SolutionVector &uLastIter) + { + int succeeded; + try { + // call the method of the base class + this->method().model().updateStaticData(uCurrentIter, uLastIter); + ParentType::newtonEndStep(uCurrentIter, uLastIter); + + succeeded = 1; + if (this->gridView_().comm().size() > 1) + succeeded = this->gridView_().comm().min(succeeded); + } + catch (Dumux::NumericalProblem &e) + { + std::cout << "rank " << this->problem_().gridView().comm().rank() + << " caught an exception while updating:" << e.what() + << "\n"; + succeeded = 0; + if (this->gridView_().comm().size() > 1) + succeeded = this->gridView_().comm().min(succeeded); + } + + if (!succeeded) { + DUNE_THROW(NumericalProblem, + "A process did not succeed in linearizing the system"); + } + } + + /*! + * \brief + * Returns true if the current solution can be considered to + * be accurate enough + */ + bool newtonConverged() + { + if (this->method().model().switched()) + return false; + + return ParentType::newtonConverged(); + } +}; +} + +#endif diff --git a/dumux/implicit/2p2c/2p2cproperties.hh b/dumux/implicit/2p2c/2p2cproperties.hh new file mode 100644 index 0000000000..0303514205 --- /dev/null +++ b/dumux/implicit/2p2c/2p2cproperties.hh @@ -0,0 +1,70 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \ingroup Properties + * \ingroup BoxProperties + * \ingroup TwoPTwoCModel + */ +/*! + * \file + * + * \brief Defines the properties required for the 2p2c BOX model. + */ +#ifndef DUMUX_2P2C_PROPERTIES_HH +#define DUMUX_2P2C_PROPERTIES_HH + +#include <dumux/boxmodels/common/boxproperties.hh> + +namespace Dumux +{ + +namespace Properties +{ +////////////////////////////////////////////////////////////////// +// Type tags +////////////////////////////////////////////////////////////////// + +//! The type tag for the isothermal single phase problems +NEW_TYPE_TAG(BoxTwoPTwoC, INHERITS_FROM(BoxModel)); + +////////////////////////////////////////////////////////////////// +// Property tags +////////////////////////////////////////////////////////////////// + +NEW_PROP_TAG(NumPhases); //!< Number of fluid phases in the system +NEW_PROP_TAG(NumComponents); //!< Number of fluid components in the system +NEW_PROP_TAG(Indices); //!< Enumerations for the model +NEW_PROP_TAG(Formulation); //!< The formulation of the model +NEW_PROP_TAG(SpatialParams); //!< The type of the spatial parameters +NEW_PROP_TAG(FluidSystem); //!< Type of the multi-component relations + +NEW_PROP_TAG(MaterialLaw); //!< The material law which ought to be used (extracted from the spatial parameters) +NEW_PROP_TAG(MaterialLawParams); //!< The parameters of the material law (extracted from the spatial parameters) + +NEW_PROP_TAG(ProblemEnableGravity); //!< Returns whether gravity is considered in the problem +NEW_PROP_TAG(ImplicitMassUpwindWeight); //!< The value of the upwind weight for the mass conservation equations +NEW_PROP_TAG(ImplicitMobilityUpwindWeight); //!< Weight for the upwind mobility in the velocity calculation +NEW_PROP_TAG(ReplaceCompEqIdx); //!< The index of the total mass balance equation, if one component balance is replaced (ReplaceCompEqIdx < NumComponents) +NEW_PROP_TAG(VtkAddVelocity); //!< Returns whether velocity vectors are written into the vtk output +NEW_PROP_TAG(BaseFluxVariables); //! The base flux variables +NEW_PROP_TAG(SpatialParamsForchCoeff); //!< Property for the forchheimer coefficient +} +} + +#endif diff --git a/dumux/implicit/2p2c/2p2cpropertydefaults.hh b/dumux/implicit/2p2c/2p2cpropertydefaults.hh new file mode 100644 index 0000000000..98c9a1f333 --- /dev/null +++ b/dumux/implicit/2p2c/2p2cpropertydefaults.hh @@ -0,0 +1,160 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \ingroup Properties + * \ingroup BoxProperties + * \ingroup TwoPTwoCModel + * \file + * + * \brief Defines default values for most properties required by the + * 2p2c box model. + */ +#ifndef DUMUX_2P2C_PROPERTY_DEFAULTS_HH +#define DUMUX_2P2C_PROPERTY_DEFAULTS_HH + +#include "2p2cmodel.hh" +#include "2p2cindices.hh" +#include "2p2cfluxvariables.hh" +#include "2p2cvolumevariables.hh" +#include "2p2cproperties.hh" +#include "2p2cnewtoncontroller.hh" + +#include <dumux/boxmodels/common/boxdarcyfluxvariables.hh> +#include <dumux/material/spatialparams/boxspatialparams.hh> + +namespace Dumux +{ + +namespace Properties { +////////////////////////////////////////////////////////////////// +// Property values +////////////////////////////////////////////////////////////////// + +/*! + * \brief Set the property for the number of components. + * + * We just forward the number from the fluid system and use an static + * assert to make sure it is 2. + */ +SET_PROP(BoxTwoPTwoC, NumComponents) +{ + private: + typedef typename GET_PROP_TYPE(TypeTag, FluidSystem) FluidSystem; + + public: + static const int value = FluidSystem::numComponents; + + static_assert(value == 2, + "Only fluid systems with 2 components are supported by the 2p-2c model!"); +}; + +/*! + * \brief Set the property for the number of fluid phases. + * + * We just forward the number from the fluid system and use an static + * assert to make sure it is 2. + */ +SET_PROP(BoxTwoPTwoC, NumPhases) +{ + private: + typedef typename GET_PROP_TYPE(TypeTag, FluidSystem) FluidSystem; + + public: + static const int value = FluidSystem::numPhases; + static_assert(value == 2, + "Only fluid systems with 2 phases are supported by the 2p-2c model!"); +}; + +SET_INT_PROP(BoxTwoPTwoC, NumEq, 2); //!< set the number of equations to 2 + +//! Set the default formulation to pw-Sn +SET_INT_PROP(BoxTwoPTwoC, + Formulation, + TwoPTwoCFormulation::pwSn); + +//! set as default that no component mass balance is replaced by the total mass balance +SET_INT_PROP(BoxTwoPTwoC, ReplaceCompEqIdx, 2); + +/*! + * \brief Set the property for the material parameters by extracting + * it from the material law. + */ +SET_PROP(BoxTwoPTwoC, MaterialLawParams) +{ + private: + typedef typename GET_PROP_TYPE(TypeTag, MaterialLaw) MaterialLaw; + + public: + typedef typename MaterialLaw::Params type; +}; + +//! Use the 2p2c local jacobian operator for the 2p2c model +SET_TYPE_PROP(BoxTwoPTwoC, + LocalResidual, + TwoPTwoCLocalResidual<TypeTag>); + +//! Use the 2p2c specific newton controller for the 2p2c model +SET_TYPE_PROP(BoxTwoPTwoC, NewtonController, TwoPTwoCNewtonController<TypeTag>); + +//! the Model property +SET_TYPE_PROP(BoxTwoPTwoC, Model, TwoPTwoCModel<TypeTag>); + +//! the VolumeVariables property +SET_TYPE_PROP(BoxTwoPTwoC, VolumeVariables, TwoPTwoCVolumeVariables<TypeTag>); + +//! the FluxVariables property +SET_TYPE_PROP(BoxTwoPTwoC, FluxVariables, TwoPTwoCFluxVariables<TypeTag>); + +//! define the base flux variables to realize Darcy flow +SET_TYPE_PROP(BoxTwoPTwoC, BaseFluxVariables, BoxDarcyFluxVariables<TypeTag>); + +//! the upwind weight for the mass conservation equations. +SET_SCALAR_PROP(BoxTwoPTwoC, ImplicitMassUpwindWeight, 1.0); + +//! set default mobility upwind weight to 1.0, i.e. fully upwind +SET_SCALAR_PROP(BoxTwoPTwoC, ImplicitMobilityUpwindWeight, 1.0); + +//! The indices required by the isothermal 2p2c model +SET_PROP(BoxTwoPTwoC, Indices) +{ private: + enum { Formulation = GET_PROP_VALUE(TypeTag, Formulation) }; + public: + typedef TwoPTwoCIndices<TypeTag, Formulation, 0> type; +}; + +//! The spatial parameters to be employed. +//! Use BoxSpatialParams by default. +SET_TYPE_PROP(BoxTwoPTwoC, SpatialParams, BoxSpatialParams<TypeTag>); + +// disable velocity output by default +SET_BOOL_PROP(BoxTwoPTwoC, VtkAddVelocity, false); + +// enable gravity by default +SET_BOOL_PROP(BoxTwoPTwoC, ProblemEnableGravity, true); + +//! default value for the forchheimer coefficient +// Source: Ward, J.C. 1964 Turbulent flow in porous media. ASCE J. Hydraul. Div 90. +// Actually the Forchheimer coefficient is also a function of the dimensions of the +// porous medium. Taking it as a constant is only a first approximation +// (Nield, Bejan, Convection in porous media, 2006, p. 10) +SET_SCALAR_PROP(BoxModel, SpatialParamsForchCoeff, 0.55);} + +} + +#endif diff --git a/dumux/implicit/2p2c/2p2cvolumevariables.hh b/dumux/implicit/2p2c/2p2cvolumevariables.hh new file mode 100644 index 0000000000..f097db5c20 --- /dev/null +++ b/dumux/implicit/2p2c/2p2cvolumevariables.hh @@ -0,0 +1,455 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \file + * + * \brief Contains the quantities which are constant within a + * finite volume in the two-phase, two-component model. + */ +#ifndef DUMUX_2P2C_VOLUME_VARIABLES_HH +#define DUMUX_2P2C_VOLUME_VARIABLES_HH + +#include <dumux/boxmodels/common/boxmodel.hh> +#include <dumux/common/math.hh> + +#include <dune/common/collectivecommunication.hh> +#include <vector> +#include <iostream> + +#include "2p2cproperties.hh" +#include "2p2cindices.hh" + +#include <dumux/material/fluidstates/compositionalfluidstate.hh> +#include <dumux/material/constraintsolvers/computefromreferencephase.hh> +#include <dumux/material/constraintsolvers/misciblemultiphasecomposition.hh> + +namespace Dumux +{ + +/*! + * \ingroup TwoPTwoCModel + * \ingroup BoxVolumeVariables + * \brief Contains the quantities which are are constant within a + * finite volume in the two-phase, two-component model. + */ +template <class TypeTag> +class TwoPTwoCVolumeVariables : public BoxVolumeVariables<TypeTag> +{ + typedef BoxVolumeVariables<TypeTag> ParentType; + + typedef typename GET_PROP_TYPE(TypeTag, VolumeVariables) Implementation; + typedef typename GET_PROP_TYPE(TypeTag, Problem) Problem; + typedef typename GET_PROP_TYPE(TypeTag, FVElementGeometry) FVElementGeometry; + typedef typename GET_PROP_TYPE(TypeTag, PrimaryVariables) PrimaryVariables; + typedef typename GET_PROP_TYPE(TypeTag, MaterialLaw) MaterialLaw; + typedef typename GET_PROP_TYPE(TypeTag, MaterialLawParams) MaterialLawParams; + enum { + numPhases = GET_PROP_VALUE(TypeTag, NumPhases), + numComponents = GET_PROP_VALUE(TypeTag, NumComponents) + }; + + typedef typename GET_PROP_TYPE(TypeTag, Indices) Indices; + enum { + wCompIdx = Indices::wCompIdx, + nCompIdx = Indices::nCompIdx, + wPhaseIdx = Indices::wPhaseIdx, + nPhaseIdx = Indices::nPhaseIdx + }; + + // present phases + enum { + wPhaseOnly = Indices::wPhaseOnly, + nPhaseOnly = Indices::nPhaseOnly, + bothPhases = Indices::bothPhases + }; + + // formulations + enum { + formulation = GET_PROP_VALUE(TypeTag, Formulation), + pwSn = TwoPTwoCFormulation::pwSn, + pnSw = TwoPTwoCFormulation::pnSw + }; + + // primary variable indices + enum { + switchIdx = Indices::switchIdx, + pressureIdx = Indices::pressureIdx + }; + + typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; + typedef typename GridView::template Codim<0>::Entity Element; + enum { dim = GridView::dimension}; + + typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; + typedef typename GET_PROP_TYPE(TypeTag, FluidSystem) FluidSystem; + typedef Dumux::MiscibleMultiPhaseComposition<Scalar, FluidSystem> MiscibleMultiPhaseComposition; + typedef Dumux::ComputeFromReferencePhase<Scalar, FluidSystem> ComputeFromReferencePhase; + +public: + //! The type of the object returned by the fluidState() method + typedef Dumux::CompositionalFluidState<Scalar, FluidSystem> FluidState; + + /*! + * \copydoc BoxVolumeVariables::update + */ + void update(const PrimaryVariables &priVars, + const Problem &problem, + const Element &element, + const FVElementGeometry &fvGeometry, + const int scvIdx, + const bool isOldSol) + { + ParentType::update(priVars, + problem, + element, + fvGeometry, + scvIdx, + isOldSol); + + completeFluidState(priVars, problem, element, fvGeometry, scvIdx, fluidState_, isOldSol); + + ///////////// + // calculate the remaining quantities + ///////////// + const MaterialLawParams &materialParams = + problem.spatialParams().materialLawParams(element, fvGeometry, scvIdx); + + // Second instance of a parameter cache. + // Could be avoided if diffusion coefficients also + // became part of the fluid state. + typename FluidSystem::ParameterCache paramCache; + paramCache.updateAll(fluidState_); + for (int phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) { + // relative permeabilities + Scalar kr; + if (phaseIdx == wPhaseIdx) + kr = MaterialLaw::krw(materialParams, saturation(wPhaseIdx)); + else // ATTENTION: krn requires the wetting phase saturation + // as parameter! + kr = MaterialLaw::krn(materialParams, saturation(wPhaseIdx)); + relativePermeability_[phaseIdx] = kr; + Valgrind::CheckDefined(relativePermeability_[phaseIdx]); + + // binary diffusion coefficients + diffCoeff_[phaseIdx] = + FluidSystem::binaryDiffusionCoefficient(fluidState_, + paramCache, + phaseIdx, + wCompIdx, + nCompIdx); + Valgrind::CheckDefined(diffCoeff_[phaseIdx]); + } + + // porosity + porosity_ = problem.spatialParams().porosity(element, + fvGeometry, + scvIdx); + Valgrind::CheckDefined(porosity_); + + // energy related quantities not contained in the fluid state + asImp_().updateEnergy_(priVars, problem, element, fvGeometry, scvIdx, isOldSol); + } + + /*! + * \copydoc BoxModel::completeFluidState + * \param isOldSol Specifies whether this is the previous solution or the current one + */ + static void completeFluidState(const PrimaryVariables& priVars, + const Problem& problem, + const Element& element, + const FVElementGeometry& fvGeometry, + int scvIdx, + FluidState& fluidState, + bool isOldSol = false) + { + Scalar t = Implementation::temperature_(priVars, problem, element, + fvGeometry, scvIdx); + fluidState.setTemperature(t); + + unsigned numDofs = problem.model().numDofs(); + unsigned numVertices = problem.gridView().size(dim); + + int globalIdx; + if (numDofs != numVertices) // element data + globalIdx = problem.model().dofMapper().map(element); + else + globalIdx = problem.model().dofMapper().map(element, scvIdx, dim); + + int phasePresence = problem.model().phasePresence(globalIdx, isOldSol); + + ///////////// + // set the saturations + ///////////// + Scalar Sn; + if (phasePresence == nPhaseOnly) + Sn = 1.0; + else if (phasePresence == wPhaseOnly) { + Sn = 0.0; + } + else if (phasePresence == bothPhases) { + if (formulation == pwSn) + Sn = priVars[switchIdx]; + else if (formulation == pnSw) + Sn = 1.0 - priVars[switchIdx]; + else DUNE_THROW(Dune::InvalidStateException, "Formulation: " << formulation << " is invalid."); + } + else DUNE_THROW(Dune::InvalidStateException, "phasePresence: " << phasePresence << " is invalid."); + fluidState.setSaturation(wPhaseIdx, 1 - Sn); + fluidState.setSaturation(nPhaseIdx, Sn); + + ///////////// + // set the pressures of the fluid phases + ///////////// + + // calculate capillary pressure + const MaterialLawParams &materialParams = + problem.spatialParams().materialLawParams(element, fvGeometry, scvIdx); + Scalar pC = MaterialLaw::pC(materialParams, 1 - Sn); + + if (formulation == pwSn) { + fluidState.setPressure(wPhaseIdx, priVars[pressureIdx]); + fluidState.setPressure(nPhaseIdx, priVars[pressureIdx] + pC); + } + else if (formulation == pnSw) { + fluidState.setPressure(nPhaseIdx, priVars[pressureIdx]); + fluidState.setPressure(wPhaseIdx, priVars[pressureIdx] - pC); + } + else DUNE_THROW(Dune::InvalidStateException, "Formulation: " << formulation << " is invalid."); + + ///////////// + // calculate the phase compositions + ///////////// + typename FluidSystem::ParameterCache paramCache; + + // now comes the tricky part: calculate phase compositions + if (phasePresence == bothPhases) { + // both phases are present, phase compositions are a + // result of the the nonwetting <-> wetting equilibrium. This is + // the job of the "MiscibleMultiPhaseComposition" + // constraint solver + MiscibleMultiPhaseComposition::solve(fluidState, + paramCache, + /*setViscosity=*/true, + /*setInternalEnergy=*/false); + + } + else if (phasePresence == nPhaseOnly) { + // only the nonwetting phase is present, i.e. nonwetting phase + // composition is stored explicitly. + + // extract _mass_ fractions in the nonwetting phase + Scalar massFractionN[numComponents]; + massFractionN[wCompIdx] = priVars[switchIdx]; + massFractionN[nCompIdx] = 1 - massFractionN[wCompIdx]; + + // calculate average molar mass of the nonwetting phase + Scalar M1 = FluidSystem::molarMass(wCompIdx); + Scalar M2 = FluidSystem::molarMass(nCompIdx); + Scalar X2 = massFractionN[nCompIdx]; + Scalar avgMolarMass = M1*M2/(M2 + X2*(M1 - M2)); + + // convert mass to mole fractions and set the fluid state + fluidState.setMoleFraction(nPhaseIdx, wCompIdx, massFractionN[wCompIdx]*avgMolarMass/M1); + fluidState.setMoleFraction(nPhaseIdx, nCompIdx, massFractionN[nCompIdx]*avgMolarMass/M2); + + // calculate the composition of the remaining phases (as + // well as the densities of all phases). this is the job + // of the "ComputeFromReferencePhase" constraint solver + ComputeFromReferencePhase::solve(fluidState, + paramCache, + nPhaseIdx, + /*setViscosity=*/true, + /*setInternalEnergy=*/false); + } + else if (phasePresence == wPhaseOnly) { + // only the wetting phase is present, i.e. wetting phase + // composition is stored explicitly. + + // extract _mass_ fractions in the nonwetting phase + Scalar massFractionW[numComponents]; + massFractionW[nCompIdx] = priVars[switchIdx]; + massFractionW[wCompIdx] = 1 - massFractionW[nCompIdx]; + + // calculate average molar mass of the nonwetting phase + Scalar M1 = FluidSystem::molarMass(wCompIdx); + Scalar M2 = FluidSystem::molarMass(nCompIdx); + Scalar X2 = massFractionW[nCompIdx]; + Scalar avgMolarMass = M1*M2/(M2 + X2*(M1 - M2)); + + // convert mass to mole fractions and set the fluid state + fluidState.setMoleFraction(wPhaseIdx, wCompIdx, massFractionW[wCompIdx]*avgMolarMass/M1); + fluidState.setMoleFraction(wPhaseIdx, nCompIdx, massFractionW[nCompIdx]*avgMolarMass/M2); + + // calculate the composition of the remaining phases (as + // well as the densities of all phases). this is the job + // of the "ComputeFromReferencePhase" constraint solver + ComputeFromReferencePhase::solve(fluidState, + paramCache, + wPhaseIdx, + /*setViscosity=*/true, + /*setInternalEnergy=*/false); + } + + for (int phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) { + // compute and set the enthalpy + Scalar h = Implementation::enthalpy_(fluidState, paramCache, phaseIdx); + fluidState.setEnthalpy(phaseIdx, h); + } + } + + /*! + * \brief Returns the phase state for the control-volume. + */ + const FluidState &fluidState() const + { return fluidState_; } + + /*! + * \brief Returns the effective saturation of a given phase within + * the control volume. + * + * \param phaseIdx The phase index + */ + Scalar saturation(const int phaseIdx) const + { return fluidState_.saturation(phaseIdx); } + + /*! + * \brief Returns the mass density of a given phase within the + * control volume. + * + * \param phaseIdx The phase index + */ + Scalar density(const int phaseIdx) const + { return fluidState_.density(phaseIdx); } + + /*! + * \brief Returns the mass density of a given phase within the + * control volume. + * + * \param phaseIdx The phase index + */ + Scalar molarDensity(const int phaseIdx) const + { return fluidState_.density(phaseIdx) / fluidState_.averageMolarMass(phaseIdx); } + + /*! + * \brief Returns the effective pressure of a given phase within + * the control volume. + * + * \param phaseIdx The phase index + */ + Scalar pressure(const int phaseIdx) const + { return fluidState_.pressure(phaseIdx); } + + /*! + * \brief Returns temperature inside the sub-control volume. + * + * Note that we assume thermodynamic equilibrium, i.e. the + * temperature of the rock matrix and of all fluid phases are + * identical. + */ + Scalar temperature() const + { return fluidState_.temperature(/*phaseIdx=*/0); } + + /*! + * \brief Returns the relative permeability of a given phase within + * the control volume. + * + * \param phaseIdx The phase index + */ + Scalar relativePermeability(const int phaseIdx) const + { + return relativePermeability_[phaseIdx]; + } + + /*! + * \brief Returns the effective mobility of a given phase within + * the control volume. + * + * \param phaseIdx The phase index + */ + Scalar mobility(const int phaseIdx) const + { + return relativePermeability_[phaseIdx]/fluidState_.viscosity(phaseIdx); + } + + /*! + * \brief Returns the effective capillary pressure within the control volume. + */ + Scalar capillaryPressure() const + { return fluidState_.pressure(nPhaseIdx) - fluidState_.pressure(wPhaseIdx); } + + /*! + * \brief Returns the average porosity within the control volume. + */ + Scalar porosity() const + { return porosity_; } + + /*! + * \brief Returns the binary diffusion coefficients for a phase + */ + Scalar diffCoeff(int phaseIdx) const + { return diffCoeff_[phaseIdx]; } + + +protected: + static Scalar temperature_(const PrimaryVariables &priVars, + const Problem& problem, + const Element &element, + const FVElementGeometry &fvGeometry, + int scvIdx) + { + return problem.boxTemperature(element, fvGeometry, scvIdx); + } + + template<class ParameterCache> + static Scalar enthalpy_(const FluidState& fluidState, + const ParameterCache& paramCache, + const int phaseIdx) + { + return 0; + } + + /*! + * \brief Called by update() to compute the energy related quantities + */ + void updateEnergy_(const PrimaryVariables &sol, + const Problem &problem, + const Element &element, + const FVElementGeometry &fvGeometry, + const int vertIdx, + bool isOldSol) + { } + + Scalar porosity_; //!< Effective porosity within the control volume + Scalar relativePermeability_[numPhases]; //!< Relative permeability within the control volume + Scalar diffCoeff_[numPhases]; //!< Binary diffusion coefficients for the phases + FluidState fluidState_; + +private: + Implementation &asImp_() + { return *static_cast<Implementation*>(this); } + + const Implementation &asImp_() const + { return *static_cast<const Implementation*>(this); } + + +}; + +} // end namespace + +#endif diff --git a/dumux/implicit/2p2c/Makefile.am b/dumux/implicit/2p2c/Makefile.am new file mode 100644 index 0000000000..f15c521752 --- /dev/null +++ b/dumux/implicit/2p2c/Makefile.am @@ -0,0 +1,4 @@ +2p2cdir = $(includedir)/dumux/boxmodels/2p2c +2p2c_HEADERS = *.hh + +include $(top_srcdir)/am/global-rules diff --git a/dumux/implicit/2p2cni/2p2cnifluxvariables.hh b/dumux/implicit/2p2cni/2p2cnifluxvariables.hh new file mode 100644 index 0000000000..ca2b2a55f0 --- /dev/null +++ b/dumux/implicit/2p2cni/2p2cnifluxvariables.hh @@ -0,0 +1,146 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \file + * + * \brief This file contains data which is required to calculate + * the heat fluxes over a face of a finite volume. + * + * This means temperature gradients and the normal matrix + * heat flux. + */ +#ifndef DUMUX_2P2CNI_FLUX_VARIABLES_HH +#define DUMUX_2P2CNI_FLUX_VARIABLES_HH + +#include <dumux/common/math.hh> +#include <dumux/boxmodels/2p2c/2p2cfluxvariables.hh> + +namespace Dumux +{ + +/*! + * \ingroup TwoPTwoCNIModel + * \ingroup BoxFluxVariables + * \brief This template class contains data which is required to + * calculate the heat fluxes over a face of a finite + * volume for the non-isothermal two-phase, two-component model. + * The mass fluxes are computed in the parent class. + * + * This means temperature gradients and the normal matrix + * heat flux. + */ +template <class TypeTag> +class TwoPTwoCNIFluxVariables : public TwoPTwoCFluxVariables<TypeTag> +{ + typedef TwoPTwoCFluxVariables<TypeTag> ParentType; + + typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; + typedef typename GET_PROP_TYPE(TypeTag, Problem) Problem; + typedef typename GET_PROP_TYPE(TypeTag, ElementVolumeVariables) ElementVolumeVariables; + typedef typename GET_PROP_TYPE(TypeTag, FVElementGeometry) FVElementGeometry; + + typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; + typedef typename GridView::template Codim<0>::Entity Element; + enum { dimWorld = GridView::dimensionworld }; + typedef Dune::FieldVector<Scalar, dimWorld> DimVector; + +public: + /* + * \brief The constructor + * + * \param problem The problem + * \param element The finite element + * \param elemGeom The finite-volume geometry in the box scheme + * \param faceIdx The local index of the SCV (sub-control-volume) face + * \param elemVolVars The volume variables of the current element + */ + TwoPTwoCNIFluxVariables(const Problem &problem, + const Element &element, + const FVElementGeometry &fvGeometry, + const int faceIdx, + const ElementVolumeVariables &elemVolVars, + bool onBoundary = false) + : ParentType(problem, element, fvGeometry, faceIdx, elemVolVars, onBoundary) + { + faceIdx_ = faceIdx; + + calculateValues_(problem, element, elemVolVars); + } + + /*! + * \brief The total heat flux \f$\mathrm{[J/s]}\f$ due to heat conduction + * of the rock matrix over the sub-control volume face in + * direction of the face normal. + */ + Scalar normalMatrixHeatFlux() const + { return normalMatrixHeatFlux_; } + + DimVector temperatureGradient() const + { return temperatureGrad_; } + +protected: + void calculateValues_(const Problem &problem, + const Element &element, + const ElementVolumeVariables &elemVolVars) + { + // calculate temperature gradient using finite element + // gradients + temperatureGrad_ = 0; + DimVector tmp(0.0); + for (int idx = 0; idx < this->fvGeometry_.numFAP; idx++) + { + tmp = this->face().grad[idx]; + + // index for the element volume variables + int volVarsIdx = this->face().fapIndices[idx]; + + tmp *= elemVolVars[volVarsIdx].temperature(); + temperatureGrad_ += tmp; + } + + // The spatial parameters calculates the actual heat flux vector + if (this->face().i != this->face().j) + problem.spatialParams().matrixHeatFlux(tmp, + *this, + elemVolVars, + temperatureGrad_, + element, + this->fvGeometry_, + faceIdx_); + else // heat flux at outflow boundaries + problem.spatialParams().boundaryMatrixHeatFlux(tmp, + *this, + elemVolVars, + this->face(), + element, + this->fvGeometry_); + + // project the heat flux vector on the face's normal vector + normalMatrixHeatFlux_ = tmp*this->face().normal; + } + +private: + Scalar normalMatrixHeatFlux_; + DimVector temperatureGrad_; + int faceIdx_; +}; + +} // end namespace + +#endif diff --git a/dumux/implicit/2p2cni/2p2cniindices.hh b/dumux/implicit/2p2cni/2p2cniindices.hh new file mode 100644 index 0000000000..4eefa0d505 --- /dev/null +++ b/dumux/implicit/2p2cni/2p2cniindices.hh @@ -0,0 +1,51 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ + +/*! + * \file + * + * \brief Defines the indices used by the 2p2cni box model + */ +#ifndef DUMUX_2P2CNI_INDICES_HH +#define DUMUX_2P2CNI_INDICES_HH + +#include <dumux/boxmodels/2p2c/2p2cindices.hh> + +namespace Dumux +{ +// \{ + +/*! + * \ingroup TwoPTwoCNIModel + * \ingroup BoxIndices + * \brief Enumerations for the non-isothermal 2-phase 2-component model + * + * \tparam formulation The formulation, either pwSn or pnSw. + * \tparam PVOffset The first index in a primary variable vector. + */ +template <class TypeTag, int formulation, int PVOffset> +class TwoPTwoCNIIndices : public TwoPTwoCIndices<TypeTag, formulation, PVOffset> +{ +public: + static const int temperatureIdx = PVOffset + 2; //! The index for temperature in primary variable vectors. + static const int energyEqIdx = PVOffset + 2; //! The index for energy in equation vectors. +}; + +} +#endif diff --git a/dumux/implicit/2p2cni/2p2cnilocalresidual.hh b/dumux/implicit/2p2cni/2p2cnilocalresidual.hh new file mode 100644 index 0000000000..2b1b43f40e --- /dev/null +++ b/dumux/implicit/2p2cni/2p2cnilocalresidual.hh @@ -0,0 +1,175 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \file + * + * \brief Element-wise calculation of the Jacobian matrix for problems + * using the non-isothermal two-phase two-component box model. + * + */ +#ifndef DUMUX_NEW_2P2CNI_LOCAL_RESIDUAL_HH +#define DUMUX_NEW_2P2CNI_LOCAL_RESIDUAL_HH + +#include <dumux/boxmodels/2p2c/2p2clocalresidual.hh> + +#include <dumux/boxmodels/2p2cni/2p2cnivolumevariables.hh> +#include <dumux/boxmodels/2p2cni/2p2cnifluxvariables.hh> +#include <dumux/boxmodels/2p2cni/2p2cniproperties.hh> + +namespace Dumux +{ +/*! + * \ingroup TwoPTwoCNIModel + * \ingroup BoxLocalResidual + * \brief Element-wise calculation of the Jacobian matrix for problems + * using the two-phase two-component box model. + */ +template<class TypeTag> +class TwoPTwoCNILocalResidual : public TwoPTwoCLocalResidual<TypeTag> +{ + typedef TwoPTwoCLocalResidual<TypeTag> ParentType; + + typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; + typedef typename GET_PROP_TYPE(TypeTag, PrimaryVariables) PrimaryVariables; + typedef typename GET_PROP_TYPE(TypeTag, VolumeVariables) VolumeVariables; + typedef typename GET_PROP_TYPE(TypeTag, FluxVariables) FluxVariables; + typedef typename GET_PROP_TYPE(TypeTag, ElementVolumeVariables) ElementVolumeVariables; + + typedef typename GET_PROP_TYPE(TypeTag, Indices) Indices; + enum { + numPhases = GET_PROP_VALUE(TypeTag, NumPhases), + energyEqIdx = Indices::energyEqIdx, + temperatureIdx = Indices::temperatureIdx, + wPhaseIdx = Indices::wPhaseIdx, + nPhaseIdx = Indices::nPhaseIdx + }; + +public: + /*! + * \brief Constructor. Sets the upwind weight. + */ + TwoPTwoCNILocalResidual() + { + // retrieve the upwind weight for the mass conservation equations. Use the value + // specified via the property system as default, and overwrite + // it by the run-time parameter from the Dune::ParameterTree + massUpwindWeight_ = GET_PARAM_FROM_GROUP(TypeTag, Scalar, Implicit, MassUpwindWeight); + }; + + /*! + * \brief Evaluate the amount all conservation quantities + * (e.g. phase mass) within a sub-control volume. + * + * The result should be averaged over the volume (e.g. phase mass + * inside a sub control volume divided by the volume) + * + * \param storage The storage of the conservation quantity (mass or energy) within the sub-control volume + * \param scvIdx The SCV (sub-control-volume) index + * \param usePrevSol Evaluate function with solution of current or previous time step + */ + void computeStorage(PrimaryVariables &storage, const int scvIdx, bool usePrevSol) const + { + // compute the storage term for phase mass + ParentType::computeStorage(storage, scvIdx, usePrevSol); + + // if flag usePrevSol is set, the solution from the previous + // time step is used, otherwise the current solution is + // used. The secondary variables are used accordingly. This + // is required to compute the derivative of the storage term + // using the implicit Euler method. + const ElementVolumeVariables &elemVolVars = usePrevSol ? this->prevVolVars_() : this->curVolVars_(); + const VolumeVariables &volVars = elemVolVars[scvIdx]; + + // compute the energy storage + storage[energyEqIdx] = + volVars.porosity()*(volVars.density(wPhaseIdx) * + volVars.internalEnergy(wPhaseIdx) * + volVars.saturation(wPhaseIdx) + + + volVars.density(nPhaseIdx) * + volVars.internalEnergy(nPhaseIdx) * + volVars.saturation(nPhaseIdx)) + + + // heat capacity is already multiplied by the density + // of the porous material and the porosity in the problem file + volVars.temperature()*volVars.heatCapacity(); + } + + /*! + * \brief Evaluates the advective mass flux and the heat flux + * over a face of a subcontrol volume and writes the result in + * the flux vector. + * + * \param flux The advective flux over the SCV (sub-control-volume) face for each component + * \param fluxVars The flux variables at the current SCV face + * + * This method is called by compute flux (base class) + */ + void computeAdvectiveFlux(PrimaryVariables &flux, + const FluxVariables &fluxVars) const + { + // advective mass flux + ParentType::computeAdvectiveFlux(flux, fluxVars); + + // advective heat flux in all phases + flux[energyEqIdx] = 0; + for (int phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) { + // vertex data of the upstream and the downstream vertices + const VolumeVariables &up = this->curVolVars_(fluxVars.upstreamIdx(phaseIdx)); + const VolumeVariables &dn = this->curVolVars_(fluxVars.downstreamIdx(phaseIdx)); + + flux[energyEqIdx] += + fluxVars.volumeFlux(phaseIdx) * (massUpwindWeight_ * // upstream vertex + ( up.density(phaseIdx) * + up.enthalpy(phaseIdx)) + + + (1-massUpwindWeight_) * // downstream vertex + ( dn.density(phaseIdx) * + dn.enthalpy(phaseIdx)) ); + } + } + + /*! + * \brief Adds the diffusive heat flux to the flux vector over + * the face of a sub-control volume. + * + * \param flux The diffusive flux over the SCV (sub-control-volume) face for each conservation quantity (mass, energy) + * \param fluxVars The flux variables at the current SCV face + * + * This method is called by compute flux (base class) + */ + void computeDiffusiveFlux(PrimaryVariables &flux, + const FluxVariables &fluxVars) const + { + // diffusive mass flux + ParentType::computeDiffusiveFlux(flux, fluxVars); + + // diffusive heat flux + flux[temperatureIdx] += + fluxVars.normalMatrixHeatFlux(); + } + +private: + Scalar massUpwindWeight_; + +}; + +} + +#endif diff --git a/dumux/implicit/2p2cni/2p2cnimodel.hh b/dumux/implicit/2p2cni/2p2cnimodel.hh new file mode 100644 index 0000000000..6dac22eb31 --- /dev/null +++ b/dumux/implicit/2p2cni/2p2cnimodel.hh @@ -0,0 +1,98 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \file + * + * \brief Adaption of the BOX scheme to the non-isothermal two-phase two-component flow model. + */ +#ifndef DUMUX_NEW_2P2CNI_MODEL_HH +#define DUMUX_NEW_2P2CNI_MODEL_HH + +#include <dumux/boxmodels/2p2c/2p2cmodel.hh> + +namespace Dumux { +/*! + * \ingroup TwoPTwoCNIModel + * \brief Adaption of the BOX scheme to the non-isothermal two-phase two-component flow model. + * + * This model implements a non-isothermal two-phase flow of two compressible and partly miscible fluids + * \f$\alpha \in \{ w, n \}\f$. Thus each component \f$\kappa \{ w, a \}\f$ can be present in + * each phase. + * Using the standard multiphase Darcy approach a mass balance equation is + * solved: + * \f{eqnarray*} + && \phi \frac{\partial (\sum_\alpha \varrho_\alpha X_\alpha^\kappa S_\alpha )}{\partial t} + - \sum_\alpha \text{div} \left\{ \varrho_\alpha X_\alpha^\kappa + \frac{k_{r\alpha}}{\mu_\alpha} \mbox{\bf K} + (\text{grad}\, p_\alpha - \varrho_{\alpha} \mbox{\bf g}) \right\}\\ + &-& \sum_\alpha \text{div} \left\{{\bf D}_{\alpha, pm}^\kappa \varrho_{\alpha} \text{grad}\, X^\kappa_{\alpha} \right\} + - \sum_\alpha q_\alpha^\kappa = 0 \qquad \kappa \in \{w, a\} \, , + \alpha \in \{w, n\} + * \f} + * For the energy balance, local thermal equilibrium is assumed which results in one + * energy conservation equation for the porous solid matrix and the fluids: + * \f{eqnarray*} + && \phi \frac{\partial \left( \sum_\alpha \varrho_\alpha u_\alpha S_\alpha \right)}{\partial t} + + \left( 1 - \phi \right) \frac{\partial (\varrho_s c_s T)}{\partial t} + - \sum_\alpha \text{div} \left\{ \varrho_\alpha h_\alpha + \frac{k_{r\alpha}}{\mu_\alpha} \mathbf{K} \left( \text{grad}\, + p_\alpha + - \varrho_\alpha \mathbf{g} \right) \right\} \\ + &-& \text{div} \left( \lambda_{pm} \text{grad} \, T \right) + - q^h = 0 \qquad \alpha \in \{w, n\} + \f} + * + * This is discretized using a fully-coupled vertex + * centered finite volume (box) scheme as spatial and + * the implicit Euler method as temporal discretization. + * + * By using constitutive relations for the capillary pressure \f$p_c = + * p_n - p_w\f$ and relative permeability \f$k_{r\alpha}\f$ and taking + * advantage of the fact that \f$S_w + S_n = 1\f$ and \f$X^\kappa_w + X^\kappa_n = 1\f$, the number of + * unknowns can be reduced to two. + * If both phases are present the primary variables are, like in the nonisothermal two-phase model, either \f$p_w\f$, \f$S_n\f$ and + * temperature or \f$p_n\f$, \f$S_w\f$ and temperature. The formulation which ought to be used can be + * specified by setting the <tt>Formulation</tt> property to either + * <tt>TwoPTwoCIndices::pWsN</tt> or <tt>TwoPTwoCIndices::pNsW</tt>. By + * default, the model uses \f$p_w\f$ and \f$S_n\f$. + * In case that only one phase (nonwetting or wetting phase) is present the second primary + * variable represents a mass fraction. The correct assignment of the second + * primary variable is performed by a phase state dependent primary variable switch. The phase state is stored for all nodes of the system. The following cases can be distinguished: + * <ul> + * <li> + * Both phases are present: The saturation is used (either\f$S_n\f$ or \f$S_w\f$, dependent on the chosen formulation). + * </li> + * <li> + * Only wetting phase is present: The mass fraction of air in the wetting phase \f$X^a_w\f$ is used. + * </li> + * <li> + * Only non-wetting phase is present: The mass fraction of water in the non-wetting phase, \f$X^w_n\f$, is used. + * </li> + * </ul> + */ +template<class TypeTag> +class TwoPTwoCNIModel : public TwoPTwoCModel<TypeTag> +{ +}; + +} + +#include "2p2cnipropertydefaults.hh" + +#endif diff --git a/dumux/implicit/2p2cni/2p2cniproperties.hh b/dumux/implicit/2p2cni/2p2cniproperties.hh new file mode 100644 index 0000000000..6e221dea1e --- /dev/null +++ b/dumux/implicit/2p2cni/2p2cniproperties.hh @@ -0,0 +1,47 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \ingroup Properties + * \ingroup BoxProperties + * \ingroup TwoPTwoCNIModel + * \file + * + * \brief Defines the properties required for the non-isothermal two-phase, + * two-component BOX model. + */ +#ifndef DUMUX_2P2CNI_PROPERTIES_HH +#define DUMUX_2P2CNI_PROPERTIES_HH + +#include <dumux/boxmodels/2p2c/2p2cproperties.hh> + +namespace Dumux +{ + +namespace Properties +{ +////////////////////////////////////////////////////////////////// +// Type tags +////////////////////////////////////////////////////////////////// + +//! The type tag for the non-isothermal two-phase, two-component problems +NEW_TYPE_TAG(BoxTwoPTwoCNI, INHERITS_FROM(BoxTwoPTwoC)); +} +} + +#endif diff --git a/dumux/implicit/2p2cni/2p2cnipropertydefaults.hh b/dumux/implicit/2p2cni/2p2cnipropertydefaults.hh new file mode 100644 index 0000000000..efc97a991c --- /dev/null +++ b/dumux/implicit/2p2cni/2p2cnipropertydefaults.hh @@ -0,0 +1,76 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \ingroup Properties + * \ingroup BoxProperties + * \ingroup TwoPTwoCNIModel + * \file + * + * \brief Defines default values for most properties required by the 2p2cni + * box model. + */ +#ifndef DUMUX_2P2CNI_PROPERTY_DEFAULTS_HH +#define DUMUX_2P2CNI_PROPERTY_DEFAULTS_HH + +#include <dumux/boxmodels/2p2c/2p2cpropertydefaults.hh> + +#include "2p2cnimodel.hh" +#include "2p2cniindices.hh" +#include "2p2cnilocalresidual.hh" +#include "2p2cnivolumevariables.hh" +#include "2p2cnifluxvariables.hh" + +namespace Dumux +{ + +namespace Properties +{ +////////////////////////////////////////////////////////////////// +// Property values +////////////////////////////////////////////////////////////////// + +SET_INT_PROP(BoxTwoPTwoCNI, NumEq, 3); //!< set the number of equations to 3 + +//! Use the 2p2cni local jacobian operator for the 2p2cni model +SET_TYPE_PROP(BoxTwoPTwoCNI, + LocalResidual, + TwoPTwoCNILocalResidual<TypeTag>); + +//! the Model property +SET_TYPE_PROP(BoxTwoPTwoCNI, Model, TwoPTwoCNIModel<TypeTag>); + +//! the VolumeVariables property +SET_TYPE_PROP(BoxTwoPTwoCNI, VolumeVariables, TwoPTwoCNIVolumeVariables<TypeTag>); + + +//! the FluxVariables property +SET_TYPE_PROP(BoxTwoPTwoCNI, FluxVariables, TwoPTwoCNIFluxVariables<TypeTag>); + +//! The indices required by the non-isothermal 2p2c model +SET_PROP(BoxTwoPTwoCNI, Indices) +{ private: + enum { formulation = GET_PROP_VALUE(TypeTag, Formulation) }; + public: + typedef TwoPTwoCNIIndices<TypeTag, formulation, 0> type; +}; + +} + +} +#endif diff --git a/dumux/implicit/2p2cni/2p2cnivolumevariables.hh b/dumux/implicit/2p2cni/2p2cnivolumevariables.hh new file mode 100644 index 0000000000..c6712a89e0 --- /dev/null +++ b/dumux/implicit/2p2cni/2p2cnivolumevariables.hh @@ -0,0 +1,144 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \file + * + * \brief Contains the quantities which are constant within a + * finite volume in the non-isothermal two-phase, two-component + * model. + */ +#ifndef DUMUX_2P2CNI_VOLUME_VARIABLES_HH +#define DUMUX_2P2CNI_VOLUME_VARIABLES_HH + +#include <dumux/boxmodels/2p2c/2p2cvolumevariables.hh> + +namespace Dumux +{ + +/*! + * \ingroup TwoPTwoCNIModel + * \ingroup BoxVolumeVariables + * \brief Contains the quantities which are are constant within a + * finite volume in the non-isothermal two-phase, two-component + * model. + */ +template <class TypeTag> +class TwoPTwoCNIVolumeVariables : public TwoPTwoCVolumeVariables<TypeTag> +{ + //! \cond 0 + typedef TwoPTwoCVolumeVariables<TypeTag> ParentType; + typedef typename ParentType::FluidState FluidState; + + typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; + typedef typename GET_PROP_TYPE(TypeTag, FVElementGeometry) FVElementGeometry; + typedef typename GET_PROP_TYPE(TypeTag, Problem) Problem; + typedef typename GET_PROP_TYPE(TypeTag, PrimaryVariables) PrimaryVariables; + typedef typename GET_PROP_TYPE(TypeTag, FluidSystem) FluidSystem; + + typedef typename GET_PROP_TYPE(TypeTag, Indices) Indices; + enum { temperatureIdx = Indices::temperatureIdx }; + + typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; + typedef typename GridView::template Codim<0>::Entity Element; + //! \endcond + +public: + /*! + * \brief Returns the total internal energy of a phase in the + * sub-control volume. + * + * \param phaseIdx The phase index + */ + Scalar internalEnergy(const int phaseIdx) const + { return this->fluidState_.internalEnergy(phaseIdx); }; + + /*! + * \brief Returns the total enthalpy of a phase in the sub-control + * volume. + * + * \param phaseIdx The phase index + */ + Scalar enthalpy(const int phaseIdx) const + { return this->fluidState_.enthalpy(phaseIdx); }; + + /*! + * \brief Returns the total heat capacity \f$\mathrm{[J/(K*m^3]}\f$ of the rock matrix in + * the sub-control volume. + */ + Scalar heatCapacity() const + { return heatCapacity_; }; + + /*! + * \brief Returns the thermal conductivity \f$\mathrm{[W/(m*K)]}\f$ of the fluid phase in + * the sub-control volume. + */ + Scalar thermalConductivity(const int phaseIdx) const + { return FluidSystem::thermalConductivity(this->fluidState_, phaseIdx); }; + + +protected: + // this method gets called by the parent class. since this method + // is protected, we are friends with our parent.. + friend class TwoPTwoCVolumeVariables<TypeTag>; + + static Scalar temperature_(const PrimaryVariables &priVars, + const Problem& problem, + const Element &element, + const FVElementGeometry &fvGeometry, + const int scvIdx) + { + return priVars[temperatureIdx]; + } + + template<class ParameterCache> + static Scalar enthalpy_(const FluidState& fluidState, + const ParameterCache& paramCache, + const int phaseIdx) + { + return FluidSystem::enthalpy(fluidState, paramCache, phaseIdx); + } + + /*! + * \brief Update all quantities for a given control volume. + * + * \param sol The solution primary variables + * \param problem The problem + * \param element The element + * \param fvGeometry The current finite volume geometry of the element + * \param scvIdx The local index of the SCV (sub-control volume) + * \param isOldSol Evaluate function with solution of current or previous time step + */ + void updateEnergy_(const PrimaryVariables &sol, + const Problem &problem, + const Element &element, + const FVElementGeometry &fvGeometry, + const int scvIdx, + bool isOldSol) + { + // compute and set the heat capacity of the solid phase + heatCapacity_ = problem.spatialParams().heatCapacity(element, fvGeometry, scvIdx); + Valgrind::CheckDefined(heatCapacity_); + }; + + Scalar heatCapacity_; +}; + +} // end namespace + +#endif diff --git a/dumux/implicit/2p2cni/Makefile.am b/dumux/implicit/2p2cni/Makefile.am new file mode 100644 index 0000000000..79135402f3 --- /dev/null +++ b/dumux/implicit/2p2cni/Makefile.am @@ -0,0 +1,4 @@ +2p2cnidir = $(includedir)/dumux/boxmodels/2p2cni +2p2cni_HEADERS = *.hh + +include $(top_srcdir)/am/global-rules diff --git a/dumux/implicit/2pdfm/2pdfmfluxvariables.hh b/dumux/implicit/2pdfm/2pdfmfluxvariables.hh new file mode 100644 index 0000000000..93ca3137d6 --- /dev/null +++ b/dumux/implicit/2pdfm/2pdfmfluxvariables.hh @@ -0,0 +1,234 @@ +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \file + * + * \brief This file contains the data which is required to calculate + * all fluxes of fluid phases over a face of a finite volume in the + * two phase discrete fracture-matrix model. + */ +#ifndef DUMUX_BOXMODELS_2PDFM_FLUX_VARIABLES_HH +#define DUMUX_BOXMODELS_2PDFM_FLUX_VARIABLES_HH + +#include <dumux/common/math.hh> +#include <dumux/common/parameters.hh> +#include <dumux/common/valgrind.hh> +#include <dumux/boxmodels/common/boxdarcyfluxvariables.hh> + +#include "2pdfmproperties.hh" + +namespace Dumux +{ + +/*! + * \ingroup TwoPDFMBoxModel + * \ingroup BoxDFMFluxVariables + * \brief Contains the data which is required to calculate the fluxes of + * the fluid phases over a face of a finite volume for the two-phase + * discrete fracture-matrix model. + * + * This means pressure and concentration gradients, phase densities at + * the intergration point, etc. + */ +template <class TypeTag> +class TwoPDFMFluxVariables : public BoxDarcyFluxVariables<TypeTag> +{ + typedef typename GET_PROP_TYPE(TypeTag, Problem) Problem; + typedef typename GET_PROP_TYPE(TypeTag, SpatialParams) SpatialParams; + typedef typename GET_PROP_TYPE(TypeTag, ElementVolumeVariables) ElementVolumeVariables; + + typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; + typedef typename GridView::template Codim<0>::Entity Element; + enum { + dim = GridView::dimension, + dimWorld = GridView::dimensionworld, + numPhases = GET_PROP_VALUE(TypeTag, NumPhases) + }; + + typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; + typedef Dune::FieldVector<Scalar, dimWorld> Vector; + + typedef typename GET_PROP_TYPE(TypeTag, FVElementGeometry) FVElementGeometry; + typedef typename FVElementGeometry::SubControlVolumeFace SCVFace; + typedef Dune::FieldVector<Scalar, dimWorld> GlobalPosition; + typedef Dune::FieldVector<Scalar, dim> LocalPosition; + typedef Dune::FieldVector<Scalar, numPhases> PhasesVector; + +public: + /*! + * \brief The constructor + * + * \param problem The problem + * \param element The finite element + * \param fvGeometry The finite-volume geometry in the box scheme + * \param faceIdx The local index of the SCV (sub-control-volume) face + * \param elemVolVars The volume variables of the current element + * \param onBoundary A boolean variable to specify whether the flux variables + * are calculated for interior SCV faces or boundary faces, default=false + */ + TwoPDFMFluxVariables(const Problem &problem, + const Element &element, + const FVElementGeometry &fvGeometry, + int faceIdx, + const ElementVolumeVariables &elemVolVars, + const bool onBoundary = false) + : BoxDarcyFluxVariables<TypeTag>(problem, element, fvGeometry, faceIdx, elemVolVars, onBoundary), + fvGeometry_(fvGeometry), faceIdx_(faceIdx), onBoundary_(onBoundary) + { + faceSCV_ = &this->face(); + + for (int phase = 0; phase < numPhases; ++phase) + { + potentialGradFracture_[phase] = 0.0; + } + + calculateGradientsInFractures_(problem, element, elemVolVars, faceIdx); + calculateVelocitiesFracture_(problem, element, elemVolVars, faceIdx); + }; + +public: + /*! + * \brief Calculates the velocities in the lower-dimenstional fracture. + * + * \param problem The problem + * \param element The finite element + * \param elemVolVars The volume variables of the current element + * \param faceIdx The local index of the SCV (sub-control-volume) face + */ + void calculateVelocitiesFracture_(const Problem &problem, + const Element &element, + const ElementVolumeVariables &elemVolVars, + int faceIdx) + { + isFracture_ = problem.spatialParams().isEdgeFracture(element, faceIdx); + fractureWidth_ = problem.spatialParams().fractureWidth(element, faceIdx); + + Scalar KFracture, KFi, KFj; //permeabilities of the fracture + if (isFracture_) + { + KFi = problem.spatialParams(). + intrinsicPermeabilityFracture(element, this->fvGeometry_, this->face().i); + KFj = problem.spatialParams(). + intrinsicPermeabilityFracture(element, this->fvGeometry_, this->face().j); + } + else + { + KFi = 0; + KFj = 0; + } + + KFracture = Dumux::harmonicMean(KFi, KFj); + + // temporary vector for the Darcy velocity + Scalar vDarcyFracture = 0; + for (int phase=0; phase < numPhases; phase++) + { + vDarcyFracture = - (KFracture * potentialGradFracture_[phase]); + + Valgrind::CheckDefined(KFracture); + Valgrind::CheckDefined(fractureWidth_); + vDarcyFracture_[phase] = (vDarcyFracture * fractureWidth_); + Valgrind::CheckDefined(vDarcyFracture_[phase]); + } + + // set the upstream and downstream vertices + for (int phase = 0; phase < numPhases; ++phase) + { + upstreamFractureIdx[phase] = faceSCV_->i; + downstreamFractureIdx[phase] = faceSCV_->j; + + if (vDarcyFracture_[phase] < 0) + { + std::swap(upstreamFractureIdx[phase], + downstreamFractureIdx[phase]); + } + } + } + + /*! + * \brief Return the pressure potential gradient in the lower dimensional fracture. + * + * \param phaseIdx The index of the fluid phase + */ + const Scalar &potentialGradFracture(int phaseIdx) const + { + return potentialGradFracture_[phaseIdx]; + } + + + PhasesVector vDarcyFracture_; + + int upstreamFractureIdx[numPhases]; + int downstreamFractureIdx[numPhases]; +protected: + // gradients + Scalar potentialGradFracture_[numPhases]; + const FVElementGeometry &fvGeometry_; + int faceIdx_; + const bool onBoundary_; + bool isFracture_; + Scalar fractureWidth_; + const SCVFace *faceSCV_; + +private: + /*! + * \brief Calculates the gradients in the lower-dimenstional fracture. + * + * \param problem The problem + * \param element The finite element + * \param elemVolVars The volume variables of the current element + * \param faceIdx The local index of the SCV (sub-control-volume) face + */ + void calculateGradientsInFractures_(const Problem &problem, + const Element &element, + const ElementVolumeVariables &elemVolVars, + int faceIdx) + { + // calculate gradients, loop over adjacent vertices + for (int idx = 0; idx < this->fvGeometry_.numFAP; idx++) + { + int i = this->face().i; + int j = this->face().j; + + // compute sum of pressure gradients for each phaseGlobalPosition + for (int phase = 0; phase < numPhases; phase++) + { + const GlobalPosition localIdx_i = element.geometry().corner(i); + const GlobalPosition localIdx_j = element.geometry().corner(j); + + isFracture_ = problem.spatialParams().isEdgeFracture(element, faceIdx); + + if (isFracture_) + { + GlobalPosition diff_ij = localIdx_j; + diff_ij -= localIdx_i; + potentialGradFracture_[phase] = + (elemVolVars[j].pressure(phase) - elemVolVars[i].pressure(phase)) + / diff_ij.two_norm(); + } + else + { + potentialGradFracture_[phase] = 0; + } + } + } + } +}; + +} // end namepace + +#endif // DUMUX_BOXMODELS_2PDFM_FLUX_VARIABLES_HH diff --git a/dumux/implicit/2pdfm/2pdfmindices.hh b/dumux/implicit/2pdfm/2pdfmindices.hh new file mode 100644 index 0000000000..6326b0508f --- /dev/null +++ b/dumux/implicit/2pdfm/2pdfmindices.hh @@ -0,0 +1,64 @@ +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ + +/*! + * \file + * + * \brief Defines the indices required for the finite volume in the + * two phase discrete fracture-matrix model. + */ +#ifndef DUMUX_BOXMODELS_2PDFM_INDICES_HH +#define DUMUX_BOXMODELS_2PDFM_INDICES_HH + +#include<dumux/boxmodels/2p/2pindices.hh> + +namespace Dumux +{ +// \{ + +/*! + * \ingroup TwoPDFMBoxModel + * \ingroup BoxIndices + * \brief The common indices for the \f$p_w-S_n\f$ formulation of the + * isothermal two-phase discrete fracture-matrix model. + * + * \tparam TypeTag The problem type tag + * \tparam formulation The formulation, either pwSn or pnSw + * \tparam PVOffset The first index in a primary variable vector. + */ +template <class TypeTag, + int formulation = TwoPFormulation::pwSn, + int PVOffset = 0> +struct TwoPDFMIndices : public TwoPIndices <TypeTag, formulation, PVOffset> +{ + // Primary variable indices + static const int pressureIdx = PVOffset + 0; //!< Index for wetting/non-wetting phase pressure (depending on formulation) in a solution vector + static const int saturationIdx = PVOffset + 1; //!< Index of the saturation of the non-wetting/wetting phase + + // indices of the primary variables + static const int pwIdx = PVOffset + 0; //!< Pressure index of the wetting phase + static const int SnIdx = PVOffset + 1; //!< Saturation index of the wetting phase + + // indices of the equations + static const int contiWEqIdx = PVOffset + 0; //!< Index of the continuity equation of the wetting phase + static const int contiNEqIdx = PVOffset + 1; //!< Index of the continuity equation of the non-wetting phase +}; + +// \} +} // namespace Dumux + +#endif // DUMUX_BOXMODELS_2PDFM_INDICES_HH diff --git a/dumux/implicit/2pdfm/2pdfmlocalresidual.hh b/dumux/implicit/2pdfm/2pdfmlocalresidual.hh new file mode 100644 index 0000000000..3ab534a7b2 --- /dev/null +++ b/dumux/implicit/2pdfm/2pdfmlocalresidual.hh @@ -0,0 +1,335 @@ +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \file + * + * \brief Element-wise calculation of the residual for the finite volume in the + * two phase discrete fracture-matrix model. + */ +#ifndef DUMUX_BOXMODELS_2PDFM_LOCAL_RESIDUAL_HH +#define DUMUX_BOXMODELS_2PDFM_LOCAL_RESIDUAL_HH + +#include <dumux/boxmodels/2p/2plocalresidual.hh> +#include "2pdfmproperties.hh" + +namespace Dumux +{ +/*! + * \ingroup TwoPDFMBoxModel + * \ingroup BoxLocalResidual + * \brief Element-wise calculation of the Jacobian matrix for problems + * using the two-phase discrete fracture box model. + */ +template<class TypeTag> +class TwoPDFMLocalResidual : public TwoPLocalResidual<TypeTag> +{ +protected: + typedef TwoPLocalResidual<TypeTag> ParentType; + typedef typename GET_PROP_TYPE(TypeTag, LocalResidual) Implementation; + typedef typename GET_PROP_TYPE(TypeTag, PrimaryVariables) PrimaryVariables; + typedef typename GET_PROP_TYPE(TypeTag, VolumeVariables) VolumeVariables; + typedef typename GET_PROP_TYPE(TypeTag, FluxVariables) FluxVariables; + typedef typename GET_PROP_TYPE(TypeTag, ElementVolumeVariables) ElementVolumeVariables; + + typedef typename GET_PROP_TYPE(TypeTag, Indices) Indices; + enum + { + contiWEqIdx = Indices::contiWEqIdx, + contiNEqIdx = Indices::contiNEqIdx, + wPhaseIdx = Indices::wPhaseIdx, + nPhaseIdx = Indices::nPhaseIdx, + numPhases = GET_PROP_VALUE(TypeTag, NumPhases) + }; + typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; + typedef Dune::UGGrid<2> GridType; + typedef typename GridType::ctype DT; + enum { + dim = GridView::dimension, + dimWorld = GridView::dimensionworld + }; + typedef typename GridView::template Codim<0>::Entity Element; + typedef typename GridView::template Codim<0>::Iterator ElementIterator; + typedef typename GridView::template Codim<dim>::Entity Vertex; + typedef typename GridView::template Codim<dim>::Iterator VertexIterator; + typedef typename GET_PROP_TYPE(TypeTag, FVElementGeometry) FVElementGeometry; + typedef typename FVElementGeometry::SubControlVolumeFace SCVFace; + + typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; + typedef Dune::FieldVector<Scalar, dimWorld> Vector; + + typedef Dune::FieldVector<Scalar, dim> LocalPosition; + typedef Dune::FieldVector<Scalar, dimWorld> GlobalPosition; + +public: + /*! + * \brief Constructor. Sets the upwind weight. + */ + TwoPDFMLocalResidual() + { + // retrieve the upwind weight for the mass conservation equations. Use the value + // specified via the property system as default, and overwrite + // it by the run-time parameter from the Dune::ParameterTree + massUpwindWeight_ = GET_PARAM_FROM_GROUP(TypeTag, Scalar, Implicit, MassUpwindWeight); + }; + + /*! + * \brief Evaluate the amount all conservation quantities + * (e.g. phase mass) within a finite sub-control volume. + * + * \param storage The phase mass within the sub-control volume + * \param scvIdx The SCV (sub-control-volume) index + * \param usePrevSol Evaluate function with solution of current or previous time step + */ + void computeStorage(PrimaryVariables &storage, int scvIdx, bool usePrevSol) const + { + // if flag usePrevSol is set, the solution from the previous + // time step is used, otherwise the current solution is + // used. The secondary variables are used accordingly. This + // is required to compute the derivative of the storage term + // using the implicit Euler method. + ParentType::computeStorage(storage,scvIdx,usePrevSol); + computeStorageFracture(storage,scvIdx,usePrevSol); + } + + /*! + * \brief Evaluate the storage term of the current solution in a + * lower-dimensional fracture. + * + * \param storage The phase mass within the sub-control volume + * \param scvIdx The SCV (sub-control-volume) index + * \param usePrevSol Evaluate function with solution of current or previous time step + */ + void computeStorageFracture(PrimaryVariables &storage, int scvIdx, bool usePrevSol) const + { + const ElementVolumeVariables &elemDat = usePrevSol ? this->prevVolVars_() + : this->curVolVars_(); + const VolumeVariables &vertDat = elemDat[scvIdx]; + const Element &elem = this->element_(); + bool isFracture = this->problem_().spatialParams().isVertexFracture(elem, scvIdx); + /* + * Sn = w_F * SnF + w_M * SnM + * First simple case before determining the real w_F is to assume that it is 0 + * and w_M = 1 + * + */ + /////////////////////////////////////////////////////////////////////// + Scalar w_F, w_M; //volumetric fractions of fracture and matrix; + Scalar fractureVolume = 0.0; + w_F = 0.0; + /* + * Calculate the fracture volume fraction w_F = 0.5 * Fwidth * 0.5 * Length + */ + Dune::GeometryType gt = elem.geometry().type(); + const typename Dune::GenericReferenceElementContainer<DT,dim>::value_type& + refElem = Dune::GenericReferenceElements<DT,dim>::general(gt); + + Scalar vol; //subcontrol volume + FVElementGeometry fvelem = this->fvGeometry_(); + vol = fvelem.subContVol[scvIdx].volume; + for (int faceIdx=0; faceIdx<refElem.size(1); faceIdx++) + { + SCVFace face = fvelem.subContVolFace[faceIdx]; + int i=face.i; + int j=face.j; + + if (this->problem_().spatialParams().isEdgeFracture(elem, faceIdx) + && (i == scvIdx || j == scvIdx)) + { + Scalar fracture_width = this->problem_().spatialParams().fractureWidth(elem, faceIdx); + + const GlobalPosition global_i = elem.geometry().corner(i); + const GlobalPosition global_j = elem.geometry().corner(j); + GlobalPosition diff_ij = global_j; + diff_ij -=global_i; + Scalar fracture_length = 0.5*diff_ij.two_norm(); + //half is taken from the other element + fractureVolume += 0.5 * fracture_length * fracture_width; + } + } + w_F = fractureVolume/vol; + w_M = 1-w_F; + /////////////////////////////////////////////////////////////////////// + Scalar storageFracture[numPhases]; + Scalar storageMatrix [numPhases]; + storageFracture[wPhaseIdx] = 0.0; + storageFracture[nPhaseIdx] = 0.0; + storageMatrix[wPhaseIdx] = 0.0; + storageMatrix[nPhaseIdx] = 0.0; + // const GlobalPosition &globalPos = elem.geometry().corner(scvIdx); + + Scalar dSM_dSF = vertDat.dSM_dSF(); + if (!this->problem_().useInterfaceCondition()) + { + dSM_dSF = 1.0; + } + + if (isFracture) + { + for (int phaseIdx = 0; phaseIdx<2; phaseIdx++) + { + storageFracture[phaseIdx] = vertDat.density(phaseIdx) + * vertDat.porosityFracture() + * w_F + * vertDat.saturationFracture(phaseIdx); + storageMatrix[phaseIdx] = vertDat.density(phaseIdx) + * vertDat.porosity() + * w_M + * dSM_dSF + * vertDat.saturationMatrix(phaseIdx); + } + } + else + { + for (int phaseIdx = 0; phaseIdx < 2; phaseIdx++) + { + storageFracture[phaseIdx] = 0.0; + storageMatrix[phaseIdx] = vertDat.density(phaseIdx) + * vertDat.porosity() + * vertDat.saturation(phaseIdx); + } + + } + // wetting phase mass + storage[contiWEqIdx] = storageFracture[wPhaseIdx] + + storageMatrix[wPhaseIdx]; + + // non-wetting phase mass + storage[contiNEqIdx] = storageFracture[nPhaseIdx] + + storageMatrix[nPhaseIdx]; + } + + /*! + * \brief Evaluates the mass flux over a face of a sub-control + * volume. + * + * \param flux The flux over the SCV (sub-control-volume) face for each phase + * \param faceIdx The index of the SCV face + * \param onBoundary A boolean variable to specify whether the flux variables + * are calculated for interior SCV faces or boundary faces, default=false + */ + void computeFlux(PrimaryVariables &flux, int faceIdx, const bool onBoundary=false) const + { + FluxVariables fluxVars(this->problem_(), + this->element_(), + this->fvGeometry_(), + faceIdx, + this->curVolVars_(), + onBoundary); + flux = 0; + asImp_()->computeAdvectiveFlux(flux, fluxVars); + asImp_()->computeAdvectiveFluxFracture(flux, fluxVars); + asImp_()->computeDiffusiveFlux(flux, fluxVars); + asImp_()->computeDiffusiveFluxFracture(flux, fluxVars); + } + + /*! + * \brief Adds the flux vector in the lower dimensional fracture to the + * flux vector over the face of a sub-control volume. + * + * This method is called by compute flux and is mainly there for + * derived models to ease adding equations selectively. + * + * \param flux The advective flux over the sub-control-volume face for each phase + * \param fluxVars The flux variables at the current SCV + */ + void computeAdvectiveFluxFracture(PrimaryVariables &flux, const FluxVariables &fluxVars) const + { + //////// + // advective fluxes of all components in all phases + //////// + Vector tmpVec; + for (int phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) + { + // calculate the flux in direction of the + // current fracture + // + // v = - (K grad p) * n + // + // (the minus comes from the Darcy law which states that + // the flux is from high to low pressure potentials.) + + // data attached to upstream and the downstream vertices + // of the current phase + const VolumeVariables &upFracture = + this->curVolVars_(fluxVars.upstreamFractureIdx[phaseIdx]); + const VolumeVariables &dnFracture = + this->curVolVars_(fluxVars.downstreamFractureIdx[phaseIdx]); + + Scalar fractureFlux = + 0.5 * fluxVars.vDarcyFracture_[phaseIdx] + * ( massUpwindWeight_ // upstream vertex + * (upFracture.density(phaseIdx) + * upFracture.mobilityFracture(phaseIdx)) + + (1 - massUpwindWeight_) // downstream vertex + * (dnFracture.density(phaseIdx) + * dnFracture.mobilityFracture(phaseIdx))); + + flux[phaseIdx] += fractureFlux; + } + } + + /*! + * \brief Adds the diffusive flux of the fracture to the flux vector over + * the face of a sub-control volume. + * + * \param flux The diffusive flux over the sub-control-volume face for each phase + * \param fluxData The flux variables at the current SCV + * + * It doesn't do anything in two-phase model but is used by the + * non-isothermal two-phase models to calculate diffusive heat + * fluxes + */ + void computeDiffusiveFluxFracture(PrimaryVariables &flux, const FluxVariables &fluxData) const + { + // diffusive fluxes + flux += 0.0; + } + + /*! + * \brief Calculate the source term of the equation + * + * \param source The source/sink in the SCV for each phase + * \param scvIdx The index of the SCV + * + */ + void computeSource(PrimaryVariables &source, int scvIdx) const + { + // retrieve the source term intrinsic to the problem + this->problem_().boxSDSource(source, + this->element_(), + this->fvGeometry_(), + scvIdx, + this->curVolVars_()); + } + +protected: + Implementation *asImp_() + { + return static_cast<Implementation *> (this); + } + const Implementation *asImp_() const + { + return static_cast<const Implementation *> (this); + } + +private: + Scalar massUpwindWeight_; +}; + +} // end namespace + +#endif // DUMUX_BOXMODELS_2PDFM_LOCAL_RESIDUAL_HH diff --git a/dumux/implicit/2pdfm/2pdfmmodel.hh b/dumux/implicit/2pdfm/2pdfmmodel.hh new file mode 100644 index 0000000000..07b9c92fd0 --- /dev/null +++ b/dumux/implicit/2pdfm/2pdfmmodel.hh @@ -0,0 +1,390 @@ +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ + +/*! +* \file +* +* \brief Adaption of the box scheme to the two-phase flow in discrete fracture-matrix +* model. +*/ + +#ifndef DUMUX_BOXMODELS_2PDFM_MODEL_HH +#define DUMUX_BOXMODELS_2PDFM_MODEL_HH + +#include <dumux/boxmodels/2p/2pmodel.hh> + +#include "2pdfmproperties.hh" +#include "2pdfmlocalresidual.hh" +#include "2pdfmproblem.hh" + +namespace Dumux +{ + +/*! + * \ingroup TwoPDFMBoxModel + * \brief A two-phase, isothermal flow model using the box scheme. + * + * This model implements two-phase flow of two immiscible fluids + * \f$\alpha \in \{ w, n \}\f$ using a standard multiphase Darcy + * approach as the equation for the conservation of momentum, i.e. + \f[ + v_\alpha = - \frac{k_{r\alpha}}{\mu_\alpha} \textbf{K} + \left(\textbf{grad}\, p_\alpha - \varrho_{\alpha} {\textbf g} \right) + \f] + * + * By inserting this into the equation for the conservation of the + * phase mass, one gets + \f[ + \phi \frac{\partial \varrho_\alpha S_\alpha}{\partial t} + - + \text{div} \left\{ + \varrho_\alpha \frac{k_{r\alpha}}{\mu_\alpha} \mbox{\bf K} \left(\textbf{grad}\, p_\alpha - \varrho_{\alpha} \mbox{\bf g} \right) + \right\} - q_\alpha = 0 \;, + \f] + * + * These equations are discretized by a fully-coupled vertex centered finite volume + * (box) scheme as spatial and the implicit Euler method as time + * discretization. + * + * By using constitutive relations for the capillary pressure \f$p_c = + * p_n - p_w\f$ and relative permeability \f$k_{r\alpha}\f$ and taking + * advantage of the fact that \f$S_w + S_n = 1\f$, the number of + * unknowns can be reduced to two. Currently the model supports + * choosing either \f$p_w\f$ and \f$S_n\f$ or \f$p_n\f$ and \f$S_w\f$ + * as primary variables. The formulation which ought to be used can be + * specified by setting the <tt>Formulation</tt> property to either + * <tt>TwoPCommonIndices::pWsN</tt> or <tt>TwoPCommonIndices::pNsW</tt>. By + * default, the model uses \f$p_w\f$ and \f$S_n\f$. + */ +template<class TypeTag > +class TwoPDFMModel : public TwoPModel<TypeTag> +{ + typedef TwoPModel<TypeTag> ParentType; + typedef typename GET_PROP_TYPE(TypeTag, FVElementGeometry) FVElementGeometry; + typedef typename GET_PROP_TYPE(TypeTag, VolumeVariables) VolumeVariables; + typedef typename GET_PROP_TYPE(TypeTag, SolutionVector) SolutionVector; + typedef typename GET_PROP_TYPE(TypeTag, FluxVariables) FluxVariables; + typedef typename GET_PROP_TYPE(TypeTag, ElementVolumeVariables) ElementVolumeVariables; + + typedef typename GET_PROP_TYPE(TypeTag, Indices) Indices; + enum { + nPhaseIdx = Indices::nPhaseIdx, + wPhaseIdx = Indices::wPhaseIdx, + pressureIdx = Indices::pressureIdx, + numPhases = GET_PROP_VALUE(TypeTag, NumPhases) + }; + + typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; + typedef typename GridView::template Codim<0>::Entity Element; + typedef typename GridView::template Codim<0>::Iterator ElementIterator; + typedef typename GridView::ctype CoordScalar; + enum { + dim = GridView::dimension, + dimWorld = GridView::dimensionworld + }; + + typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; + typedef Dune::FieldVector<Scalar, numPhases> PhasesVector; + typedef Dune::FieldVector<Scalar, dimWorld> GlobalPosition; + +public: + /*! + * \brief Returns the relative weight of a primary variable for + * calculating relative errors. + * + * \param globalIdx The global index of the vertex + * \param pvIdx The index of the primary variable + */ + DUNE_DEPRECATED + Scalar primaryVarWeight(int globalIdx, int pvIdx) const + { + if (pressureIdx == pvIdx) + { + return std::min(10.0 / this->prevSol()[globalIdx][pvIdx], 1.0); + } + + return 1.0; + } + + /*! + * \brief Append all quantities of interest which can be derived + * from the solution of the current time step to the VTK + * writer. + * + * \param sol The global solution vector + * \param writer The writer for multi-file VTK datasets + */ + template<class MultiWriter> + void addOutputVtkFields(const SolutionVector &sol, MultiWriter &writer) + { + bool velocityOutput = GET_PARAM_FROM_GROUP(TypeTag, bool, Vtk, AddVelocity); + typedef Dune::BlockVector<Dune::FieldVector<double, 1> > ScalarField; + typedef Dune::BlockVector<Dune::FieldVector<double, dim> > VectorField; + + // get the number of degrees of freedom and the number of vertices + unsigned numDofs = this->numDofs(); + unsigned numVertices = this->problem_().gridView().size(dim); + + // velocity output currently only works for vertex data + if (numDofs != numVertices) + { + velocityOutput = false; + } + + // create the required scalar fields + ScalarField *pW = writer.allocateManagedBuffer(numDofs); + ScalarField *pN = writer.allocateManagedBuffer(numDofs); + ScalarField *pC = writer.allocateManagedBuffer(numDofs); + ScalarField *Sw = writer.allocateManagedBuffer(numDofs); + ScalarField *Sn = writer.allocateManagedBuffer(numDofs); + ScalarField *rhoW = writer.allocateManagedBuffer(numDofs); + ScalarField *rhoN = writer.allocateManagedBuffer(numDofs); + ScalarField *mobW = writer.allocateManagedBuffer(numDofs); + ScalarField *mobN = writer.allocateManagedBuffer(numDofs); + ScalarField *poro = writer.allocateManagedBuffer(numDofs); + ScalarField *Te = writer.allocateManagedBuffer(numDofs); + ScalarField *cellNum =writer.allocateManagedBuffer (numDofs); + VectorField *velocityN = writer.template allocateManagedBuffer<double, dim>(numDofs); + VectorField *velocityW = writer.template allocateManagedBuffer<double, dim>(numDofs); + + if(velocityOutput) // check if velocity output is demanded + { + // initialize velocity fields + for (unsigned int i = 0; i < numVertices; ++i) + { + (*velocityN)[i] = Scalar(0); + (*velocityW)[i] = Scalar(0); + (*cellNum)[i] = Scalar(0.0); + } + } + unsigned numElements = this->gridView_().size(0); + ScalarField *rank = writer.allocateManagedBuffer(numElements); + + FVElementGeometry fvGeometry; + VolumeVariables volVars; + ElementVolumeVariables elemVolVars; + + ElementIterator elemIt = this->gridView_().template begin<0>(); + ElementIterator elemEndIt = this->gridView_().template end<0>(); + for (; elemIt != elemEndIt; ++elemIt) + { + if(velocityOutput && !elemIt->geometry().type().isCube()){ + DUNE_THROW(Dune::InvalidStateException, + "Currently, velocity output only works for cubes. " + "Please set the EnableVelocityOutput property to false!"); + } + int idx = this->elementMapper().map(*elemIt); + (*rank)[idx] = this->gridView_().comm().rank(); + + fvGeometry.update(this->gridView_(), *elemIt); + + if (numDofs == numElements) // element data + { + volVars.update(sol[idx], + this->problem_(), + *elemIt, + fvGeometry, + /*scvIdx=*/0, + false); + + (*pW)[idx] = volVars.pressure(wPhaseIdx); + (*pN)[idx] = volVars.pressure(nPhaseIdx); + (*pC)[idx] = volVars.capillaryPressure(); + (*Sw)[idx] = volVars.saturation(wPhaseIdx); + (*Sn)[idx] = volVars.saturation(nPhaseIdx); + (*rhoW)[idx] = volVars.density(wPhaseIdx); + (*rhoN)[idx] = volVars.density(nPhaseIdx); + (*mobW)[idx] = volVars.mobility(wPhaseIdx); + (*mobN)[idx] = volVars.mobility(nPhaseIdx); + (*poro)[idx] = volVars.porosity(); + (*Te)[idx] = volVars.temperature(); + } + else // vertex data + { + int numVerts = elemIt->template count<dim> (); + for (int scvIdx = 0; scvIdx < numVerts; ++scvIdx) + { + int globalIdx = this->vertexMapper().map(*elemIt, scvIdx, dim); + volVars.update(sol[globalIdx], + this->problem_(), + *elemIt, + fvGeometry, + scvIdx, + false); + + (*pW)[globalIdx] = volVars.pressure(wPhaseIdx); + (*pN)[globalIdx] = volVars.pressure(nPhaseIdx); + (*pC)[globalIdx] = volVars.capillaryPressure(); + (*Sw)[globalIdx] = volVars.saturation(wPhaseIdx); + (*Sn)[globalIdx] = volVars.saturation(nPhaseIdx); + (*rhoW)[globalIdx] = volVars.density(wPhaseIdx); + (*rhoN)[globalIdx] = volVars.density(nPhaseIdx); + (*mobW)[globalIdx] = volVars.mobility(wPhaseIdx); + (*mobN)[globalIdx] = volVars.mobility(nPhaseIdx); + (*poro)[globalIdx] = volVars.porosity(); + (*Te)[globalIdx] = volVars.temperature(); + if(velocityOutput) + { + (*cellNum)[globalIdx] += 1; + } + } + + if(velocityOutput) + { + // calculate vertex velocities + GlobalPosition tmpVelocity[numPhases]; + + for(int phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) + { + tmpVelocity[phaseIdx] = Scalar(0.0); + } + + typedef Dune::BlockVector<Dune::FieldVector<Scalar, dim> > SCVVelocities; + SCVVelocities scvVelocityW(8), scvVelocityN(8); + + scvVelocityW = 0; + scvVelocityN = 0; + + ElementVolumeVariables elemVolVars; + + elemVolVars.update(this->problem_(), + *elemIt, + fvGeometry, + false /* oldSol? */); + + for (int faceIdx = 0; faceIdx < fvGeometry.numEdges; faceIdx++) + { + FluxVariables fluxVars(this->problem_(), + *elemIt, + fvGeometry, + faceIdx, + elemVolVars); + + for (int phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) + { + // local position of integration point + const Dune::FieldVector<Scalar, dim>& localPosIP = fvGeometry.subContVolFace[faceIdx].ipLocal; + + // Transformation of the global normal vector to normal vector in the reference element + const typename Element::Geometry::JacobianTransposed& jacobianT1 = + elemIt->geometry().jacobianTransposed(localPosIP); + + const GlobalPosition globalNormal = fluxVars.face().normal; + GlobalPosition localNormal; + jacobianT1.mv(globalNormal, localNormal); + // note only works for cubes + const Scalar localArea = pow(2,-(dim-1)); + + localNormal /= localNormal.two_norm(); + + // Get the Darcy velocities. The Darcy velocities are divided by the area of the subcontrolvolumeface + // in the reference element. + PhasesVector q; + q[phaseIdx] = fluxVars.volumeFlux(phaseIdx) / localArea; + + // transform the normal Darcy velocity into a vector + tmpVelocity[phaseIdx] = localNormal; + tmpVelocity[phaseIdx] *= q[phaseIdx]; + + if(phaseIdx == wPhaseIdx) + { + scvVelocityW[fluxVars.face().i] += tmpVelocity[phaseIdx]; + scvVelocityW[fluxVars.face().j] += tmpVelocity[phaseIdx]; + } + else if(phaseIdx == nPhaseIdx) + { + scvVelocityN[fluxVars.face().i] += tmpVelocity[phaseIdx]; + scvVelocityN[fluxVars.face().j] += tmpVelocity[phaseIdx]; + } + } + } + typedef Dune::GenericReferenceElements<Scalar, dim> ReferenceElements; + const Dune::FieldVector<Scalar, dim> &localPos + = ReferenceElements::general(elemIt->geometry().type()).position(0, 0); + + // get the transposed Jacobian of the element mapping + const typename Element::Geometry::JacobianTransposed& jacobianT2 + = elemIt->geometry().jacobianTransposed(localPos); + + // transform vertex velocities from local to global coordinates + for (int i = 0; i < numVerts; ++i) + { + int globalIdx = this->vertexMapper().map(*elemIt, i, dim); + // calculate the subcontrolvolume velocity by the Piola transformation + Dune::FieldVector<CoordScalar, dim> scvVelocity(0); + + jacobianT2.mtv(scvVelocityW[i], scvVelocity); + scvVelocity /= elemIt->geometry().integrationElement(localPos); + // add up the wetting phase subcontrolvolume velocities for each vertex + (*velocityW)[globalIdx] += scvVelocity; + + jacobianT2.mtv(scvVelocityN[i], scvVelocity); + scvVelocity /= elemIt->geometry().integrationElement(localPos); + // add up the nonwetting phase subcontrolvolume velocities for each vertex + (*velocityN)[globalIdx] += scvVelocity; + } + } + } + } + + if (numDofs == numElements) // element data + { + writer.attachCellData(*Sn, "Sn"); + writer.attachCellData(*Sw, "Sw"); + writer.attachCellData(*pN, "pn"); + writer.attachCellData(*pW, "pw"); + writer.attachCellData(*pC, "pc"); + writer.attachCellData(*rhoW, "rhoW"); + writer.attachCellData(*rhoN, "rhoN"); + writer.attachCellData(*mobW, "mobW"); + writer.attachCellData(*mobN, "mobN"); + writer.attachCellData(*poro, "porosity"); + writer.attachCellData(*Te, "temperature"); + } + else // vertex data + { + writer.attachVertexData(*Sn, "Sn"); + writer.attachVertexData(*Sw, "Sw"); + writer.attachVertexData(*pN, "pn"); + writer.attachVertexData(*pW, "pw"); + writer.attachVertexData(*pC, "pc"); + writer.attachVertexData(*rhoW, "rhoW"); + writer.attachVertexData(*rhoN, "rhoN"); + writer.attachVertexData(*mobW, "mobW"); + writer.attachVertexData(*mobN, "mobN"); + writer.attachVertexData(*poro, "porosity"); + writer.attachVertexData(*Te, "temperature"); + if(velocityOutput) // check if velocity output is demanded + { + // divide the vertex velocities by the number of adjacent scvs i.e. cells + for(unsigned int globalIdx = 0; globalIdx < numVertices; ++globalIdx) + { + (*velocityW)[globalIdx] /= (*cellNum)[globalIdx]; + (*velocityN)[globalIdx] /= (*cellNum)[globalIdx]; + } + writer.attachVertexData(*velocityW, "velocityW", dim); + writer.attachVertexData(*velocityN, "velocityN", dim); + } + } + writer.attachCellData(*rank, "process rank"); + } +}; +} // end namespace + +#include "2pdfmpropertydefaults.hh" + +#endif // DUMUX_BOXMODELS_2PDFM_MODEL_HH diff --git a/dumux/implicit/2pdfm/2pdfmproblem.hh b/dumux/implicit/2pdfm/2pdfmproblem.hh new file mode 100644 index 0000000000..fd65e2b68d --- /dev/null +++ b/dumux/implicit/2pdfm/2pdfmproblem.hh @@ -0,0 +1,79 @@ +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \file + * \brief Base class for all problems which use the two-phase DFM box model + */ +#ifndef DUMUX_BOXMODELS_2PDFM_PROBLEM_HH +#define DUMUX_BOXMODELS_2PDFM_PROBLEM_HH + +#include <dumux/boxmodels/common/porousmediaboxproblem.hh> +#include "2pdfmproperties.hh" + +namespace Dumux +{ +/*! + * \ingroup BoxBaseProblems + * \ingroup TwoPBoxModel + * \brief Base class for all problems which use the two-phase box model + */ +template<class TypeTag> +class TwoPDFMProblem : public PorousMediaBoxProblem<TypeTag> +{ + typedef PorousMediaBoxProblem<TypeTag> ParentType; + + typedef typename GET_PROP_TYPE(TypeTag, TimeManager) TimeManager; + typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; + typedef typename GET_PROP_TYPE(TypeTag, SpatialParams) SpatialParams; + +public: + /*! + * \brief The constructor + * + * \param timeManager The time manager + * \param gridView The grid view + * \param verbose Turn verbosity on or off + */ + DUNE_DEPRECATED_MSG("use PorousMediaBoxProblem instead") + TwoPDFMProblem(TimeManager &timeManager, + const GridView &gridView, + bool verbose = true) + : ParentType(timeManager, gridView) + {} + + /*! + * \brief The constructor + * + * \param timeManager The time manager + * \param gridView The grid view + * \param spatialParams The spatial parameters object + * \param verbose Turn verbosity on or off + */ + TwoPDFMProblem(TimeManager &timeManager, + const GridView &gridView, + SpatialParams &spatialParams, + bool verbose = true) + : ParentType(timeManager, gridView) + { + this->newSpatialParams_ = false; + delete this->spatialParams_; + this->spatialParams_ = &spatialParams; + } +}; +} // end namespace + +#endif // DUMUX_BOXMODELS_2PDFM_PROBLEM_HH diff --git a/dumux/implicit/2pdfm/2pdfmproperties.hh b/dumux/implicit/2pdfm/2pdfmproperties.hh new file mode 100644 index 0000000000..7d0d2631e0 --- /dev/null +++ b/dumux/implicit/2pdfm/2pdfmproperties.hh @@ -0,0 +1,69 @@ +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \file + * + * \brief Defines the properties required for the two phase DFM BOX model. + * + * \ingroup Properties + * \ingroup BoxProperties + * \ingroup TwoPDFMBoxModel + */ + +#ifndef DUMUX_BOXMODELS_2PDFM_PROPERTIES_HH +#define DUMUX_BOXMODELS_2PDFM_PROPERTIES_HH + +#include <dumux/boxmodels/common/boxproperties.hh> + +namespace Dumux +{ + +//////////////////////////////// +// properties +//////////////////////////////// +namespace Properties +{ + +////////////////////////////////////////////////////////////////// +// Type tags +////////////////////////////////////////////////////////////////// + +//! The type tag for 2pDFM problems +NEW_TYPE_TAG(BoxTwoPDFM, INHERITS_FROM(BoxModel)); + +////////////////////////////////////////////////////////////////// +// Property tags +////////////////////////////////////////////////////////////////// + +NEW_PROP_TAG(NumPhases); //!< Number of fluid phases in the system +NEW_PROP_TAG(ProblemEnableGravity); //!< Returns whether gravity is considered in the problem +NEW_PROP_TAG(ImplicitMassUpwindWeight); //!< The value of the weight of the upwind direction in the mass conservation equations +NEW_PROP_TAG(ImplicitMobilityUpwindWeight); //!< Weight for the upwind mobility in the velocity calculation +NEW_PROP_TAG(Formulation); //!< The formulation of the model +NEW_PROP_TAG(TwoPDFMIndices); //!< Enumerations for the 2pDFM models +NEW_PROP_TAG(SpatialParams); //!< The type of the spatial parameters +NEW_PROP_TAG(MaterialLaw); //!< The material law which ought to be used (extracted from the spatial parameters) +NEW_PROP_TAG(MaterialLawParams); //!< The context material law (extracted from the spatial parameters) +NEW_PROP_TAG(WettingPhase); //!< The wetting phase for two-phase models +NEW_PROP_TAG(NonwettingPhase); //!< The non-wetting phase for two-phase models +NEW_PROP_TAG(FluidSystem); //!<The fluid systems including the information about the phases +NEW_PROP_TAG(FluidState); //!<The phases state +NEW_PROP_TAG(VtkAddVelocity); //!< Returns whether velocity vectors are written into the vtk output +} // end namespace Properties +} // end namespace Dumux + +#endif // DUMUX_BOXMODELS_2PDFM_PROPERTIES_HH diff --git a/dumux/implicit/2pdfm/2pdfmpropertydefaults.hh b/dumux/implicit/2pdfm/2pdfmpropertydefaults.hh new file mode 100644 index 0000000000..84121c23b0 --- /dev/null +++ b/dumux/implicit/2pdfm/2pdfmpropertydefaults.hh @@ -0,0 +1,144 @@ +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \file + * + * \brief Defines default values for the properties required by the + * 2pDFM box model. + * + * \ingroup Properties + * \ingroup TwoPDFMBoxModel + * \ingroup BoxProperties + */ +#ifndef DUMUX_BOXMODELS_2PDFM_PROPERTY_DEFAULTS_HH +#define DUMUX_BOXMODELS_2PDFM_PROPERTY_DEFAULTS_HH + +#include <dumux/boxmodels/common/boxdarcyfluxvariables.hh> +#include <dumux/material/components/nullcomponent.hh> +#include <dumux/material/fluidstates/immisciblefluidstate.hh> +#include <dumux/material/fluidsystems/2pimmisciblefluidsystem.hh> +#include <dumux/material/fluidsystems/gasphase.hh> +#include <dumux/material/fluidsystems/liquidphase.hh> +#include <dumux/material/spatialparams/boxspatialparams.hh> + +#include "2pdfmmodel.hh" +#include "2pdfmproblem.hh" +#include "2pdfmindices.hh" +#include "2pdfmfluxvariables.hh" +#include "2pdfmvolumevariables.hh" +#include "2pdfmproperties.hh" + +namespace Dumux +{ +namespace Properties +{ +////////////////////////////////////////////////////////////////// +// Property defaults +////////////////////////////////////////////////////////////////// +SET_INT_PROP(BoxTwoPDFM, NumEq, 2); //!< set the number of equations to 2 +SET_INT_PROP(BoxTwoPDFM, NumPhases, 2); //!< The number of phases in the 2p model is 2 + +//! Set the default formulation to pWsN +SET_INT_PROP(BoxTwoPDFM, + Formulation, + TwoPFormulation::pwSn); + +//! Use the 2p local jacobian operator for the 2p model +SET_TYPE_PROP(BoxTwoPDFM, + LocalResidual, + TwoPDFMLocalResidual<TypeTag>); + +//! the Model property +SET_TYPE_PROP(BoxTwoPDFM, Model, TwoPDFMModel<TypeTag>); + +//! the VolumeVariables property +SET_TYPE_PROP(BoxTwoPDFM, VolumeVariables, TwoPDFMVolumeVariables<TypeTag>); + +//! the FluxVariables property +SET_TYPE_PROP(BoxTwoPDFM, FluxVariables, TwoPDFMFluxVariables<TypeTag>); + +//! the upwind weight for the mass conservation equations. +SET_SCALAR_PROP(BoxTwoPDFM, ImplicitMassUpwindWeight, 1.0); + +//! weight for the upwind mobility in the velocity calculation +SET_SCALAR_PROP(BoxTwoPDFM, ImplicitMobilityUpwindWeight, 1.0); + +//! The indices required by the isothermal 2pDFM model +SET_PROP(BoxTwoPDFM, Indices) +{ private: + enum { Formulation = GET_PROP_VALUE(TypeTag, Formulation) }; + public: + typedef TwoPDFMIndices<TypeTag, Formulation, 0> type; +}; + + +//! The spatial parameters to be employed. +//! Use BoxSpatialParams by default. +SET_TYPE_PROP(BoxTwoPDFM, SpatialParams, BoxSpatialParams<TypeTag>); + +/*! + * \brief Set the property for the material parameters by extracting + * it from the material law. + */ +SET_TYPE_PROP(BoxTwoPDFM, + MaterialLawParams, + typename GET_PROP_TYPE(TypeTag, MaterialLaw)::Params); + +SET_PROP(BoxTwoPDFM, WettingPhase) +{ private: + typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; +public: + typedef Dumux::LiquidPhase<Scalar, Dumux::NullComponent<Scalar> > type; +}; + +SET_PROP(BoxTwoPDFM, NonwettingPhase) +{ private: + typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; +public: + typedef Dumux::LiquidPhase<Scalar, Dumux::NullComponent<Scalar> > type; +}; + +SET_PROP(BoxTwoPDFM, FluidSystem) +{ private: + typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; + typedef typename GET_PROP_TYPE(TypeTag, WettingPhase) WettingPhase; + typedef typename GET_PROP_TYPE(TypeTag, NonwettingPhase) NonwettingPhase; + +public: + typedef Dumux::FluidSystems::TwoPImmiscible<Scalar, + WettingPhase, + NonwettingPhase> type; +}; + +SET_PROP(BoxTwoPDFM, FluidState) +{ +private: + typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; + typedef typename GET_PROP_TYPE(TypeTag, FluidSystem) FluidSystem; +public: + typedef ImmiscibleFluidState<Scalar, FluidSystem> type; +}; + +// disable velocity output by default +SET_BOOL_PROP(BoxTwoPDFM, VtkAddVelocity, false); + +// enable gravity by default +SET_BOOL_PROP(BoxTwoPDFM, ProblemEnableGravity, true); +} // end namespace Properties +} // end namespace Dumux + +#endif // DUMUX_BOXMODELS_2PDFM_PROPERTY_DEFAULTS_HH diff --git a/dumux/implicit/2pdfm/2pdfmvolumevariables.hh b/dumux/implicit/2pdfm/2pdfmvolumevariables.hh new file mode 100644 index 0000000000..520d902aa3 --- /dev/null +++ b/dumux/implicit/2pdfm/2pdfmvolumevariables.hh @@ -0,0 +1,386 @@ +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \file + * + * \brief Contains the quantities which are constant within a + * finite volume in the two-phase discrete fracture-matrix model. + */ +#ifndef DUMUX_BOXMODELS_2PDFM_VOLUME_VARIABLES_HH +#define DUMUX_BOXMODELS_2PDFM_VOLUME_VARIABLES_HH + +#include <dune/common/fvector.hh> + +#include <dumux/boxmodels/2p/2pvolumevariables.hh> + +#include "2pdfmproperties.hh" + +namespace Dumux +{ +/*! + * \ingroup TwoPDFMBoxModel + * \ingroup BoxVolumeVariables + * \brief Contains the quantities which are are constant within a + * finite volume in the two-phase discrete fracture-matrix model. + */ +template <class TypeTag> +class TwoPDFMVolumeVariables : public TwoPVolumeVariables<TypeTag> +{ + typedef TwoPVolumeVariables<TypeTag> ParentType; + + typedef typename GET_PROP_TYPE(TypeTag, VolumeVariables) Implementation; + typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; + typedef typename GET_PROP_TYPE(TypeTag, Problem) Problem; + typedef typename GET_PROP_TYPE(TypeTag, FluidSystem) FluidSystem; + typedef typename GET_PROP_TYPE(TypeTag, FluidState) FluidState; + typedef typename GET_PROP_TYPE(TypeTag, MaterialLaw) MaterialLaw; + typedef typename GET_PROP_TYPE(TypeTag, MaterialLawParams) MaterialLawParams; + typedef typename GET_PROP_TYPE(TypeTag, FVElementGeometry) FVElementGeometry; + typedef typename GET_PROP_TYPE(TypeTag, PrimaryVariables) PrimaryVariables; + typedef typename FVElementGeometry::SubControlVolumeFace SCVFace; + + typedef typename GET_PROP_TYPE(TypeTag, Indices) Indices; + enum { + pwSn = Indices::pwSn, + pnSw = Indices::pnSw, + pressureIdx = Indices::pressureIdx, + saturationIdx = Indices::saturationIdx, + wPhaseIdx = Indices::wPhaseIdx, + nPhaseIdx = Indices::nPhaseIdx, + numPhases = GET_PROP_VALUE(TypeTag, NumPhases), + formulation = GET_PROP_VALUE(TypeTag, Formulation) + }; + + typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; + typedef typename GridView::template Codim<0>::Entity Element; + typedef Dune::UGGrid<2> GridType; + typedef typename GridType::ctype DT; + enum { + dim = GridView::dimension, + dimWorld = GridView::dimensionworld + }; + typedef Dune::FieldVector<Scalar, dimWorld> GlobalPosition; + typedef Dune::FieldVector<Scalar, dim> LocalPosition; + +public: + /*! + * \copydoc BoxVolumeVariables::update + */ + void update(const PrimaryVariables &priVars, + const Problem &problem, + const Element &element, + const FVElementGeometry &fvGeometry, + int scvIdx, + bool isOldSol) + { + ParentType::update(priVars, + problem, + element, + fvGeometry, + scvIdx, + isOldSol); + + this->completeFluidState(priVars, problem, element, fvGeometry, scvIdx, fluidState_); + + const MaterialLawParams &materialParams = + problem.spatialParams().materialLawParams(element, fvGeometry, scvIdx); + + mobilityMatrix_[wPhaseIdx] = + MaterialLaw::krw(materialParams, fluidState_.saturation(wPhaseIdx)) + / fluidState_.viscosity(wPhaseIdx); + + mobilityMatrix_[nPhaseIdx] = + MaterialLaw::krn(materialParams, fluidState_.saturation(wPhaseIdx)) + / fluidState_.viscosity(nPhaseIdx); + + // porosity + porosityMatrix_ = problem.spatialParams().porosity(element, fvGeometry, scvIdx); + + // energy related quantities not belonging to the fluid state + asImp_().updateEnergy_(priVars, problem, element, fvGeometry, scvIdx, isOldSol); + asImp_().updateFracture(priVars, problem, element, fvGeometry, scvIdx, isOldSol); + } + + /*! + * \brief Construct the volume variables for all fracture vertices. + * + * \param priVars Primary variables. + * \param problem The problem which needs to be simulated. + * \param element The DUNE Codim<0> entity for which the volume variables ought to be calculated + * \param fvGeometry The finite volume geometry of the element + * \param scvIdx Sub-control volume index + * \param isOldSol Tells whether the model's previous or current solution should be used. + */ + void updateFracture(const PrimaryVariables &priVars, + const Problem &problem, + const Element &element, + const FVElementGeometry &fvGeometry, + int scvIdx, + bool isOldSol) + { + PrimaryVariables varsFracture; + const MaterialLawParams &materialParamsMatrix = + problem.spatialParams().materialLawParams(element, fvGeometry, scvIdx); + Scalar pressure[numPhases]; + Scalar pMatrix[numPhases]; + Scalar pFract[numPhases]; + + satNMatrix_ = priVars[saturationIdx]; + satWMatrix_ = 1.0 - satNMatrix_; + satN_ = satNMatrix_; + satW_ = satWMatrix_; + + pCMatrix_ = MaterialLaw::pC(materialParamsMatrix, satWMatrix_); + pC_ = pCMatrix_; + //pressures + pMatrix[wPhaseIdx] = priVars[pressureIdx]; + pMatrix[nPhaseIdx] = pMatrix[wPhaseIdx] + pCMatrix_; + //Initialize pFract with the same values as the ones in the matrix + pFract[wPhaseIdx] = pMatrix[wPhaseIdx]; + pFract[nPhaseIdx] = satNMatrix_; + + varsFracture[pressureIdx] = pFract[wPhaseIdx]; + varsFracture[saturationIdx] = pFract[wPhaseIdx]; + + this->completeFluidState(priVars, problem, element, fvGeometry, scvIdx, fluidStateFracture_); + + //Checks if the node is on a fracture + isNodeOnFracture_ = problem.spatialParams().isVertexFracture(element, scvIdx); + + /////////////////////////////////////////////////////////////////////////////// + if (isNodeOnFracture_) + { + const MaterialLawParams &materialParamsFracture = + problem.spatialParams().materialLawParamsFracture(element, fvGeometry, scvIdx); + + satNFracture_ = priVars[saturationIdx]; + satWFracture_ = 1 - satNFracture_; + pCFracture_ = MaterialLaw::pC(materialParamsFracture, satWFracture_); + pFract[wPhaseIdx] = priVars[pressureIdx]; + pFract[nPhaseIdx] = pFract[wPhaseIdx] + pCFracture_; + pEntryMatrix_ = MaterialLaw::pC(materialParamsMatrix, 1); + + //use interface condition - extended capillary pressure inteface condition + if (problem.useInterfaceCondition()) + { + interfaceCondition(materialParamsMatrix); + } + pC_ = pCFracture_; + satW_ = satWFracture_; //for plotting we are interested in the saturations of the fracture + satN_ = satNFracture_; + mobilityFracture_[wPhaseIdx] = + MaterialLaw::krw(materialParamsFracture, fluidStateFracture_.saturation(wPhaseIdx)) + / fluidStateFracture_.viscosity(wPhaseIdx); + + mobilityFracture_[nPhaseIdx] = + MaterialLaw::krn(materialParamsFracture, fluidStateFracture_.saturation(wPhaseIdx)) + / fluidStateFracture_.viscosity(nPhaseIdx); + + // derivative resulted from BrooksCorey pc_Sw formulation + dSM_dSF_ = (1 - problem.spatialParams().SwrM_) / (1 - problem.spatialParams().SwrF_) + * pow((problem.spatialParams().pdM_/ problem.spatialParams().pdF_),problem.spatialParams().lambdaM_) + * (problem.spatialParams().lambdaM_ / problem.spatialParams().lambdaF_) + * pow((satWFracture_ - problem.spatialParams().SwrF_ ) / (1 - problem.spatialParams().SwrF_), + (problem.spatialParams().lambdaM_ / problem.spatialParams().lambdaF_) - 1); + }// end if (node) + /////////////////////////////////////////////////////////////////////////////// + else + { + /* the values of pressure and saturation of the fractures in the volumes where + there are no fracture are set unphysical*/ + satNFracture_ = -1; + satWFracture_ = -1; + pCFracture_ = -1e100; + pFract[wPhaseIdx] = -1e100; + pFract[nPhaseIdx] = -1e100; + pEntryMatrix_ = -1e100; + mobilityFracture_[wPhaseIdx] = 0.0; + mobilityFracture_[nPhaseIdx] = 0.0; + } + /////////////////////////////////////////////////////////////////////////////// + pressure[wPhaseIdx] = priVars[pressureIdx]; + pressure[nPhaseIdx] = pressure[wPhaseIdx] + pC_; + + porosityFracture_ = problem.spatialParams().porosityFracture(element, + fvGeometry, + scvIdx); + } + + /*! + * \brief Extended capillary pressure saturation interface condition + * + * \param materialParamsMatrix the material law o calculate the Sw as inverse of capillary pressure function + * + * This method is called by updateFracture + */ + void interfaceCondition(const MaterialLawParams &materialParamsMatrix) + { + /*2nd condition Niessner, J., R. Helmig, H. Jakobs, and J.E. Roberts. 2005, eq.10 + * if the capillary pressure in the fracture is smaller than the entry pressure + * in the matrix than in the matrix + * */ + if (pCFracture_ <= pEntryMatrix_) + { + satWMatrix_ = 1.0; + satNMatrix_ = 1 - satWMatrix_; + } + //3rd condition Niessner, J., R. Helmig, H. Jakobs, and J.E. Roberts. 2005, eq.10 + else + { + /* + * Inverse capillary pressure function SwM = pcM^(-1)(pcF(SwF)) + */ + satWMatrix_ = MaterialLaw::Sw(materialParamsMatrix, pCFracture_); + satNMatrix_ = 1 - satWMatrix_; + } + } + + /*! + * \brief Calculates the volume of the fracture inside the SCV + */ + Scalar calculateSCVFractureVolume ( const Problem &problem, + const Element &element, + const FVElementGeometry &fvGeometry, + int scvIdx) + { + Scalar volSCVFracture; + Dune::GeometryType gt = element.geometry().type(); + const typename Dune::GenericReferenceElementContainer<DT,dim>::value_type& + refElem = Dune::GenericReferenceElements<DT,dim>::general(gt); + + for (int faceIdx=0; faceIdx<refElem.size(1); faceIdx++) + { + SCVFace face = fvGeometry.subContVolFace[faceIdx]; + int i=face.i; + int j=face.j; + + if (problem.spatialParams().isEdgeFracture(element, faceIdx) + && (i == scvIdx || j == scvIdx)) + { + Scalar fracture_width = problem.spatialParams().fractureWidth(); + + const GlobalPosition global_i = element.geometry().corner(i); + const GlobalPosition global_j = element.geometry().corner(j); + GlobalPosition diff_ij = global_j; + diff_ij -=global_i; + //fracture length in the subcontrol volume is half d_ij + Scalar fracture_length = 0.5*diff_ij.two_norm(); + + volSCVFracture += 0.5 * fracture_length * fracture_width; + } + } + return volSCVFracture; + } + + /*! + * \brief Returns the effective saturation fracture of a given phase within + * the control volume. + * + * \param phaseIdx The phase index + */ + Scalar saturationFracture(int phaseIdx) const + { + if (phaseIdx == wPhaseIdx) + return satWFracture_; + else + return satNFracture_; + } + Scalar saturationMatrix(int phaseIdx) const + { + if (phaseIdx == wPhaseIdx) + return satWMatrix_; + else + return satNMatrix_; + } + + /*! + * \brief Returns the effective mobility of a given phase within + * the control volume. + * + * \param phaseIdx The phase index + */ + Scalar mobility(int phaseIdx) const + { return mobilityMatrix_[phaseIdx]; } + + /*! + * \brief Returns the effective mobility of a given phase within + * the control volume. + * + * \param phaseIdx The phase index + */ + Scalar mobilityFracture(int phaseIdx) const + { return mobilityFracture_[phaseIdx]; } + + /*! + * \brief Returns the average porosity within the matrix control volume. + */ + Scalar porosity() const + { return porosityMatrix_; } + + /*! + * \brief Returns the average porosity within the fracture. + */ + Scalar porosityFracture() const + { return porosityFracture_; } + + /*! + * \brief Returns the average permeability within the fracture. + */ + Scalar permeabilityFracture() const + { return permeabilityFracture_; } + + /*! + * \brief Returns the derivative dSM/dSF + */ + Scalar dSM_dSF() const + { return dSM_dSF_;} + +protected: + FluidState fluidState_; + FluidState fluidStateFracture_; + Scalar porosityMatrix_; + Scalar porosityFracture_; + Scalar permeability_; + Scalar permeabilityFracture_; + Scalar mobilityMatrix_[numPhases]; + Scalar mobilityFracture_[numPhases]; + + Scalar satW_; + Scalar satWFracture_; + Scalar satWMatrix_; + Scalar satN_; + Scalar satNFracture_; + Scalar satNMatrix_; + + Scalar pC_; + Scalar pCFracture_; + Scalar pCMatrix_; + Scalar pEntryMatrix_; + Scalar dSM_dSF_; + + bool isNodeOnFracture_; + +private: + Implementation &asImp_() + { return *static_cast<Implementation*>(this); } + + const Implementation &asImp_() const + { return *static_cast<const Implementation*>(this); } +}; +} // end namespace + +#endif // DUMUX_BOXMODELS_2PDFM_VOLUME_VARIABLES_HH diff --git a/dumux/implicit/2pdfm/Makefile.am b/dumux/implicit/2pdfm/Makefile.am new file mode 100644 index 0000000000..117f16d6c2 --- /dev/null +++ b/dumux/implicit/2pdfm/Makefile.am @@ -0,0 +1,4 @@ +2pdfmdir = $(includedir)/dumux/boxmodels/2pdfm +2pdfm_HEADERS = *.hh + +include $(top_srcdir)/am/global-rules diff --git a/dumux/implicit/2pni/2pnifluxvariables.hh b/dumux/implicit/2pni/2pnifluxvariables.hh new file mode 100644 index 0000000000..ae3f474f72 --- /dev/null +++ b/dumux/implicit/2pni/2pnifluxvariables.hh @@ -0,0 +1,127 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \file + * + * \brief This file contains the data which is required to calculate + * all fluxes (mass and energy) of all phases over a face of a finite volume. + * + * This means pressure and temperature gradients, phase densities at + * the integration point, etc. + */ +#ifndef DUMUX_2PNI_FLUX_VARIABLES_HH +#define DUMUX_2PNI_FLUX_VARIABLES_HH + +#include <dumux/common/math.hh> +#include <dumux/boxmodels/common/boxdarcyfluxvariables.hh> + +namespace Dumux +{ + +/*! + * \ingroup TwoPNIModel + * \ingroup BoxFluxVariables + * \brief This template class contains the data which is required to + * calculate all fluxes (mass and energy) of all phases over a + * face of a finite volume for the non-isothermal two-phase model. + * + * This means pressure and concentration gradients, phase densities at + * the integration point, etc. + */ +template <class TypeTag> +class TwoPNIFluxVariables : public BoxDarcyFluxVariables<TypeTag> +{ + typedef BoxDarcyFluxVariables<TypeTag> ParentType; + + typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; + typedef typename GET_PROP_TYPE(TypeTag, Problem) Problem; + typedef typename GET_PROP_TYPE(TypeTag, ElementVolumeVariables) ElementVolumeVariables; + typedef typename GET_PROP_TYPE(TypeTag, FVElementGeometry) FVElementGeometry; + + typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; + typedef typename GridView::template Codim<0>::Entity Element; + enum { dimWorld = GridView::dimensionworld }; + + typedef typename GridView::ctype CoordScalar; + typedef Dune::FieldVector<CoordScalar, dimWorld> Vector; + +public: + + /* + * \brief The constructor + * + * \param problem The problem + * \param element The finite element + * \param fvGeometry The finite-volume geometry in the box scheme + * \param faceIdx The local index of the SCV (sub-control-volume) face + * \param elemVolVars The volume variables of the current element + * \param onBoundary A boolean variable to specify whether the flux variables + * are calculated for interior SCV faces or boundary faces, default=false + */ + + TwoPNIFluxVariables(const Problem &problem, + const Element &element, + const FVElementGeometry &fvGeometry, + int faceIdx, + const ElementVolumeVariables &elemVolVars, + const bool onBoundary = false) + : ParentType(problem, element, fvGeometry, faceIdx, elemVolVars, onBoundary) + { + // calculate temperature gradient using finite element + // gradients + Vector temperatureGrad(0); + for (int idx = 0; idx < fvGeometry.numFAP; idx++) + { + Vector feGrad = this->face().grad[idx]; + + // index for the element volume variables + int volVarsIdx = this->face().fapIndices[idx]; + + feGrad *= elemVolVars[volVarsIdx].temperature(); + temperatureGrad += feGrad; + } + + // The spatial parameters calculates the actual heat flux vector + Vector heatFlux; + problem.spatialParams().matrixHeatFlux(heatFlux, + *this, + elemVolVars, + temperatureGrad, + element, + fvGeometry, + faceIdx); + // project the heat flux vector on the face's normal vector + normalMatrixHeatFlux_ = heatFlux * this->face().normal; + } + + /*! + * \brief The total heat flux \f$\mathrm{[J/s]}\f$ due to heat conduction + * of the rock matrix over the sub-control volume's face in + * direction of the face normal. + */ + Scalar normalMatrixHeatFlux() const + { return normalMatrixHeatFlux_; } + +private: + Scalar normalMatrixHeatFlux_; +}; + +} // end namepace + +#endif diff --git a/dumux/implicit/2pni/2pniindices.hh b/dumux/implicit/2pni/2pniindices.hh new file mode 100644 index 0000000000..2eafe784d3 --- /dev/null +++ b/dumux/implicit/2pni/2pniindices.hh @@ -0,0 +1,48 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \file + * + * \brief Defines the indices used by the non-isotherm two-phase BOX model. + */ +#ifndef DUMUX_2PNI_INDICES_HH +#define DUMUX_2PNI_INDICES_HH + +#include <dumux/boxmodels/2p/2pindices.hh> + +namespace Dumux +{ +// \{ + +/*! + * \ingroup TwoPNIModel + * \ingroup BoxIndices + * \brief Enumerations for the non-isothermal two-phase model + */ +template <class TypeTag, int PVOffset = 0> +class TwoPNIIndices : public TwoPIndices<TypeTag, PVOffset> +{ +public: + static const int temperatureIdx = PVOffset + 2; //! The primary variable index for temperature + static const int energyEqIdx = PVOffset + 2; //! The equation index of the energy equation +}; +// \} +} + +#endif diff --git a/dumux/implicit/2pni/2pnilocalresidual.hh b/dumux/implicit/2pni/2pnilocalresidual.hh new file mode 100644 index 0000000000..bb1f1e3b90 --- /dev/null +++ b/dumux/implicit/2pni/2pnilocalresidual.hh @@ -0,0 +1,182 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \file + * + * \brief Element-wise calculation of the Jacobian matrix for problems + * using the non-isothermal two-phase box model. + * + */ +#ifndef DUMUX_NEW_2PNI_LOCAL_RESIDUAL_HH +#define DUMUX_NEW_2PNI_LOCAL_RESIDUAL_HH + +#include "2pniproperties.hh" + +#include <dumux/boxmodels/2p/2plocalresidual.hh> + +#include <dumux/boxmodels/2pni/2pnivolumevariables.hh> +#include <dumux/boxmodels/2pni/2pnifluxvariables.hh> + +namespace Dumux +{ +/*! + * \ingroup TwoPNIModel + * \ingroup BoxLocalResidual + * \brief Element-wise calculation of the Jacobian matrix for problems + * using the non-isothermal two-phase box model. + */ + +template<class TypeTag> +class TwoPNILocalResidual : public TwoPLocalResidual<TypeTag> +{ + typedef TwoPLocalResidual<TypeTag> ParentType; + + typedef typename GET_PROP_TYPE(TypeTag, PrimaryVariables) PrimaryVariables; + typedef typename GET_PROP_TYPE(TypeTag, VolumeVariables) VolumeVariables; + typedef typename GET_PROP_TYPE(TypeTag, FluxVariables) FluxVariables; + typedef typename GET_PROP_TYPE(TypeTag, ElementVolumeVariables) ElementVolumeVariables; + + typedef typename GET_PROP_TYPE(TypeTag, Indices) Indices; + enum { + numPhases = GET_PROP_VALUE(TypeTag, NumPhases), + temperatureIdx = Indices::temperatureIdx, + energyEqIdx = Indices::energyEqIdx, + wPhaseIdx = Indices::wPhaseIdx, + nPhaseIdx = Indices::nPhaseIdx + }; + + typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; + enum { dimWorld = GridView::dimensionworld }; + + typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; + typedef Dune::FieldVector<Scalar, dimWorld> Vector; + +public: + /*! + * \brief Constructor. Sets the upwind weight. + */ + TwoPNILocalResidual() + { + // retrieve the upwind weight for the mass conservation equations. Use the value + // specified via the property system as default, and overwrite + // it by the run-time parameter from the Dune::ParameterTree + massUpwindWeight_ = GET_PARAM_FROM_GROUP(TypeTag, Scalar, Implicit, MassUpwindWeight); + }; + + /*! + * \brief Evaluate the amount all conservation quantites + * (e.g. phase mass and energy storage) within a sub-control volume. + * + * The result should be averaged over the volume (e.g. phase mass + * inside a sub control volume divided by the volume) + * + * \param storage The phase mass within the sub-control volume + * \param scvIdx The SCV (sub-control-volume) index + * \param usePrevSol Evaluate function with solution of current or previous time step + */ + void computeStorage(PrimaryVariables &storage, int scvIdx, bool usePrevSol) const + { + // compute the storage term for phase mass + ParentType::computeStorage(storage, scvIdx, usePrevSol); + + // if flag usePrevSol is set, the solution from the previous + // time step is used, otherwise the current solution is + // used. The secondary variables are used accordingly. This + // is required to compute the derivative of the storage term + // using the implicit euler method. + const ElementVolumeVariables &elemVolVars = usePrevSol ? this->prevVolVars_() : this->curVolVars_(); + const VolumeVariables &volVars = elemVolVars[scvIdx]; + + // compute the energy storage + storage[temperatureIdx] = + volVars.porosity()*(volVars.density(wPhaseIdx) * + volVars.internalEnergy(wPhaseIdx) * + volVars.saturation(wPhaseIdx) + + + volVars.density(nPhaseIdx) * + volVars.internalEnergy(nPhaseIdx) * + volVars.saturation(nPhaseIdx)) + + volVars.temperature()*volVars.heatCapacity(); + } + + /*! + * \brief Evaluates the advective mass flux and the heat flux + * over a face of a subcontrol volume and writes the result in + * the flux vector. + * + * \param flux The advective flux over the sub-control-volume face for each phase + * \param fluxVars The flux variables at the current SCV + * + * + * This method is called by compute flux (base class) + */ + void computeAdvectiveFlux(PrimaryVariables &flux, + const FluxVariables &fluxVars) const + { + // advective mass flux + ParentType::computeAdvectiveFlux(flux, fluxVars); + + // advective heat flux in all phases + flux[energyEqIdx] = 0; + for (int phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) { + // data attached to upstream and the downstream vertices + // of the current phase + const VolumeVariables &up = this->curVolVars_(fluxVars.upstreamIdx(phaseIdx)); + const VolumeVariables &dn = this->curVolVars_(fluxVars.downstreamIdx(phaseIdx)); + + // add advective energy flux in current phase + flux[energyEqIdx] += + fluxVars.volumeFlux(phaseIdx) + * + (( massUpwindWeight_)* + up.density(phaseIdx)* + up.enthalpy(phaseIdx) + + + (1 - massUpwindWeight_)* + dn.density(phaseIdx)* + dn.enthalpy(phaseIdx)); + } + } + + /*! + * \brief Adds the diffusive heat flux to the flux vector over + * the face of a sub-control volume. + * + * \param flux The diffusive flux over the sub-control-volume face for each phase + * \param fluxVars The flux variables at the current SCV + * + */ + void computeDiffusiveFlux(PrimaryVariables &flux, + const FluxVariables &fluxVars) const + { + // diffusive mass flux + ParentType::computeDiffusiveFlux(flux, fluxVars); + + // diffusive heat flux + flux[energyEqIdx] += fluxVars.normalMatrixHeatFlux(); + } + +private: + Scalar massUpwindWeight_; + +}; + +} + +#endif diff --git a/dumux/implicit/2pni/2pnimodel.hh b/dumux/implicit/2pni/2pnimodel.hh new file mode 100644 index 0000000000..81352b3569 --- /dev/null +++ b/dumux/implicit/2pni/2pnimodel.hh @@ -0,0 +1,93 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \file + * + * \brief Adaption of the BOX scheme to the non-isothermal twophase flow model. + */ +#ifndef DUMUX_2PNI_MODEL_HH +#define DUMUX_2PNI_MODEL_HH + +#include <dumux/boxmodels/2p/2pmodel.hh> + +namespace Dumux { + +/*! + * \ingroup TwoPNIBoxModel + * \brief A two-phase, non-isothermal flow model using the box scheme. + * + * This model implements a non-isothermal two-phase flow for two + * immiscible fluids \f$\alpha \in \{ w, n \}\f$. Using the standard + * multiphase Darcy approach, the mass conservation equations for both + * phases can be described as follows: + * \f[ + \phi \frac{\partial \phi \varrho_\alpha S_\alpha}{\partial t} + - + \text{div} + \left\{ + \varrho_\alpha \frac{k_{r\alpha}}{\mu_\alpha} \mathrm{K} + \left( \textrm{grad}\, p_\alpha - \varrho_{\alpha} \mathbf{g} \right) + \right\} + - + q_\alpha = 0 \qquad \alpha \in \{w, n\} + \f] + * + * For the energy balance, local thermal equilibrium is assumed. This + * results in one energy conservation equation for the porous solid + * matrix and the fluids: + + \f{align*}{ + \frac{\partial \phi \sum_\alpha \varrho_\alpha u_\alpha S_\alpha}{\partial t} + & + + \left( 1 - \phi \right) \frac{\partial (\varrho_s c_s T)}{\partial t} + - + \sum_\alpha \text{div} + \left\{ + \varrho_\alpha h_\alpha + \frac{k_{r\alpha}}{\mu_\alpha} \mathbf{K} + \left( \textbf{grad}\,p_\alpha - \varrho_\alpha \mbox{\bf g} \right) + \right\} \\ + & - \text{div} \left(\lambda_{pm} \textbf{grad} \, T \right) + - q^h = 0, \qquad \alpha \in \{w, n\} \;, + \f} + * where \f$h_\alpha\f$ is the specific enthalpy of a fluid phase + * \f$\alpha\f$ and \f$u_\alpha = h_\alpha - + * p_\alpha/\varrho_\alpha\f$ is the specific internal energy of the + * phase. + * + * The equations are discretized using a fully-coupled vertex centered + * finite volume (box) scheme as spatial and the implicit Euler method + * as time discretization. + * + * Currently the model supports choosing either \f$p_w\f$, \f$S_n\f$ + * and \f$T\f$ or \f$p_n\f$, \f$S_w\f$ and \f$T\f$ as primary + * variables. The formulation which ought to be used can be specified + * by setting the <tt>Formulation</tt> property to either + * <tt>TwoPNIIndices::pWsN</tt> or <tt>TwoPIndices::pNsW</tt>. By + * default, the model uses \f$p_w\f$, \f$S_n\f$ and \f$T\f$. + */ +template<class TypeTag> +class TwoPNIModel: public TwoPModel<TypeTag> +{}; + +} + +#include "2pnipropertydefaults.hh" + +#endif diff --git a/dumux/implicit/2pni/2pniproperties.hh b/dumux/implicit/2pni/2pniproperties.hh new file mode 100644 index 0000000000..d58ba13095 --- /dev/null +++ b/dumux/implicit/2pni/2pniproperties.hh @@ -0,0 +1,47 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \ingroup Properties + * \ingroup BoxProperties + * \ingroup TwoPNIBoxModel + * \file + * + * \brief Defines the properties required for the non-isotherm two-phase BOX model. + */ +#ifndef DUMUX_2PNI_PROPERTIES_HH +#define DUMUX_2PNI_PROPERTIES_HH + +#include <dumux/boxmodels/2p/2pproperties.hh> + +namespace Dumux +{ + +namespace Properties +{ +////////////////////////////////////////////////////////////////// +// Type tags +////////////////////////////////////////////////////////////////// + +//! The type tag for the non-isothermal two-phase problems +NEW_TYPE_TAG(BoxTwoPNI, INHERITS_FROM(BoxTwoP)); + +} +} + +#endif diff --git a/dumux/implicit/2pni/2pnipropertydefaults.hh b/dumux/implicit/2pni/2pnipropertydefaults.hh new file mode 100644 index 0000000000..2a96f75eb1 --- /dev/null +++ b/dumux/implicit/2pni/2pnipropertydefaults.hh @@ -0,0 +1,72 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \ingroup Properties + * \ingroup BoxProperties + * \ingroup TwoPNIBoxModel + * \file + * + * \brief Defines the default values for most of the properties + * required by the non-isotherm two-phase box model. + */ + +#ifndef DUMUX_2PNI_PROPERTY_DEFAULTS_HH +#define DUMUX_2PNI_PROPERTY_DEFAULTS_HH + +#include "2pniproperties.hh" +#include "2pnimodel.hh" +#include "2pnilocalresidual.hh" +#include "2pnivolumevariables.hh" +#include "2pnifluxvariables.hh" +#include "2pniindices.hh" + +namespace Dumux +{ + + +namespace Properties +{ +////////////////////////////////////////////////////////////////// +// Property values +////////////////////////////////////////////////////////////////// + +SET_INT_PROP(BoxTwoPNI, NumEq, 3); //!< set the number of equations to 3 + +//! Use the 2pni local jacobian operator for the 2pni model +SET_TYPE_PROP(BoxTwoPNI, + LocalResidual, + TwoPNILocalResidual<TypeTag>); + +//! the Model property +SET_TYPE_PROP(BoxTwoPNI, Model, TwoPNIModel<TypeTag>); + +//! the VolumeVariables property +SET_TYPE_PROP(BoxTwoPNI, VolumeVariables, TwoPNIVolumeVariables<TypeTag>); + +//! the FluxVariables property +SET_TYPE_PROP(BoxTwoPNI, FluxVariables, TwoPNIFluxVariables<TypeTag>); + +//! The indices required by the non-isothermal two-phase model +SET_TYPE_PROP(BoxTwoPNI, Indices, TwoPNIIndices<TypeTag, 0>); + +} + +} + +#endif diff --git a/dumux/implicit/2pni/2pnivolumevariables.hh b/dumux/implicit/2pni/2pnivolumevariables.hh new file mode 100644 index 0000000000..eec5780365 --- /dev/null +++ b/dumux/implicit/2pni/2pnivolumevariables.hh @@ -0,0 +1,124 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \file + * + * \brief Contains the quantities which are are constant within a + * finite volume in the non-isothermal two-phase model. + */ +#ifndef DUMUX_2PNI_VOLUME_VARIABLES_HH +#define DUMUX_2PNI_VOLUME_VARIABLES_HH + +#include <dumux/boxmodels/2p/2pvolumevariables.hh> + +namespace Dumux +{ + +/*! + * \ingroup TwoPNIModel + * \ingroup BoxVolumeVariables + * \brief Contains the quantities which are constant within a + * finite volume in the non-isothermal two-phase model. + */ +template <class TypeTag> +class TwoPNIVolumeVariables : public TwoPVolumeVariables<TypeTag> +{ + typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; + typedef typename GET_PROP_TYPE(TypeTag, FVElementGeometry) FVElementGeometry; + typedef typename GET_PROP_TYPE(TypeTag, Problem) Problem; + typedef typename GET_PROP_TYPE(TypeTag, FluidSystem) FluidSystem; + typedef typename GET_PROP_TYPE(TypeTag, FluidState) FluidState; + typedef typename GET_PROP_TYPE(TypeTag, PrimaryVariables) PrimaryVariables; + + typedef typename GET_PROP_TYPE(TypeTag, Indices) Indices; + enum { temperatureIdx = Indices::temperatureIdx }; + + typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; + typedef typename GridView::template Codim<0>::Entity Element; + +public: + /*! + * \brief Returns the total internal energy of a phase in the + * sub-control volume. + * + * \param phaseIdx The phase index + * + */ + Scalar internalEnergy(int phaseIdx) const + { return this->fluidState_.internalEnergy(phaseIdx); }; + + /*! + * \brief Returns the total enthalpy of a phase in the sub-control + * volume. + * + * \param phaseIdx The phase index + */ + Scalar enthalpy(int phaseIdx) const + { return this->fluidState_.enthalpy(phaseIdx); }; + + /*! + * \brief Returns the total heat capacity \f$\mathrm{[J/K*m^3]}\f$ of the rock matrix in + * the sub-control volume. + */ + Scalar heatCapacity() const + { return heatCapacity_; }; + +protected: + // this method gets called by the parent class. since this method + // is protected, we are friends with our parent.. + friend class TwoPVolumeVariables<TypeTag>; + + static Scalar temperature_(const PrimaryVariables &priVars, + const Problem& problem, + const Element &element, + const FVElementGeometry &fvGeometry, + int scvIdx) + { + return priVars[Indices::temperatureIdx]; + } + + template<class ParameterCache> + static Scalar enthalpy_(const FluidState& fluidState, + const ParameterCache& paramCache, + int phaseIdx) + { + return FluidSystem::enthalpy(fluidState, paramCache, phaseIdx); + } + + /*! + * \brief Called by update() to compute the energy related quantities + */ + void updateEnergy_(const PrimaryVariables &priVars, + const Problem &problem, + const Element &element, + const FVElementGeometry &fvGeometry, + int scvIdx, + bool isOldSol) + { + // copmute and set the heat capacity of the solid phase + heatCapacity_ = problem.spatialParams().heatCapacity(element, fvGeometry, scvIdx); + Valgrind::CheckDefined(heatCapacity_); + } + + Scalar heatCapacity_; +}; + +} // end namepace + +#endif diff --git a/dumux/implicit/2pni/Makefile.am b/dumux/implicit/2pni/Makefile.am new file mode 100644 index 0000000000..5e4f338ee1 --- /dev/null +++ b/dumux/implicit/2pni/Makefile.am @@ -0,0 +1,4 @@ +2pnidir = $(includedir)/dumux/boxmodels/2pni +2pni_HEADERS = *.hh + +include $(top_srcdir)/am/global-rules diff --git a/dumux/implicit/3p3c/3p3cfluxvariables.hh b/dumux/implicit/3p3c/3p3cfluxvariables.hh new file mode 100644 index 0000000000..a687fb3d64 --- /dev/null +++ b/dumux/implicit/3p3c/3p3cfluxvariables.hh @@ -0,0 +1,335 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \file + * + * \brief This file contains the data which is required to calculate + * all fluxes of components over a face of a finite volume for + * the two-phase, two-component model. + */ +/*! + * \ingroup ThreePThreeCModel + */ +#ifndef DUMUX_3P3C_FLUX_VARIABLES_HH +#define DUMUX_3P3C_FLUX_VARIABLES_HH + +#include <dumux/common/math.hh> +#include <dumux/common/spline.hh> + +#include "3p3cproperties.hh" + +namespace Dumux +{ + +/*! + * \brief This template class contains the data which is required to + * calculate all fluxes of components over a face of a finite + * volume for the two-phase, two-component model. + * + * This means pressure and concentration gradients, phase densities at + * the integration point, etc. + */ +template <class TypeTag> +class ThreePThreeCFluxVariables : public GET_PROP_TYPE(TypeTag, BaseFluxVariables) +{ + typedef typename GET_PROP_TYPE(TypeTag, BaseFluxVariables) BaseFluxVariables; + typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; + typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; + + typedef typename GET_PROP_TYPE(TypeTag, Problem) Problem; + typedef typename GET_PROP_TYPE(TypeTag, VolumeVariables) VolumeVariables; + + typedef typename GridView::template Codim<0>::Entity Element; + typedef typename GET_PROP_TYPE(TypeTag, ElementVolumeVariables) ElementVolumeVariables; + + enum { + dim = GridView::dimension, + dimWorld = GridView::dimensionworld, + numPhases = GET_PROP_VALUE(TypeTag, NumPhases), + numComponents = GET_PROP_VALUE(TypeTag, NumComponents) + }; + + typedef typename GET_PROP_TYPE(TypeTag, FVElementGeometry) FVElementGeometry; + typedef typename GET_PROP_TYPE(TypeTag, SpatialParams) SpatialParams; + typedef typename FVElementGeometry::SubControlVolumeFace SCVFace; + + typedef Dune::FieldVector<Scalar, dim> DimVector; + typedef Dune::FieldMatrix<Scalar, dim, dim> DimMatrix; + + typedef typename GET_PROP_TYPE(TypeTag, Indices) Indices; + enum { + wPhaseIdx = Indices::wPhaseIdx, + nPhaseIdx = Indices::nPhaseIdx, + gPhaseIdx = Indices::gPhaseIdx, + + wCompIdx = Indices::wCompIdx, + nCompIdx = Indices::nCompIdx, + gCompIdx = Indices::gCompIdx + }; + +public: + /* + * \brief The constructor + * + * \param problem The problem + * \param element The finite element + * \param fvGeometry The finite-volume geometry in the box scheme + * \param faceIdx The local index of the SCV (sub-control-volume) face + * \param elemVolVars The volume variables of the current element + * \param onBoundary A boolean variable to specify whether the flux variables + * are calculated for interior SCV faces or boundary faces, default=false + */ + ThreePThreeCFluxVariables(const Problem &problem, + const Element &element, + const FVElementGeometry &fvGeometry, + const int faceIdx, + const ElementVolumeVariables &elemVolVars, + const bool onBoundary = false) + : BaseFluxVariables(problem, element, fvGeometry, faceIdx, elemVolVars, onBoundary) + { + for (int phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) { + density_[phaseIdx] = Scalar(0); + molarDensity_[phaseIdx] = Scalar(0); + massFractionCompWGrad_[phaseIdx] = Scalar(0); + massFractionCompNGrad_[phaseIdx] = Scalar(0); + massFractionCompGGrad_[phaseIdx] = Scalar(0); + moleFractionCompWGrad_[phaseIdx] = Scalar(0); + moleFractionCompNGrad_[phaseIdx] = Scalar(0); + moleFractionCompGGrad_[phaseIdx] = Scalar(0); + } + + calculateGradients_(problem, element, elemVolVars); + calculateporousDiffCoeff_(problem, element, elemVolVars); + }; + +private: + void calculateGradients_(const Problem &problem, + const Element &element, + const ElementVolumeVariables &elemVolVars) + { + // calculate gradients + DimVector tmp(0.0); + for (int idx = 0; + idx < this->fvGeometry_.numFAP; + idx++) // loop over adjacent vertices + { + // FE gradient at vertex idx + const DimVector &feGrad = this->face().grad[idx]; + + // index for the element volume variables + int volVarsIdx = this->face().fapIndices[idx]; + + // the concentration gradient of the components + // component in the phases + tmp = feGrad; + tmp *= elemVolVars[volVarsIdx].fluidState().massFraction(wPhaseIdx, wCompIdx); + massFractionCompWGrad_[wPhaseIdx] += tmp; + + tmp = feGrad; + tmp *= elemVolVars[volVarsIdx].fluidState().massFraction(nPhaseIdx, wCompIdx); + massFractionCompWGrad_[nPhaseIdx] += tmp; + + tmp = feGrad; + tmp *= elemVolVars[volVarsIdx].fluidState().massFraction(gPhaseIdx, wCompIdx); + massFractionCompWGrad_[gPhaseIdx] += tmp; + + tmp = feGrad; + tmp *= elemVolVars[volVarsIdx].fluidState().massFraction(wPhaseIdx, nCompIdx); + massFractionCompNGrad_[wPhaseIdx] += tmp; + + tmp = feGrad; + tmp *= elemVolVars[volVarsIdx].fluidState().massFraction(nPhaseIdx, nCompIdx); + massFractionCompNGrad_[nPhaseIdx] += tmp; + + tmp = feGrad; + tmp *= elemVolVars[volVarsIdx].fluidState().massFraction(gPhaseIdx, nCompIdx); + massFractionCompNGrad_[gPhaseIdx] += tmp; + + tmp = feGrad; + tmp *= elemVolVars[volVarsIdx].fluidState().massFraction(wPhaseIdx, gCompIdx); + massFractionCompGGrad_[wPhaseIdx] += tmp; + + tmp = feGrad; + tmp *= elemVolVars[volVarsIdx].fluidState().massFraction(nPhaseIdx, gCompIdx); + massFractionCompGGrad_[nPhaseIdx] += tmp; + + tmp = feGrad; + tmp *= elemVolVars[volVarsIdx].fluidState().massFraction(gPhaseIdx, gCompIdx); + massFractionCompGGrad_[gPhaseIdx] += tmp; + + // the molar concentration gradients of the components + // in the phases + tmp = feGrad; + tmp *= elemVolVars[volVarsIdx].fluidState().moleFraction(wPhaseIdx, wCompIdx); + moleFractionCompWGrad_[wPhaseIdx] += tmp; + + tmp = feGrad; + tmp *= elemVolVars[volVarsIdx].fluidState().moleFraction(nPhaseIdx, wCompIdx); + moleFractionCompWGrad_[nPhaseIdx] += tmp; + + tmp = feGrad; + tmp *= elemVolVars[volVarsIdx].fluidState().moleFraction(gPhaseIdx, wCompIdx); + moleFractionCompWGrad_[gPhaseIdx] += tmp; + + tmp = feGrad; + tmp *= elemVolVars[volVarsIdx].fluidState().moleFraction(wPhaseIdx, nCompIdx); + moleFractionCompNGrad_[wPhaseIdx] += tmp; + + tmp = feGrad; + tmp *= elemVolVars[volVarsIdx].fluidState().moleFraction(nPhaseIdx, nCompIdx); + moleFractionCompNGrad_[nPhaseIdx] += tmp; + + tmp = feGrad; + tmp *= elemVolVars[volVarsIdx].fluidState().moleFraction(gPhaseIdx, nCompIdx); + moleFractionCompNGrad_[gPhaseIdx] += tmp; + + tmp = feGrad; + tmp *= elemVolVars[volVarsIdx].fluidState().moleFraction(wPhaseIdx, gCompIdx); + moleFractionCompGGrad_[wPhaseIdx] += tmp; + + tmp = feGrad; + tmp *= elemVolVars[volVarsIdx].fluidState().moleFraction(nPhaseIdx, gCompIdx); + moleFractionCompGGrad_[nPhaseIdx] += tmp; + + tmp = feGrad; + tmp *= elemVolVars[volVarsIdx].fluidState().moleFraction(gPhaseIdx, gCompIdx); + moleFractionCompGGrad_[gPhaseIdx] += tmp; + } + } + + Scalar rhoFactor_(int phaseIdx, int scvIdx, const ElementVolumeVariables &elemVolVars) + { + static const Scalar eps = 1e-2; + const Scalar sat = elemVolVars[scvIdx].density(phaseIdx); + if (sat > eps) + return 0.5; + if (sat <= 0) + return 0; + + static const Dumux::Spline<Scalar> sp(0, eps, // x0, x1 + 0, 0.5, // y0, y1 + 0, 0); // m0, m1 + return sp.eval(sat); + } + + void calculateporousDiffCoeff_(const Problem &problem, + const Element &element, + const ElementVolumeVariables &elemVolVars) + { + + const VolumeVariables &volVarsI = elemVolVars[this->face().i]; + const VolumeVariables &volVarsJ = elemVolVars[this->face().j]; + + Dune::FieldMatrix<Scalar, numPhases, numComponents> diffusionCoefficientMatrix_i = volVarsI.diffusionCoefficient(); + Dune::FieldMatrix<Scalar, numPhases, numComponents> diffusionCoefficientMatrix_j = volVarsJ.diffusionCoefficient(); + + for (int phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) + { + // make sure to calculate only diffusion coefficents + // for phases which exist in both finite volumes + /* \todo take care: This should be discussed once again + * as long as a meaningful value can be found for the required mole fraction + * diffusion should work even without this one here */ + if (volVarsI.saturation(phaseIdx) <= 0 || + volVarsJ.saturation(phaseIdx) <= 0) + { + porousDiffCoeff_[phaseIdx][wCompIdx] = 0.0; + porousDiffCoeff_[phaseIdx][nCompIdx] = 0.0; + porousDiffCoeff_[phaseIdx][gCompIdx] = 0.0; + continue; + } + + // calculate tortuosity at the nodes i and j needed + // for porous media diffusion coefficient + + Scalar tauI = + 1.0/(volVarsI.porosity() * volVarsI.porosity()) * + pow(volVarsI.porosity() * volVarsI.saturation(phaseIdx), 7.0/3); + Scalar tauJ = + 1.0/(volVarsJ.porosity() * volVarsJ.porosity()) * + pow(volVarsJ.porosity() * volVarsJ.saturation(phaseIdx), 7.0/3); + // Diffusion coefficient in the porous medium + + // -> harmonic mean + porousDiffCoeff_[phaseIdx][wCompIdx] = harmonicMean(volVarsI.porosity() * volVarsI.saturation(phaseIdx) * tauI * diffusionCoefficientMatrix_i[phaseIdx][wCompIdx], + volVarsJ.porosity() * volVarsJ.saturation(phaseIdx) * tauJ * diffusionCoefficientMatrix_j[phaseIdx][wCompIdx]); + porousDiffCoeff_[phaseIdx][nCompIdx] = harmonicMean(volVarsI.porosity() * volVarsI.saturation(phaseIdx) * tauI * diffusionCoefficientMatrix_i[phaseIdx][nCompIdx], + volVarsJ.porosity() * volVarsJ.saturation(phaseIdx) * tauJ * diffusionCoefficientMatrix_j[phaseIdx][nCompIdx]); + porousDiffCoeff_[phaseIdx][gCompIdx] = harmonicMean(volVarsI.porosity() * volVarsI.saturation(phaseIdx) * tauI * diffusionCoefficientMatrix_i[phaseIdx][gCompIdx], + volVarsJ.porosity() * volVarsJ.saturation(phaseIdx) * tauJ * diffusionCoefficientMatrix_j[phaseIdx][gCompIdx]); + + } + } + +public: + /*! + * \brief The diffusivity matrix + */ + Dune::FieldMatrix<Scalar, numPhases, numComponents> porousDiffCoeff() const + { return porousDiffCoeff_; }; + + /*! + * \brief Return density \f$\mathrm{[kg/m^3]}\f$ of a phase. + */ + Scalar density(int phaseIdx) const + { return density_[phaseIdx]; } + + /*! + * \brief Return molar density \f$\mathrm{[mol/m^3]}\f$ of a phase. + */ + Scalar molarDensity(int phaseIdx) const + { return molarDensity_[phaseIdx]; } + + const DimVector &massFractionCompWGrad(int phaseIdx) const + {return massFractionCompWGrad_[phaseIdx];} + + const DimVector &massFractionCompNGrad(int phaseIdx) const + { return massFractionCompNGrad_[phaseIdx]; }; + + const DimVector &massFractionCompGGrad(int phaseIdx) const + { return massFractionCompGGrad_[phaseIdx]; }; + + const DimVector &moleFractionCompWGrad(int phaseIdx) const + { return moleFractionCompWGrad_[phaseIdx]; }; + + const DimVector &moleFractionCompNGrad(int phaseIdx) const + { return moleFractionCompNGrad_[phaseIdx]; }; + + const DimVector &moleFractionCompGGrad(int phaseIdx) const + { return moleFractionCompGGrad_[phaseIdx]; }; + +protected: + // gradients + DimVector massFractionCompWGrad_[numPhases]; + DimVector massFractionCompNGrad_[numPhases]; + DimVector massFractionCompGGrad_[numPhases]; + DimVector moleFractionCompWGrad_[numPhases]; + DimVector moleFractionCompNGrad_[numPhases]; + DimVector moleFractionCompGGrad_[numPhases]; + + // density of each face at the integration point + Scalar density_[numPhases], molarDensity_[numPhases]; + + // the diffusivity matrix for the porous medium + Dune::FieldMatrix<Scalar, numPhases, numComponents> porousDiffCoeff_; +}; + +} // end namepace + +#endif diff --git a/dumux/implicit/3p3c/3p3cindices.hh b/dumux/implicit/3p3c/3p3cindices.hh new file mode 100644 index 0000000000..33935edd82 --- /dev/null +++ b/dumux/implicit/3p3c/3p3cindices.hh @@ -0,0 +1,85 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \file + * + * \brief Defines the indices required for the 3p3c BOX model. + */ +#ifndef DUMUX_3P3C_INDICES_HH +#define DUMUX_3P3C_INDICES_HH + +#include "3p3cproperties.hh" + +namespace Dumux +{ + +/*! + * \brief The indices for the isothermal ThreePThreeC model. + * + * \tparam formulation The formulation, only pgSwSn + * \tparam PVOffset The first index in a primary variable vector. + */ +template <class TypeTag, int PVOffset = 0> +class ThreePThreeCIndices +{ + typedef typename GET_PROP_TYPE(TypeTag, FluidSystem) FluidSystem; + +public: + // Phase indices + static const int wPhaseIdx = FluidSystem::wPhaseIdx; //!< index of the wetting liquid phase + static const int nPhaseIdx = FluidSystem::nPhaseIdx; //!< index of the nonwetting liquid phase + static const int gPhaseIdx = FluidSystem::gPhaseIdx; //!< index of the gas phase + + // Component indices to indicate the main component + // of the corresponding phase at atmospheric pressure 1 bar + // and room temperature 20°C: + static const int wCompIdx = FluidSystem::wCompIdx; + static const int nCompIdx = FluidSystem::nCompIdx; + static const int gCompIdx = FluidSystem::gCompIdx; + + // present phases (-> 'pseudo' primary variable) + static const int threePhases = 1; //!< All three phases are present + static const int wPhaseOnly = 2; //!< Only the water phase is present + static const int gnPhaseOnly = 3; //!< Only gas and NAPL phases are present + static const int wnPhaseOnly = 4; //!< Only water and NAPL phases are present + static const int gPhaseOnly = 5; //!< Only gas phase is present + static const int wgPhaseOnly = 6; //!< Only water and gas phases are present + + // Primary variable indices + static const int pressureIdx = PVOffset + 0; //!< Index for gas phase pressure in a solution vector + static const int switch1Idx = PVOffset + 1; //!< Index 1 of saturation or mole fraction + static const int switch2Idx = PVOffset + 2; //!< Index 2 of saturation or mole fraction + + static const int pgIdx = pressureIdx; //!< Index for gas phase pressure in a solution vector + static const int SOrX1Idx = switch1Idx; //!< Index of the either the saturation of the gas phase or the mass fraction secondary component if a phase is not present + static const int SOrX2Idx = switch2Idx; //!< Index of the either the saturation of the gas phase or the mass fraction secondary component if a phase is not present + + // equation indices + static const int conti0EqIdx = PVOffset + wCompIdx; //!< Index of the mass conservation equation for the water component + static const int conti1EqIdx = conti0EqIdx + nCompIdx; //!< Index of the mass conservation equation for the contaminant component + static const int conti2EqIdx = conti0EqIdx + gCompIdx; //!< Index of the mass conservation equation for the gas component + + static const int contiWEqIdx = conti0EqIdx + wCompIdx; //!< index of the mass conservation equation for the water component + static const int contiNEqIdx = conti0EqIdx + nCompIdx; //!< index of the mass conservation equation for the contaminant component + static const int contiGEqIdx = conti0EqIdx + gCompIdx; //!< index of the mass conservation equation for the air component +}; + +} + +#endif diff --git a/dumux/implicit/3p3c/3p3clocalresidual.hh b/dumux/implicit/3p3c/3p3clocalresidual.hh new file mode 100644 index 0000000000..03b0e2474f --- /dev/null +++ b/dumux/implicit/3p3c/3p3clocalresidual.hh @@ -0,0 +1,264 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \file + * + * \brief Element-wise calculation of the Jacobian matrix for problems + * using the two-phase two-component box model. + */ +#ifndef DUMUX_3P3C_LOCAL_RESIDUAL_HH +#define DUMUX_3P3C_LOCAL_RESIDUAL_HH + +#include <dumux/boxmodels/common/boxmodel.hh> +#include <dumux/common/math.hh> + +#include "3p3cproperties.hh" +#include "3p3cvolumevariables.hh" +#include "3p3cfluxvariables.hh" +#include "3p3cnewtoncontroller.hh" +#include "3p3cproperties.hh" + +#include <iostream> +#include <vector> + +//#define VELOCITY_OUTPUT 1 // uncomment this line if an output of the velocity is needed + +namespace Dumux +{ +/*! + * \ingroup ThreePThreeCModel + * \brief Element-wise calculation of the Jacobian matrix for problems + * using the two-phase two-component box model. + * + * This class is used to fill the gaps in BoxLocalResidual for the 3P3C flow. + */ +template<class TypeTag> +class ThreePThreeCLocalResidual: public GET_PROP_TYPE(TypeTag, BaseLocalResidual) +{ +protected: + typedef typename GET_PROP_TYPE(TypeTag, LocalResidual) Implementation; + + typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; + typedef typename GET_PROP_TYPE(TypeTag, PrimaryVariables) PrimaryVariables; + typedef typename GET_PROP_TYPE(TypeTag, Indices) Indices; + + enum { + numPhases = GET_PROP_VALUE(TypeTag, NumPhases), + numComponents = GET_PROP_VALUE(TypeTag, NumComponents), + + conti0EqIdx = Indices::conti0EqIdx,//!< Index of the mass conservation equation for the water component + conti1EqIdx = Indices::conti1EqIdx,//!< Index of the mass conservation equation for the contaminant component + conti2EqIdx = Indices::conti2EqIdx,//!< Index of the mass conservation equation for the gas component + + wPhaseIdx = Indices::wPhaseIdx, + nPhaseIdx = Indices::nPhaseIdx, + gPhaseIdx = Indices::gPhaseIdx, + + wCompIdx = Indices::wCompIdx, + nCompIdx = Indices::nCompIdx, + gCompIdx = Indices::gCompIdx + }; + + typedef typename GET_PROP_TYPE(TypeTag, VolumeVariables) VolumeVariables; + typedef typename GET_PROP_TYPE(TypeTag, ElementVolumeVariables) ElementVolumeVariables; + typedef typename GET_PROP_TYPE(TypeTag, FluxVariables) FluxVariables; + +public: + /*! + * \brief Evaluate the amount all conservation quantities + * (e.g. phase mass) within a sub-control volume. + * + * The result should be averaged over the volume (e.g. phase mass + * inside a sub control volume divided by the volume) + * + * \param storage The mass of the component within the sub-control volume + * \param scvIdx The SCV (sub-control-volume) index + * \param usePrevSol Evaluate function with solution of current or previous time step + */ + void computeStorage(PrimaryVariables &storage, const int scvIdx, bool usePrevSol) const + { + // if flag usePrevSol is set, the solution from the previous + // time step is used, otherwise the current solution is + // used. The secondary variables are used accordingly. This + // is required to compute the derivative of the storage term + // using the implicit euler method. + const ElementVolumeVariables &elemVolVars = + usePrevSol + ? this->prevVolVars_() + : this->curVolVars_(); + const VolumeVariables &volVars = elemVolVars[scvIdx]; + + // compute storage term of all components within all phases + storage = 0; + for (int compIdx = 0; compIdx < numComponents; ++compIdx) + { + for (int phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) + { + storage[conti0EqIdx + compIdx] += + volVars.porosity() + * volVars.saturation(phaseIdx) + * volVars.molarDensity(phaseIdx) + * volVars.fluidState().moleFraction(phaseIdx, compIdx); + } + } + } + + /*! + * \brief Evaluates the total flux of all conservation quantities + * over a face of a sub-control volume. + * + * \param flux The flux over the SCV (sub-control-volume) face for each component + * \param faceIdx The index of the SCV face + * \param onBoundary A boolean variable to specify whether the flux variables + * are calculated for interior SCV faces or boundary faces, default=false + */ + void computeFlux(PrimaryVariables &flux, const int faceIdx, const bool onBoundary=false) const + { + FluxVariables fluxVars(this->problem_(), + this->element_(), + this->fvGeometry_(), + faceIdx, + this->curVolVars_(), + onBoundary); + + flux = 0; + asImp_()->computeAdvectiveFlux(flux, fluxVars); + asImp_()->computeDiffusiveFlux(flux, fluxVars); + } + + /*! + * \brief Evaluates the advective mass flux of all components over + * a face of a subcontrol volume. + * + * \param flux The advective flux over the sub-control-volume face for each component + * \param fluxVars The flux variables at the current SCV + */ + + void computeAdvectiveFlux(PrimaryVariables &flux, const FluxVariables &fluxVars) const + { + Scalar massUpwindWeight = GET_PARAM_FROM_GROUP(TypeTag, Scalar, Implicit, MassUpwindWeight); + + //////// + // advective fluxes of all components in all phases + //////// + for (int phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) + { + // data attached to upstream and the downstream vertices + // of the current phase + const VolumeVariables &up = this->curVolVars_(fluxVars.upstreamIdx(phaseIdx)); + const VolumeVariables &dn = this->curVolVars_(fluxVars.downstreamIdx(phaseIdx)); + + for (int compIdx = 0; compIdx < numComponents; ++compIdx) + { + // add advective flux of current component in current + // phase + // if alpha > 0 und alpha < 1 then both upstream and downstream + // nodes need their contribution + // if alpha == 1 (which is mostly the case) then, the downstream + // node is not evaluated + int eqIdx = conti0EqIdx + compIdx; + flux[eqIdx] += fluxVars.volumeFlux(phaseIdx) + * (massUpwindWeight + * up.fluidState().molarDensity(phaseIdx) + * up.fluidState().moleFraction(phaseIdx, compIdx) + + + (1.0 - massUpwindWeight) + * dn.fluidState().molarDensity(phaseIdx) + * dn.fluidState().moleFraction(phaseIdx, compIdx)); + } + } + } + + /*! + * \brief Adds the diffusive mass flux of all components over + * a face of a subcontrol volume. + * + * \param flux The diffusive flux over the sub-control-volume face for each component + * \param fluxVars The flux variables at the current SCV + */ + + void computeDiffusiveFlux(PrimaryVariables &flux, const FluxVariables &fluxVars) const + { + // TODO: reference!? Dune::FieldMatrix<Scalar, numPhases, numComponents> averagedPorousDiffCoeffMatrix = fluxVars.porousDiffCoeff(); + // add diffusive flux of gas component in liquid phase + Scalar tmp = - fluxVars.porousDiffCoeff()[wPhaseIdx][gCompIdx] * fluxVars.molarDensity(wPhaseIdx); + tmp *= (fluxVars.moleFractionCompGGrad(wPhaseIdx) * fluxVars.face().normal); + Scalar jGW = tmp; + + tmp = - fluxVars.porousDiffCoeff()[wPhaseIdx][nCompIdx] * fluxVars.molarDensity(wPhaseIdx); + tmp *= (fluxVars.moleFractionCompNGrad(wPhaseIdx) * fluxVars.face().normal); + Scalar jNW = tmp; + + Scalar jWW = -(jGW+jNW); + + tmp = - fluxVars.porousDiffCoeff()[gPhaseIdx][wCompIdx] * fluxVars.molarDensity(gPhaseIdx); + tmp *= (fluxVars.moleFractionCompWGrad(gPhaseIdx) * fluxVars.face().normal); + Scalar jWG = tmp; + + tmp = - fluxVars.porousDiffCoeff()[gPhaseIdx][nCompIdx] * fluxVars.molarDensity(gPhaseIdx); + tmp *= (fluxVars.moleFractionCompNGrad(gPhaseIdx) * fluxVars.face().normal); + Scalar jNG = tmp; + + Scalar jGG = -(jWG+jNG); + + tmp = - fluxVars.porousDiffCoeff()[nPhaseIdx][wCompIdx] * fluxVars.molarDensity(nPhaseIdx); + tmp *= (fluxVars.moleFractionCompWGrad(nPhaseIdx) * fluxVars.face().normal); + Scalar jWN = tmp; + + tmp = - fluxVars.porousDiffCoeff()[nPhaseIdx][gCompIdx] * fluxVars.molarDensity(nPhaseIdx); + tmp *= (fluxVars.moleFractionCompGGrad(nPhaseIdx) * fluxVars.face().normal); + Scalar jGN = tmp; + + Scalar jNN = -(jGN+jWN); + + flux[conti0EqIdx] += jWW+jWG+jWN; + flux[conti1EqIdx] += jNW+jNG+jNN; + flux[conti2EqIdx] += jGW+jGG+jGN; + } + + /*! + * \brief Calculate the source term of the equation + * + * \param source The source/sink in the SCV for each component + * \param scvIdx The index of the SCV + */ + void computeSource(PrimaryVariables &source, const int scvIdx) + { + this->problem_().boxSDSource(source, + this->element_(), + this->fvGeometry_(), + scvIdx, + this->curVolVars_()); + } + +protected: + Implementation *asImp_() + { + return static_cast<Implementation *> (this); + } + + const Implementation *asImp_() const + { + return static_cast<const Implementation *> (this); + } +}; + +} // end namepace + +#endif diff --git a/dumux/implicit/3p3c/3p3cmodel.hh b/dumux/implicit/3p3c/3p3cmodel.hh new file mode 100644 index 0000000000..c12cadbb93 --- /dev/null +++ b/dumux/implicit/3p3c/3p3cmodel.hh @@ -0,0 +1,952 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \file + * + * \brief Adaption of the BOX scheme to the three-phase three-component flow model. + * + * The model is designed for simulating three fluid phases with water, gas, and + * a liquid contaminant (NAPL - non-aqueous phase liquid) + */ +#ifndef DUMUX_3P3C_MODEL_HH +#define DUMUX_3P3C_MODEL_HH + +#include <dumux/material/fluidstates/compositionalfluidstate.hh> + +#include "3p3cproperties.hh" +#include "3p3clocalresidual.hh" + +namespace Dumux +{ +/*! + * \ingroup ThreePThreeCModel + * \brief Adaption of the BOX scheme to the three-phase three-component flow model. + * + * This model implements three-phase three-component flow of three fluid phases + * \f$\alpha \in \{ water, gas, NAPL \}\f$ each composed of up to three components + * \f$\kappa \in \{ water, air, contaminant \}\f$. The standard multiphase Darcy + * approach is used as the equation for the conservation of momentum: + * \f[ + v_\alpha = - \frac{k_{r\alpha}}{\mu_\alpha} \mbox{\bf K} + \left(\text{grad}\, p_\alpha - \varrho_{\alpha} \mbox{\bf g} \right) + * \f] + * + * By inserting this into the equations for the conservation of the + * components, one transport equation for each component is obtained as + * \f{eqnarray*} + && \phi \frac{\partial (\sum_\alpha \varrho_{\text{mol}, \alpha} x_\alpha^\kappa + S_\alpha )}{\partial t} + - \sum\limits_\alpha \text{div} \left\{ \frac{k_{r\alpha}}{\mu_\alpha} + \varrho_{\text{mol}, \alpha} x_\alpha^\kappa \mbox{\bf K} + (\text{grad}\, p_\alpha - \varrho_{\text{mass}, \alpha} \mbox{\bf g}) \right\} + \nonumber \\ + \nonumber \\ + && - \sum\limits_\alpha \text{div} \left\{ D_{pm}^\kappa \varrho_{\text{mol}, + \alpha } \text{grad}\, x_\alpha^\kappa \right\} + - q^\kappa = 0 \qquad \forall \kappa , \; \forall \alpha + \f} + * + * Note that these balance equations are molar. + * + * The equations are discretized using a fully-coupled vertex + * centered finite volume (BOX) scheme as spatial scheme and + * the implicit Euler method as temporal discretization. + * + * The model uses commonly applied auxiliary conditions like + * \f$S_w + S_n + S_g = 1\f$ for the saturations and + * \f$x^w_\alpha + x^a_\alpha + x^c_\alpha = 1\f$ for the mole fractions. + * Furthermore, the phase pressures are related to each other via + * capillary pressures between the fluid phases, which are functions of + * the saturation, e.g. according to the approach of Parker et al. + * + * The used primary variables are dependent on the locally present fluid phases + * An adaptive primary variable switch is included. The phase state is stored for all nodes + * of the system. The following cases can be distinguished: + * <ul> + * <li> All three phases are present: Primary variables are two saturations \f$(S_w\f$ and \f$S_n)\f$, and a pressure, in this case \f$p_g\f$. </li> + * <li> Only the water phase is present: Primary variables are now the mole fractions of air and contaminant in the water phase \f$(x_w^a\f$ and \f$x_w^c)\f$, as well as the gas pressure, which is, of course, in a case where only the water phase is present, just the same as the water pressure. </li> + * <li> Gas and NAPL phases are present: Primary variables \f$(S_n\f$, \f$x_g^w\f$, \f$p_g)\f$. </li> + * <li> Water and NAPL phases are present: Primary variables \f$(S_n\f$, \f$x_w^a\f$, \f$p_g)\f$. </li> + * <li> Only gas phase is present: Primary variables \f$(x_g^w\f$, \f$x_g^c\f$, \f$p_g)\f$. </li> + * <li> Water and gas phases are present: Primary variables \f$(S_w\f$, \f$x_w^g\f$, \f$p_g)\f$. </li> + * </ul> + */ +template<class TypeTag> +class ThreePThreeCModel: public GET_PROP_TYPE(TypeTag, BaseModel) +{ + typedef typename GET_PROP_TYPE(TypeTag, BaseModel) ParentType; + + typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; + typedef typename GET_PROP_TYPE(TypeTag, Problem) Problem; + typedef typename GET_PROP_TYPE(TypeTag, FluidSystem) FluidSystem; + typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; + + typedef typename GET_PROP_TYPE(TypeTag, FVElementGeometry) FVElementGeometry; + typedef typename GET_PROP_TYPE(TypeTag, PrimaryVariables) PrimaryVariables; + typedef typename GET_PROP_TYPE(TypeTag, VolumeVariables) VolumeVariables; + typedef typename GET_PROP_TYPE(TypeTag, SolutionVector) SolutionVector; + typedef typename GET_PROP_TYPE(TypeTag, Indices) Indices; + + enum { + dim = GridView::dimension, + dimWorld = GridView::dimensionworld, + + numPhases = GET_PROP_VALUE(TypeTag, NumPhases), + numComponents = GET_PROP_VALUE(TypeTag, NumComponents), + + pressureIdx = Indices::pressureIdx, + switch1Idx = Indices::switch1Idx, + switch2Idx = Indices::switch2Idx, + + + wPhaseIdx = Indices::wPhaseIdx, + nPhaseIdx = Indices::nPhaseIdx, + gPhaseIdx = Indices::gPhaseIdx, + + wCompIdx = Indices::wCompIdx, + nCompIdx = Indices::nCompIdx, + gCompIdx = Indices::gCompIdx, + + threePhases = Indices::threePhases, + wPhaseOnly = Indices::wPhaseOnly, + gnPhaseOnly = Indices::gnPhaseOnly, + wnPhaseOnly = Indices::wnPhaseOnly, + gPhaseOnly = Indices::gPhaseOnly, + wgPhaseOnly = Indices::wgPhaseOnly + + }; + + + typedef typename GridView::template Codim<dim>::Entity Vertex; + typedef typename GridView::template Codim<0>::Entity Element; + typedef typename GridView::template Codim<0>::Iterator ElementIterator; + typedef typename GridView::template Codim<dim>::Iterator VertexIterator; + + typedef Dune::FieldVector<Scalar, dimWorld> GlobalPosition; +public: + /*! + * \brief Initialize the static data with the initial solution. + * + * \param problem The problem to be solved + */ + void init(Problem &problem) + { + ParentType::init(problem); + + staticVertexDat_.resize(this->gridView_().size(dim)); + + setSwitched_(false); + + VertexIterator it = this->gridView_().template begin<dim> (); + const VertexIterator &endit = this->gridView_().template end<dim> (); + for (; it != endit; ++it) + { + int globalIdx = this->dofMapper().map(*it); + const GlobalPosition &globalPos = it->geometry().corner(0); + + // initialize phase presence + staticVertexDat_[globalIdx].phasePresence + = this->problem_().initialPhasePresence(*it, globalIdx, + globalPos); + staticVertexDat_[globalIdx].wasSwitched = false; + + staticVertexDat_[globalIdx].oldPhasePresence + = staticVertexDat_[globalIdx].phasePresence; + } + } + + /*! + * \brief Compute the total storage inside one phase of all + * conservation quantities. + * + * \param dest Contains the storage of each component for one phase + * \param phaseIdx The phase index + */ + void globalPhaseStorage(PrimaryVariables &dest, int phaseIdx) + { + dest = 0; + + ElementIterator elemIt = this->gridView_().template begin<0>(); + const ElementIterator elemEndIt = this->gridView_().template end<0>(); + for (; elemIt != elemEndIt; ++elemIt) { + this->localResidual().evalPhaseStorage(*elemIt, phaseIdx); + + for (int i = 0; i < elemIt->template count<dim>(); ++i) + dest += this->localResidual().residual(i); + } + + if (this->gridView_().comm().size() > 1) + dest = this->gridView_().comm().sum(dest); + } + + /*! + * \brief Called by the update() method if applying the newton + * method was unsuccessful. + */ + void updateFailed() + { + ParentType::updateFailed(); + + setSwitched_(false); + resetPhasePresence_(); + }; + + /*! + * \brief Called by the problem if a time integration was + * successful, post processing of the solution is done and the + * result has been written to disk. + * + * This should prepare the model for the next time integration. + */ + void advanceTimeLevel() + { + ParentType::advanceTimeLevel(); + + // update the phase state + updateOldPhasePresence_(); + setSwitched_(false); + } + + /*! + * \brief Return true if the primary variables were switched for + * at least one vertex after the last timestep. + */ + bool switched() const + { + return switchFlag_; + } + + /*! + * \brief Returns the phase presence of the current or the old solution of a vertex. + * + * \param globalVertexIdx The global vertex index + * \param oldSol Evaluate function with solution of current or previous time step + */ + int phasePresence(int globalVertexIdx, bool oldSol) const + { + return + oldSol + ? staticVertexDat_[globalVertexIdx].oldPhasePresence + : staticVertexDat_[globalVertexIdx].phasePresence; + } + + /*! + * \brief Append all quantities of interest which can be derived + * from the solution of the current time step to the VTK + * writer. + * + * \param sol The solution vector + * \param writer The writer for multi-file VTK datasets + */ + template<class MultiWriter> + void addOutputVtkFields(const SolutionVector &sol, + MultiWriter &writer) + { + typedef Dune::BlockVector<Dune::FieldVector<double, 1> > ScalarField; + + // create the required scalar fields + unsigned numVertices = this->problem_().gridView().size(dim); + + ScalarField *saturation[numPhases]; + ScalarField *pressure[numPhases]; + ScalarField *density[numPhases]; + + for (int phaseIdx = 0; phaseIdx < numPhases; ++ phaseIdx) { + saturation[phaseIdx] = writer.allocateManagedBuffer(numVertices); + pressure[phaseIdx] = writer.allocateManagedBuffer(numVertices); + density[phaseIdx] = writer.allocateManagedBuffer(numVertices); + } + + ScalarField *phasePresence = writer.allocateManagedBuffer (numVertices); + ScalarField *moleFraction[numPhases][numComponents]; + for (int i = 0; i < numPhases; ++i) + for (int j = 0; j < numComponents; ++j) + moleFraction[i][j] = writer.allocateManagedBuffer (numVertices); + ScalarField *temperature = writer.allocateManagedBuffer (numVertices); + ScalarField *poro = writer.allocateManagedBuffer(numVertices); + ScalarField *perm = writer.allocateManagedBuffer(numVertices); + + unsigned numElements = this->gridView_().size(0); + ScalarField *rank = + writer.allocateManagedBuffer (numElements); + + FVElementGeometry fvGeometry; + VolumeVariables volVars; + + ElementIterator elemIt = this->gridView_().template begin<0>(); + ElementIterator elemEndIt = this->gridView_().template end<0>(); + for (; elemIt != elemEndIt; ++elemIt) + { + int idx = this->problem_().elementMapper().map(*elemIt); + (*rank)[idx] = this->gridView_().comm().rank(); + fvGeometry.update(this->gridView_(), *elemIt); + + int numVerts = elemIt->template count<dim> (); + for (int i = 0; i < numVerts; ++i) + { + int globalIdx = this->vertexMapper().map(*elemIt, i, dim); + volVars.update(sol[globalIdx], + this->problem_(), + *elemIt, + fvGeometry, + i, + false); + + for (int phaseIdx = 0; phaseIdx < numPhases; ++ phaseIdx) { + (*saturation[phaseIdx])[globalIdx] = volVars.fluidState().saturation(phaseIdx); + (*pressure[phaseIdx])[globalIdx] = volVars.fluidState().pressure(phaseIdx); + (*density[phaseIdx])[globalIdx] = volVars.fluidState().density(phaseIdx); + } + + for (int phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) { + for (int compIdx = 0; compIdx < numComponents; ++compIdx) { + (*moleFraction[phaseIdx][compIdx])[globalIdx] = + volVars.fluidState().moleFraction(phaseIdx, + compIdx); + + Valgrind::CheckDefined((*moleFraction[phaseIdx][compIdx])[globalIdx]); + } + } + + (*poro)[globalIdx] = volVars.porosity(); + (*perm)[globalIdx] = volVars.permeability(); + (*temperature)[globalIdx] = volVars.temperature(); + (*phasePresence)[globalIdx] = staticVertexDat_[globalIdx].phasePresence; + } + + } + + writer.attachVertexData(*saturation[wPhaseIdx], "Sw"); + writer.attachVertexData(*saturation[nPhaseIdx], "Sn"); + writer.attachVertexData(*saturation[gPhaseIdx], "Sg"); + writer.attachVertexData(*pressure[wPhaseIdx], "pw"); + writer.attachVertexData(*pressure[nPhaseIdx], "pn"); + writer.attachVertexData(*pressure[gPhaseIdx], "pg"); + writer.attachVertexData(*density[wPhaseIdx], "rhow"); + writer.attachVertexData(*density[nPhaseIdx], "rhon"); + writer.attachVertexData(*density[gPhaseIdx], "rhog"); + + for (int i = 0; i < numPhases; ++i) + { + for (int j = 0; j < numComponents; ++j) + { + std::ostringstream oss; + oss << "x^" + << FluidSystem::phaseName(i) + << "_" + << FluidSystem::componentName(j); + writer.attachVertexData(*moleFraction[i][j], oss.str().c_str()); + } + } + writer.attachVertexData(*poro, "porosity"); + writer.attachVertexData(*perm, "permeability"); + writer.attachVertexData(*temperature, "temperature"); + writer.attachVertexData(*phasePresence, "phase presence"); + writer.attachCellData(*rank, "process rank"); + } + + /*! + * \brief Write the current solution to a restart file. + * + * \param outStream The output stream of one vertex for the restart file + * \param vert The vertex + */ + void serializeEntity(std::ostream &outStream, const Vertex &vert) + { + // write primary variables + ParentType::serializeEntity(outStream, vert); + + int vertIdx = this->dofMapper().map(vert); + if (!outStream.good()) + DUNE_THROW(Dune::IOError, "Could not serialize vertex " << vertIdx); + + outStream << staticVertexDat_[vertIdx].phasePresence << " "; + } + + /*! + * \brief Reads the current solution for a vertex from a restart + * file. + * + * \param inStream The input stream of one vertex from the restart file + * \param vert The vertex + */ + void deserializeEntity(std::istream &inStream, const Vertex &vert) + { + // read primary variables + ParentType::deserializeEntity(inStream, vert); + + // read phase presence + int vertIdx = this->dofMapper().map(vert); + if (!inStream.good()) + DUNE_THROW(Dune::IOError, + "Could not deserialize vertex " << vertIdx); + + inStream >> staticVertexDat_[vertIdx].phasePresence; + staticVertexDat_[vertIdx].oldPhasePresence + = staticVertexDat_[vertIdx].phasePresence; + + } + + /*! + * \brief Update the static data of all vertices in the grid. + * + * \param curGlobalSol The current global solution + * \param oldGlobalSol The previous global solution + */ + void updateStaticData(SolutionVector &curGlobalSol, + const SolutionVector &oldGlobalSol) + { + bool wasSwitched = false; + + for (unsigned i = 0; i < staticVertexDat_.size(); ++i) + staticVertexDat_[i].visited = false; + + FVElementGeometry fvGeometry; + static VolumeVariables volVars; + ElementIterator it = this->gridView_().template begin<0> (); + const ElementIterator &endit = this->gridView_().template end<0> (); + for (; it != endit; ++it) + { + fvGeometry.update(this->gridView_(), *it); + for (int i = 0; i < fvGeometry.numVertices; ++i) + { + int globalIdx = this->vertexMapper().map(*it, i, dim); + + if (staticVertexDat_[globalIdx].visited) + continue; + + staticVertexDat_[globalIdx].visited = true; + volVars.update(curGlobalSol[globalIdx], + this->problem_(), + *it, + fvGeometry, + i, + false); + const GlobalPosition &global = it->geometry().corner(i); + if (primaryVarSwitch_(curGlobalSol, + volVars, + globalIdx, + global)) + wasSwitched = true; + } + } + + // make sure that if there was a variable switch in an + // other partition we will also set the switch flag + // for our partition. + if (this->gridView_().comm().size() > 1) + wasSwitched = this->gridView_().comm().max(wasSwitched); + + setSwitched_(wasSwitched); + } + +protected: + /*! + * \brief Data which is attached to each vertex and is not only + * stored locally. + */ + struct StaticVars + { + int phasePresence; + bool wasSwitched; + + int oldPhasePresence; + bool visited; + }; + + /*! + * \brief Reset the current phase presence of all vertices to the old one. + * + * This is done after an update failed. + */ + void resetPhasePresence_() + { + int numVertices = this->gridView_().size(dim); + for (int i = 0; i < numVertices; ++i) + { + staticVertexDat_[i].phasePresence + = staticVertexDat_[i].oldPhasePresence; + staticVertexDat_[i].wasSwitched = false; + } + } + + /*! + * \brief Set the old phase of all verts state to the current one. + */ + void updateOldPhasePresence_() + { + int numVertices = this->gridView_().size(dim); + for (int i = 0; i < numVertices; ++i) + { + staticVertexDat_[i].oldPhasePresence + = staticVertexDat_[i].phasePresence; + staticVertexDat_[i].wasSwitched = false; + } + } + + /*! + * \brief Set whether there was a primary variable switch after in + * the last timestep. + */ + void setSwitched_(bool yesno) + { + switchFlag_ = yesno; + } + + // perform variable switch at a vertex; Returns true if a + // variable switch was performed. + bool primaryVarSwitch_(SolutionVector &globalSol, + const VolumeVariables &volVars, + int globalIdx, + const GlobalPosition &globalPos) + { + // evaluate primary variable switch + bool wouldSwitch = false; + int phasePresence = staticVertexDat_[globalIdx].phasePresence; + int newPhasePresence = phasePresence; + + // check if a primary var switch is necessary + if (phasePresence == threePhases) + { + Scalar Smin = 0; + if (staticVertexDat_[globalIdx].wasSwitched) + Smin = -0.01; + + if (volVars.saturation(gPhaseIdx) <= Smin) + { + wouldSwitch = true; + // gas phase disappears + std::cout << "Gas phase disappears at vertex " << globalIdx + << ", coordinates: " << globalPos << ", Sg: " + << volVars.saturation(gPhaseIdx) << std::endl; + newPhasePresence = wnPhaseOnly; + + globalSol[globalIdx][switch1Idx] + = volVars.fluidState().moleFraction(wPhaseIdx, gCompIdx); + } + else if (volVars.saturation(wPhaseIdx) <= Smin) + { + wouldSwitch = true; + // water phase disappears + std::cout << "Water phase disappears at vertex " << globalIdx + << ", coordinates: " << globalPos << ", Sw: " + << volVars.saturation(wPhaseIdx) << std::endl; + newPhasePresence = gnPhaseOnly; + + globalSol[globalIdx][switch1Idx] + = volVars.fluidState().moleFraction(gPhaseIdx, wCompIdx); + } + else if (volVars.saturation(nPhaseIdx) <= Smin) + { + wouldSwitch = true; + // NAPL phase disappears + std::cout << "NAPL phase disappears at vertex " << globalIdx + << ", coordinates: " << globalPos << ", Sn: " + << volVars.saturation(nPhaseIdx) << std::endl; + newPhasePresence = wgPhaseOnly; + + globalSol[globalIdx][switch2Idx] + = volVars.fluidState().moleFraction(gPhaseIdx, nCompIdx); + } + } + else if (phasePresence == wPhaseOnly) + { + bool gasFlag = 0; + bool nonwettingFlag = 0; + // calculate fractions of the partial pressures in the + // hypothetical gas phase + Scalar xwg = volVars.fluidState().moleFraction(gPhaseIdx, wCompIdx); + Scalar xgg = volVars.fluidState().moleFraction(gPhaseIdx, gCompIdx); + Scalar xng = volVars.fluidState().moleFraction(gPhaseIdx, nCompIdx); + /* take care: + for xgg in case wPhaseOnly we compute xgg=henry_air*x2w + for xwg in case wPhaseOnly we compute xwg=pwsat + for xng in case wPhaseOnly we compute xng=henry_NAPL*x1w + */ + + Scalar xgMax = 1.0; + if (xwg + xgg + xng > xgMax) + wouldSwitch = true; + if (staticVertexDat_[globalIdx].wasSwitched) + xgMax *= 1.02; + + // if the sum of the mole fractions would be larger than + // 100%, gas phase appears + if (xwg + xgg + xng > xgMax) + { + // gas phase appears + std::cout << "gas phase appears at vertex " << globalIdx + << ", coordinates: " << globalPos << ", xwg + xgg + xng: " + << xwg + xgg + xng << std::endl; + gasFlag = 1; + } + + // calculate fractions in the hypothetical NAPL phase + Scalar xnn = volVars.fluidState().moleFraction(nPhaseIdx, nCompIdx); + /* take care: + for xnn in case wPhaseOnly we compute xnn=henry_mesitylene*x1w, + where a hypothetical gas pressure is assumed for the Henry + x0n is set to NULL (all NAPL phase is dirty) + x2n is set to NULL (all NAPL phase is dirty) + */ + + Scalar xnMax = 1.0; + if (xnn > xnMax) + wouldSwitch = true; + if (staticVertexDat_[globalIdx].wasSwitched) + xnMax *= 1.02; + + // if the sum of the hypothetical mole fractions would be larger than + // 100%, NAPL phase appears + if (xnn > xnMax) + { + // NAPL phase appears + std::cout << "NAPL phase appears at vertex " << globalIdx + << ", coordinates: " << globalPos << ", xnn: " + << xnn << std::endl; + nonwettingFlag = 1; + } + + if ((gasFlag == 1) && (nonwettingFlag == 0)) + { + newPhasePresence = wgPhaseOnly; + globalSol[globalIdx][switch1Idx] = 0.9999; + globalSol[globalIdx][switch2Idx] = 0.0001; + } + else if ((gasFlag == 1) && (nonwettingFlag == 1)) + { + newPhasePresence = threePhases; + globalSol[globalIdx][switch1Idx] = 0.9999; + globalSol[globalIdx][switch2Idx] = 0.0001; + } + else if ((gasFlag == 0) && (nonwettingFlag == 1)) + { + newPhasePresence = wnPhaseOnly; + globalSol[globalIdx][switch1Idx] + = volVars.fluidState().moleFraction(wPhaseIdx, gCompIdx); + globalSol[globalIdx][switch2Idx] = 0.0001; + } + } + else if (phasePresence == gnPhaseOnly) + { + bool nonwettingFlag = 0; + bool wettingFlag = 0; + + Scalar Smin = 0.0; + if (staticVertexDat_[globalIdx].wasSwitched) + Smin = -0.01; + + if (volVars.saturation(nPhaseIdx) <= Smin) + { + wouldSwitch = true; + // NAPL phase disappears + std::cout << "NAPL phase disappears at vertex " << globalIdx + << ", coordinates: " << globalPos << ", Sn: " + << volVars.saturation(nPhaseIdx) << std::endl; + nonwettingFlag = 1; + } + + + // calculate fractions of the hypothetical water phase + Scalar xww = volVars.fluidState().moleFraction(wPhaseIdx, wCompIdx); + /* + take care:, xww, if no water is present, then take xww=xwg*pg/pwsat . + If this is larger than 1, then water appears + */ + Scalar xwMax = 1.0; + if (xww > xwMax) + wouldSwitch = true; + if (staticVertexDat_[globalIdx].wasSwitched) + xwMax *= 1.02; + + // if the sum of the mole fractions would be larger than + // 100%, gas phase appears + if (xww > xwMax) + { + // water phase appears + std::cout << "water phase appears at vertex " << globalIdx + << ", coordinates: " << globalPos << ", xww=xwg*pg/pwsat : " + << xww << std::endl; + wettingFlag = 1; + } + + if ((wettingFlag == 1) && (nonwettingFlag == 0)) + { + newPhasePresence = threePhases; + globalSol[globalIdx][switch1Idx] = 0.0001; + globalSol[globalIdx][switch2Idx] = volVars.saturation(nPhaseIdx); + } + else if ((wettingFlag == 1) && (nonwettingFlag == 1)) + { + newPhasePresence = wgPhaseOnly; + globalSol[globalIdx][switch1Idx] = 0.0001; + globalSol[globalIdx][switch2Idx] + = volVars.fluidState().moleFraction(gPhaseIdx, nCompIdx); + } + else if ((wettingFlag == 0) && (nonwettingFlag == 1)) + { + newPhasePresence = gPhaseOnly; + globalSol[globalIdx][switch1Idx] + = volVars.fluidState().moleFraction(gPhaseIdx, wCompIdx); + globalSol[globalIdx][switch2Idx] + = volVars.fluidState().moleFraction(gPhaseIdx, nCompIdx); + } + } + else if (phasePresence == wnPhaseOnly) + { + bool nonwettingFlag = 0; + bool gasFlag = 0; + + Scalar Smin = 0.0; + if (staticVertexDat_[globalIdx].wasSwitched) + Smin = -0.01; + + if (volVars.saturation(nPhaseIdx) <= Smin) + { + wouldSwitch = true; + // NAPL phase disappears + std::cout << "NAPL phase disappears at vertex " << globalIdx + << ", coordinates: " << globalPos << ", Sn: " + << volVars.saturation(nPhaseIdx) << std::endl; + nonwettingFlag = 1; + } + + // calculate fractions of the partial pressures in the + // hypothetical gas phase + Scalar xwg = volVars.fluidState().moleFraction(gPhaseIdx, wCompIdx); + Scalar xgg = volVars.fluidState().moleFraction(gPhaseIdx, gCompIdx); + Scalar xng = volVars.fluidState().moleFraction(gPhaseIdx, nCompIdx); + /* take care: + for xgg in case wPhaseOnly we compute xgg=henry_air*x2w + for xwg in case wPhaseOnly we compute xwg=pwsat + for xng in case wPhaseOnly we compute xng=henry_NAPL*x1w + */ + Scalar xgMax = 1.0; + if (xwg + xgg + xng > xgMax) + wouldSwitch = true; + if (staticVertexDat_[globalIdx].wasSwitched) + xgMax *= 1.02; + + // if the sum of the mole fractions would be larger than + // 100%, gas phase appears + if (xwg + xgg + xng > xgMax) + { + // gas phase appears + std::cout << "gas phase appears at vertex " << globalIdx + << ", coordinates: " << globalPos << ", xwg + xgg + xng: " + << xwg + xgg + xng << std::endl; + gasFlag = 1; + } + + if ((gasFlag == 1) && (nonwettingFlag == 0)) + { + newPhasePresence = threePhases; + globalSol[globalIdx][switch1Idx] = volVars.saturation(wPhaseIdx); + globalSol[globalIdx][switch2Idx] = volVars.saturation(nPhaseIdx);; + } + else if ((gasFlag == 1) && (nonwettingFlag == 1)) + { + newPhasePresence = wgPhaseOnly; + globalSol[globalIdx][switch1Idx] = volVars.saturation(wPhaseIdx); + globalSol[globalIdx][switch2Idx] + = volVars.fluidState().moleFraction(gPhaseIdx, nCompIdx); + } + else if ((gasFlag == 0) && (nonwettingFlag == 1)) + { + newPhasePresence = wPhaseOnly; + globalSol[globalIdx][switch1Idx] + = volVars.fluidState().moleFraction(wPhaseIdx, gCompIdx); + globalSol[globalIdx][switch2Idx] + = volVars.fluidState().moleFraction(wPhaseIdx, nCompIdx); + } + } + else if (phasePresence == gPhaseOnly) + { + bool nonwettingFlag = 0; + bool wettingFlag = 0; + + // calculate fractions in the hypothetical NAPL phase + Scalar xnn = volVars.fluidState().moleFraction(nPhaseIdx, nCompIdx); + /* + take care:, xnn, if no NAPL phase is there, take xnn=xng*pg/pcsat + if this is larger than 1, then NAPL appears + */ + + Scalar xnMax = 1.0; + if (xnn > xnMax) + wouldSwitch = true; + if (staticVertexDat_[globalIdx].wasSwitched) + xnMax *= 1.02; + + // if the sum of the hypothetical mole fraction would be larger than + // 100%, NAPL phase appears + if (xnn > xnMax) + { + // NAPL phase appears + std::cout << "NAPL phase appears at vertex " << globalIdx + << ", coordinates: " << globalPos << ", xnn: " + << xnn << std::endl; + nonwettingFlag = 1; + } + // calculate fractions of the hypothetical water phase + Scalar xww = volVars.fluidState().moleFraction(wPhaseIdx, wCompIdx); + /* + take care:, xww, if no water is present, then take xww=xwg*pg/pwsat . + If this is larger than 1, then water appears + */ + Scalar xwMax = 1.0; + if (xww > xwMax) + wouldSwitch = true; + if (staticVertexDat_[globalIdx].wasSwitched) + xwMax *= 1.02; + + // if the sum of the mole fractions would be larger than + // 100%, gas phase appears + if (xww > xwMax) + { + // water phase appears + std::cout << "water phase appears at vertex " << globalIdx + << ", coordinates: " << globalPos << ", xww=xwg*pg/pwsat : " + << xww << std::endl; + wettingFlag = 1; + } + if ((wettingFlag == 1) && (nonwettingFlag == 0)) + { + newPhasePresence = wgPhaseOnly; + globalSol[globalIdx][switch1Idx] = 0.0001; + globalSol[globalIdx][switch2Idx] + = volVars.fluidState().moleFraction(gPhaseIdx, nCompIdx); + } + else if ((wettingFlag == 1) && (nonwettingFlag == 1)) + { + newPhasePresence = threePhases; + globalSol[globalIdx][switch1Idx] = 0.0001; + globalSol[globalIdx][switch2Idx] = 0.0001; + } + else if ((wettingFlag == 0) && (nonwettingFlag == 1)) + { + newPhasePresence = gnPhaseOnly; + globalSol[globalIdx][switch1Idx] + = volVars.fluidState().moleFraction(gPhaseIdx, wCompIdx); + globalSol[globalIdx][switch2Idx] = 0.0001; + } + } + else if (phasePresence == wgPhaseOnly) + { + bool nonwettingFlag = 0; + bool gasFlag = 0; + bool wettingFlag = 0; + + // get the fractions in the hypothetical NAPL phase + Scalar xnn = volVars.fluidState().moleFraction(nPhaseIdx, nCompIdx); + + // take care: if the NAPL phase is not present, take + // xnn=xng*pg/pcsat if this is larger than 1, then NAPL + // appears + Scalar xnMax = 1.0; + if (xnn > xnMax) + wouldSwitch = true; + if (staticVertexDat_[globalIdx].wasSwitched) + xnMax *= 1.02; + + // if the sum of the hypothetical mole fraction would be larger than + // 100%, NAPL phase appears + if (xnn > xnMax) + { + // NAPL phase appears + std::cout << "NAPL phase appears at vertex " << globalIdx + << ", coordinates: " << globalPos << ", xnn: " + << xnn << std::endl; + nonwettingFlag = 1; + } + + Scalar Smin = -1.e-6; + if (staticVertexDat_[globalIdx].wasSwitched) + Smin = -0.01; + + if (volVars.saturation(gPhaseIdx) <= Smin) + { + wouldSwitch = true; + // gas phase disappears + std::cout << "Gas phase disappears at vertex " << globalIdx + << ", coordinates: " << globalPos << ", Sg: " + << volVars.saturation(gPhaseIdx) << std::endl; + gasFlag = 1; + } + + Smin = 0.0; + if (staticVertexDat_[globalIdx].wasSwitched) + Smin = -0.01; + + if (volVars.saturation(wPhaseIdx) <= Smin) + { + wouldSwitch = true; + // gas phase disappears + std::cout << "Water phase disappears at vertex " << globalIdx + << ", coordinates: " << globalPos << ", Sw: " + << volVars.saturation(wPhaseIdx) << std::endl; + wettingFlag = 1; + } + + if ((gasFlag == 0) && (nonwettingFlag == 1) && (wettingFlag == 1)) + { + newPhasePresence = gnPhaseOnly; + globalSol[globalIdx][switch1Idx] + = volVars.fluidState().moleFraction(gPhaseIdx, wCompIdx); + globalSol[globalIdx][switch2Idx] = 0.0001; + } + else if ((gasFlag == 0) && (nonwettingFlag == 1) && (wettingFlag == 0)) + { + newPhasePresence = threePhases; + globalSol[globalIdx][switch1Idx] = volVars.saturation(wPhaseIdx); + globalSol[globalIdx][switch2Idx] = 0.0; + } + else if ((gasFlag == 1) && (nonwettingFlag == 0) && (wettingFlag == 0)) + { + newPhasePresence = wPhaseOnly; + globalSol[globalIdx][switch1Idx] + = volVars.fluidState().moleFraction(wPhaseIdx, gCompIdx); + globalSol[globalIdx][switch2Idx] + = volVars.fluidState().moleFraction(wPhaseIdx, nCompIdx); + } + else if ((gasFlag == 0) && (nonwettingFlag == 0) && (wettingFlag == 1)) + { + newPhasePresence = gPhaseOnly; + globalSol[globalIdx][switch1Idx] + = volVars.fluidState().moleFraction(gPhaseIdx, wCompIdx); + globalSol[globalIdx][switch2Idx] + = volVars.fluidState().moleFraction(gPhaseIdx, nCompIdx); + } + } + + staticVertexDat_[globalIdx].phasePresence = newPhasePresence; + staticVertexDat_[globalIdx].wasSwitched = wouldSwitch; + return phasePresence != newPhasePresence; + } + + // parameters given in constructor + std::vector<StaticVars> staticVertexDat_; + bool switchFlag_; +}; + +} + +#include "3p3cpropertydefaults.hh" + +#endif diff --git a/dumux/implicit/3p3c/3p3cnewtoncontroller.hh b/dumux/implicit/3p3c/3p3cnewtoncontroller.hh new file mode 100644 index 0000000000..f0d47996da --- /dev/null +++ b/dumux/implicit/3p3c/3p3cnewtoncontroller.hh @@ -0,0 +1,85 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \file + * \brief A 3p3c specific controller for the newton solver. + * + * This controller 'knows' what a 'physically meaningful' solution is + * which allows the newton method to abort quicker if the solution is + * way out of bounds. + */ +#ifndef DUMUX_3P3C_NEWTON_CONTROLLER_HH +#define DUMUX_3P3C_NEWTON_CONTROLLER_HH + +#include "3p3cproperties.hh" + +#include <dumux/nonlinear/newtoncontroller.hh> + +namespace Dumux { +/*! + * \ingroup ThreePThreeCModel + * \brief A 3p3c specific controller for the newton solver. + * + * This controller 'knows' what a 'physically meaningful' solution is + * which allows the newton method to abort quicker if the solution is + * way out of bounds. + */ +template <class TypeTag> +class ThreePThreeCNewtonController : public NewtonController<TypeTag> +{ + typedef NewtonController<TypeTag> ParentType; + typedef typename GET_PROP_TYPE(TypeTag, Problem) Problem; + typedef typename GET_PROP_TYPE(TypeTag, SolutionVector) SolutionVector; + +public: + ThreePThreeCNewtonController(const Problem &problem) + : ParentType(problem) + {}; + + + /*! + * \brief Called after each Newton update + * + * \param uCurrentIter The current global solution vector + * \param uLastIter The previous global solution vector + */ + void newtonEndStep(SolutionVector &uCurrentIter, + const SolutionVector &uLastIter) + { + // call the method of the base class + this->method().model().updateStaticData(uCurrentIter, uLastIter); + ParentType::newtonEndStep(uCurrentIter, uLastIter); + } + + + /*! + * \brief Returns true if the current solution can be considered to + * be accurate enough + */ + bool newtonConverged() + { + if (this->method().model().switched()) + return false; + + return ParentType::newtonConverged(); + }; +}; +} + +#endif diff --git a/dumux/implicit/3p3c/3p3cproperties.hh b/dumux/implicit/3p3c/3p3cproperties.hh new file mode 100644 index 0000000000..90ddfaa0d3 --- /dev/null +++ b/dumux/implicit/3p3c/3p3cproperties.hh @@ -0,0 +1,66 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \ingroup ThreePThreeCModel + */ +/*! + * \file + * + * \brief Defines the properties required for the 3p3c BOX model. + */ +#ifndef DUMUX_3P3C_PROPERTIES_HH +#define DUMUX_3P3C_PROPERTIES_HH + +#include <dumux/boxmodels/common/boxproperties.hh> + +namespace Dumux +{ + +namespace Properties +{ +////////////////////////////////////////////////////////////////// +// Type tags +////////////////////////////////////////////////////////////////// + +//! The type tag for the isothermal single phase problems +NEW_TYPE_TAG(BoxThreePThreeC, INHERITS_FROM(BoxModel)); + +////////////////////////////////////////////////////////////////// +// Property tags +////////////////////////////////////////////////////////////////// + +NEW_PROP_TAG(NumPhases); //!< Number of fluid phases in the system +NEW_PROP_TAG(NumComponents); //!< Number of fluid components in the system +NEW_PROP_TAG(Indices); //!< Enumerations for the model +NEW_PROP_TAG(SpatialParams); //!< The type of the spatial parameters +NEW_PROP_TAG(FluidSystem); //!< Type of the multi-component relations + +NEW_PROP_TAG(MaterialLaw); //!< The material law which ought to be used (extracted from the spatial parameters) +NEW_PROP_TAG(MaterialLawParams); //!< The parameters of the material law (extracted from the spatial parameters) + +NEW_PROP_TAG(ProblemEnableGravity); //!< Returns whether gravity is considered in the problem +NEW_PROP_TAG(ImplicitMassUpwindWeight); //!< The value of the upwind parameter for the mobility +NEW_PROP_TAG(ImplicitMobilityUpwindWeight); //!< Weight for the upwind mobility in the velocity calculation +NEW_PROP_TAG(UseConstraintSolver); //!< Determines whether a constraint solver should be used explicitly +NEW_PROP_TAG(BaseFluxVariables); //! The base flux variables +NEW_PROP_TAG(SpatialParamsForchCoeff); //!< Property for the forchheimer coefficient +} +} + +#endif diff --git a/dumux/implicit/3p3c/3p3cpropertydefaults.hh b/dumux/implicit/3p3c/3p3cpropertydefaults.hh new file mode 100644 index 0000000000..421ddab7bc --- /dev/null +++ b/dumux/implicit/3p3c/3p3cpropertydefaults.hh @@ -0,0 +1,143 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \ingroup ThreePThreeCModel + */ +/*! + * \file + * + * \brief Defines default values for most properties required by the + * 3p3c box model. + */ +#ifndef DUMUX_3P3C_PROPERTY_DEFAULTS_HH +#define DUMUX_3P3C_PROPERTY_DEFAULTS_HH + +#include "3p3cindices.hh" + +#include "3p3cmodel.hh" +#include "3p3cindices.hh" +#include "3p3cfluxvariables.hh" +#include "3p3cvolumevariables.hh" +#include "3p3cproperties.hh" +#include "3p3cnewtoncontroller.hh" +// #include "3p3cboundaryvariables.hh" + +#include <dumux/boxmodels/common/boxdarcyfluxvariables.hh> +#include <dumux/material/spatialparams/boxspatialparams.hh> + +namespace Dumux +{ + +namespace Properties { +////////////////////////////////////////////////////////////////// +// Property values +////////////////////////////////////////////////////////////////// + +/*! + * \brief Set the property for the number of components. + * + * We just forward the number from the fluid system and use an static + * assert to make sure it is 2. + */ +SET_PROP(BoxThreePThreeC, NumComponents) +{ + private: + typedef typename GET_PROP_TYPE(TypeTag, FluidSystem) FluidSystem; + + public: + static const int value = FluidSystem::numComponents; + + static_assert(value == 3, + "Only fluid systems with 3 components are supported by the 3p3c model!"); +}; + +/*! + * \brief Set the property for the number of fluid phases. + * + * We just forward the number from the fluid system and use an static + * assert to make sure it is 2. + */ +SET_PROP(BoxThreePThreeC, NumPhases) +{ + private: + typedef typename GET_PROP_TYPE(TypeTag, FluidSystem) FluidSystem; + + public: + static const int value = FluidSystem::numPhases; + static_assert(value == 3, + "Only fluid systems with 3 phases are supported by the 3p3c model!"); +}; + +SET_INT_PROP(BoxThreePThreeC, NumEq, 3); //!< set the number of equations to 2 + +/*! + * \brief Set the property for the material parameters by extracting + * it from the material law. + */ +SET_TYPE_PROP(BoxThreePThreeC, MaterialLawParams, typename GET_PROP_TYPE(TypeTag, MaterialLaw)::Params); + +//! The local residual function of the conservation equations +SET_TYPE_PROP(BoxThreePThreeC, LocalResidual, ThreePThreeCLocalResidual<TypeTag>); + +//! Use the 3p3c specific newton controller for the 3p3c model +SET_TYPE_PROP(BoxThreePThreeC, NewtonController, ThreePThreeCNewtonController<TypeTag>); + +//! the Model property +SET_TYPE_PROP(BoxThreePThreeC, Model, ThreePThreeCModel<TypeTag>); + +//! the VolumeVariables property +SET_TYPE_PROP(BoxThreePThreeC, VolumeVariables, ThreePThreeCVolumeVariables<TypeTag>); + +//! the FluxVariables property +SET_TYPE_PROP(BoxThreePThreeC, FluxVariables, ThreePThreeCFluxVariables<TypeTag>); + +//! define the base flux variables to realize Darcy flow +SET_TYPE_PROP(BoxThreePThreeC, BaseFluxVariables, BoxDarcyFluxVariables<TypeTag>); + +//! the upwind factor for the mobility. +SET_SCALAR_PROP(BoxThreePThreeC, ImplicitMassUpwindWeight, 1.0); + +//! set default mobility upwind weight to 1.0, i.e. fully upwind +SET_SCALAR_PROP(BoxThreePThreeC, ImplicitMobilityUpwindWeight, 1.0); + +//! Determines whether a constraint solver should be used explicitly +SET_BOOL_PROP(BoxThreePThreeC, UseConstraintSolver, false); + +//! The indices required by the isothermal 3p3c model +SET_TYPE_PROP(BoxThreePThreeC, Indices, ThreePThreeCIndices<TypeTag, /*PVOffset=*/0>); + +//! The spatial parameters to be employed. +//! Use BoxSpatialParams by default. +SET_TYPE_PROP(BoxThreePThreeC, SpatialParams, BoxSpatialParams<TypeTag>); + +// enable gravity by default +SET_BOOL_PROP(BoxThreePThreeC, ProblemEnableGravity, true); + +//! default value for the forchheimer coefficient +// Source: Ward, J.C. 1964 Turbulent flow in porous media. ASCE J. Hydraul. Div 90. +// Actually the Forchheimer coefficient is also a function of the dimensions of the +// porous medium. Taking it as a constant is only a first approximation +// (Nield, Bejan, Convection in porous media, 2006, p. 10) +SET_SCALAR_PROP(BoxModel, SpatialParamsForchCoeff, 0.55); + +} + +} + +#endif diff --git a/dumux/implicit/3p3c/3p3cvolumevariables.hh b/dumux/implicit/3p3c/3p3cvolumevariables.hh new file mode 100644 index 0000000000..ce6349ef8c --- /dev/null +++ b/dumux/implicit/3p3c/3p3cvolumevariables.hh @@ -0,0 +1,732 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \file + * + * \brief Contains the quantities which are constant within a + * finite volume in the two-phase, two-component model. + */ +#ifndef DUMUX_3P3C_VOLUME_VARIABLES_HH +#define DUMUX_3P3C_VOLUME_VARIABLES_HH + +#include <dumux/boxmodels/common/boxmodel.hh> +#include <dumux/common/math.hh> + +#include <dune/common/collectivecommunication.hh> +#include <vector> +#include <iostream> + +#include "3p3cproperties.hh" + +#include <dumux/material/constants.hh> +#include <dumux/material/fluidstates/compositionalfluidstate.hh> +#include <dumux/material/constraintsolvers/computefromreferencephase.hh> +#include <dumux/material/constraintsolvers/misciblemultiphasecomposition.hh> + +namespace Dumux +{ + +/*! + * \ingroup ThreePThreeCModel + * \brief Contains the quantities which are are constant within a + * finite volume in the two-phase, two-component model. + */ +template <class TypeTag> +class ThreePThreeCVolumeVariables : public BoxVolumeVariables<TypeTag> +{ + typedef BoxVolumeVariables<TypeTag> ParentType; + typedef typename GET_PROP_TYPE(TypeTag, VolumeVariables) Implementation; + + typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; + typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; + typedef typename GET_PROP_TYPE(TypeTag, Problem) Problem; + typedef typename GET_PROP_TYPE(TypeTag, FVElementGeometry) FVElementGeometry; + typedef typename GET_PROP_TYPE(TypeTag, PrimaryVariables) PrimaryVariables; + typedef typename GET_PROP_TYPE(TypeTag, FluidSystem) FluidSystem; + typedef typename GET_PROP_TYPE(TypeTag, MaterialLaw) MaterialLaw; + typedef typename GET_PROP_TYPE(TypeTag, MaterialLawParams) MaterialLawParams; + + // constraint solvers + typedef Dumux::MiscibleMultiPhaseComposition<Scalar, FluidSystem> MiscibleMultiPhaseComposition; + typedef Dumux::ComputeFromReferencePhase<Scalar, FluidSystem> ComputeFromReferencePhase; + + typedef typename GET_PROP_TYPE(TypeTag, Indices) Indices; + enum { + dim = GridView::dimension, + + numPhases = GET_PROP_VALUE(TypeTag, NumPhases), + numComponents = GET_PROP_VALUE(TypeTag, NumComponents), + + wCompIdx = Indices::wCompIdx, + gCompIdx = Indices::gCompIdx, + nCompIdx = Indices::nCompIdx, + + wPhaseIdx = Indices::wPhaseIdx, + gPhaseIdx = Indices::gPhaseIdx, + nPhaseIdx = Indices::nPhaseIdx, + + switch1Idx = Indices::switch1Idx, + switch2Idx = Indices::switch2Idx, + pressureIdx = Indices::pressureIdx + }; + + // present phases + enum { + threePhases = Indices::threePhases, + wPhaseOnly = Indices::wPhaseOnly, + gnPhaseOnly = Indices::gnPhaseOnly, + wnPhaseOnly = Indices::wnPhaseOnly, + gPhaseOnly = Indices::gPhaseOnly, + wgPhaseOnly = Indices::wgPhaseOnly + }; + + typedef typename GridView::template Codim<0>::Entity Element; + + static const Scalar R; // universial gas constant + +public: + //! The type of the object returned by the fluidState() method + typedef Dumux::CompositionalFluidState<Scalar, FluidSystem> FluidState; + + + /*! + * \copydoc BoxVolumeVariables::update + */ + void update(const PrimaryVariables &priVars, + const Problem &problem, + const Element &element, + const FVElementGeometry &fvGeometry, + const int scvIdx, + bool isOldSol) + { + ParentType::update(priVars, + problem, + element, + fvGeometry, + scvIdx, + isOldSol); + + bool useConstraintSolver = GET_PROP_VALUE(TypeTag, UseConstraintSolver); + + // capillary pressure parameters + const MaterialLawParams &materialParams = + problem.spatialParams().materialLawParams(element, fvGeometry, scvIdx); + + int globalVertIdx = problem.model().dofMapper().map(element, scvIdx, dim); + int phasePresence = problem.model().phasePresence(globalVertIdx, isOldSol); + + Scalar temp = Implementation::temperature_(priVars, problem, element, fvGeometry, scvIdx); + fluidState_.setTemperature(temp); + + /* first the saturations */ + if (phasePresence == threePhases) + { + Sw_ = priVars[switch1Idx]; + Sn_ = priVars[switch2Idx]; + Sg_ = 1. - Sw_ - Sn_; + } + else if (phasePresence == wPhaseOnly) + { + Sw_ = 1.; + Sn_ = 0.; + Sg_ = 0.; + } + else if (phasePresence == gnPhaseOnly) + { + Sw_ = 0.; + Sn_ = priVars[switch2Idx]; + Sg_ = 1. - Sn_; + } + else if (phasePresence == wnPhaseOnly) + { + Sn_ = priVars[switch2Idx]; + Sw_ = 1. - Sn_; + Sg_ = 0.; + } + else if (phasePresence == gPhaseOnly) + { + Sw_ = 0.; + Sn_ = 0.; + Sg_ = 1.; + } + else if (phasePresence == wgPhaseOnly) + { + Sw_ = priVars[switch1Idx]; + Sn_ = 0.; + Sg_ = 1. - Sw_; + } + else DUNE_THROW(Dune::InvalidStateException, "phasePresence: " << phasePresence << " is invalid."); + Valgrind::CheckDefined(Sg_); + + fluidState_.setSaturation(wPhaseIdx, Sw_); + fluidState_.setSaturation(gPhaseIdx, Sg_); + fluidState_.setSaturation(nPhaseIdx, Sn_); + + /* now the pressures */ + pg_ = priVars[pressureIdx]; + + // calculate capillary pressures + Scalar pCGW = MaterialLaw::pCGW(materialParams, Sw_); + Scalar pCNW = MaterialLaw::pCNW(materialParams, Sw_); + Scalar pCGN = MaterialLaw::pCGN(materialParams, Sw_ + Sn_); + + Scalar pcAlpha = MaterialLaw::pCAlpha(materialParams, Sn_); + Scalar pcNW1 = 0.0; // TODO: this should be possible to assign in the problem file + + pn_ = pg_- pcAlpha * pCGN - (1.-pcAlpha)*(pCGW - pcNW1); + pw_ = pn_ - pcAlpha * pCNW - (1.-pcAlpha)*pcNW1; + + fluidState_.setPressure(wPhaseIdx, pw_); + fluidState_.setPressure(gPhaseIdx, pg_); + fluidState_.setPressure(nPhaseIdx, pn_); + + // calculate and set all fugacity coefficients. this is + // possible because we require all phases to be an ideal + // mixture, i.e. fugacity coefficients are not supposed to + // depend on composition! + typename FluidSystem::ParameterCache paramCache; + // assert(FluidSystem::isIdealGas(gPhaseIdx)); + for (int phaseIdx = 0; phaseIdx < numPhases; ++ phaseIdx) { + assert(FluidSystem::isIdealMixture(phaseIdx)); + + for (int compIdx = 0; compIdx < numComponents; ++ compIdx) { + Scalar phi = FluidSystem::fugacityCoefficient(fluidState_, paramCache, phaseIdx, compIdx); + fluidState_.setFugacityCoefficient(phaseIdx, compIdx, phi); + } + } + + // now comes the tricky part: calculate phase composition + if (phasePresence == threePhases) { + // all phases are present, phase compositions are a + // result of the the gas <-> liquid equilibrium. This is + // the job of the "MiscibleMultiPhaseComposition" + // constraint solver ... + if (useConstraintSolver) { + MiscibleMultiPhaseComposition::solve(fluidState_, + paramCache, + /*setViscosity=*/true, + /*setInternalEnergy=*/false); + } + // ... or calculated explicitly this way ... + else { + Scalar partPressH2O = FluidSystem::fugacityCoefficient(fluidState_, + wPhaseIdx, + wCompIdx) * pw_; + Scalar partPressNAPL = FluidSystem::fugacityCoefficient(fluidState_, + nPhaseIdx, + nCompIdx) * pn_; + Scalar partPressAir = pg_ - partPressH2O - partPressNAPL; + + Scalar xgn = partPressNAPL/pg_; + Scalar xgw = partPressH2O/pg_; + Scalar xgg = partPressAir/pg_; + + // actually, it's nothing else than Henry coefficient + Scalar xwn = partPressNAPL + / (FluidSystem::fugacityCoefficient(fluidState_, + wPhaseIdx,nCompIdx) + * pw_); + Scalar xwg = partPressAir + / (FluidSystem::fugacityCoefficient(fluidState_, + wPhaseIdx,gCompIdx) + * pw_); + Scalar xww = 1.-xwg-xwn; + + Scalar xnn = 1.-2.e-10; + Scalar xna = 1.e-10; + Scalar xnw = 1.e-10; + + fluidState_.setMoleFraction(wPhaseIdx, wCompIdx, xww); + fluidState_.setMoleFraction(wPhaseIdx, gCompIdx, xwg); + fluidState_.setMoleFraction(wPhaseIdx, nCompIdx, xwn); + fluidState_.setMoleFraction(gPhaseIdx, wCompIdx, xgw); + fluidState_.setMoleFraction(gPhaseIdx, gCompIdx, xgg); + fluidState_.setMoleFraction(gPhaseIdx, nCompIdx, xgn); + fluidState_.setMoleFraction(nPhaseIdx, wCompIdx, xnw); + fluidState_.setMoleFraction(nPhaseIdx, gCompIdx, xna); + fluidState_.setMoleFraction(nPhaseIdx, nCompIdx, xnn); + + Scalar rhoW = FluidSystem::density(fluidState_, wPhaseIdx); + Scalar rhoG = FluidSystem::density(fluidState_, gPhaseIdx); + Scalar rhoN = FluidSystem::density(fluidState_, nPhaseIdx); + + fluidState_.setDensity(wPhaseIdx, rhoW); + fluidState_.setDensity(gPhaseIdx, rhoG); + fluidState_.setDensity(nPhaseIdx, rhoN); + } + } + else if (phasePresence == wPhaseOnly) { + // only the water phase is present, water phase composition is + // stored explicitly. + + // extract mole fractions in the water phase + Scalar xwg = priVars[switch1Idx]; + Scalar xwn = priVars[switch2Idx]; + Scalar xww = 1 - xwg - xwn; + + // write water mole fractions in the fluid state + fluidState_.setMoleFraction(wPhaseIdx, wCompIdx, xww); + fluidState_.setMoleFraction(wPhaseIdx, gCompIdx, xwg); + fluidState_.setMoleFraction(wPhaseIdx, nCompIdx, xwn); + + // calculate the composition of the remaining phases (as + // well as the densities of all phases). this is the job + // of the "ComputeFromReferencePhase" constraint solver ... + if (useConstraintSolver) + { + ComputeFromReferencePhase::solve(fluidState_, + paramCache, + wPhaseIdx, + /*setViscosity=*/true, + /*setInternalEnergy=*/false); + } + // ... or calculated explicitly this way ... + else { + // note that the gas phase is actually not existing! + // thus, this is used as phase switch criterion + Scalar xgg = xwg * FluidSystem::fugacityCoefficient(fluidState_, + wPhaseIdx,gCompIdx) + * pw_ / pg_; + Scalar xgn = xwn * FluidSystem::fugacityCoefficient(fluidState_, + wPhaseIdx,nCompIdx) + * pw_ / pg_; + Scalar xgw = FluidSystem::fugacityCoefficient(fluidState_, + wPhaseIdx,wCompIdx) + * pw_ / pg_; + + + // note that the gas phase is actually not existing! + // thus, this is used as phase switch criterion + Scalar xnn = xwn * FluidSystem::fugacityCoefficient(fluidState_, + wPhaseIdx,nCompIdx) + * pw_; + Scalar xna = 1.e-10; + Scalar xnw = 1.e-10; + + fluidState_.setMoleFraction(gPhaseIdx, wCompIdx, xgw); + fluidState_.setMoleFraction(gPhaseIdx, gCompIdx, xgg); + fluidState_.setMoleFraction(gPhaseIdx, nCompIdx, xgn); + fluidState_.setMoleFraction(nPhaseIdx, wCompIdx, xnw); + fluidState_.setMoleFraction(nPhaseIdx, gCompIdx, xna); + fluidState_.setMoleFraction(nPhaseIdx, nCompIdx, xnn); + + Scalar rhoW = FluidSystem::density(fluidState_, wPhaseIdx); + Scalar rhoG = FluidSystem::density(fluidState_, gPhaseIdx); + Scalar rhoN = FluidSystem::density(fluidState_, nPhaseIdx); + + fluidState_.setDensity(wPhaseIdx, rhoW); + fluidState_.setDensity(gPhaseIdx, rhoG); + fluidState_.setDensity(nPhaseIdx, rhoN); + } + } + else if (phasePresence == gnPhaseOnly) { + // only gas and NAPL phases are present + // we have all (partly hypothetical) phase pressures + // and temperature and the mole fraction of water in + // the gas phase + + // we have all (partly hypothetical) phase pressures + // and temperature and the mole fraction of water in + // the gas phase + Scalar partPressNAPL = fluidState_.fugacityCoefficient(nPhaseIdx, nCompIdx)*pn_; + + Scalar xgw = priVars[switch1Idx]; + Scalar xgn = partPressNAPL/pg_; + Scalar xgg = 1.-xgw-xgn; + + // write mole fractions in the fluid state + fluidState_.setMoleFraction(gPhaseIdx, wCompIdx, xgw); + fluidState_.setMoleFraction(gPhaseIdx, gCompIdx, xgg); + fluidState_.setMoleFraction(gPhaseIdx, nCompIdx, xgn); + + // calculate the composition of the remaining phases (as + // well as the densities of all phases). this is the job + // of the "ComputeFromReferencePhase" constraint solver + ComputeFromReferencePhase::solve(fluidState_, + paramCache, + gPhaseIdx, + /*setViscosity=*/true, + /*setInternalEnergy=*/false); + } + else if (phasePresence == wnPhaseOnly) { + // only water and NAPL phases are present + Scalar pPartialC = fluidState_.fugacityCoefficient(nPhaseIdx,nCompIdx)*pn_; + Scalar henryC = fluidState_.fugacityCoefficient(wPhaseIdx,nCompIdx)*pw_; + + Scalar xwg = priVars[switch1Idx]; + Scalar xwn = pPartialC/henryC; + Scalar xww = 1.-xwg-xwn; + + // write mole fractions in the fluid state + fluidState_.setMoleFraction(wPhaseIdx, wCompIdx, xww); + fluidState_.setMoleFraction(wPhaseIdx, gCompIdx, xwg); + fluidState_.setMoleFraction(wPhaseIdx, nCompIdx, xwn); + + // calculate the composition of the remaining phases (as + // well as the densities of all phases). this is the job + // of the "ComputeFromReferencePhase" constraint solver + ComputeFromReferencePhase::solve(fluidState_, + paramCache, + wPhaseIdx, + /*setViscosity=*/true, + /*setInternalEnergy=*/false); + } + else if (phasePresence == gPhaseOnly) { + // only the gas phase is present, gas phase composition is + // stored explicitly here below. + + const Scalar xgw = priVars[switch1Idx]; + const Scalar xgn = priVars[switch2Idx]; + Scalar xgg = 1 - xgw - xgn; + + // write mole fractions in the fluid state + fluidState_.setMoleFraction(gPhaseIdx, wCompIdx, xgw); + fluidState_.setMoleFraction(gPhaseIdx, gCompIdx, xgg); + fluidState_.setMoleFraction(gPhaseIdx, nCompIdx, xgn); + + // calculate the composition of the remaining phases (as + // well as the densities of all phases). this is the job + // of the "ComputeFromReferencePhase" constraint solver ... + if (useConstraintSolver) + { + ComputeFromReferencePhase::solve(fluidState_, + paramCache, + gPhaseIdx, + /*setViscosity=*/true, + /*setInternalEnergy=*/false); + } + // ... or calculated explicitly this way ... + else { + + // note that the water phase is actually not existing! + // thus, this is used as phase switch criterion + Scalar xww = xgw * pg_ + / (FluidSystem::fugacityCoefficient(fluidState_, + wPhaseIdx,wCompIdx) + * pw_); + Scalar xwn = 1.e-10; + Scalar xwg = 1.e-10; + + // note that the NAPL phase is actually not existing! + // thus, this is used as phase switch criterion + Scalar xnn = xgn * pg_ + / (FluidSystem::fugacityCoefficient(fluidState_, + nPhaseIdx,nCompIdx) + * pn_); + Scalar xna = 1.e-10; + Scalar xnw = 1.e-10; + + fluidState_.setMoleFraction(wPhaseIdx, wCompIdx, xww); + fluidState_.setMoleFraction(wPhaseIdx, gCompIdx, xwg); + fluidState_.setMoleFraction(wPhaseIdx, nCompIdx, xwn); + fluidState_.setMoleFraction(nPhaseIdx, wCompIdx, xnw); + fluidState_.setMoleFraction(nPhaseIdx, gCompIdx, xna); + fluidState_.setMoleFraction(nPhaseIdx, nCompIdx, xnn); + + Scalar rhoW = FluidSystem::density(fluidState_, wPhaseIdx); + Scalar rhoG = FluidSystem::density(fluidState_, gPhaseIdx); + Scalar rhoN = FluidSystem::density(fluidState_, nPhaseIdx); + + fluidState_.setDensity(wPhaseIdx, rhoW); + fluidState_.setDensity(gPhaseIdx, rhoG); + fluidState_.setDensity(nPhaseIdx, rhoN); + } + } + else if (phasePresence == wgPhaseOnly) { + // only water and gas phases are present + Scalar xgn = priVars[switch2Idx]; + Scalar partPressH2O = fluidState_.fugacityCoefficient(wPhaseIdx, wCompIdx)*pw_; + + Scalar xgw = partPressH2O/pg_; + Scalar xgg = 1.-xgn-xgw; + + // write mole fractions in the fluid state + fluidState_.setMoleFraction(gPhaseIdx, wCompIdx, xgw); + fluidState_.setMoleFraction(gPhaseIdx, gCompIdx, xgg); + fluidState_.setMoleFraction(gPhaseIdx, nCompIdx, xgn); + + // calculate the composition of the remaining phases (as + // well as the densities of all phases). this is the job + // of the "ComputeFromReferencePhase" constraint solver ... + if (useConstraintSolver) + { + ComputeFromReferencePhase::solve(fluidState_, + paramCache, + gPhaseIdx, + /*setViscosity=*/true, + /*setInternalEnergy=*/false); + } + // ... or calculated explicitly this way ... + else { + // actually, it's nothing else than Henry coefficient + Scalar xwn = xgn * pg_ + / (FluidSystem::fugacityCoefficient(fluidState_, + wPhaseIdx,nCompIdx) + * pw_); + Scalar xwg = xgg * pg_ + / (FluidSystem::fugacityCoefficient(fluidState_, + wPhaseIdx,gCompIdx) + * pw_); + Scalar xww = 1.-xwg-xwn; + + // note that the NAPL phase is actually not existing! + // thus, this is used as phase switch criterion + Scalar xnn = xgn * pg_ + / (FluidSystem::fugacityCoefficient(fluidState_, + nPhaseIdx,nCompIdx) + * pn_);; + Scalar xna = 1.e-10; + Scalar xnw = 1.e-10; + + fluidState_.setMoleFraction(wPhaseIdx, wCompIdx, xww); + fluidState_.setMoleFraction(wPhaseIdx, gCompIdx, xwg); + fluidState_.setMoleFraction(wPhaseIdx, nCompIdx, xwn); + fluidState_.setMoleFraction(nPhaseIdx, wCompIdx, xnw); + fluidState_.setMoleFraction(nPhaseIdx, gCompIdx, xna); + fluidState_.setMoleFraction(nPhaseIdx, nCompIdx, xnn); + + Scalar rhoW = FluidSystem::density(fluidState_, wPhaseIdx); + Scalar rhoG = FluidSystem::density(fluidState_, gPhaseIdx); + Scalar rhoN = FluidSystem::density(fluidState_, nPhaseIdx); + + fluidState_.setDensity(wPhaseIdx, rhoW); + fluidState_.setDensity(gPhaseIdx, rhoG); + fluidState_.setDensity(nPhaseIdx, rhoN); + } + } + else + assert(false); // unhandled phase state + + for (int phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) { + // Mobilities + const Scalar mu = + FluidSystem::viscosity(fluidState_, + paramCache, + phaseIdx); + fluidState_.setViscosity(phaseIdx,mu); + + Scalar kr; + kr = MaterialLaw::kr(materialParams, phaseIdx, + fluidState_.saturation(wPhaseIdx), + fluidState_.saturation(nPhaseIdx), + fluidState_.saturation(gPhaseIdx)); + mobility_[phaseIdx] = kr / mu; + Valgrind::CheckDefined(mobility_[phaseIdx]); + } + + // material dependent parameters for NAPL adsorption + bulkDensTimesAdsorpCoeff_ = + MaterialLaw::bulkDensTimesAdsorpCoeff(materialParams); + + /* ATTENTION: The conversion to effective diffusion parameters + * for the porous media happens at another place! + */ + + // diffusivity coefficents + diffusionCoefficient_[gPhaseIdx][wCompIdx] = + FluidSystem::diffusionCoefficient(fluidState_, + paramCache, + gPhaseIdx, + wCompIdx); + diffusionCoefficient_[gPhaseIdx][nCompIdx] = + FluidSystem::diffusionCoefficient(fluidState_, + paramCache, + gPhaseIdx, + nCompIdx); + diffusionCoefficient_[gPhaseIdx][gCompIdx] = 0.0; // dummy, should not be used ! + + diffusionCoefficient_[wPhaseIdx][gCompIdx] = + FluidSystem::diffusionCoefficient(fluidState_, + paramCache, + wPhaseIdx, + gCompIdx); + diffusionCoefficient_[wPhaseIdx][nCompIdx] = + FluidSystem::diffusionCoefficient(fluidState_, + paramCache, + wPhaseIdx, + nCompIdx); + diffusionCoefficient_[wPhaseIdx][wCompIdx] = 0.0; // dummy, should not be used ! + + /* no diffusion in NAPL phase considered at the moment */ + diffusionCoefficient_[nPhaseIdx][nCompIdx] = 0.0; + diffusionCoefficient_[nPhaseIdx][wCompIdx] = 0.0; + diffusionCoefficient_[nPhaseIdx][gCompIdx] = 0.0; + + Valgrind::CheckDefined(diffusionCoefficient_); + + // porosity + porosity_ = problem.spatialParams().porosity(element, + fvGeometry, + scvIdx); + Valgrind::CheckDefined(porosity_); + + // permeability + permeability_ = problem.spatialParams().intrinsicPermeability(element, + fvGeometry, + scvIdx); + Valgrind::CheckDefined(permeability_); + + // energy related quantities not contained in the fluid state + asImp_().updateEnergy_(priVars, problem, element, fvGeometry, scvIdx, isOldSol); + } + + /*! + * \brief Returns the phase state for the control-volume. + */ + const FluidState &fluidState() const + { return fluidState_; } + + /*! + * \brief Returns the effective saturation of a given phase within + * the control volume. + * + * \param phaseIdx The phase index + */ + Scalar saturation(const int phaseIdx) const + { return fluidState_.saturation(phaseIdx); } + + /*! + * \brief Returns the mass density of a given phase within the + * control volume. + * + * \param phaseIdx The phase index + */ + Scalar density(const int phaseIdx) const + { return fluidState_.density(phaseIdx); } + + /*! + * \brief Returns the molar density of a given phase within the + * control volume. + * + * \param phaseIdx The phase index + */ + Scalar molarDensity(const int phaseIdx) const + { return fluidState_.density(phaseIdx) / fluidState_.averageMolarMass(phaseIdx); } + + /*! + * \brief Returns the effective pressure of a given phase within + * the control volume. + * + * \param phaseIdx The phase index + */ + Scalar pressure(const int phaseIdx) const + { return fluidState_.pressure(phaseIdx); } + + /*! + * \brief Returns temperature inside the sub-control volume. + * + * Note that we assume thermodynamic equilibrium, i.e. the + * temperatures of the rock matrix and of all fluid phases are + * identical. + */ + Scalar temperature() const + { return fluidState_.temperature(/*phaseIdx=*/0); } + + /*! + * \brief Returns the effective mobility of a given phase within + * the control volume. + * + * \param phaseIdx The phase index + */ + Scalar mobility(const int phaseIdx) const + { + return mobility_[phaseIdx]; + } + + /*! + * \brief Returns the effective capillary pressure within the control volume. + */ + Scalar capillaryPressure() const + { return fluidState_.capillaryPressure(); } + + /*! + * \brief Returns the average porosity within the control volume. + */ + Scalar porosity() const + { return porosity_; } + + /*! + * \brief Returns the permeability within the control volume. + */ + Scalar permeability() const + { return permeability_; } + + /*! + * \brief Returns the diffusivity coefficient matrix + */ + Dune::FieldMatrix<Scalar, numPhases, numComponents> diffusionCoefficient() const + { return diffusionCoefficient_; } + + /*! + * \brief Returns the adsorption information + */ + Scalar bulkDensTimesAdsorpCoeff() const + { return bulkDensTimesAdsorpCoeff_; } + + +protected: + + static Scalar temperature_(const PrimaryVariables &priVars, + const Problem &problem, + const Element &element, + const FVElementGeometry &fvGeometry, + const int scvIdx) + { + return problem.boxTemperature(element, fvGeometry, scvIdx); + } + + /*! + * \brief Called by update() to compute the energy related quantities + */ + void updateEnergy_(const PrimaryVariables &priVars, + const Problem &problem, + const Element &element, + const FVElementGeometry &fvGeometry, + const int scvIdx, + bool isOldSol) + { } + + Scalar Sw_, Sg_, Sn_, pg_, pw_, pn_; + + Scalar moleFrac_[numPhases][numComponents]; + Scalar massFrac_[numPhases][numComponents]; + + Scalar porosity_; //!< Effective porosity within the control volume + Scalar permeability_; //!< Effective porosity within the control volume + Scalar mobility_[numPhases]; //!< Effective mobility within the control volume + Scalar bulkDensTimesAdsorpCoeff_; //!< the basis for calculating adsorbed NAPL + /* We need a tensor here !! */ + //!< Binary diffusion coefficients of the 3 components in the phases + Dune::FieldMatrix<Scalar, numPhases, numComponents> diffusionCoefficient_; + FluidState fluidState_; + +private: + Implementation &asImp_() + { return *static_cast<Implementation*>(this); } + + const Implementation &asImp_() const + { return *static_cast<const Implementation*>(this); } +}; + +template <class TypeTag> +const typename ThreePThreeCVolumeVariables<TypeTag>::Scalar ThreePThreeCVolumeVariables<TypeTag>::R = Constants<typename GET_PROP_TYPE(TypeTag, Scalar)>::R; + +} // end namepace + +#endif diff --git a/dumux/implicit/3p3c/Makefile.am b/dumux/implicit/3p3c/Makefile.am new file mode 100644 index 0000000000..561eac1db7 --- /dev/null +++ b/dumux/implicit/3p3c/Makefile.am @@ -0,0 +1,4 @@ +3p3cdir = $(includedir)/dumux/boxmodels/3p3c +3p3c_HEADERS = *.hh + +include $(top_srcdir)/am/global-rules diff --git a/dumux/implicit/3p3cni/3p3cnifluxvariables.hh b/dumux/implicit/3p3cni/3p3cnifluxvariables.hh new file mode 100644 index 0000000000..83b40fd835 --- /dev/null +++ b/dumux/implicit/3p3cni/3p3cnifluxvariables.hh @@ -0,0 +1,129 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \file + * + * \brief This file contains the data which is required to calculate + * all fluxes (mass of components and energy) over a face of a finite volume. + * + * This means pressure, concentration and temperature gradients, phase + * densities at the integration point, etc. + */ +#ifndef DUMUX_3P3CNI_FLUX_VARIABLES_HH +#define DUMUX_3P3CNI_FLUX_VARIABLES_HH + +#include <dumux/common/math.hh> +#include <dumux/boxmodels/3p3c/3p3cfluxvariables.hh> + +namespace Dumux +{ + +/*! + * \ingroup ThreePThreeCNIModel + * \brief This template class contains the data which is required to + * calculate all fluxes (mass of components and energy) over a face of a finite + * volume for the non-isothermal three-phase, three-component model. + * + * This means pressure and concentration gradients, phase densities at + * the integration point, etc. + */ +template <class TypeTag> +class ThreePThreeCNIFluxVariables : public ThreePThreeCFluxVariables<TypeTag> +{ + typedef ThreePThreeCFluxVariables<TypeTag> ParentType; + typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; + typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; + + typedef typename GET_PROP_TYPE(TypeTag, Problem) Problem; + + typedef typename GridView::ctype CoordScalar; + typedef typename GridView::template Codim<0>::Entity Element; + typedef typename GET_PROP_TYPE(TypeTag, ElementVolumeVariables) ElementVolumeVariables; + + enum { + dim = GridView::dimension, + dimWorld = GridView::dimensionworld + }; + + typedef typename GET_PROP_TYPE(TypeTag, FVElementGeometry) FVElementGeometry; + + typedef Dune::FieldVector<CoordScalar, dim> DimVector; + +public: + /* + * \brief The constructor + * + * \param problem The problem + * \param element The finite element + * \param fvGeometry The finite-volume geometry in the box scheme + * \param faceIdx The local index of the SCV (sub-control-volume) face + * \param elemVolVars The volume variables of the current element + * \param onBoundary A boolean variable to specify whether the flux variables + * are calculated for interior SCV faces or boundary faces, default=false + */ + ThreePThreeCNIFluxVariables(const Problem &problem, + const Element &element, + const FVElementGeometry &fvGeometry, + const int faceIdx, + const ElementVolumeVariables &elemVolVars, + const bool onBoundary = false) + : ParentType(problem, element, fvGeometry, faceIdx, elemVolVars, onBoundary) + { + // calculate temperature gradient using finite element + // gradients + DimVector temperatureGrad(0); + DimVector tmp(0.0); + for (int vertIdx = 0; vertIdx < fvGeometry.numFAP; vertIdx++) + { + tmp = this->face().grad[vertIdx]; + + // index for the element volume variables + int volVarsIdx = this->face().fapIndices[vertIdx]; + + tmp *= elemVolVars[volVarsIdx].temperature(); + temperatureGrad += tmp; + } + + // The spatial parameters calculates the actual heat flux vector + problem.spatialParams().matrixHeatFlux(tmp, + *this, + elemVolVars, + temperatureGrad, + element, + fvGeometry, + faceIdx); + // project the heat flux vector on the face's normal vector + normalMatrixHeatFlux_ = tmp*this->face().normal; + } + + /*! + * \brief The total heat flux \f$\mathrm{[J/s]}\f$ due to heat conduction + * of the rock matrix over the sub-control volume's face in + * direction of the face normal. + */ + Scalar normalMatrixHeatFlux() const + { return normalMatrixHeatFlux_; } + +private: + Scalar normalMatrixHeatFlux_; +}; + +} // end namepace + +#endif diff --git a/dumux/implicit/3p3cni/3p3cniindices.hh b/dumux/implicit/3p3cni/3p3cniindices.hh new file mode 100644 index 0000000000..753a23a540 --- /dev/null +++ b/dumux/implicit/3p3cni/3p3cniindices.hh @@ -0,0 +1,51 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ + +/*! + * \file + * + * \brief Defines the indices used by the 3p3cni box model + */ +#ifndef DUMUX_3P3CNI_INDICES_HH +#define DUMUX_3P3CNI_INDICES_HH + +#include <dumux/boxmodels/3p3c/3p3cindices.hh> + +namespace Dumux +{ +/*! + * \ingroup ThreePThreeCNIModel + */ +// \{ + +/*! + * \brief Enumerations for the non-isothermal 3-phase 3-component model + * + * \tparam PVOffset The first index in a primary variable vector. + */ +template <class TypeTag, int PVOffset> +class ThreePThreeCNIIndices : public ThreePThreeCIndices<TypeTag, PVOffset> +{ +public: + static const int temperatureIdx = PVOffset + 3; //! The index for temperature in primary variable vectors. + static const int energyEqIdx = PVOffset + 3; //! The index for energy in equation vectors. +}; + +} +#endif diff --git a/dumux/implicit/3p3cni/3p3cnilocalresidual.hh b/dumux/implicit/3p3cni/3p3cnilocalresidual.hh new file mode 100644 index 0000000000..5dfb6dce37 --- /dev/null +++ b/dumux/implicit/3p3cni/3p3cnilocalresidual.hh @@ -0,0 +1,193 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \file + * + * \brief Element-wise calculation of the Jacobian matrix for problems + * using the non-isothermal three-phase three-component box model. + * + */ +#ifndef DUMUX_NEW_3P3CNI_LOCAL_RESIDUAL_HH +#define DUMUX_NEW_3P3CNI_LOCAL_RESIDUAL_HH + +#include <dumux/boxmodels/3p3c/3p3clocalresidual.hh> + + +#include "3p3cnivolumevariables.hh" +#include "3p3cnifluxvariables.hh" + +#include "3p3cniproperties.hh" + +namespace Dumux +{ +/*! + * \ingroup ThreePThreeCNIModel + * \brief Element-wise calculation of the Jacobian matrix for problems + * using the three-phase three-component box model. + */ +template<class TypeTag> +class ThreePThreeCNILocalResidual : public ThreePThreeCLocalResidual<TypeTag> +{ + typedef ThreePThreeCLocalResidual<TypeTag> ParentType; + + typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; + + + typedef typename GET_PROP_TYPE(TypeTag, PrimaryVariables) PrimaryVariables; + + typedef typename GET_PROP_TYPE(TypeTag, Indices) Indices; + + enum { + + numPhases = GET_PROP_VALUE(TypeTag, NumPhases), + + energyEqIdx = Indices::energyEqIdx, + temperatureIdx = Indices::temperatureIdx, + + wPhaseIdx = Indices::wPhaseIdx, + nPhaseIdx = Indices::nPhaseIdx, + gPhaseIdx = Indices::gPhaseIdx + }; + + + typedef typename GET_PROP_TYPE(TypeTag, VolumeVariables) VolumeVariables; + typedef typename GET_PROP_TYPE(TypeTag, FluxVariables) FluxVariables; + typedef typename GET_PROP_TYPE(TypeTag, ElementVolumeVariables) ElementVolumeVariables; + +public: + /*! + * \brief Evaluate the amount all conservation quantities + * (e.g. phase mass) within a sub-control volume. + * + * The result should be averaged over the volume (e.g. phase mass + * inside a sub control volume divided by the volume) + * + * \param storage The storage of the conservation quantitiy (mass or energy) within the sub-control volume + * \param scvIdx The SCV (sub-control-volume) index + * \param usePrevSol Evaluate function with solution of current or previous time step + */ + void computeStorage(PrimaryVariables &storage, const int scvIdx, bool usePrevSol) const + { + // compute the storage term for phase mass + ParentType::computeStorage(storage, scvIdx, usePrevSol); + + // if flag usePrevSol is set, the solution from the previous + // time step is used, otherwise the current solution is + // used. The secondary variables are used accordingly. This + // is required to compute the derivative of the storage term + // using the implicit euler method. + const ElementVolumeVariables &elemDat = usePrevSol ? this->prevVolVars_() : this->curVolVars_(); + const VolumeVariables &vertDat = elemDat[scvIdx]; + + // compute the energy storage + Scalar wdens = vertDat.density(wPhaseIdx); + Scalar ndens = vertDat.density(nPhaseIdx); + Scalar gdens = vertDat.density(gPhaseIdx); + Scalar wInEnerg = vertDat.internalEnergy(wPhaseIdx); + Scalar nInEnerg = vertDat.internalEnergy(nPhaseIdx); + Scalar gInEnerg = vertDat.internalEnergy(gPhaseIdx); + Scalar wsat = vertDat.saturation(wPhaseIdx); + Scalar nsat = vertDat.saturation(nPhaseIdx); + Scalar gsat = vertDat.saturation(gPhaseIdx); + Scalar temp = vertDat.temperature(); + Scalar heatCap = vertDat.heatCapacity(); + Scalar poro = vertDat.porosity(); + + storage[energyEqIdx] = temp*heatCap + + poro * (gdens*gInEnerg*gsat + + wdens*wInEnerg*wsat + + ndens*nInEnerg*nsat); + /* + vertDat.porosity()*(vertDat.density(wPhaseIdx) * + vertDat.internalEnergy(wPhaseIdx) * + vertDat.saturation(wPhaseIdx) + + + vertDat.density(nPhaseIdx) * + vertDat.internalEnergy(nPhaseIdx) * + vertDat.saturation(nPhaseIdx) + + + vertDat.density(gPhaseIdx) * + vertDat.internalEnergy(gPhaseIdx) * + vertDat.saturation(gPhaseIdx)) + + + vertDat.temperature()*vertDat.heatCapacity(); + */ + } + + /*! + * \brief Evaluates the advective mass flux and the heat flux + * over a face of a subcontrol volume and writes the result in + * the flux vector. + * + * \param flux The advective flux over the SCV (sub-control-volume) face for each component + * \param fluxData The flux variables at the current SCV face + * + * This method is called by compute flux (base class) + */ + void computeAdvectiveFlux(PrimaryVariables &flux, + const FluxVariables &fluxData) const + { + // advective mass flux + ParentType::computeAdvectiveFlux(flux, fluxData); + + static const Scalar massUpwindWeight = GET_PARAM_FROM_GROUP(TypeTag, Scalar, Implicit, MassUpwindWeight); + + // advective heat flux in all phases + flux[energyEqIdx] = 0; + for (int phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) { + // vertex data of the upstream and the downstream vertices + const VolumeVariables &up = this->curVolVars_(fluxData.upstreamIdx(phaseIdx)); + const VolumeVariables &dn = this->curVolVars_(fluxData.downstreamIdx(phaseIdx)); + + flux[energyEqIdx] += + fluxData.volumeFlux(phaseIdx) * ( + massUpwindWeight * // upstream vertex + ( up.density(phaseIdx) * + up.enthalpy(phaseIdx)) + + + (1-massUpwindWeight) * // downstream vertex + ( dn.density(phaseIdx) * + dn.enthalpy(phaseIdx)) ); + } + } + + /*! + * \brief Adds the diffusive heat flux to the flux vector over + * the face of a sub-control volume. + * + * \param flux The diffusive flux over the SCV (sub-control-volume) face for each conservation quantity (mass, energy) + * \param fluxData The flux variables at the current SCV face + * + * This method is called by compute flux (base class) + */ + void computeDiffusiveFlux(PrimaryVariables &flux, + const FluxVariables &fluxData) const + { + // diffusive mass flux + ParentType::computeDiffusiveFlux(flux, fluxData); + + // diffusive heat flux + flux[temperatureIdx] += + fluxData.normalMatrixHeatFlux(); + } +}; + +} + +#endif diff --git a/dumux/implicit/3p3cni/3p3cnimodel.hh b/dumux/implicit/3p3cni/3p3cnimodel.hh new file mode 100644 index 0000000000..7873dee5ce --- /dev/null +++ b/dumux/implicit/3p3cni/3p3cnimodel.hh @@ -0,0 +1,108 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \file + * + * \brief Adaption of the BOX scheme to the non-isothermal three-phase three-component flow model. + */ +#ifndef DUMUX_NEW_3P3CNI_MODEL_HH +#define DUMUX_NEW_3P3CNI_MODEL_HH + +#include <dumux/boxmodels/3p3c/3p3cmodel.hh> + +namespace Dumux { +/*! + * \ingroup ThreePThreeCNIModel + * \brief Adaption of the BOX scheme to the non-isothermal three-phase three-component flow model. + * + * This model implements three-phase three-component flow of three fluid phases + * \f$\alpha \in \{ water, gas, NAPL \}\f$ each composed of up to three components + * \f$\kappa \in \{ water, air, contaminant \}\f$. The standard multiphase Darcy + * approach is used as the equation for the conservation of momentum: + * \f[ + v_\alpha = - \frac{k_{r\alpha}}{\mu_\alpha} \mbox{\bf K} + \left(\text{grad}\, p_\alpha - \varrho_{\alpha} \mbox{\bf g} \right) + * \f] + * + * By inserting this into the equations for the conservation of the + * components, one transport equation for each component is obtained as + * \f{eqnarray*} + && \phi \frac{\partial (\sum_\alpha \varrho_{\text{mol}, \alpha} x_\alpha^\kappa + S_\alpha )}{\partial t} + - \sum\limits_\alpha \text{div} \left\{ \frac{k_{r\alpha}}{\mu_\alpha} + \varrho_{\text{mol}, \alpha} x_\alpha^\kappa \mbox{\bf K} + (\text{grad}\; p_\alpha - \varrho_{\text{mass}, \alpha} \mbox{\bf g}) \right\} + \nonumber \\ + \nonumber \\ + && - \sum\limits_\alpha \text{div} \left\{ D_{pm}^\kappa \varrho_{\text{mol}, + \alpha } \text{grad} \; x_\alpha^\kappa \right\} + - q^\kappa = 0 \qquad \forall \kappa , \; \forall \alpha + \f} + * + * Note that these balance equations above are molar. + * In addition to that, a single balance of thermal energy is formulated + * for the fluid-filled porous medium under the assumption of local thermal + * equilibrium + * \f{eqnarray*} + && \phi \frac{\partial \left( \sum_\alpha \varrho_\alpha u_\alpha S_\alpha \right)}{\partial t} + + \left( 1 - \phi \right) \frac{\partial (\varrho_s c_s T)}{\partial t} + - \sum_\alpha \text{div} \left\{ \varrho_\alpha h_\alpha + \frac{k_{r\alpha}}{\mu_\alpha} \mathbf{K} \left( \text{grad}\, + p_\alpha + - \varrho_\alpha \mathbf{g} \right) \right\} \\ + &-& \text{div} \left( \lambda_{pm} \text{grad} \, T \right) + - q^h = 0 \qquad \alpha \in \{w, n, g\} + \f} + * + + * + * The equations are discretized using a fully-coupled vertex + * centered finite volume (BOX) scheme as spatial scheme and + * the implicit Euler method as temporal discretization. + * + * The model uses commonly applied auxiliary conditions like + * \f$S_w + S_n + S_g = 1\f$ for the saturations and + * \f$x^w_\alpha + x^a_\alpha + x^c_\alpha = 1\f$ for the mole fractions. + * Furthermore, the phase pressures are related to each other via + * capillary pressures between the fluid phases, which are functions of + * the saturation, e.g. according to the approach of Parker et al. + * + * The used primary variables are dependent on the locally present fluid phases + * An adaptive primary variable switch is included. The phase state is stored for all nodes + * of the system. The following cases can be distinguished: + * <ul> + * <li> All three phases are present: Primary variables are two saturations \f$(S_w\f$ and \f$S_n)\f$, a pressure, in this case \f$p_g\f$, and the temperature \f$T\f$. </li> + * <li> Only the water phase is present: Primary variables are now the mole fractions of air and contaminant in the water phase \f$(x_w^a\f$ and \f$x_w^c)\f$, as well as temperature and the gas pressure, which is, of course, in a case where only the water phase is present, just the same as the water pressure. </li> + * <li> Gas and NAPL phases are present: Primary variables \f$(S_n\f$, \f$x_g^w\f$, \f$p_g\f$, \f$T)\f$. </li> + * <li> Water and NAPL phases are present: Primary variables \f$(S_n\f$, \f$x_w^a\f$, \f$p_g\f$, \f$T)\f$. </li> + * <li> Only gas phase is present: Primary variables \f$(x_g^w\f$, \f$x_g^c\f$, \f$p_g\f$, \f$T)\f$. </li> + * <li> Water and gas phases are present: Primary variables \f$(S_w\f$, \f$x_w^g\f$, \f$p_g\f$, \f$T)\f$. </li> + * </ul> + * + */ +template<class TypeTag> +class ThreePThreeCNIModel : public ThreePThreeCModel<TypeTag> +{ +}; + +} + +#include "3p3cnipropertydefaults.hh" + +#endif diff --git a/dumux/implicit/3p3cni/3p3cniproperties.hh b/dumux/implicit/3p3cni/3p3cniproperties.hh new file mode 100644 index 0000000000..0c69522f03 --- /dev/null +++ b/dumux/implicit/3p3cni/3p3cniproperties.hh @@ -0,0 +1,47 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \ingroup ThreePThreeCNIModel + */ +/*! + * \file + * + * \brief Defines the properties required for the non-isothermal three-phase, + * three-component BOX model. + */ +#ifndef DUMUX_3P3CNI_PROPERTIES_HH +#define DUMUX_3P3CNI_PROPERTIES_HH + +#include <dumux/boxmodels/3p3c/3p3cproperties.hh> + +namespace Dumux +{ + +namespace Properties +{ +////////////////////////////////////////////////////////////////// +// Type tags +////////////////////////////////////////////////////////////////// + +//! The type tag for the non-isothermal three-phase, three-component problems +NEW_TYPE_TAG(BoxThreePThreeCNI, INHERITS_FROM(BoxThreePThreeC)); +} +} + +#endif diff --git a/dumux/implicit/3p3cni/3p3cnipropertydefaults.hh b/dumux/implicit/3p3cni/3p3cnipropertydefaults.hh new file mode 100644 index 0000000000..a00787502d --- /dev/null +++ b/dumux/implicit/3p3cni/3p3cnipropertydefaults.hh @@ -0,0 +1,71 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \ingroup ThreePThreeCNIModel + */ +/*! + * \file + * + * \brief Defines default values for most properties required by the 3p3cni + * box model. + */ +#ifndef DUMUX_3P3CNI_PROPERTY_DEFAULTS_HH +#define DUMUX_3P3CNI_PROPERTY_DEFAULTS_HH + +#include <dumux/boxmodels/3p3c/3p3cpropertydefaults.hh> + +#include "3p3cnimodel.hh" +#include "3p3cniindices.hh" +#include "3p3cnilocalresidual.hh" +#include "3p3cnivolumevariables.hh" +#include "3p3cnifluxvariables.hh" + +namespace Dumux +{ + +namespace Properties +{ +////////////////////////////////////////////////////////////////// +// Property values +////////////////////////////////////////////////////////////////// + +SET_INT_PROP(BoxThreePThreeCNI, NumEq, 4); //!< set the number of equations to 4 + +//! Use the 3p3cni local jacobian operator for the 3p3cni model +SET_TYPE_PROP(BoxThreePThreeCNI, + LocalResidual, + ThreePThreeCNILocalResidual<TypeTag>); + +//! the Model property +SET_TYPE_PROP(BoxThreePThreeCNI, Model, ThreePThreeCNIModel<TypeTag>); + +//! the VolumeVariables property +SET_TYPE_PROP(BoxThreePThreeCNI, VolumeVariables, ThreePThreeCNIVolumeVariables<TypeTag>); + + +//! the FluxVariables property +SET_TYPE_PROP(BoxThreePThreeCNI, FluxVariables, ThreePThreeCNIFluxVariables<TypeTag>); + +//! The indices required by the non-isothermal 3p3c model +SET_TYPE_PROP(BoxThreePThreeCNI, Indices, ThreePThreeCNIIndices<TypeTag, 0>); + +} + +} +#endif diff --git a/dumux/implicit/3p3cni/3p3cnivolumevariables.hh b/dumux/implicit/3p3cni/3p3cnivolumevariables.hh new file mode 100644 index 0000000000..d24cecadf1 --- /dev/null +++ b/dumux/implicit/3p3cni/3p3cnivolumevariables.hh @@ -0,0 +1,157 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \file + * + * \brief Contains the quantities which are constant within a + * finite volume in the non-isothermal three-phase, three-component + * model. + */ +#ifndef DUMUX_3P3CNI_VOLUME_VARIABLES_HH +#define DUMUX_3P3CNI_VOLUME_VARIABLES_HH + +#include <dumux/boxmodels/3p3c/3p3cvolumevariables.hh> + + +namespace Dumux +{ + +/*! + * \ingroup ThreePThreeCNIModel + * \brief Contains the quantities which are are constant within a + * finite volume in the non-isothermal three-phase, three-component + * model. + */ +template <class TypeTag> +class ThreePThreeCNIVolumeVariables : public ThreePThreeCVolumeVariables<TypeTag> +{ + //! \cond 0 + typedef ThreePThreeCVolumeVariables<TypeTag> ParentType; + + typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; + typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; + + typedef typename GridView::template Codim<0>::Entity Element; + typedef typename GET_PROP_TYPE(TypeTag, FVElementGeometry) FVElementGeometry; + typedef typename GET_PROP_TYPE(TypeTag, Problem) Problem; + + typedef typename GET_PROP_TYPE(TypeTag, FluidSystem) FluidSystem; + typedef typename GET_PROP_TYPE(TypeTag, Indices) Indices; + enum { numPhases = GET_PROP_VALUE(TypeTag, NumPhases) }; + enum { temperatureIdx = Indices::temperatureIdx }; + + typedef typename GET_PROP_TYPE(TypeTag, PrimaryVariables) PrimaryVariables; + //! \endcond + +public: + /*! + * \copydoc BoxVolumeVariables::update + */ + void update(const PrimaryVariables &priVars, + const Problem &problem, + const Element &element, + const FVElementGeometry &fvGeometry, + const int scvIdx, + bool isOldSol) + { + // vertex update data for the mass balance + ParentType::update(priVars, + problem, + element, + fvGeometry, + scvIdx, + isOldSol); + + typename FluidSystem::ParameterCache paramCache; + paramCache.updateAll(this->fluidState()); + + // the internal energies and the enthalpies + for (int phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) { + Scalar h = FluidSystem::enthalpy(this->fluidState_, paramCache, phaseIdx); + this->fluidState_.setEnthalpy(phaseIdx, h); + } + }; + + /*! + * \brief Returns the total internal energy of a phase in the + * sub-control volume. + * + * \param phaseIdx The phase index + */ + Scalar internalEnergy(int phaseIdx) const + { return this->fluidState_.internalEnergy(phaseIdx); }; + + /*! + * \brief Returns the total enthalpy of a phase in the sub-control + * volume. + * + * \param phaseIdx The phase index + */ + Scalar enthalpy(int phaseIdx) const + { return this->fluidState_.enthalpy(phaseIdx); }; + + /*! + * \brief Returns the total heat capacity \f$\mathrm{[J/(K*m^3]}\f$ of the rock matrix in + * the sub-control volume. + */ + Scalar heatCapacity() const + { return heatCapacity_; }; + +protected: + // this method gets called by the parent class. since this method + // is protected, we are friends with our parent.. + friend class ThreePThreeCVolumeVariables<TypeTag>; + + static Scalar temperature_(const PrimaryVariables &primaryVars, + const Problem& problem, + const Element &element, + const FVElementGeometry &fvGeometry, + const int scvIdx) + { + return primaryVars[temperatureIdx]; + } + + /*! + * \brief Update all quantities for a given control volume. + * + * \param priVars The solution primary variables + * \param problem The problem + * \param element The element + * \param fvGeometry Evaluate function with solution of current or previous time step + * \param scvIdx The local index of the SCV (sub-control volume) + * \param isOldSol Evaluate function with solution of current or previous time step + */ + void updateEnergy_(const PrimaryVariables &priVars, + const Problem &problem, + const Element &element, + const FVElementGeometry &fvGeometry, + const int scvIdx, + bool isOldSol) + { + // copmute and set the heat capacity of the solid phase + heatCapacity_ = problem.spatialParams().heatCapacity(element, fvGeometry, scvIdx); + Valgrind::CheckDefined(heatCapacity_); + }; + + Scalar heatCapacity_; +}; + +} // end namepace + +#endif diff --git a/dumux/implicit/3p3cni/Makefile.am b/dumux/implicit/3p3cni/Makefile.am new file mode 100644 index 0000000000..9045919660 --- /dev/null +++ b/dumux/implicit/3p3cni/Makefile.am @@ -0,0 +1,4 @@ +3p3cnidir = $(includedir)/dumux/boxmodels/3p3cni +3p3cni_HEADERS = *.hh + +include $(top_srcdir)/am/global-rules diff --git a/dumux/implicit/Makefile.am b/dumux/implicit/Makefile.am new file mode 100644 index 0000000000..0c3d2aaeab --- /dev/null +++ b/dumux/implicit/Makefile.am @@ -0,0 +1,5 @@ +SUBDIRS = common co2 co2ni 1p 1p2c 2p 2p2c 2p2cni 2pni 2pdfm 3p3c 3p3cni mpnc richards + +boxmodelsdir = $(includedir)/dumux/boxmodels + +include $(top_srcdir)/am/global-rules diff --git a/dumux/implicit/box/Makefile.am b/dumux/implicit/box/Makefile.am new file mode 100644 index 0000000000..2fd0a582d6 --- /dev/null +++ b/dumux/implicit/box/Makefile.am @@ -0,0 +1,4 @@ +commondir = $(includedir)/dumux/boxmodels/common +common_HEADERS = *.hh + +include $(top_srcdir)/am/global-rules diff --git a/dumux/implicit/box/boxassembler.hh b/dumux/implicit/box/boxassembler.hh new file mode 100644 index 0000000000..18e196c3e8 --- /dev/null +++ b/dumux/implicit/box/boxassembler.hh @@ -0,0 +1,858 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \file + * + * \brief An assembler for the global Jacobian matrix for models using the box discretization. + */ +#ifndef DUMUX_BOX_ASSEMBLER_HH +#define DUMUX_BOX_ASSEMBLER_HH + +#include <dune/grid/common/gridenums.hh> + +#include <dumux/boxmodels/common/boxproperties.hh> +#include <dumux/linear/vertexborderlistfromgrid.hh> +#include <dumux/linear/foreignoverlapfrombcrsmatrix.hh> +#include <dumux/parallel/vertexhandles.hh> + +namespace Dumux { + +/*! + * \brief An assembler for the global Jacobian matrix for models using the box discretization. + */ +template<class TypeTag> +class BoxAssembler +{ + typedef typename GET_PROP_TYPE(TypeTag, Model) Model; + typedef typename GET_PROP_TYPE(TypeTag, Problem) Problem; + typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; + typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; + typedef typename GET_PROP_TYPE(TypeTag, VertexMapper) VertexMapper; + typedef typename GET_PROP_TYPE(TypeTag, ElementMapper) ElementMapper; + + typedef typename GET_PROP_TYPE(TypeTag, SolutionVector) SolutionVector; + typedef typename GET_PROP_TYPE(TypeTag, JacobianMatrix) JacobianMatrix; + typedef typename GET_PROP_TYPE(TypeTag, PrimaryVariables) PrimaryVariables; + + enum{ dim = GridView::dimension }; + typedef typename GridView::template Codim<0>::Entity Element; + typedef typename GridView::template Codim<0>::Iterator ElementIterator; + typedef typename GridView::template Codim<dim>::EntityPointer VertexPointer; + + enum { numEq = GET_PROP_VALUE(TypeTag, NumEq) }; + typedef Dune::FieldMatrix<Scalar, numEq, numEq> MatrixBlock; + typedef Dune::FieldVector<Scalar, numEq> VectorBlock; + + // copying the jacobian assembler is not a good idea + BoxAssembler(const BoxAssembler &); + +public: + /*! + * \brief The colors of elements and vertices required for partial + * Jacobian reassembly. + */ + enum EntityColor { + /*! + * Vertex/element that needs to be reassembled because some + * relative error is above the tolerance + */ + Red = 0, + + /*! + * Vertex/element that needs to be reassembled because a + * neighboring element/vertex is red + */ + Yellow = 1, + + /*! + * Yellow vertex has only non-green neighbor elements. + * + * This means that its relative error is below the tolerance, + * but its defect can be linearized without any additional + * cost. This is just an "internal" color which is not used + * ouside of the jacobian assembler. + */ + Orange = 2, + + /*! + * Vertex/element that does not need to be reassembled + */ + Green = 3 + }; + + BoxAssembler() + { + problemPtr_ = 0; + matrix_ = 0; + + // set reassemble accuracy to 0, so that if partial reassembly + // of the jacobian matrix is disabled, the reassemble accuracy + // is always smaller than the current relative tolerance + reassembleAccuracy_ = 0.0; + } + + ~BoxAssembler() + { + delete matrix_; + } + + /*! + * \brief Initialize the jacobian assembler. + * + * At this point we can assume that all objects in the problem and + * the model have been allocated. We can not assume that they are + * fully initialized, though. + * + * \param problem The problem object + */ + void init(Problem& problem) + { + problemPtr_ = &problem; + + // initialize the BCRS matrix + createMatrix_(); + + // initialize the jacobian matrix and the right hand side + // vector + *matrix_ = 0; + reuseMatrix_ = false; + + int numVerts = gridView_().size(dim); + int numElems = gridView_().size(0); + + residual_.resize(numVerts); + + // initialize the storage part of the Jacobian matrix. Since + // we only need this if Jacobian matrix recycling is enabled, + // we do not waste space if it is disabled + if (enableJacobianRecycling_()) { + storageJacobian_.resize(numVerts); + storageTerm_.resize(numVerts); + } + + if (gridView_().comm().size() > 1) + totalElems_ = gridView_().comm().sum(numElems); + else + totalElems_ = numElems; + + // initialize data needed for partial reassembly + if (enablePartialReassemble_()) { + vertexColor_.resize(numVerts); + vertexDelta_.resize(numVerts); + elementColor_.resize(numElems); + } + reassembleAll(); + } + + /*! + * \brief Assemble the global Jacobian of the residual and the residual for the current solution. + * + * The current state of affairs (esp. the previous and the current + * solutions) is represented by the model object. + */ + void assemble() + { + bool printReassembleStatistics = enablePartialReassemble_() && !reuseMatrix_; + int succeeded; + try { + assemble_(); + succeeded = 1; + if (gridView_().comm().size() > 1) + succeeded = gridView_().comm().min(succeeded); + } + catch (Dumux::NumericalProblem &e) + { + std::cout << "rank " << problem_().gridView().comm().rank() + << " caught an exception while assembling:" << e.what() + << "\n"; + succeeded = 0; + if (gridView_().comm().size() > 1) + succeeded = gridView_().comm().min(succeeded); + } + + if (!succeeded) { + DUNE_THROW(NumericalProblem, + "A process did not succeed in linearizing the system"); + } + + if (printReassembleStatistics) + { + if (gridView_().comm().size() > 1) + { + greenElems_ = gridView_().comm().sum(greenElems_); + reassembleAccuracy_ = gridView_().comm().max(nextReassembleAccuracy_); + } + else + { + reassembleAccuracy_ = nextReassembleAccuracy_; + } + + problem_().newtonController().endIterMsg() + << ", reassembled " + << totalElems_ - greenElems_ << "/" << totalElems_ + << " (" << 100*Scalar(totalElems_ - greenElems_)/totalElems_ << "%) elems @accuracy=" + << reassembleAccuracy_; + } + + // reset all vertex colors to green + for (unsigned int i = 0; i < vertexColor_.size(); ++i) { + vertexColor_[i] = Green; + } + } + + /*! + * \brief If Jacobian matrix recycling is enabled, this method + * specifies whether the next call to assemble() just + * rescales the storage term or does a full reassembly + * + * \param yesno If true, only rescale; else do full Jacobian assembly. + */ + void setMatrixReuseable(const bool yesno = true) + { + if (enableJacobianRecycling_()) + reuseMatrix_ = yesno; + } + + /*! + * \brief If partial Jacobian matrix reassembly is enabled, this + * method causes all elements to be reassembled in the next + * assemble() call. + */ + void reassembleAll() + { + // do not reuse the current linearization + reuseMatrix_ = false; + + // do not use partial reassembly for the next iteration + nextReassembleAccuracy_ = 0.0; + if (enablePartialReassemble_()) { + std::fill(vertexColor_.begin(), + vertexColor_.end(), + Red); + std::fill(elementColor_.begin(), + elementColor_.end(), + Red); + std::fill(vertexDelta_.begin(), + vertexDelta_.end(), + 0.0); + } + } + + /*! + * \brief Returns the largest relative error of a "green" vertex + * for the most recent call of the assemble() method. + * + * This only has an effect if partial Jacobian reassembly is + * enabled. If it is disabled, then this method always returns 0. + * + * This returns the _actual_ relative computed seen by + * computeColors(), not the tolerance which it was given. + */ + Scalar reassembleAccuracy() const + { return reassembleAccuracy_; } + + /*! + * \brief Update the distance where the non-linear system was + * originally insistently linearized and the point where it + * will be linerized the next time. + * + * This only has an effect if partial reassemble is enabled. + */ + void updateDiscrepancy(const SolutionVector &u, + const SolutionVector &uDelta) + { + if (!enablePartialReassemble_()) + return; + + // update the vector with the distances of the current + // evaluation point used for linearization from the original + // evaluation point + for (unsigned int i = 0; i < vertexDelta_.size(); ++i) { + PrimaryVariables currentPriVars(u[i]); + PrimaryVariables nextPriVars(currentPriVars); + nextPriVars -= uDelta[i]; + + // we need to add the distance the solution was moved for + // this vertex + Scalar dist = model_().relativeErrorVertex(i, + currentPriVars, + nextPriVars); + vertexDelta_[i] += std::abs(dist); + } + + } + + /*! + * \brief Force to reassemble a given vertex next time the + * assemble() method is called. + * + * \param globalVertIdx The global index of the vertex which ought + * to be red. + */ + void markVertexRed(const int globalVertIdx) + { + if (!enablePartialReassemble_()) + return; + + vertexColor_[globalVertIdx] = Red; + } + + /*! + * \brief Determine the colors of vertices and elements for partial + * reassembly given a relative tolerance. + * + * The following approach is used: + * + * - Set all vertices and elements to 'green' + * - Mark all vertices as 'red' which exhibit an relative error above + * the tolerance + * - Mark all elements which feature a 'red' vetex as 'red' + * - Mark all vertices which are not 'red' and are part of a + * 'red' element as 'yellow' + * - Mark all elements which are not 'red' and contain a + * 'yellow' vertex as 'yellow' + * + * \param relTol The relative error below which a vertex won't be + * reassembled. Note that this specifies the + * worst-case relative error between the last + * linearization point and the current solution and + * _not_ the delta vector of the Newton iteration! + */ + void computeColors(const Scalar relTol) + { + if (!enablePartialReassemble_()) + return; + + ElementIterator elemIt = gridView_().template begin<0>(); + ElementIterator elemEndIt = gridView_().template end<0>(); + + // mark the red vertices and update the tolerance of the + // linearization which actually will get achieved + nextReassembleAccuracy_ = 0; + for (unsigned int i = 0; i < vertexColor_.size(); ++i) { + if (vertexDelta_[i] > relTol) + // mark vertex as red if discrepancy is larger than + // the relative tolerance + vertexColor_[i] = Red; + else + nextReassembleAccuracy_ = + std::max(nextReassembleAccuracy_, vertexDelta_[i]); + } + + // Mark all red elements + for (; elemIt != elemEndIt; ++elemIt) { + // find out whether the current element features a red + // vertex + bool isRed = false; + int numVerts = elemIt->template count<dim>(); + for (int i=0; i < numVerts; ++i) { + int globalI = vertexMapper_().map(*elemIt, i, dim); + if (vertexColor_[globalI] == Red) { + isRed = true; + break; + } + } + + // if yes, the element color is also red, else it is not + // red, i.e. green for the mean time + int globalElemIdx = elementMapper_().map(*elemIt); + if (isRed) + elementColor_[globalElemIdx] = Red; + else + elementColor_[globalElemIdx] = Green; + } + + // Mark yellow vertices (as orange for the mean time) + elemIt = gridView_().template begin<0>(); + for (; elemIt != elemEndIt; ++elemIt) { + int elemIdx = this->elementMapper_().map(*elemIt); + if (elementColor_[elemIdx] != Red) + continue; // non-red elements do not tint vertices + // yellow! + + int numVerts = elemIt->template count<dim>(); + for (int i=0; i < numVerts; ++i) { + int globalI = vertexMapper_().map(*elemIt, i, dim); + // if a vertex is already red, don't recolor it to + // yellow! + if (vertexColor_[globalI] != Red) { + vertexColor_[globalI] = Orange; + } + } + } + + // at this point we communicate the yellow vertices to the + // neighboring processes because a neigbor process may not see + // the red vertex for yellow border vertices + VertexHandleMin<EntityColor, std::vector<EntityColor>, VertexMapper> + minHandle(vertexColor_, vertexMapper_()); + gridView_().communicate(minHandle, + Dune::InteriorBorder_InteriorBorder_Interface, + Dune::ForwardCommunication); + + // Mark yellow elements + elemIt = gridView_().template begin<0>(); + for (; elemIt != elemEndIt; ++elemIt) { + int elemIdx = this->elementMapper_().map(*elemIt); + if (elementColor_[elemIdx] == Red) { + continue; // element is red already! + } + + // check whether the element features a yellow + // (resp. orange at this point) vertex + bool isYellow = false; + int numVerts = elemIt->template count<dim>(); + for (int i=0; i < numVerts; ++i) { + int globalI = vertexMapper_().map(*elemIt, i, dim); + if (vertexColor_[globalI] == Orange) { + isYellow = true; + break; + } + } + + if (isYellow) + elementColor_[elemIdx] = Yellow; + } + + // Demote orange vertices to yellow ones if it has at least + // one green element as a neighbor. + elemIt = gridView_().template begin<0>(); + for (; elemIt != elemEndIt; ++elemIt) { + int elemIdx = this->elementMapper_().map(*elemIt); + if (elementColor_[elemIdx] != Green) + continue; // yellow and red elements do not make + // orange vertices yellow! + + int numVerts = elemIt->template count<dim>(); + for (int i=0; i < numVerts; ++i) { + int globalI = vertexMapper_().map(*elemIt, i, dim); + // if a vertex is orange, recolor it to yellow! + if (vertexColor_[globalI] == Orange) + vertexColor_[globalI] = Yellow; + } + } + + // demote the border orange vertices + VertexHandleMax<EntityColor, std::vector<EntityColor>, VertexMapper> + maxHandle(vertexColor_, + vertexMapper_()); + gridView_().communicate(maxHandle, + Dune::InteriorBorder_InteriorBorder_Interface, + Dune::ForwardCommunication); + + // promote the remaining orange vertices to red + for (unsigned int i=0; i < vertexColor_.size(); ++i) { + // if a vertex is green or yellow don't do anything! + if (vertexColor_[i] == Green || vertexColor_[i] == Yellow) + continue; + + // make sure the vertex is red (this is a no-op vertices + // which are already red!) + vertexColor_[i] = Red; + + // set the error of this vertex to 0 because the system + // will be consistently linearized at this vertex + vertexDelta_[i] = 0.0; + } + } + + /*! + * \brief Returns the reassemble color of a vertex + * + * \param element An element which contains the vertex + * \param vertIdx The local index of the vertex in the element. + */ + int vertexColor(const Element &element, const int vertIdx) const + { + if (!enablePartialReassemble_()) + return Red; // reassemble unconditionally! + + int globalIdx = vertexMapper_().map(element, vertIdx, dim); + return vertexColor_[globalIdx]; + } + + /*! + * \brief Returns the reassemble color of a vertex + * + * \param globalVertIdx The global index of the vertex. + */ + int vertexColor(const int globalVertIdx) const + { + if (!enablePartialReassemble_()) + return Red; // reassemble unconditionally! + return vertexColor_[globalVertIdx]; + } + + /*! + * \brief Returns the Jacobian reassemble color of an element + * + * \param element The Codim-0 DUNE entity + */ + int elementColor(const Element &element) const + { + if (!enablePartialReassemble_()) + return Red; // reassemble unconditionally! + + int globalIdx = elementMapper_().map(element); + return elementColor_[globalIdx]; + } + + /*! + * \brief Returns the Jacobian reassemble color of an element + * + * \param globalElementIdx The global index of the element. + */ + int elementColor(const int globalElementIdx) const + { + if (!enablePartialReassemble_()) + return Red; // reassemble unconditionally! + return elementColor_[globalElementIdx]; + } + + /*! + * \brief Return constant reference to global Jacobian matrix. + */ + const JacobianMatrix& matrix() const + { return *matrix_; } + JacobianMatrix& matrix() + { return *matrix_; } + + /*! + * \brief Return constant reference to global residual vector. + */ + const SolutionVector& residual() const + { return residual_; } + SolutionVector& residual() + { return residual_; } + +private: + static bool enableJacobianRecycling_() + { return GET_PARAM_FROM_GROUP(TypeTag, bool, Implicit, EnableJacobianRecycling); } + static bool enablePartialReassemble_() + { return GET_PARAM_FROM_GROUP(TypeTag, bool, Implicit, EnablePartialReassemble); } + + // Construct the BCRS matrix for the global jacobian + void createMatrix_() + { + int numVerticesGlobal = gridView_().size(dim); + + // allocate raw matrix + matrix_ = new JacobianMatrix(numVerticesGlobal, numVerticesGlobal, JacobianMatrix::random); + + // find out the global indices of the neighboring vertices of + // each vertex + typedef std::set<int> NeighborSet; + std::vector<NeighborSet> neighbors(numVerticesGlobal); + ElementIterator eIt = gridView_().template begin<0>(); + const ElementIterator eEndIt = gridView_().template end<0>(); + for (; eIt != eEndIt; ++eIt) { + const Element &elem = *eIt; + int numVerticesLocal = elem.template count<dim>(); + + // if the element is not in the interior or the process + // border, all dofs just contain main-diagonal entries + if (elem.partitionType() != Dune::InteriorEntity && + elem.partitionType() != Dune::BorderEntity) + { + for (int i = 0; i < numVerticesLocal; ++i) { + int globalI = vertexMapper_().map(*eIt, i, dim); + neighbors[globalI].insert(globalI); + } + } + else + { + // loop over all element vertices + for (int i = 0; i < numVerticesLocal - 1; ++i) { + int globalI = vertexMapper_().map(*eIt, i, dim); + for (int j = i + 1; j < numVerticesLocal; ++j) { + int globalJ = vertexMapper_().map(*eIt, j, dim); + // make sure that vertex j is in the neighbor set + // of vertex i and vice-versa + neighbors[globalI].insert(globalJ); + neighbors[globalJ].insert(globalI); + } + } + } + } + + // make vertices neighbors to themselfs + for (int i = 0; i < numVerticesGlobal; ++i) { + neighbors[i].insert(i); + } + + // allocate space for the rows of the matrix + for (int i = 0; i < numVerticesGlobal; ++i) { + matrix_->setrowsize(i, neighbors[i].size()); + } + matrix_->endrowsizes(); + + // fill the rows with indices. each vertex talks to all of its + // neighbors. (it also talks to itself since vertices are + // sometimes quite egocentric.) + for (int i = 0; i < numVerticesGlobal; ++i) { + typename NeighborSet::iterator nIt = neighbors[i].begin(); + typename NeighborSet::iterator nEndIt = neighbors[i].end(); + for (; nIt != nEndIt; ++nIt) { + matrix_->addindex(i, *nIt); + } + } + matrix_->endindices(); + } + + // reset the global linear system of equations. if partial + // reassemble is enabled, this means that the jacobian matrix must + // only be erased partially! + void resetSystem_() + { + // do not do anything if we can re-use the current linearization + if (reuseMatrix_) + return; + + // reset the right hand side. + residual_ = 0.0; + + if (!enablePartialReassemble_()) { + // If partial reassembly of the jacobian is not enabled, + // we can just reset everything! + (*matrix_) = 0; + + // reset the parts needed for Jacobian recycling + if (enableJacobianRecycling_()) { + int numVerticesGlobal = matrix_->N(); + for (int i=0; i < numVerticesGlobal; ++ i) { + storageJacobian_[i] = 0; + storageTerm_[i] = 0; + } + } + + return; + } + + // reset all entries corrosponding to a red or yellow vertex + for (unsigned int rowIdx = 0; rowIdx < matrix_->N(); ++rowIdx) { + if (vertexColor_[rowIdx] == Green) + continue; // the equations for this control volume are + // already below the treshold + + // here we have yellow or red vertices... + + // reset the parts needed for Jacobian recycling + if (enableJacobianRecycling_()) { + storageJacobian_[rowIdx] = 0; + storageTerm_[rowIdx] = 0; + } + + // set all matrix entries in the row to 0 + typedef typename JacobianMatrix::ColIterator ColIterator; + ColIterator colIt = (*matrix_)[rowIdx].begin(); + const ColIterator &colEndIt = (*matrix_)[rowIdx].end(); + for (; colIt != colEndIt; ++colIt) { + (*colIt) = 0.0; + } + } + } + + // linearize the whole system + void assemble_() + { + resetSystem_(); + + // if we can "recycle" the current linearization, we do it + // here and be done with it... + Scalar curDt = problem_().timeManager().timeStepSize(); + if (reuseMatrix_) { + int numVerticesGlobal = storageJacobian_.size(); + for (int i = 0; i < numVerticesGlobal; ++i) { + // rescale the mass term of the jacobian matrix + MatrixBlock &J_i_i = (*matrix_)[i][i]; + + J_i_i -= storageJacobian_[i]; + storageJacobian_[i] *= oldDt_/curDt; + J_i_i += storageJacobian_[i]; + + // use the flux term plus the source term as the new + // residual (since the delta in the d(storage)/dt is 0 + // for the first iteration and the residual is + // approximately 0 in the last iteration, the flux + // term plus the source term must be equal to the + // negative change of the storage term of the last + // iteration of the last time step...) + residual_[i] = storageTerm_[i]; + residual_[i] *= -1; + } + + reuseMatrix_ = false; + oldDt_ = curDt; + return; + } + + oldDt_ = curDt; + greenElems_ = 0; + + // reassemble the elements... + ElementIterator elemIt = gridView_().template begin<0>(); + ElementIterator elemEndIt = gridView_().template end<0>(); + for (; elemIt != elemEndIt; ++elemIt) { + const Element &elem = *elemIt; + if (elem.partitionType() != Dune::InteriorEntity && + elem.partitionType() != Dune::BorderEntity) + { + assembleGhostElement_(elem); + } + else + { + assembleElement_(elem); + } + } + } + + // assemble a non-ghost element + void assembleElement_(const Element &elem) + { + if (enablePartialReassemble_()) { + int globalElemIdx = model_().elementMapper().map(elem); + if (elementColor_[globalElemIdx] == Green) { + ++greenElems_; + + assembleGreenElement_(elem); + return; + } + } + + model_().localJacobian().assemble(elem); + + int numVerticesLocal = elem.template count<dim>(); + for (int i=0; i < numVerticesLocal; ++ i) { + int globI = vertexMapper_().map(elem, i, dim); + + // update the right hand side + residual_[globI] += model_().localJacobian().residual(i); + for (int j = 0; j < residual_[globI].dimension; ++j) + assert(std::isfinite(residual_[globI][j])); + if (enableJacobianRecycling_()) { + storageTerm_[globI] += + model_().localJacobian().storageTerm(i); + } + + // only update the jacobian matrix for non-green vertices + if (vertexColor(globI) != Green) { + if (enableJacobianRecycling_()) + storageJacobian_[globI] += + model_().localJacobian().storageJacobian(i); + + // update the jacobian matrix + for (int j=0; j < numVerticesLocal; ++ j) { + int globJ = vertexMapper_().map(elem, j, dim); + (*matrix_)[globI][globJ] += + model_().localJacobian().mat(i,j); + } + } + } + } + + // "assemble" a green element. green elements only get the + // residual updated, but the jacobian is left alone... + void assembleGreenElement_(const Element &elem) + { + model_().localResidual().eval(elem); + + int numVerticesLocal = elem.template count<dim>(); + for (int i=0; i < numVerticesLocal; ++ i) { + int globI = vertexMapper_().map(elem, i, dim); + + // update the right hand side + residual_[globI] += model_().localResidual().residual(i); + if (enableJacobianRecycling_()) + storageTerm_[globI] += model_().localResidual().storageTerm(i); + } + } + + // "assemble" a ghost element + void assembleGhostElement_(const Element &elem) + { + int numVerticesLocal = elem.template count<dim>(); + for (int i=0; i < numVerticesLocal; ++i) { + const VertexPointer vp = elem.template subEntity<dim>(i); + + if (vp->partitionType() == Dune::InteriorEntity || + vp->partitionType() == Dune::BorderEntity) + { + // do not change the non-ghost vertices + continue; + } + + // set main diagonal entries for the vertex + int vIdx = vertexMapper_().map(*vp); + typedef typename JacobianMatrix::block_type BlockType; + BlockType &J = (*matrix_)[vIdx][vIdx]; + for (int j = 0; j < BlockType::rows; ++j) + J[j][j] = 1.0; + + // set residual for the vertex + residual_[vIdx] = 0; + } + } + + + Problem &problem_() + { return *problemPtr_; } + const Problem &problem_() const + { return *problemPtr_; } + const Model &model_() const + { return problem_().model(); } + Model &model_() + { return problem_().model(); } + const GridView &gridView_() const + { return problem_().gridView(); } + const VertexMapper &vertexMapper_() const + { return problem_().vertexMapper(); } + const ElementMapper &elementMapper_() const + { return problem_().elementMapper(); } + + Problem *problemPtr_; + + // the jacobian matrix + JacobianMatrix *matrix_; + // the right-hand side + SolutionVector residual_; + + // attributes required for jacobian matrix recycling + bool reuseMatrix_; + // The storage part of the local Jacobian + std::vector<MatrixBlock> storageJacobian_; + std::vector<VectorBlock> storageTerm_; + // time step size of last assembly + Scalar oldDt_; + + + // attributes required for partial jacobian reassembly + std::vector<EntityColor> vertexColor_; + std::vector<EntityColor> elementColor_; + std::vector<Scalar> vertexDelta_; + + int totalElems_; + int greenElems_; + + Scalar nextReassembleAccuracy_; + Scalar reassembleAccuracy_; +}; + +} // namespace Dumux + +#endif diff --git a/dumux/implicit/box/boxdarcyfluxvariables.hh b/dumux/implicit/box/boxdarcyfluxvariables.hh new file mode 100644 index 0000000000..6e5d3df449 --- /dev/null +++ b/dumux/implicit/box/boxdarcyfluxvariables.hh @@ -0,0 +1,313 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \file + * + * \brief This file contains the data which is required to calculate + * all fluxes of fluid phases over a face of a finite volume. + * + * This means pressure and temperature gradients, phase densities at + * the integration point, etc. + */ +#ifndef DUMUX_BOX_DARCY_FLUX_VARIABLES_HH +#define DUMUX_BOX_DARCY_FLUX_VARIABLES_HH + +#include "boxproperties.hh" + +#include <dumux/common/parameters.hh> +#include <dumux/common/math.hh> + +namespace Dumux +{ + +namespace Properties +{ +// forward declaration of properties +NEW_PROP_TAG(ImplicitMobilityUpwindWeight); +NEW_PROP_TAG(SpatialParams); +NEW_PROP_TAG(NumPhases); +NEW_PROP_TAG(ProblemEnableGravity); +} + +/*! + * \ingroup BoxModel + * \ingroup BoxFluxVariables + * \brief Evaluates the normal component of the Darcy velocity + * on a (sub)control volume face. + */ +template <class TypeTag> +class BoxDarcyFluxVariables +{ + typedef typename GET_PROP_TYPE(TypeTag, Problem) Problem; + typedef typename GET_PROP_TYPE(TypeTag, SpatialParams) SpatialParams; + typedef typename GET_PROP_TYPE(TypeTag, ElementVolumeVariables) ElementVolumeVariables; + typedef typename GET_PROP_TYPE(TypeTag, VolumeVariables) VolumeVariables; + + typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; + typedef typename GridView::template Codim<0>::Entity Element; + + enum { dim = GridView::dimension} ; + enum { dimWorld = GridView::dimensionworld} ; + enum { numPhases = GET_PROP_VALUE(TypeTag, NumPhases)} ; + + typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; + typedef Dune::FieldMatrix<Scalar, dimWorld, dimWorld> Tensor; + typedef Dune::FieldVector<Scalar, dimWorld> DimVector; + + typedef typename GET_PROP_TYPE(TypeTag, FVElementGeometry) FVElementGeometry; + typedef typename FVElementGeometry::SubControlVolumeFace SCVFace; + +public: + /* + * \brief The constructor + * + * \param problem The problem + * \param element The finite element + * \param fvGeometry The finite-volume geometry in the box scheme + * \param faceIdx The local index of the SCV (sub-control-volume) face + * \param elemVolVars The volume variables of the current element + * \param onBoundary A boolean variable to specify whether the flux variables + * are calculated for interior SCV faces or boundary faces, default=false + */ + BoxDarcyFluxVariables(const Problem &problem, + const Element &element, + const FVElementGeometry &fvGeometry, + const int faceIdx, + const ElementVolumeVariables &elemVolVars, + const bool onBoundary = false) + : fvGeometry_(fvGeometry), faceIdx_(faceIdx), onBoundary_(onBoundary) + { + mobilityUpwindWeight_ = GET_PARAM_FROM_GROUP(TypeTag, Scalar, Implicit, MobilityUpwindWeight); + calculateGradients_(problem, element, elemVolVars); + calculateNormalVelocity_(problem, element, elemVolVars); + } + +public: + /*! + * \brief Return the volumetric flux over a face of a given phase. + * + * This is the calculated velocity multiplied by the unit normal + * and the area of the face. + * face().normal + * has already the magnitude of the area. + * + * \param phaseIdx index of the phase + */ + Scalar volumeFlux(const unsigned int phaseIdx) const + { return volumeFlux_[phaseIdx]; } + + /*! + * \brief Return the velocity of a given phase. + * + * This is the full velocity vector on the + * face (without being multiplied with normal). + * + * \param phaseIdx index of the phase + */ + DimVector velocity(const unsigned int phaseIdx) const + { return velocity_[phaseIdx] ; } + + /*! + * \brief Return intrinsic permeability multiplied with potential + * gradient multiplied with normal. + * I.e. everything that does not need upwind decisions. + * + * \param phaseIdx index of the phase + */ + Scalar kGradPNormal(const unsigned int phaseIdx) const + { return kGradPNormal_[phaseIdx] ; } + + /*! + * \brief Return the local index of the downstream control volume + * for a given phase. + * + * \param phaseIdx index of the phase + */ + const unsigned int downstreamIdx(const unsigned phaseIdx) const + { return downstreamIdx_[phaseIdx]; } + + /*! + * \brief Return the local index of the upstream control volume + * for a given phase. + * + * \param phaseIdx index of the phase + */ + const unsigned int upstreamIdx(const unsigned phaseIdx) const + { return upstreamIdx_[phaseIdx]; } + + /*! + * \brief Return the SCV (sub-control-volume) face. This may be either + * a face within the element or a face on the element boundary, + * depending on the value of onBoundary_. + */ + const SCVFace &face() const + { + if (onBoundary_) + return fvGeometry_.boundaryFace[faceIdx_]; + else + return fvGeometry_.subContVolFace[faceIdx_]; + } + +protected: + const FVElementGeometry &fvGeometry_; //!< Information about the geometry of discretization + const unsigned int faceIdx_; //!< The index of the sub control volume face + const bool onBoundary_; //!< Specifying whether we are currently on the boundary of the simulation domain + unsigned int upstreamIdx_[numPhases] , downstreamIdx_[numPhases]; //!< local index of the upstream / downstream vertex + Scalar volumeFlux_[numPhases] ; //!< Velocity multiplied with normal (magnitude=area) + DimVector velocity_[numPhases] ; //!< The velocity as determined by Darcy's law or by the Forchheimer relation + Scalar kGradPNormal_[numPhases] ; //!< Permeability multiplied with gradient in potential, multiplied with normal (magnitude=area) + DimVector kGradP_[numPhases] ; //!< Permeability multiplied with gradient in potential + DimVector gradPotential_[numPhases] ; //!< Gradient of potential, which drives flow + Scalar mobilityUpwindWeight_; //!< Upwind weight for mobility. Set to one for full upstream weighting + + /* + * \brief Calculation of the potential gradients + * + * \param problem The problem + * \param element The finite element + * \param elemVolVars The volume variables of the current element + * are calculated for interior SCV faces or boundary faces, default=false + */ + void calculateGradients_(const Problem &problem, + const Element &element, + const ElementVolumeVariables &elemVolVars) + { + // loop over all phases + for (int phaseIdx = 0; phaseIdx < numPhases; phaseIdx++) + { + gradPotential_[phaseIdx]= 0.0 ; + for (int idx = 0; + idx < fvGeometry_.numFAP; + idx++) // loop over adjacent vertices + { + // FE gradient at vertex idx + const DimVector &feGrad = face().grad[idx]; + + // index for the element volume variables + int volVarsIdx = face().fapIndices[idx]; + + // the pressure gradient + DimVector tmp(feGrad); + tmp *= elemVolVars[volVarsIdx].fluidState().pressure(phaseIdx); + gradPotential_[phaseIdx] += tmp; + } + + // correct the pressure gradient by the gravitational acceleration + if (GET_PARAM_FROM_GROUP(TypeTag, bool, Problem, EnableGravity)) + { + // estimate the gravitational acceleration at a given SCV face + // using the arithmetic mean + DimVector g(problem.boxGravity(element, fvGeometry_, face().i)); + g += problem.boxGravity(element, fvGeometry_, face().j); + g /= 2; + + // calculate the phase density at the integration point. we + // only do this if the wetting phase is present in both cells + Scalar SI = elemVolVars[face().i].fluidState().saturation(phaseIdx); + Scalar SJ = elemVolVars[face().j].fluidState().saturation(phaseIdx); + Scalar rhoI = elemVolVars[face().i].fluidState().density(phaseIdx); + Scalar rhoJ = elemVolVars[face().j].fluidState().density(phaseIdx); + Scalar fI = std::max(0.0, std::min(SI/1e-5, 0.5)); + Scalar fJ = std::max(0.0, std::min(SJ/1e-5, 0.5)); + if (fI + fJ == 0) + // doesn't matter because no wetting phase is present in + // both cells! + fI = fJ = 0.5; + const Scalar density = (fI*rhoI + fJ*rhoJ)/(fI + fJ); + + // make gravity acceleration a force + DimVector f(g); + f *= density; + + // calculate the final potential gradient + gradPotential_[phaseIdx] -= f; + } // gravity + } // loop over all phases + } + + /* + * \brief Actual calculation of the normal Darcy velocities. + * + * \param problem The problem + * \param element The finite element + * \param elemVolVars The volume variables of the current element + */ + void calculateNormalVelocity_(const Problem &problem, + const Element &element, + const ElementVolumeVariables &elemVolVars) + { + // calculate the mean intrinsic permeability + const SpatialParams &spatialParams = problem.spatialParams(); + Tensor K; + spatialParams.meanK(K, + spatialParams.intrinsicPermeability(element, + fvGeometry_, + face().i), + spatialParams.intrinsicPermeability(element, + fvGeometry_, + face().j)); + // loop over all phases + for (int phaseIdx = 0; phaseIdx < numPhases; phaseIdx++) + { + // calculate the flux in the normal direction of the + // current sub control volume face: + // + // v = - (K_f grad phi) * n + // with K_f = rho g / mu K + // + // Mind, that the normal has the length of it's area. + // This means that we are actually calculating + // Q = - (K grad phi) dot n /|n| * A + + + K.mv(gradPotential_[phaseIdx], kGradP_[phaseIdx]); + kGradPNormal_[phaseIdx] = kGradP_[phaseIdx]*face().normal; + + // determine the upwind direction + if (kGradPNormal_[phaseIdx] < 0) + { + upstreamIdx_[phaseIdx] = face().i; + downstreamIdx_[phaseIdx] = face().j; + } + else + { + upstreamIdx_[phaseIdx] = face().j; + downstreamIdx_[phaseIdx] = face().i; + } + + // obtain the upwind volume variables + const VolumeVariables& upVolVars = elemVolVars[ upstreamIdx(phaseIdx) ]; + const VolumeVariables& downVolVars = elemVolVars[ downstreamIdx(phaseIdx) ]; + + // the minus comes from the Darcy relation which states that + // the flux is from high to low potentials. + // set the velocity + velocity_[phaseIdx] = kGradP_[phaseIdx]; + velocity_[phaseIdx] *= - ( mobilityUpwindWeight_*upVolVars.mobility(phaseIdx) + + (1.0 - mobilityUpwindWeight_)*downVolVars.mobility(phaseIdx)) ; + + // set the volume flux + volumeFlux_[phaseIdx] = velocity_[phaseIdx] * face().normal ; + }// loop all phases + } +}; + +} // end namespace + +#endif diff --git a/dumux/implicit/box/boxelementboundarytypes.hh b/dumux/implicit/box/boxelementboundarytypes.hh new file mode 100644 index 0000000000..fe7e02a09c --- /dev/null +++ b/dumux/implicit/box/boxelementboundarytypes.hh @@ -0,0 +1,148 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \file + * + * \brief Boundary types gathered on an element + */ +#ifndef DUMUX_BOX_ELEMENT_BOUNDARY_TYPES_HH +#define DUMUX_BOX_ELEMENT_BOUNDARY_TYPES_HH + +#include "boxproperties.hh" + +#include <dumux/common/valgrind.hh> + +namespace Dumux +{ + +/*! + * \ingroup BoxModel + * \ingroup BoxBoundaryTypes + * + * \brief This class stores an array of BoundaryTypes objects + */ +template<class TypeTag> +class BoxElementBoundaryTypes : public std::vector<typename GET_PROP_TYPE(TypeTag, BoundaryTypes) > +{ + typedef typename GET_PROP_TYPE(TypeTag, BoundaryTypes) BoundaryTypes; + typedef std::vector<BoundaryTypes> ParentType; + + typedef typename GET_PROP_TYPE(TypeTag, Problem) Problem; + typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; + typedef typename GET_PROP_TYPE(TypeTag, FVElementGeometry) FVElementGeometry; + + enum { dim = GridView::dimension }; + typedef typename GridView::template Codim<0>::Entity Element; + typedef typename GridView::template Codim<dim>::EntityPointer VertexPointer; + +public: + /*! + * \brief Copy constructor. + * + * Copying a the boundary types of an element should be explicitly + * requested + */ + explicit BoxElementBoundaryTypes(const BoxElementBoundaryTypes &v) + : ParentType(v) + {} + + /*! + * \brief Default constructor. + */ + BoxElementBoundaryTypes() + { + hasDirichlet_ = false; + hasNeumann_ = false; + hasOutflow_ = false; + } + + /*! + * \brief Update the boundary types for all vertices of an element. + * + * \param problem The problem object which needs to be simulated + * \param element The DUNE Codim<0> entity for which the boundary + * types should be collected + */ + void update(const Problem &problem, + const Element &element) + { + int numVerts = element.template count<dim>(); + this->resize(numVerts); + + hasDirichlet_ = false; + hasNeumann_ = false; + hasOutflow_ = false; + + for (int i = 0; i < numVerts; ++i) { + (*this)[i].reset(); + + if (problem.model().onBoundary(element, i)) { + const VertexPointer vptr = element.template subEntity<dim>(i); + problem.boundaryTypes((*this)[i], *vptr); + + hasDirichlet_ = hasDirichlet_ || (*this)[i].hasDirichlet(); + hasNeumann_ = hasNeumann_ || (*this)[i].hasNeumann(); + hasOutflow_ = hasOutflow_ || (*this)[i].hasOutflow(); + } + } + } + + /*! + * \brief Update the boundary types for all vertices of an element. + * + * \param problem The problem object which needs to be simulated + * \param element The DUNE Codim<0> entity for which the boundary + * types should be collected + * \param fvGeometry The element's finite volume geometry + */ + void update(const Problem &problem, + const Element &element, + const FVElementGeometry &fvGeometry) + { update(problem, element); } + + /*! + * \brief Returns whether the element has a vertex which contains + * a Dirichlet value. + */ + bool hasDirichlet() const + { return hasDirichlet_; } + + /*! + * \brief Returns whether the element potentially features a + * Neumann boundary segment. + */ + bool hasNeumann() const + { return hasNeumann_; } + + /*! + * \brief Returns whether the element potentially features an + * outflow boundary segment. + */ + bool hasOutflow() const + { return hasOutflow_; } + +protected: + bool hasDirichlet_; + bool hasNeumann_; + bool hasOutflow_; +}; + +} // namespace Dumux + +#endif diff --git a/dumux/implicit/box/boxelementvolumevariables.hh b/dumux/implicit/box/boxelementvolumevariables.hh new file mode 100644 index 0000000000..c791bee09b --- /dev/null +++ b/dumux/implicit/box/boxelementvolumevariables.hh @@ -0,0 +1,138 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \file + * + * \brief Volume variables gathered on an element + */ +#ifndef DUMUX_BOX_ELEMENT_VOLUME_VARIABLES_HH +#define DUMUX_BOX_ELEMENT_VOLUME_VARIABLES_HH + +#include "boxproperties.hh" + + +namespace Dumux +{ + +/*! + * \ingroup BoxModel + * + * \brief This class stores an array of VolumeVariables objects, one + * volume variables object for each of the element's vertices + */ +template<class TypeTag> +class BoxElementVolumeVariables : public std::vector<typename GET_PROP_TYPE(TypeTag, VolumeVariables) > +{ + typedef typename GET_PROP_TYPE(TypeTag, Problem) Problem; + typedef typename GET_PROP_TYPE(TypeTag, FVElementGeometry) FVElementGeometry; + typedef typename GET_PROP_TYPE(TypeTag, SolutionVector) SolutionVector; + typedef typename GET_PROP_TYPE(TypeTag, VertexMapper) VertexMapper; + typedef typename GET_PROP_TYPE(TypeTag, PrimaryVariables) PrimaryVariables; + + typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; + typedef typename GridView::template Codim<0>::Entity Element; + + enum { dim = GridView::dimension }; + enum { numEq = GET_PROP_VALUE(TypeTag, NumEq) }; + +public: + /*! + * \brief The constructor. + */ + BoxElementVolumeVariables() + { } + + /*! + * \brief Construct the volume variables for all of vertices of an element. + * + * \param problem The problem which needs to be simulated. + * \param element The DUNE Codim<0> entity for which the volume variables ought to be calculated + * \param fvGeometry The finite volume geometry of the element + * \param oldSol Tells whether the model's previous or current solution should be used. + */ + void update(const Problem &problem, + const Element &element, + const FVElementGeometry &fvGeometry, + const bool oldSol) + { + const SolutionVector &globalSol = + oldSol? + problem.model().prevSol(): + problem.model().curSol(); + const VertexMapper &vertexMapper = problem.vertexMapper(); + // we assert that the i-th shape function is + // associated to the i-th vert of the element. + int numVertices = element.template count<dim>(); + this->resize(numVertices); + for (int scvIdx = 0; scvIdx < numVertices; scvIdx++) { + const PrimaryVariables &priVars + = globalSol[vertexMapper.map(element, scvIdx, dim)]; + + // reset evaluation point to zero + (*this)[scvIdx].setEvalPoint(0); + + (*this)[scvIdx].update(priVars, + problem, + element, + fvGeometry, + scvIdx, + oldSol); + } + } + + /*! + * \brief Construct the volume variables for all of vertices of an + * element given a solution vector computed by PDELab. + * + * \tparam ElementSolutionVector The container type which stores the + * primary variables of the element + * using _local_ indices + * + * \param problem The problem which needs to be simulated. + * \param element The DUNE Codim<0> entity for which the volume variables ought to be calculated + * \param fvGeometry The finite volume geometry of the element + * \param elementSolVector The local solution for the element using PDELab ordering + */ + template<typename ElementSolutionVector> + void updatePDELab(const Problem &problem, + const Element &element, + const FVElementGeometry &fvGeometry, + const ElementSolutionVector& elementSolVector) + { + int numVertices = element.template count<dim>(); + this->resize(numVertices); + for (int scvIdx = 0; scvIdx < numVertices; scvIdx++) + { + PrimaryVariables priVars(0); + for (int eqnIdx = 0; eqnIdx < numEq; eqnIdx++) + priVars[eqnIdx] = elementSolVector[scvIdx + eqnIdx*numVertices]; + (*this)[scvIdx].update(priVars, + problem, + element, + fvGeometry, + scvIdx, + false); + + } + } +}; + +} // namespace Dumux + +#endif diff --git a/dumux/implicit/box/boxforchheimerfluxvariables.hh b/dumux/implicit/box/boxforchheimerfluxvariables.hh new file mode 100644 index 0000000000..e7f7e3bf32 --- /dev/null +++ b/dumux/implicit/box/boxforchheimerfluxvariables.hh @@ -0,0 +1,358 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \file + * + * \brief This file contains the data which is required to calculate + * all fluxes of fluid phases over a face of a finite volume, + * according to the Forchheimer-relation between velocity and pressure. + * + */ +#ifndef DUMUX_BOX_FORCHHEIMER_FLUX_VARIABLES_HH +#define DUMUX_BOX_FORCHHEIMER_FLUX_VARIABLES_HH + +#include "boxproperties.hh" + +#include <dumux/common/parameters.hh> +#include <dumux/common/math.hh> +#include <dumux/boxmodels/common/boxdarcyfluxvariables.hh> + +namespace Dumux +{ + +namespace Properties +{ +// forward declaration of properties +NEW_PROP_TAG(MobilityUpwindWeight); +NEW_PROP_TAG(SpatialParams); +NEW_PROP_TAG(NumPhases); +NEW_PROP_TAG(ProblemEnableGravity); +} + +/*! + * \ingroup BoxModel + * \ingroup BoxFluxVariables + * \brief Evaluates the normal component of the Forchheimer velocity + * on a (sub)control volume face. + * + * The commonly used Darcy relation looses it's validity for \f$ Re < 1\f$. + * If one encounters flow velocities in porous media above this Reynolds number, + * the Forchheimer relation can be used. Like the Darcy relation, it relates + * the gradient in potential to velocity. + * However, this relation is not linear (as in the Darcy case) any more. + * + * Therefore, a Newton scheme is implemented in this class in order to calculate a + * velocity from the current set of variables. This velocity can subsequently be used + * e.g. by the localresidual. + * + * For Reynolds numbers above \f$ 500\f$ the (Standard) forchheimer relation also + * looses it's validity. + * + * The Forchheimer equation looks as follows: + * \f[ \nabla \left({p_\alpha + \rho_\alpha g z} \right)= - \frac{\mu_\alpha}{k_{r \alpha} K} \underline{v_\alpha} - \frac{c_F}{\eta_{\alpha r} \sqrt{K}} \rho_\alpha |\underline{v_\alpha}| \underline{v_\alpha} \f] + * + * For the formulation that is actually used in this class, see the documentation of the function calculating the residual. + * + * - This algorithm does not find a solution if the fluid is incompressible and the + * initial pressure distribution is uniform. + * - This algorithm assumes the relative passabilities to have the same functional + * form as the relative permeabilities. + */ +template <class TypeTag> +class BoxForchheimerFluxVariables + : public BoxDarcyFluxVariables<TypeTag> +{ + typedef typename GET_PROP_TYPE(TypeTag, Problem) Problem; + typedef typename GET_PROP_TYPE(TypeTag, SpatialParams) SpatialParams; + typedef typename GET_PROP_TYPE(TypeTag, ElementVolumeVariables) ElementVolumeVariables; + typedef typename GET_PROP_TYPE(TypeTag, VolumeVariables) VolumeVariables; + typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; + typedef typename GridView::template Codim<0>::Entity Element; + + enum { dim = GridView::dimension} ; + enum { dimWorld = GridView::dimensionworld} ; + enum { numPhases = GET_PROP_VALUE(TypeTag, NumPhases)} ; + + typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; + typedef Dune::FieldMatrix<Scalar, dimWorld, dimWorld> Tensor; + typedef Dune::FieldVector<Scalar, dimWorld> DimVector; + typedef typename GET_PROP_TYPE(TypeTag, FVElementGeometry) FVElementGeometry; + typedef typename FVElementGeometry::SubControlVolumeFace SCVFace; + +public: + /*! + * \brief The constructor + * + * \param problem The problem + * \param element The finite element + * \param fvGeometry The finite-volume geometry in the box scheme + * \param faceIdx The local index of the SCV (sub-control-volume) face + * \param elemVolVars The volume variables of the current element + * \param onBoundary A boolean variable to specify whether the flux variables + * are calculated for interior SCV faces or boundary faces, default=false + */ + BoxForchheimerFluxVariables(const Problem &problem, + const Element &element, + const FVElementGeometry &fvGeometry, + const unsigned int faceIdx, + const ElementVolumeVariables &elemVolVars, + const bool onBoundary = false) + : BoxDarcyFluxVariables<TypeTag>(problem, element, fvGeometry, faceIdx, elemVolVars, onBoundary) + { + calculateNormalVelocity_(problem, element, elemVolVars); + } + +protected: + /*! + * \brief Function for calculation of velocities. + * + * \param problem The problem + * \param element The finite element + * \param elemVolVars The volume variables of the current element + */ + void calculateNormalVelocity_(const Problem &problem, + const Element &element, + const ElementVolumeVariables &elemVolVars) + { + // calculate the mean intrinsic permeability + const SpatialParams &spatialParams = problem.spatialParams(); + Tensor K; + spatialParams.meanK(K, + spatialParams.intrinsicPermeability(element, + this->fvGeometry_, + this->face().i), + spatialParams.intrinsicPermeability(element, + this->fvGeometry_, + this->face().j)); + + // obtain the Forchheimer coefficient from the spatial parameters + const Scalar forchCoeff = spatialParams.forchCoeff(element, + this->fvGeometry_, + this->face().i); + + // Make sure the permeability matrix does not have off-diagonal entries + assert( isDiagonal_(K) ); + + Tensor sqrtK(0.0); + for (int i = 0; i < dim; ++i) + sqrtK[i][i] = std::sqrt(K[i][i]); + + // loop over all phases + for (int phaseIdx = 0; phaseIdx < numPhases; phaseIdx++) + { + // initial guess of velocity: Darcy relation + // first taken from base class, later overwritten in base class + DimVector velocity = this-> velocity(phaseIdx); + + DimVector deltaV; // the change in velocity between Newton iterations + DimVector residual(10e10); // the residual (function value that is to be minimized ) of the equation that is to be fulfilled + DimVector tmp; // temporary variable for numerical differentiation + Tensor gradF; // slope of equation that is to be solved + + // search by means of the Newton method for a root of Forchheimer equation + for (int k = 0; residual.two_norm() > 1e-12 ; ++k) { + + if (k >= 30) + DUNE_THROW(NumericalProblem, "could not determine forchheimer velocity within "<< k <<" iterations"); + + // calculate current value of Forchheimer equation + forchheimerResidual_(residual, + forchCoeff, + sqrtK, + K, + velocity, + elemVolVars, + this->gradPotential_[phaseIdx], + phaseIdx); + + // newton's method: slope (gradF) and current value (residual) of function is known, + forchheimerDerivative_(gradF, + forchCoeff, + sqrtK, + velocity, + elemVolVars, + phaseIdx); + + // solve for change in velocity ("x-Axis intercept") + gradF.solve(deltaV, residual); + velocity -= deltaV; + } + + this->velocity_[phaseIdx] = velocity ; // store the converged velocity solution + this->volumeFlux_[phaseIdx] = velocity * this->face().normal; // velocity dot normal times cross sectional area is volume flux + }// end loop over all phases + } + + /*! \brief Function for calculation of Forchheimer relation. + * + * The relative passability \f$ \eta_r\f$ is the "Forchheimer-equivalent" to the relative permeability \f$ k_r\f$. + * We use the same function as for \f$ k_r \f$ (VG, BC, linear) other authors use a simple power law e.g.: \f$\eta_{rw} = S_w^3\f$ + * + * Some rearrangements have been made to the original Forchheimer relation: + * + * \f[ \underline{v_\alpha} + c_F \sqrt{K} \frac{\rho_\alpha}{\mu_\alpha } |\underline{v_\alpha}| \underline{v_\alpha} + \frac{k_{r \alpha}}{\mu_\alpha} K \nabla \left(p_\alpha + \rho_\alpha g z \right)= 0 \f] + * + * This already includes the assumption \f$ k_r(S_w) = \eta_r(S_w)\f$: + * - \f$\eta_{rw} = S_w^x\f$ looks very similar to e.g. Van Genuchten relative permeabilities + * - Fichot, et al. (2006), Nuclear Engineering and Design, state that several authors claim that \f$ k_r(S_w), \eta_r(S_w)\f$ can be chosen equal + * - It leads to the equation not degenerating for the case of \f$S_w=1\f$, because I do not need to multiply with two different functions, and therefore there are terms not being zero. + * - If this assumption is not to be made: Some regularization needs to be introduced ensuring that not all terms become zero for \f$S_w=1\f$. + * + * As long as the correct velocity is not found yet, the above equation is not fulfilled, but + * the left hand side yields some residual. This function calculates the left hand side of the + * equation and returns the residual. + * + * \param residual The current function value for the given velocity + * \param forchCoeff The Forchheimer coefficient + * \param sqrtK A diagonal matrix whose entries are square roots of the permeability matrix entries + * \param K The permeability matrix + * \param velocity The current velocity approximation. + * \param elemVolVars The volume variables of the current element + * \param gradPotential The gradient in potential + * \param phaseIdx The index of the currently considered phase + */ + void forchheimerResidual_(DimVector & residual, + const Scalar forchCoeff, + const Tensor & sqrtK, + const Tensor & K, + const DimVector & velocity, + const ElementVolumeVariables & elemVolVars, + const DimVector & gradPotential , + const unsigned int phaseIdx) const + { + const VolumeVariables upVolVars = elemVolVars[this->upstreamIdx(phaseIdx)]; + const VolumeVariables downVolVars = elemVolVars[this->downstreamIdx(phaseIdx)]; + + // Obtaining the physical quantities + const Scalar mobility = this->mobilityUpwindWeight_ * upVolVars.mobility(phaseIdx) + + (1.-this->mobilityUpwindWeight_)* downVolVars.mobility(phaseIdx) ; + + const Scalar viscosity = this->mobilityUpwindWeight_ * upVolVars.fluidState().viscosity(phaseIdx) + + (1.-this->mobilityUpwindWeight_)* downVolVars.fluidState().viscosity(phaseIdx) ; + + const Scalar density = this->mobilityUpwindWeight_ * upVolVars.fluidState().density(phaseIdx) + + (1.-this->mobilityUpwindWeight_) * downVolVars.fluidState().density(phaseIdx) ; + + // residual = v + residual = velocity; + + // residual += k_r/mu K grad p + K.usmv(mobility, gradPotential, residual); + + // residual += c_F rho / mu abs(v) sqrt(K) v + sqrtK.usmv(forchCoeff * density / viscosity * velocity.two_norm(), velocity, residual); + } + + + /*! \brief Function for calculation of the gradient of Forchheimer relation with respect to velocity. + * + * This function already exploits that \f$ \sqrt{K}\f$ is a diagonal matrix. Therefore, we only have + * to deal with the main entries. + * The gradient of the Forchheimer relations looks as follows (mind that \f$ \sqrt{K}\f$ is a tensor): + * + * \f[ f\left(\underline{v_\alpha}\right) = + * \left( + * \begin{array}{ccc} + * 1 & 0 &0 \\ + * 0 & 1 &0 \\ + * 0 & 0 &1 \\ + * \end{array} + * \right) + * + + * c_F \frac{\rho_\alpha}{\mu_\alpha} |\underline{v}_\alpha| \sqrt{K} + * + + * c_F \frac{\rho_\alpha}{\mu_\alpha}\frac{1}{|\underline{v}_\alpha|} \sqrt{K} + * \left( + * \begin{array}{ccc} + * v_x^2 & v_xv_y & v_xv_z \\ + * v_yv_x & v_{y}^2 & v_yv_z \\ + * v_zv_x & v_zv_y &v_{z}^2 \\ + * \end{array} + * \right) + * \f] + * + * \param derivative The gradient of the forchheimer equation + * \param forchCoeff Forchheimer coefficient + * \param sqrtK A diagonal matrix whose entries are square roots of the permeability matrix entries. + * \param velocity The current velocity approximation. + * \param elemVolVars The volume variables of the current element + * \param phaseIdx The index of the currently considered phase + */ + void forchheimerDerivative_(Tensor & derivative, + const Scalar forchCoeff, + const Tensor & sqrtK, + const DimVector & velocity, + const ElementVolumeVariables & elemVolVars, + const unsigned int phaseIdx) const + { + const VolumeVariables upVolVars = elemVolVars[this->upstreamIdx(phaseIdx)]; + const VolumeVariables downVolVars = elemVolVars[this->downstreamIdx(phaseIdx)]; + + // Obtaining the physical quantities + const Scalar viscosity = this->mobilityUpwindWeight_ * upVolVars.fluidState().viscosity(phaseIdx) + + (1.-this->mobilityUpwindWeight_)* downVolVars.fluidState().viscosity(phaseIdx) ; + + const Scalar density = this->mobilityUpwindWeight_ * upVolVars.fluidState().density(phaseIdx) + + (1.-this->mobilityUpwindWeight_) * downVolVars.fluidState().density(phaseIdx) ; + + // Initialize because for low velocities, we add and do not set the entries. + derivative = 0.; + + const Scalar absV = velocity.two_norm() ; + // This part of the derivative is only calculated if the velocity is sufficiently small: do not divide by zero! + // This should not be very bad because the derivative is only intended to give an approximation of the gradient of the + // function that goes into the Newton scheme. + // This is important in the case of a one-phase region in two-phase flow. The non-existing phase has a velocity of zero (kr=0). + // f = sqrtK * vTimesV (this is a matrix) + // f *= forchCoeff density / |velocity| / viscosity (multiply all entries with scalars) + if(absV > 1e-20) + for (int i=0; i<dim; i++) + for (int k=0; k<dim; k++) + derivative[i][k]= sqrtK[i][i] * velocity[i] * velocity[k] * forchCoeff * density / absV / viscosity; + + // add on the main diagonal: + // 1 + sqrtK_i forchCoeff density |v| / viscosity + for (int i=0; i<dim; i++) + derivative[i][i] += (1.0 + ( sqrtK[i][i]*forchCoeff * density * absV / viscosity ) ) ; + } + + /*! + * \brief Check whether all off-diagonal entries of a tensor are zero. + * + * \param K the tensor that is to be checked. + * + * \return True iff all off-diagonals are zero. + * + */ + const bool isDiagonal_(const Tensor & K) const + { + for (int i = 0; i < dim; i++) { + for (int k = 0; k < dim; k++) { + if ((i != k) && (std::abs(K[i][k]) >= 1e-25)) { + return false; + } + } + } + return true; + } +}; + +} // end namespace + +#endif diff --git a/dumux/implicit/box/boxfvelementgeometry.hh b/dumux/implicit/box/boxfvelementgeometry.hh new file mode 100644 index 0000000000..fd0b5c89e2 --- /dev/null +++ b/dumux/implicit/box/boxfvelementgeometry.hh @@ -0,0 +1,933 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \file + * + * \brief Represents the finite volume geometry of a single element in + * the box scheme. + */ +#ifndef DUMUX_BOX_FV_ELEMENTGEOMETRY_HH +#define DUMUX_BOX_FV_ELEMENTGEOMETRY_HH + +#include <dune/common/version.hh> +#include <dune/geometry/referenceelements.hh> +#include <dune/grid/common/intersectioniterator.hh> +#include <dune/localfunctions/lagrange/pqkfactory.hh> + +#include <dumux/common/parameters.hh> +#include <dumux/common/propertysystem.hh> +#include "boxproperties.hh" + +namespace Dumux +{ +namespace Properties +{ +NEW_PROP_TAG(GridView); +NEW_PROP_TAG(Scalar); +NEW_PROP_TAG(ImplicitUseTwoPointFlux); +} + +///////////////////// +// HACK: Functions to initialize the subcontrol volume data +// structures of BoxFVElementGeomerty. +// +// this is a work around to be able to to use specialization for +// doing this. it is required since it is not possible to +// specialize member functions of template classes because of some +// weird reason I didn't really get... + +//! \cond INTERNAL + +template <typename BoxFVElementGeometry, int dim> +class _BoxFVElemGeomHelper +{ +public: + static void fillSubContVolData(BoxFVElementGeometry &eg, int numVertices) + { + DUNE_THROW(Dune::NotImplemented, "_BoxFVElemGeomHelper::fillSubContVolData dim = " << dim); + } +}; + +template <typename BoxFVElementGeometry> +class _BoxFVElemGeomHelper<BoxFVElementGeometry, 1> +{ +public: + enum { dim = 1 }; + static void fillSubContVolData(BoxFVElementGeometry &eg, int numVertices) + { + // 1D + eg.subContVol[0].volume = 0.5*eg.elementVolume; + eg.subContVol[1].volume = 0.5*eg.elementVolume; + } +}; + +template <typename BoxFVElementGeometry> +class _BoxFVElemGeomHelper<BoxFVElementGeometry, 2> +{ +public: + enum { dim = 2 }; + + static void fillSubContVolData(BoxFVElementGeometry &eg, int numVertices) + { + switch (numVertices) { + case 3: // 2D, triangle + eg.subContVol[0].volume = eg.elementVolume/3; + eg.subContVol[1].volume = eg.elementVolume/3; + eg.subContVol[2].volume = eg.elementVolume/3; + break; + case 4: // 2D, quadrilinear + eg.subContVol[0].volume = eg.quadrilateralArea(eg.subContVol[0].global, eg.edgeCoord[2], eg.elementGlobal, eg.edgeCoord[0]); + eg.subContVol[1].volume = eg.quadrilateralArea(eg.subContVol[1].global, eg.edgeCoord[1], eg.elementGlobal, eg.edgeCoord[2]); + eg.subContVol[2].volume = eg.quadrilateralArea(eg.subContVol[2].global, eg.edgeCoord[0], eg.elementGlobal, eg.edgeCoord[3]); + eg.subContVol[3].volume = eg.quadrilateralArea(eg.subContVol[3].global, eg.edgeCoord[3], eg.elementGlobal, eg.edgeCoord[1]); + break; + default: + DUNE_THROW(Dune::NotImplemented, "_BoxFVElemGeomHelper::fillSubContVolData dim = " << dim << ", numVertices = " << numVertices); + } + } +}; + +template <typename BoxFVElementGeometry> +class _BoxFVElemGeomHelper<BoxFVElementGeometry, 3> +{ +public: + enum { dim = 3 }; + + static void fillSubContVolData(BoxFVElementGeometry &eg, int numVertices) + { + switch (numVertices) { + case 4: // 3D, tetrahedron + for (int k = 0; k < eg.numVertices; k++) + eg.subContVol[k].volume = eg.elementVolume / 4.0; + break; + case 5: // 3D, pyramid + eg.subContVol[0].volume = eg.hexahedronVolume(eg.subContVol[0].global, + eg.edgeCoord[2], + eg.faceCoord[0], + eg.edgeCoord[0], + eg.edgeCoord[4], + eg.faceCoord[3], + eg.elementGlobal, + eg.faceCoord[1]); + eg.subContVol[1].volume = eg.hexahedronVolume(eg.subContVol[1].global, + eg.edgeCoord[1], + eg.faceCoord[0], + eg.edgeCoord[2], + eg.edgeCoord[5], + eg.faceCoord[2], + eg.elementGlobal, + eg.faceCoord[3]); + eg.subContVol[2].volume = eg.hexahedronVolume(eg.subContVol[2].global, + eg.edgeCoord[0], + eg.faceCoord[0], + eg.edgeCoord[3], + eg.edgeCoord[6], + eg.faceCoord[1], + eg.elementGlobal, + eg.faceCoord[4]); + eg.subContVol[3].volume = eg.hexahedronVolume(eg.subContVol[3].global, + eg.edgeCoord[3], + eg.faceCoord[0], + eg.edgeCoord[1], + eg.edgeCoord[7], + eg.faceCoord[4], + eg.elementGlobal, + eg.faceCoord[2]); + eg.subContVol[4].volume = eg.elementVolume - + eg.subContVol[0].volume - + eg.subContVol[1].volume - + eg.subContVol[2].volume - + eg.subContVol[3].volume; + break; + case 6: // 3D, prism + eg.subContVol[0].volume = eg.hexahedronVolume(eg.subContVol[0].global, + eg.edgeCoord[3], + eg.faceCoord[3], + eg.edgeCoord[4], + eg.edgeCoord[0], + eg.faceCoord[0], + eg.elementGlobal, + eg.faceCoord[1]); + eg.subContVol[1].volume = eg.hexahedronVolume(eg.subContVol[1].global, + eg.edgeCoord[5], + eg.faceCoord[3], + eg.edgeCoord[3], + eg.edgeCoord[1], + eg.faceCoord[2], + eg.elementGlobal, + eg.faceCoord[0]); + eg.subContVol[2].volume = eg.hexahedronVolume(eg.subContVol[2].global, + eg.edgeCoord[4], + eg.faceCoord[3], + eg.edgeCoord[5], + eg.edgeCoord[2], + eg.faceCoord[1], + eg.elementGlobal, + eg.faceCoord[2]); + eg.subContVol[3].volume = eg.hexahedronVolume(eg.edgeCoord[0], + eg.faceCoord[0], + eg.elementGlobal, + eg.faceCoord[1], + eg.subContVol[3].global, + eg.edgeCoord[6], + eg.faceCoord[4], + eg.edgeCoord[7]); + eg.subContVol[4].volume = eg.hexahedronVolume(eg.edgeCoord[1], + eg.faceCoord[2], + eg.elementGlobal, + eg.faceCoord[0], + eg.subContVol[4].global, + eg.edgeCoord[8], + eg.faceCoord[4], + eg.edgeCoord[6]); + eg.subContVol[5].volume = eg.hexahedronVolume(eg.edgeCoord[2], + eg.faceCoord[1], + eg.elementGlobal, + eg.faceCoord[2], + eg.subContVol[5].global, + eg.edgeCoord[7], + eg.faceCoord[4], + eg.edgeCoord[8]); + break; + case 8: // 3D, hexahedron + eg.subContVol[0].volume = eg.hexahedronVolume(eg.subContVol[0].global, + eg.edgeCoord[6], + eg.faceCoord[4], + eg.edgeCoord[4], + eg.edgeCoord[0], + eg.faceCoord[2], + eg.elementGlobal, + eg.faceCoord[0]); + eg.subContVol[1].volume = eg.hexahedronVolume(eg.subContVol[1].global, + eg.edgeCoord[5], + eg.faceCoord[4], + eg.edgeCoord[6], + eg.edgeCoord[1], + eg.faceCoord[1], + eg.elementGlobal, + eg.faceCoord[2]); + eg.subContVol[2].volume = eg.hexahedronVolume(eg.subContVol[2].global, + eg.edgeCoord[4], + eg.faceCoord[4], + eg.edgeCoord[7], + eg.edgeCoord[2], + eg.faceCoord[0], + eg.elementGlobal, + eg.faceCoord[3]); + eg.subContVol[3].volume = eg.hexahedronVolume(eg.subContVol[3].global, + eg.edgeCoord[7], + eg.faceCoord[4], + eg.edgeCoord[5], + eg.edgeCoord[3], + eg.faceCoord[3], + eg.elementGlobal, + eg.faceCoord[1]); + eg.subContVol[4].volume = eg.hexahedronVolume(eg.edgeCoord[0], + eg.faceCoord[2], + eg.elementGlobal, + eg.faceCoord[0], + eg.subContVol[4].global, + eg.edgeCoord[10], + eg.faceCoord[5], + eg.edgeCoord[8]); + eg.subContVol[5].volume = eg.hexahedronVolume(eg.edgeCoord[1], + eg.faceCoord[1], + eg.elementGlobal, + eg.faceCoord[2], + eg.subContVol[5].global, + eg.edgeCoord[9], + eg.faceCoord[5], + eg.edgeCoord[10]); + eg.subContVol[6].volume = eg.hexahedronVolume(eg.edgeCoord[2], + eg.faceCoord[0], + eg.elementGlobal, + eg.faceCoord[3], + eg.subContVol[6].global, + eg.edgeCoord[8], + eg.faceCoord[5], + eg.edgeCoord[11]); + eg.subContVol[7].volume = eg.hexahedronVolume(eg.edgeCoord[3], + eg.faceCoord[3], + eg.elementGlobal, + eg.faceCoord[1], + eg.subContVol[7].global, + eg.edgeCoord[11], + eg.faceCoord[5], + eg.edgeCoord[9]); + break; + default: + DUNE_THROW(Dune::NotImplemented, "_BoxFVElemGeomHelper::fillSubContVolData dim = " << dim << ", numVertices = " << numVertices); + } + } +}; + +//! \endcond + +// END HACK +///////////////////// + +/*! + * \brief Represents the finite volume geometry of a single element in + * the box scheme. + * + * The box scheme is a vertex centered finite volume approach. This + * means that each vertex corrosponds to a control volume which + * intersects each of the vertex' neighboring elements. If only + * looking at a single element of the primary grid (which is what this + * class does), the element is subdivided into multiple fragments of + * control volumes called sub-control volumes. Each of the element's + * vertices corrosponds to exactly one sub-control volume in this + * scenario. + * + * For the box methods the sub-control volumes are constructed by + * connecting the element's center with each edge of the element. + */ +template<class TypeTag> +class BoxFVElementGeometry +{ + typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; + enum{dim = GridView::dimension}; + enum{dimWorld = GridView::dimensionworld}; + + typedef BoxFVElementGeometry<TypeTag> ThisType; + + /** \todo Please doc me! */ + friend class _BoxFVElemGeomHelper<ThisType, dim>; + + typedef _BoxFVElemGeomHelper<ThisType, dim> BoxFVElemGeomHelper; + + enum{maxNC = (dim < 3 ? 4 : 8)}; + enum{maxNE = (dim < 3 ? 4 : 12)}; + enum{maxNF = (dim < 3 ? 1 : 6)}; + enum{maxCOS = (dim < 3 ? 2 : 4)}; + enum{maxBF = (dim < 3 ? 8 : 24)}; + enum{maxNFAP = (dim < 3 ? 4 : 8)}; + typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; + typedef typename GridView::ctype CoordScalar; + typedef typename GridView::Traits::template Codim<0>::Entity Element; + typedef typename Element::Geometry Geometry; + typedef Dune::FieldVector<Scalar,dimWorld> Vector; + typedef Dune::FieldVector<CoordScalar,dimWorld> GlobalPosition; + typedef Dune::FieldVector<CoordScalar,dim> LocalPosition; + typedef typename GridView::IntersectionIterator IntersectionIterator; +#if DUNE_VERSION_NEWER_REV(DUNE_GRID, 2, 3, 0) + typedef typename Geometry::JacobianInverseTransposed JacobianInverseTransposed; +#else + typedef typename Geometry::Jacobian JacobianInverseTransposed; +#endif + + typedef Dune::PQkLocalFiniteElementCache<CoordScalar, Scalar, dim, 1> LocalFiniteElementCache; + typedef typename LocalFiniteElementCache::FiniteElementType LocalFiniteElement; + typedef typename LocalFiniteElement::Traits::LocalBasisType::Traits LocalBasisTraits; + typedef typename LocalBasisTraits::JacobianType ShapeJacobian; + + Scalar quadrilateralArea(const GlobalPosition& p0, const GlobalPosition& p1, const GlobalPosition& p2, const GlobalPosition& p3) + { + return 0.5*fabs((p3[0] - p1[0])*(p2[1] - p0[1]) - (p3[1] - p1[1])*(p2[0] - p0[0])); + } + + void crossProduct(Vector& c, const Vector& a, const Vector& b) + { + c[0] = a[1]*b[2] - a[2]*b[1]; + c[1] = a[2]*b[0] - a[0]*b[2]; + c[2] = a[0]*b[1] - a[1]*b[0]; + } + + Scalar pyramidVolume (const GlobalPosition& p0, const GlobalPosition& p1, const GlobalPosition& p2, const GlobalPosition& p3, const GlobalPosition& p4) + { + Vector a(p2); a -= p0; + Vector b(p3); b -= p1; + + Vector n; + crossProduct(n, a, b); + + a = p4; a -= p0; + + return 1.0/6.0*(n*a); + } + + Scalar prismVolume (const GlobalPosition& p0, const GlobalPosition& p1, const GlobalPosition& p2, const GlobalPosition& p3, const GlobalPosition& p4, const GlobalPosition& p5) + { + Vector a(p4); + for (int k = 0; k < dimWorld; ++k) + a[k] -= p0[k]; + Vector b(p1); + for (int k = 0; k < dimWorld; ++k) + b[k] -= p3[k]; + Vector m; + crossProduct(m, a, b); + + a = p1; + for (int k = 0; k < dimWorld; ++k) + a[k] -= p0[k]; + b = p2; + for (int k = 0; k < dimWorld; ++k) + b[k] -= p0[k]; + Vector n; + crossProduct(n, a, b); + n += m; + + a = p5; + for (int k = 0; k < dimWorld; ++k) + a[k] -= p0[k]; + + return fabs(1.0/6.0*(n*a)); + } + + Scalar hexahedronVolume (const GlobalPosition& p0, const GlobalPosition& p1, const GlobalPosition& p2, const GlobalPosition& p3, + const GlobalPosition& p4, const GlobalPosition& p5, const GlobalPosition& p6, const GlobalPosition& p7) + { + return + prismVolume(p0,p1,p2,p4,p5,p6) + + prismVolume(p0,p2,p3,p4,p6,p7); + } + + void normalOfQuadrilateral3D(Vector &normal, const GlobalPosition& p0, const GlobalPosition& p1, const GlobalPosition& p2, const GlobalPosition& p3) + { + Vector a(p2); + for (int k = 0; k < dimWorld; ++k) + a[k] -= p0[k]; + Vector b(p3); + for (int k = 0; k < dimWorld; ++k) + b[k] -= p1[k]; + + crossProduct(normal, a, b); + normal *= 0.5; + } + + Scalar quadrilateralArea3D(const GlobalPosition& p0, const GlobalPosition& p1, const GlobalPosition& p2, const GlobalPosition& p3) + { + Vector normal; + normalOfQuadrilateral3D(normal, p0, p1, p2, p3); + return normal.two_norm(); + } + + void getFaceIndices(int numVertices, int k, int& leftFace, int& rightFace) + { + static const int edgeToFaceTet[2][6] = { + {1, 0, 3, 2, 1, 3}, + {0, 2, 0, 1, 3, 2} + }; + static const int edgeToFacePyramid[2][8] = { + {1, 2, 3, 4, 1, 3, 4, 2}, + {0, 0, 0, 0, 3, 2, 1, 4} + }; + static const int edgeToFacePrism[2][9] = { + {1, 0, 2, 0, 1, 2, 4, 4, 4}, + {0, 2, 1, 3, 3, 3, 0, 1, 2} + }; + static const int edgeToFaceHex[2][12] = { + {0, 2, 3, 1, 4, 1, 2, 4, 0, 5, 5, 3}, + {2, 1, 0, 3, 0, 4, 4, 3, 5, 1, 2, 5} + }; + + switch (numVertices) { + case 4: + leftFace = edgeToFaceTet[0][k]; + rightFace = edgeToFaceTet[1][k]; + break; + case 5: + leftFace = edgeToFacePyramid[0][k]; + rightFace = edgeToFacePyramid[1][k]; + break; + case 6: + leftFace = edgeToFacePrism[0][k]; + rightFace = edgeToFacePrism[1][k]; + break; + case 8: + leftFace = edgeToFaceHex[0][k]; + rightFace = edgeToFaceHex[1][k]; + break; + default: + DUNE_THROW(Dune::NotImplemented, "BoxFVElementGeometry :: getFaceIndices for numVertices = " << numVertices); + break; + } + } + + void getEdgeIndices(int numVertices, int face, int vert, int& leftEdge, int& rightEdge) + { + static const int faceAndVertexToLeftEdgeTet[4][4] = { + { 0, 0, 2, -1}, + { 0, 0, -1, 3}, + { 1, -1, 1, 3}, + {-1, 2, 2, 4} + }; + static const int faceAndVertexToRightEdgeTet[4][4] = { + { 1, 2, 1, -1}, + { 3, 4, -1, 4}, + { 3, -1, 5, 5}, + {-1, 4, 5, 5} + }; + static const int faceAndVertexToLeftEdgePyramid[5][5] = { + { 0, 2, 3, 1, -1}, + { 0, -1, 0, -1, 4}, + {-1, 1, -1, 1, 5}, + { 2, 2, -1, -1, 4}, + {-1, -1, 3, 3, 7} + }; + static const int faceAndVertexToRightEdgePyramid[5][5] = { + { 2, 1, 0, 3, -1}, + { 4, -1, 6, -1, 6}, + {-1, 5, -1, 7, 7}, + { 4, 5, -1, -1, 5}, + {-1, -1, 6, 7, 6} + }; + static const int faceAndVertexToLeftEdgePrism[5][6] = { + { 3, 3, -1, 0, 1, -1}, + { 4, -1, 4, 0, -1, 2}, + {-1, 5, 5, -1, 1, 2}, + { 3, 3, 5, -1, -1, -1}, + {-1, -1, -1, 6, 6, 8} + }; + static const int faceAndVertexToRightEdgePrism[5][6] = { + { 0, 1, -1, 6, 6, -1}, + { 0, -1, 2, 7, -1, 7}, + {-1, 1, 2, -1, 8, 8}, + { 4, 5, 4, -1, -1, -1}, + {-1, -1, -1, 7, 8, 7} + }; + static const int faceAndVertexToLeftEdgeHex[6][8] = { + { 0, -1, 4, -1, 8, -1, 2, -1}, + {-1, 5, -1, 3, -1, 1, -1, 9}, + { 6, 1, -1, -1, 0, 10, -1, -1}, + {-1, -1, 2, 7, -1, -1, 11, 3}, + { 4, 6, 7, 5, -1, -1, -1, -1}, + {-1, -1, -1, -1, 10, 9, 8, 11} + }; + static const int faceAndVertexToRightEdgeHex[6][8] = { + { 4, -1, 2, -1, 0, -1, 8, -1}, + {-1, 1, -1, 5, -1, 9, -1, 3}, + { 0, 6, -1, -1, 10, 1, -1, -1}, + {-1, -1, 7, 3, -1, -1, 2, 11}, + { 6, 5, 4, 7, -1, -1, -1, -1}, + {-1, -1, -1, -1, 8, 10, 11, 9} + }; + + switch (numVertices) { + case 4: + leftEdge = faceAndVertexToLeftEdgeTet[face][vert]; + rightEdge = faceAndVertexToRightEdgeTet[face][vert]; + break; + case 5: + leftEdge = faceAndVertexToLeftEdgePyramid[face][vert]; + rightEdge = faceAndVertexToRightEdgePyramid[face][vert]; + break; + case 6: + leftEdge = faceAndVertexToLeftEdgePrism[face][vert]; + rightEdge = faceAndVertexToRightEdgePrism[face][vert]; + break; + case 8: + leftEdge = faceAndVertexToLeftEdgeHex[face][vert]; + rightEdge = faceAndVertexToRightEdgeHex[face][vert]; + break; + default: + DUNE_THROW(Dune::NotImplemented, "BoxFVElementGeometry :: getFaceIndices for numVertices = " << numVertices); + break; + } + } + +public: + int boundaryFaceIndex(const int face, const int vertInFace) const + { + return (face*maxCOS + vertInFace); + } + + struct SubControlVolume //! FV intersected with element + { + LocalPosition local; //!< local vert position + GlobalPosition global; //!< global vert position + LocalPosition localCenter; //!< local position of scv center + Scalar volume; //!< volume of scv + Dune::FieldVector<Vector, maxNC> grad; //! derivative of shape function associated with the sub control volume + Dune::FieldVector<Vector, maxNC> gradCenter; //! derivative of shape function at the center of the sub control volume + Dune::FieldVector<Scalar, maxNC> shapeValue; //! value of shape function associated with the sub control volume + bool inner; + }; + + struct SubControlVolumeFace //! interior face of a sub control volume + { + int i,j; //!< scvf seperates corner i and j of elem + LocalPosition ipLocal; //!< integration point in local coords + GlobalPosition ipGlobal; //!< integration point in global coords + Vector normal; //!< normal on face pointing to CV j or outward of the domain with length equal to |scvf| + Scalar area; //!< area of face + Dune::FieldVector<Vector, maxNC> grad; //!< derivatives of shape functions at ip + Dune::FieldVector<Scalar, maxNC> shapeValue; //!< value of shape functions at ip + Dune::FieldVector<int, maxNFAP> fapIndices; //!< indices w.r.t.neighbors of the flux approximation points + }; + + typedef SubControlVolumeFace BoundaryFace; //!< compatibility typedef + + LocalPosition elementLocal; //!< local coordinate of element center + GlobalPosition elementGlobal; //!< global coordinate of element center + Scalar elementVolume; //!< element volume + SubControlVolume subContVol[maxNC]; //!< data of the sub control volumes + SubControlVolumeFace subContVolFace[maxNE]; //!< data of the sub control volume faces + BoundaryFace boundaryFace[maxBF]; //!< data of the boundary faces + GlobalPosition edgeCoord[maxNE]; //!< global coordinates of the edge centers + GlobalPosition faceCoord[maxNF]; //!< global coordinates of the face centers + int numVertices; //!< number of verts + int numEdges; //!< number of edges + int numFaces; //!< number of faces (0 in < 3D) + int numSCV; //!< number of subcontrol volumes + int numFAP; //!< number of flux approximation points + + const LocalFiniteElementCache feCache_; + + void update(const GridView& gridView, const Element& element) + { + const Geometry& geometry = element.geometry(); + Dune::GeometryType gt = geometry.type(); + + const typename Dune::GenericReferenceElementContainer<CoordScalar,dim>::value_type& + referenceElement = Dune::GenericReferenceElements<CoordScalar,dim>::general(gt); + + const LocalFiniteElement + &localFiniteElement = feCache_.get(gt); + + elementVolume = geometry.volume(); + elementLocal = referenceElement.position(0,0); + elementGlobal = geometry.global(elementLocal); + + numVertices = referenceElement.size(dim); + numEdges = referenceElement.size(dim-1); + numFaces = (dim<3)?0:referenceElement.size(1); + numSCV = numVertices; + + bool useTwoPointFlux + = GET_PARAM_FROM_GROUP(TypeTag, bool, Implicit, UseTwoPointFlux); + if (useTwoPointFlux) + numFAP = 2; + else + numFAP = numVertices; + + // corners: + for (int vert = 0; vert < numVertices; vert++) { + subContVol[vert].local = referenceElement.position(vert, dim); + subContVol[vert].global = geometry.global(subContVol[vert].local); + subContVol[vert].inner = true; + } + + // edges: + for (int edge = 0; edge < numEdges; edge++) { + edgeCoord[edge] = geometry.global(referenceElement.position(edge, dim-1)); + } + + // faces: + for (int face = 0; face < numFaces; face++) { + faceCoord[face] = geometry.global(referenceElement.position(face, 1)); + } + + // fill sub control volume data use specialization for this + // \todo maybe it would be a good idea to migrate everything + // which is dependend of the grid's dimension to + // _BoxFVElemGeomHelper in order to benefit from more aggressive + // compiler optimizations... + BoxFVElemGeomHelper::fillSubContVolData(*this, numVertices); + + // fill sub control volume face data: + for (int k = 0; k < numEdges; k++) { // begin loop over edges / sub control volume faces + int i = referenceElement.subEntity(k, dim-1, 0, dim); + int j = referenceElement.subEntity(k, dim-1, 1, dim); + if (numEdges == 4 && (i == 2 || j == 2)) + std::swap(i, j); + subContVolFace[k].i = i; + subContVolFace[k].j = j; + + // calculate the local integration point and + // the face normal. note that since dim is a + // constant which is known at compile time + // the compiler can optimize away all if + // cases which don't apply. + LocalPosition ipLocal; + Vector diffVec; + if (dim==1) { + subContVolFace[k].ipLocal = 0.5; + subContVolFace[k].normal = 1.0; + ipLocal = subContVolFace[k].ipLocal; + } + else if (dim==2) { + ipLocal = referenceElement.position(k, dim-1) + elementLocal; + ipLocal *= 0.5; + subContVolFace[k].ipLocal = ipLocal; + diffVec = elementGlobal - edgeCoord[k]; + subContVolFace[k].normal[0] = diffVec[1]; + subContVolFace[k].normal[1] = -diffVec[0]; + + diffVec = subContVol[j].global; + for (int m = 0; m < dimWorld; ++m) + diffVec[m] -= subContVol[i].global[m]; + // make sure the normal points to the right direction + if (subContVolFace[k].normal * diffVec < 0) + subContVolFace[k].normal *= -1; + + } + else if (dim==3) { + int leftFace; + int rightFace; + getFaceIndices(numVertices, k, leftFace, rightFace); + ipLocal = referenceElement.position(k, dim-1) + elementLocal + + referenceElement.position(leftFace, 1) + + referenceElement.position(rightFace, 1); + ipLocal *= 0.25; + subContVolFace[k].ipLocal = ipLocal; + normalOfQuadrilateral3D(subContVolFace[k].normal, + edgeCoord[k], faceCoord[rightFace], + elementGlobal, faceCoord[leftFace]); + } + + if (useTwoPointFlux) + { + GlobalPosition distVec = subContVol[i].global; + distVec -= subContVol[j].global; + distVec /= distVec.two_norm2(); + + // gradients using a two-point flux approximation + for (int idx = 0; idx < 2; idx++) + { + subContVolFace[k].grad[idx] = distVec; + subContVolFace[k].shapeValue[idx] = 0.5; + } + subContVolFace[k].grad[1] *= -1.0; + + subContVolFace[k].fapIndices[0] = subContVolFace[k].i; + subContVolFace[k].fapIndices[1] = subContVolFace[k].j; + } + else + { + // get the global integration point and the Jacobian inverse + subContVolFace[k].ipGlobal = geometry.global(ipLocal); + JacobianInverseTransposed jacInvT = + geometry.jacobianInverseTransposed(ipLocal); + + // calculate the shape function gradients + //typedef Dune::FieldVector< Dune::FieldVector< CoordScalar, dim >, 1 > ShapeJacobian; + typedef Dune::FieldVector< Scalar, 1 > ShapeValue; + std::vector<ShapeJacobian> localJac; + std::vector<ShapeValue> shapeVal; + localFiniteElement.localBasis().evaluateJacobian(subContVolFace[k].ipLocal, localJac); + localFiniteElement.localBasis().evaluateFunction(subContVolFace[k].ipLocal, shapeVal); + for (int vert = 0; vert < numVertices; vert++) { + jacInvT.mv(localJac[vert][0], subContVolFace[k].grad[vert]); + subContVolFace[k].shapeValue[vert] = Scalar(shapeVal[vert]); + subContVolFace[k].fapIndices[vert] = vert; + } + } + } // end loop over edges / sub control volume faces + + // fill boundary face data: + IntersectionIterator endit = gridView.iend(element); + for (IntersectionIterator it = gridView.ibegin(element); it != endit; ++it) + if (it->boundary()) + { + int face = it->indexInInside(); + int numVerticesOfFace = referenceElement.size(face, 1, dim); + for (int vertInFace = 0; vertInFace < numVerticesOfFace; vertInFace++) + { + int vertInElement = referenceElement.subEntity(face, 1, vertInFace, dim); + int bfIdx = boundaryFaceIndex(face, vertInFace); + subContVol[vertInElement].inner = false; + switch ((short) dim) { + case 1: + boundaryFace[bfIdx].ipLocal = referenceElement.position(vertInElement, dim); + boundaryFace[bfIdx].area = 1.0; + break; + case 2: + boundaryFace[bfIdx].ipLocal = referenceElement.position(vertInElement, dim) + + referenceElement.position(face, 1); + boundaryFace[bfIdx].ipLocal *= 0.5; + boundaryFace[bfIdx].area = 0.5*it->geometry().volume(); + break; + case 3: + int leftEdge; + int rightEdge; + getEdgeIndices(numVertices, face, vertInElement, leftEdge, rightEdge); + boundaryFace[bfIdx].ipLocal = referenceElement.position(vertInElement, dim) + + referenceElement.position(face, 1) + + referenceElement.position(leftEdge, dim-1) + + referenceElement.position(rightEdge, dim-1); + boundaryFace[bfIdx].ipLocal *= 0.25; + boundaryFace[bfIdx].area = quadrilateralArea3D(subContVol[vertInElement].global, + edgeCoord[rightEdge], faceCoord[face], edgeCoord[leftEdge]); + break; + default: + DUNE_THROW(Dune::NotImplemented, "BoxFVElementGeometry for dim = " << dim); + } + boundaryFace[bfIdx].ipGlobal = geometry.global(boundaryFace[bfIdx].ipLocal); + boundaryFace[bfIdx].i = vertInElement; + boundaryFace[bfIdx].j = vertInElement; + + // ASSUME constant normal + Dune::FieldVector<CoordScalar, dim-1> localDimM1(0); + boundaryFace[bfIdx].normal = it->unitOuterNormal(localDimM1); + boundaryFace[bfIdx].normal *= boundaryFace[bfIdx].area; + + typedef Dune::FieldVector< Scalar, 1 > ShapeValue; + std::vector<ShapeJacobian> localJac; + std::vector<ShapeValue> shapeVal; + localFiniteElement.localBasis().evaluateJacobian(boundaryFace[bfIdx].ipLocal, localJac); + localFiniteElement.localBasis().evaluateFunction(boundaryFace[bfIdx].ipLocal, shapeVal); + + JacobianInverseTransposed jacInvT = + geometry.jacobianInverseTransposed(boundaryFace[bfIdx].ipLocal); + for (int vert = 0; vert < numVertices; vert++) + { + jacInvT.mv(localJac[vert][0], boundaryFace[bfIdx].grad[vert]); + boundaryFace[bfIdx].shapeValue[vert] = Scalar(shapeVal[vert]); + boundaryFace[bfIdx].fapIndices[vert] = vert; + } + } + } + + bool evalGradientsAtSCVCenter = GET_PROP_VALUE(TypeTag, EvalGradientsAtSCVCenter); + if(evalGradientsAtSCVCenter) + { + // calculate gradients at the center of the scv + for (int scvIdx = 0; scvIdx < numVertices; scvIdx++){ + if (dim == 2) + { + switch (scvIdx) + { + case 0: + if (numVertices == 4) { + subContVol[scvIdx].localCenter[0] = 0.25; + subContVol[scvIdx].localCenter[1] = 0.25; + } + else { + subContVol[scvIdx].localCenter[0] = 1.0/6.0; + subContVol[scvIdx].localCenter[1] = 1.0/6.0; + } + break; + case 1: + if (numVertices == 4) { + subContVol[scvIdx].localCenter[0] = 0.75; + subContVol[scvIdx].localCenter[1] = 0.25; + } + else { + subContVol[scvIdx].localCenter[0] = 4.0/6.0; + subContVol[scvIdx].localCenter[1] = 1.0/6.0; + } + break; + case 2: + if (numVertices == 4) { + subContVol[scvIdx].localCenter[0] = 0.25; + subContVol[scvIdx].localCenter[1] = 0.75; + } + else { + subContVol[scvIdx].localCenter[0] = 1.0/6.0; + subContVol[scvIdx].localCenter[1] = 4.0/6.0; + } + break; + case 3: + subContVol[scvIdx].localCenter[0] = 0.75; + subContVol[scvIdx].localCenter[1] = 0.75; + break; + } + } + + else if (dim == 3) + { + switch (scvIdx) + { + case 0: + if (numVertices == 8) { + subContVol[scvIdx].localCenter[0] = 0.25; + subContVol[scvIdx].localCenter[1] = 0.25; + subContVol[scvIdx].localCenter[2] = 0.25; + } + else if (numVertices == 4) { + subContVol[scvIdx].localCenter[0] = 3.0/16.0; + subContVol[scvIdx].localCenter[1] = 3.0/16.0; + subContVol[scvIdx].localCenter[2] = 3.0/16.0; + } + break; + case 1: + if (numVertices == 8) { + subContVol[scvIdx].localCenter[0] = 0.75; + subContVol[scvIdx].localCenter[1] = 0.25; + subContVol[scvIdx].localCenter[2] = 0.25; + } + else if (numVertices == 4) { + subContVol[scvIdx].localCenter[0] = 7.0/16.0; + subContVol[scvIdx].localCenter[1] = 3.0/16.0; + subContVol[scvIdx].localCenter[2] = 3.0/16.0; + } + break; + case 2: + if (numVertices == 8) { + subContVol[scvIdx].localCenter[0] = 0.25; + subContVol[scvIdx].localCenter[1] = 0.75; + subContVol[scvIdx].localCenter[2] = 0.25; + } + else if (numVertices == 4) { + subContVol[scvIdx].localCenter[0] = 3.0/16.0; + subContVol[scvIdx].localCenter[1] = 7.0/16.0; + subContVol[scvIdx].localCenter[2] = 3.0/16.0; + } + break; + case 3: + if (numVertices == 8) { + subContVol[scvIdx].localCenter[0] = 0.75; + subContVol[scvIdx].localCenter[1] = 0.75; + subContVol[scvIdx].localCenter[2] = 0.25; + } + else if (numVertices == 4) { + subContVol[scvIdx].localCenter[0] = 3.0/16.0; + subContVol[scvIdx].localCenter[1] = 3.0/16.0; + subContVol[scvIdx].localCenter[2] = 7.0/16.0; + } + break; + case 4: + subContVol[scvIdx].localCenter[0] = 0.25; + subContVol[scvIdx].localCenter[1] = 0.25; + subContVol[scvIdx].localCenter[2] = 0.75; + break; + case 5: + subContVol[scvIdx].localCenter[0] = 0.75; + subContVol[scvIdx].localCenter[1] = 0.25; + subContVol[scvIdx].localCenter[2] = 0.75; + break; + case 6: + subContVol[scvIdx].localCenter[0] = 0.25; + subContVol[scvIdx].localCenter[1] = 0.75; + subContVol[scvIdx].localCenter[2] = 0.75; + break; + case 7: + subContVol[scvIdx].localCenter[0] = 0.75; + subContVol[scvIdx].localCenter[1] = 0.75; + subContVol[scvIdx].localCenter[2] = 0.75; + break; + } + } + std::vector<ShapeJacobian> localJac; + localFiniteElement.localBasis().evaluateJacobian(subContVol[scvIdx].localCenter, localJac); + + JacobianInverseTransposed jacInvT = + geometry.jacobianInverseTransposed(subContVol[scvIdx].localCenter); + for (int vert = 0; vert < numVertices; vert++) + jacInvT.mv(localJac[vert][0], subContVol[scvIdx].gradCenter[vert]); + } + } + } +}; + +} + +#endif + diff --git a/dumux/implicit/box/boxlocaljacobian.hh b/dumux/implicit/box/boxlocaljacobian.hh new file mode 100644 index 0000000000..7fc5140d25 --- /dev/null +++ b/dumux/implicit/box/boxlocaljacobian.hh @@ -0,0 +1,534 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \file + * \brief Caculates the Jacobian of the local residual for box models + */ +#ifndef DUMUX_BOX_LOCAL_JACOBIAN_HH +#define DUMUX_BOX_LOCAL_JACOBIAN_HH + +#include <dune/istl/matrix.hh> + +#include <dumux/common/math.hh> +#include "boxelementboundarytypes.hh" + +namespace Dumux +{ +/*! + * \ingroup BoxModel + * \ingroup BoxLocalJacobian + * \brief Calculates the Jacobian of the local residual for box models + * + * The default behavior is to use numeric differentiation, i.e. + * forward or backward differences (2nd order), or central + * differences (3rd order). The method used is determined by the + * "NumericDifferenceMethod" property: + * + * - if the value of this property is smaller than 0, backward + * differences are used, i.e.: + * \f[ + \frac{\partial f(x)}{\partial x} \approx \frac{f(x) - f(x - \epsilon)}{\epsilon} + * \f] + * + * - if the value of this property is 0, central + * differences are used, i.e.: + * \f[ + \frac{\partial f(x)}{\partial x} \approx \frac{f(x + \epsilon) - f(x - \epsilon)}{2 \epsilon} + * \f] + * + * - if the value of this property is larger than 0, forward + * differences are used, i.e.: + * \f[ + \frac{\partial f(x)}{\partial x} \approx \frac{f(x + \epsilon) - f(x)}{\epsilon} + * \f] + * + * Here, \f$ f \f$ is the residual function for all equations, \f$x\f$ + * is the value of a sub-control volume's primary variable at the + * evaluation point and \f$\epsilon\f$ is a small value larger than 0. + */ +template<class TypeTag> +class BoxLocalJacobian +{ +private: + typedef typename GET_PROP_TYPE(TypeTag, LocalJacobian) Implementation; + typedef typename GET_PROP_TYPE(TypeTag, LocalResidual) LocalResidual; + typedef typename GET_PROP_TYPE(TypeTag, Problem) Problem; + typedef typename GET_PROP_TYPE(TypeTag, Model) Model; + typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; + typedef typename GridView::template Codim<0>::Entity Element; + typedef typename GET_PROP_TYPE(TypeTag, JacobianAssembler) JacobianAssembler; + + enum { + numEq = GET_PROP_VALUE(TypeTag, NumEq), + dim = GridView::dimension, + + Green = JacobianAssembler::Green + }; + + typedef typename GET_PROP_TYPE(TypeTag, FVElementGeometry) FVElementGeometry; + typedef typename GET_PROP_TYPE(TypeTag, VertexMapper) VertexMapper; + typedef typename GET_PROP_TYPE(TypeTag, ElementSolutionVector) ElementSolutionVector; + typedef typename GET_PROP_TYPE(TypeTag, PrimaryVariables) PrimaryVariables; + + typedef typename GET_PROP_TYPE(TypeTag, VolumeVariables) VolumeVariables; + typedef typename GET_PROP_TYPE(TypeTag, ElementVolumeVariables) ElementVolumeVariables; + typedef typename GET_PROP_TYPE(TypeTag, ElementBoundaryTypes) ElementBoundaryTypes; + + typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; + typedef Dune::FieldMatrix<Scalar, numEq, numEq> MatrixBlock; + typedef Dune::Matrix<MatrixBlock> LocalBlockMatrix; + + // copying a local jacobian is not a good idea + BoxLocalJacobian(const BoxLocalJacobian &); + +public: + BoxLocalJacobian() + { + numericDifferenceMethod_ = GET_PARAM_FROM_GROUP(TypeTag, int, Implicit, NumericDifferenceMethod); + Valgrind::SetUndefined(problemPtr_); + } + + + /*! + * \brief Initialize the local Jacobian object. + * + * At this point we can assume that everything has been allocated, + * although some objects may not yet be completely initialized. + * + * \param prob The problem which we want to simulate. + */ + void init(Problem &prob) + { + problemPtr_ = &prob; + localResidual_.init(prob); + + // assume quadrilinears as elements with most vertices + A_.setSize(2<<dim, 2<<dim); + storageJacobian_.resize(2<<dim); + } + + /*! + * \brief Assemble an element's local Jacobian matrix of the + * defect. + * + * \param element The DUNE Codim<0> entity which we look at. + */ + void assemble(const Element &element) + { + // set the current grid element and update the element's + // finite volume geometry + elemPtr_ = &element; + fvElemGeom_.update(gridView_(), element); + reset_(); + + bcTypes_.update(problem_(), element_(), fvElemGeom_); + + // this is pretty much a HACK because the internal state of + // the problem is not supposed to be changed during the + // evaluation of the residual. (Reasons: It is a violation of + // abstraction, makes everything more prone to errors and is + // not thread save.) The real solution are context objects! + problem_().updateCouplingParams(element_()); + + // set the hints for the volume variables + model_().setHints(element, prevVolVars_, curVolVars_); + + // update the secondary variables for the element at the last + // and the current time levels + prevVolVars_.update(problem_(), + element_(), + fvElemGeom_, + true /* isOldSol? */); + + curVolVars_.update(problem_(), + element_(), + fvElemGeom_, + false /* isOldSol? */); + + // update the hints of the model + model_().updateCurHints(element, curVolVars_); + + // calculate the local residual + localResidual().eval(element_(), + fvElemGeom_, + prevVolVars_, + curVolVars_, + bcTypes_); + residual_ = localResidual().residual(); + storageTerm_ = localResidual().storageTerm(); + + model_().updatePVWeights(element_(), curVolVars_); + + // calculate the local jacobian matrix + int numVertices = fvElemGeom_.numVertices; + ElementSolutionVector partialDeriv(numVertices); + PrimaryVariables storageDeriv; + for (int j = 0; j < numVertices; j++) { + for (int pvIdx = 0; pvIdx < numEq; pvIdx++) { + asImp_().evalPartialDerivative_(partialDeriv, + storageDeriv, + j, + pvIdx); + + // update the local stiffness matrix with the current partial + // derivatives + updateLocalJacobian_(j, + pvIdx, + partialDeriv, + storageDeriv); + } + } + } + + /*! + * \brief Returns a reference to the object which calculates the + * local residual. + */ + const LocalResidual &localResidual() const + { return localResidual_; } + + /*! + * \brief Returns a reference to the object which calculates the + * local residual. + */ + LocalResidual &localResidual() + { return localResidual_; } + + /*! + * \brief Returns the Jacobian of the equations at vertex i to the + * primary variables at vertex j. + * + * \param i The local vertex (or sub-control volume) index on which + * the equations are defined + * \param j The local vertex (or sub-control volume) index which holds + * primary variables + */ + const MatrixBlock &mat(const int i, const int j) const + { return A_[i][j]; } + + /*! + * \brief Returns the Jacobian of the storage term at vertex i. + * + * \param i The local vertex (or sub-control volume) index + */ + const MatrixBlock &storageJacobian(const int i) const + { return storageJacobian_[i]; } + + /*! + * \brief Returns the residual of the equations at vertex i. + * + * \param i The local vertex (or sub-control volume) index on which + * the equations are defined + */ + const PrimaryVariables &residual(const int i) const + { return residual_[i]; } + + /*! + * \brief Returns the storage term for vertex i. + * + * \param i The local vertex (or sub-control volume) index on which + * the equations are defined + */ + const PrimaryVariables &storageTerm(const int i) const + { return storageTerm_[i]; } + + /*! + * \brief Returns the epsilon value which is added and removed + * from the current solution. + * + * \param scvIdx The local index of the element's vertex for + * which the local derivative ought to be calculated. + * \param pvIdx The index of the primary variable which gets varied + */ + Scalar numericEpsilon(const int scvIdx, + const int pvIdx) const + { + // define the base epsilon as the geometric mean of 1 and the + // resolution of the scalar type. E.g. for standard 64 bit + // floating point values, the resolution is about 10^-16 and + // the base epsilon is thus approximately 10^-8. + /* + static const Scalar baseEps + = Dumux::geometricMean<Scalar>(std::numeric_limits<Scalar>::epsilon(), 1.0); + */ + static const Scalar baseEps = 1e-10; + assert(std::numeric_limits<Scalar>::epsilon()*1e4 < baseEps); + // the epsilon value used for the numeric differentiation is + // now scaled by the absolute value of the primary variable... + Scalar priVar = this->curVolVars_[scvIdx].priVar(pvIdx); + return baseEps*(std::abs(priVar) + 1.0); + } + +protected: + Implementation &asImp_() + { return *static_cast<Implementation*>(this); } + const Implementation &asImp_() const + { return *static_cast<const Implementation*>(this); } + + /*! + * \brief Returns a reference to the problem. + */ + const Problem &problem_() const + { + Valgrind::CheckDefined(problemPtr_); + return *problemPtr_; + }; + + /*! + * \brief Returns a reference to the grid view. + */ + const GridView &gridView_() const + { return problem_().gridView(); } + + /*! + * \brief Returns a reference to the element. + */ + const Element &element_() const + { + Valgrind::CheckDefined(elemPtr_); + return *elemPtr_; + }; + + /*! + * \brief Returns a reference to the model. + */ + const Model &model_() const + { return problem_().model(); }; + + /*! + * \brief Returns a reference to the jacobian assembler. + */ + const JacobianAssembler &jacAsm_() const + { return model_().jacobianAssembler(); } + + /*! + * \brief Returns a reference to the vertex mapper. + */ + const VertexMapper &vertexMapper_() const + { return problem_().vertexMapper(); }; + + /*! + * \brief Reset the local jacobian matrix to 0 + */ + void reset_() + { + int n = element_().template count<dim>(); + for (int i = 0; i < n; ++ i) { + storageJacobian_[i] = 0.0; + for (int j = 0; j < n; ++ j) { + A_[i][j] = 0.0; + } + } + } + + /*! + * \brief Compute the partial derivatives to a primary variable at + * an degree of freedom. + * + * This method can be overwritten by the implementation if a + * better scheme than numerical differentiation is available. + * + * The default implementation of this method uses numeric + * differentiation, i.e. forward or backward differences (2nd + * order), or central differences (3rd order). The method used is + * determined by the "NumericDifferenceMethod" property: + * + * - if the value of this property is smaller than 0, backward + * differences are used, i.e.: + * \f[ + \frac{\partial f(x)}{\partial x} \approx \frac{f(x) - f(x - \epsilon)}{\epsilon} + * \f] + * + * - if the value of this property is 0, central + * differences are used, i.e.: + * \f[ + \frac{\partial f(x)}{\partial x} \approx \frac{f(x + \epsilon) - f(x - \epsilon)}{2 \epsilon} + * \f] + * + * - if the value of this property is larger than 0, forward + * differences are used, i.e.: + * \f[ + \frac{\partial f(x)}{\partial x} \approx \frac{f(x + \epsilon) - f(x)}{\epsilon} + * \f] + * + * Here, \f$ f \f$ is the residual function for all equations, \f$x\f$ + * is the value of a sub-control volume's primary variable at the + * evaluation point and \f$\epsilon\f$ is a small value larger than 0. + * + * \param partialDeriv The vector storing the partial derivatives of all + * equations + * \param storageDeriv the mass matrix contributions + * \param scvIdx The sub-control volume index of the current + * finite element for which the partial derivative + * ought to be calculated + * \param pvIdx The index of the primary variable at the scvIdx' + * sub-control volume of the current finite element + * for which the partial derivative ought to be + * calculated + */ + void evalPartialDerivative_(ElementSolutionVector &partialDeriv, + PrimaryVariables &storageDeriv, + const int scvIdx, + const int pvIdx) + { + int globalIdx = vertexMapper_().map(element_(), scvIdx, dim); + + PrimaryVariables priVars(model_().curSol()[globalIdx]); + VolumeVariables origVolVars(curVolVars_[scvIdx]); + + curVolVars_[scvIdx].setEvalPoint(&origVolVars); + Scalar eps = asImp_().numericEpsilon(scvIdx, pvIdx); + Scalar delta = 0; + + if (numericDifferenceMethod_ >= 0) { + // we are not using backward differences, i.e. we need to + // calculate f(x + \epsilon) + + // deflect primary variables + priVars[pvIdx] += eps; + delta += eps; + + // calculate the residual + curVolVars_[scvIdx].update(priVars, + problem_(), + element_(), + fvElemGeom_, + scvIdx, + false); + localResidual().eval(element_(), + fvElemGeom_, + prevVolVars_, + curVolVars_, + bcTypes_); + + // store the residual and the storage term + partialDeriv = localResidual().residual(); + storageDeriv = localResidual().storageTerm()[scvIdx]; + } + else { + // we are using backward differences, i.e. we don't need + // to calculate f(x + \epsilon) and we can recycle the + // (already calculated) residual f(x) + partialDeriv = residual_; + storageDeriv = storageTerm_[scvIdx]; + } + + + if (numericDifferenceMethod_ <= 0) { + // we are not using forward differences, i.e. we don't + // need to calculate f(x - \epsilon) + + // deflect the primary variables + priVars[pvIdx] -= delta + eps; + delta += eps; + + // calculate residual again + curVolVars_[scvIdx].update(priVars, + problem_(), + element_(), + fvElemGeom_, + scvIdx, + false); + localResidual().eval(element_(), + fvElemGeom_, + prevVolVars_, + curVolVars_, + bcTypes_); + partialDeriv -= localResidual().residual(); + storageDeriv -= localResidual().storageTerm()[scvIdx]; + } + else { + // we are using forward differences, i.e. we don't need to + // calculate f(x - \epsilon) and we can recycle the + // (already calculated) residual f(x) + partialDeriv -= residual_; + storageDeriv -= storageTerm_[scvIdx]; + } + + // divide difference in residuals by the magnitude of the + // deflections between the two function evaluation + partialDeriv /= delta; + storageDeriv /= delta; + + // restore the original state of the element's volume variables + curVolVars_[scvIdx] = origVolVars; + +#if HAVE_VALGRIND + for (unsigned i = 0; i < partialDeriv.size(); ++i) + Valgrind::CheckDefined(partialDeriv[i]); +#endif + } + + /*! + * \brief Updates the current local Jacobian matrix with the + * partial derivatives of all equations in regard to the + * primary variable 'pvIdx' at vertex 'scvIdx' . + */ + void updateLocalJacobian_(const int scvIdx, + const int pvIdx, + const ElementSolutionVector &partialDeriv, + const PrimaryVariables &storageDeriv) + { + // store the derivative of the storage term + for (int eqIdx = 0; eqIdx < numEq; eqIdx++) { + storageJacobian_[scvIdx][eqIdx][pvIdx] = storageDeriv[eqIdx]; + } + + for (int i = 0; i < fvElemGeom_.numVertices; i++) + { + // Green vertices are not to be changed! + if (jacAsm_().vertexColor(element_(), i) != Green) { + for (int eqIdx = 0; eqIdx < numEq; eqIdx++) { + // A[i][scvIdx][eqIdx][pvIdx] is the rate of change of + // the residual of equation 'eqIdx' at vertex 'i' + // depending on the primary variable 'pvIdx' at vertex + // 'scvIdx'. + this->A_[i][scvIdx][eqIdx][pvIdx] = partialDeriv[i][eqIdx]; + Valgrind::CheckDefined(this->A_[i][scvIdx][eqIdx][pvIdx]); + } + } + } + } + + const Element *elemPtr_; + FVElementGeometry fvElemGeom_; + + ElementBoundaryTypes bcTypes_; + + // The problem we would like to solve + Problem *problemPtr_; + + // secondary variables at the previous and at the current time + // levels + ElementVolumeVariables prevVolVars_; + ElementVolumeVariables curVolVars_; + + LocalResidual localResidual_; + + LocalBlockMatrix A_; + std::vector<MatrixBlock> storageJacobian_; + + ElementSolutionVector residual_; + ElementSolutionVector storageTerm_; + + int numericDifferenceMethod_; +}; +} + +#endif diff --git a/dumux/implicit/box/boxlocalresidual.hh b/dumux/implicit/box/boxlocalresidual.hh new file mode 100644 index 0000000000..8c02754133 --- /dev/null +++ b/dumux/implicit/box/boxlocalresidual.hh @@ -0,0 +1,742 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \file + * \brief Calculates the residual of models based on the box scheme element-wise. + */ +#ifndef DUMUX_BOX_LOCAL_RESIDUAL_HH +#define DUMUX_BOX_LOCAL_RESIDUAL_HH + +#include <dune/istl/matrix.hh> +#include <dune/grid/common/geometry.hh> + +#include <dumux/common/valgrind.hh> + +#include "boxproperties.hh" + +namespace Dumux +{ +/*! + * \ingroup BoxModel + * \ingroup BoxLocalResidual + * \brief Element-wise calculation of the residual matrix for models + * based on the box scheme. + * + * \todo Please doc me more! + */ +template<class TypeTag> +class BoxLocalResidual +{ +private: + typedef typename GET_PROP_TYPE(TypeTag, LocalResidual) Implementation; + typedef typename GET_PROP_TYPE(TypeTag, Problem) Problem; + typedef typename GET_PROP_TYPE(TypeTag, Model) Model; + typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; + typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; + + enum { + numEq = GET_PROP_VALUE(TypeTag, NumEq), + dim = GridView::dimension + }; + + typedef typename GridView::template Codim<0>::Entity Element; + typedef typename GridView::template Codim<dim>::EntityPointer VertexPointer; + typedef typename GridView::IntersectionIterator IntersectionIterator; + + typedef typename GridView::Grid::ctype CoordScalar; + typedef typename Dune::GenericReferenceElements<CoordScalar, dim> ReferenceElements; + typedef typename Dune::GenericReferenceElement<CoordScalar, dim> ReferenceElement; + + typedef typename GET_PROP_TYPE(TypeTag, FVElementGeometry) FVElementGeometry; + typedef typename GET_PROP_TYPE(TypeTag, VertexMapper) VertexMapper; + typedef typename GET_PROP_TYPE(TypeTag, ElementSolutionVector) ElementSolutionVector; + typedef typename GET_PROP_TYPE(TypeTag, PrimaryVariables) PrimaryVariables; + typedef typename GET_PROP_TYPE(TypeTag, BoundaryTypes) BoundaryTypes; + typedef typename GET_PROP_TYPE(TypeTag, ElementBoundaryTypes) ElementBoundaryTypes; + typedef typename GET_PROP_TYPE(TypeTag, VolumeVariables) VolumeVariables; + typedef typename GET_PROP_TYPE(TypeTag, ElementVolumeVariables) ElementVolumeVariables; + + // copying the local residual class is not a good idea + BoxLocalResidual(const BoxLocalResidual &); + +public: + BoxLocalResidual() + { } + + ~BoxLocalResidual() + { } + + /*! + * \brief Initialize the local residual. + * + * This assumes that all objects of the simulation have been fully + * allocated but not necessarily initialized completely. + * + * \param problem The representation of the physical problem to be + * solved. + */ + void init(Problem &problem) + { problemPtr_ = &problem; } + + /*! + * \brief Compute the local residual, i.e. the deviation of the + * equations from zero. + * + * \param element The DUNE Codim<0> entity for which the residual + * ought to be calculated + */ + void eval(const Element &element) + { + FVElementGeometry fvGeometry; + + fvGeometry.update(gridView_(), element); + fvElemGeomPtr_ = &fvGeometry; + + ElementVolumeVariables volVarsPrev, volVarsCur; + // update the hints + model_().setHints(element, volVarsPrev, volVarsCur); + + volVarsPrev.update(problem_(), + element, + fvGeometry_(), + true /* oldSol? */); + volVarsCur.update(problem_(), + element, + fvGeometry_(), + false /* oldSol? */); + + ElementBoundaryTypes bcTypes; + bcTypes.update(problem_(), element, fvGeometry_()); + + // this is pretty much a HACK because the internal state of + // the problem is not supposed to be changed during the + // evaluation of the residual. (Reasons: It is a violation of + // abstraction, makes everything more prone to errors and is + // not thread save.) The real solution are context objects! + problem_().updateCouplingParams(element); + + asImp_().eval(element, fvGeometry_(), volVarsPrev, volVarsCur, bcTypes); + } + + /*! + * \brief Compute the storage term for the current solution. + * + * This can be used to figure out how much of each conservation + * quantity is inside the element. + * + * \param element The DUNE Codim<0> entity for which the storage + * term ought to be calculated + */ + void evalStorage(const Element &element) + { + elemPtr_ = &element; + + FVElementGeometry fvGeometry; + fvGeometry.update(gridView_(), element); + fvElemGeomPtr_ = &fvGeometry; + + ElementBoundaryTypes bcTypes; + bcTypes.update(problem_(), element, fvGeometry_()); + bcTypesPtr_ = &bcTypes; + + // no previous volume variables! + prevVolVarsPtr_ = 0; + + ElementVolumeVariables volVars; + + // update the hints + model_().setHints(element, volVars); + + // calculate volume current variables + volVars.update(problem_(), element, fvGeometry_(), false); + curVolVarsPtr_ = &volVars; + + asImp_().evalStorage_(); + } + + /*! + * \brief Compute the flux term for the current solution. + * + * \param element The DUNE Codim<0> entity for which the residual + * ought to be calculated + * \param curVolVars The volume averaged variables for all + * sub-contol volumes of the element + */ + void evalFluxes(const Element &element, + const ElementVolumeVariables &curVolVars) + { + elemPtr_ = &element; + + FVElementGeometry fvGeometry; + fvGeometry.update(gridView_(), element); + fvElemGeomPtr_ = &fvGeometry; + + ElementBoundaryTypes bcTypes; + bcTypes.update(problem_(), element, fvGeometry_()); + + residual_.resize(fvGeometry_().numVertices); + residual_ = 0; + + bcTypesPtr_ = &bcTypes; + prevVolVarsPtr_ = 0; + curVolVarsPtr_ = &curVolVars; + asImp_().evalFluxes_(); + } + + /*! + * \brief Compute the local residual, i.e. the deviation of the + * equations from zero. + * + * \param element The DUNE Codim<0> entity for which the residual + * ought to be calculated + * \param fvGeometry The finite-volume geometry of the element + * \param prevVolVars The volume averaged variables for all + * sub-control volumes of the element at the previous + * time level + * \param curVolVars The volume averaged variables for all + * sub-control volumes of the element at the current + * time level + * \param bcTypes The types of the boundary conditions for all + * vertices of the element + */ + void eval(const Element &element, + const FVElementGeometry &fvGeometry, + const ElementVolumeVariables &prevVolVars, + const ElementVolumeVariables &curVolVars, + const ElementBoundaryTypes &bcTypes) + { + Valgrind::CheckDefined(prevVolVars); + Valgrind::CheckDefined(curVolVars); + +#if !defined NDEBUG && HAVE_VALGRIND + for (int i=0; i < fvGeometry.numVertices; i++) { + prevVolVars[i].checkDefined(); + curVolVars[i].checkDefined(); + } +#endif // HAVE_VALGRIND + + elemPtr_ = &element; + fvElemGeomPtr_ = &fvGeometry; + bcTypesPtr_ = &bcTypes; + prevVolVarsPtr_ = &prevVolVars; + curVolVarsPtr_ = &curVolVars; + + // resize the vectors for all terms + int numVerts = fvGeometry_().numVertices; + residual_.resize(numVerts); + storageTerm_.resize(numVerts); + + residual_ = 0.0; + storageTerm_ = 0.0; + + asImp_().evalFluxes_(); + +#if !defined NDEBUG && HAVE_VALGRIND + for (int i=0; i < fvGeometry_().numVertices; i++) + Valgrind::CheckDefined(residual_[i]); +#endif // HAVE_VALGRIND + + asImp_().evalVolumeTerms_(); + +#if !defined NDEBUG && HAVE_VALGRIND + for (int i=0; i < fvGeometry_().numVertices; i++) { + Valgrind::CheckDefined(residual_[i]); + } +#endif // HAVE_VALGRIND + + // evaluate the boundary conditions + asImp_().evalBoundary_(); + +#if !defined NDEBUG && HAVE_VALGRIND + for (int i=0; i < fvGeometry_().numVertices; i++) + Valgrind::CheckDefined(residual_[i]); +#endif // HAVE_VALGRIND + } + + /*! + * \brief Returns the local residual for all sub-control + * volumes of the element. + */ + const ElementSolutionVector &residual() const + { return residual_; } + + /*! + * \brief Returns the local residual for a given sub-control + * volume of the element. + * + * \param scvIdx The local index of the sub-control volume + * (i.e. the element's local vertex index) + */ + const PrimaryVariables &residual(const int scvIdx) const + { return residual_[scvIdx]; } + + /*! + * \brief Returns the storage term for all sub-control volumes of the + * element. + */ + const ElementSolutionVector &storageTerm() const + { return storageTerm_; } + + /*! + * \brief Returns the storage term for a given sub-control volumes + * of the element. + */ + const PrimaryVariables &storageTerm(const int scvIdx) const + { return storageTerm_[scvIdx]; } + +protected: + Implementation &asImp_() + { + assert(static_cast<Implementation*>(this) != 0); + return *static_cast<Implementation*>(this); + } + + const Implementation &asImp_() const + { + assert(static_cast<const Implementation*>(this) != 0); + return *static_cast<const Implementation*>(this); + } + + /*! + * \brief Evaluate the boundary conditions + * of the current element. + */ + void evalBoundary_() + { + if (bcTypes_().hasNeumann() || bcTypes_().hasOutflow()) + asImp_().evalBoundaryFluxes_(); +#if !defined NDEBUG && HAVE_VALGRIND + for (int i=0; i < fvGeometry_().numVertices; i++) + Valgrind::CheckDefined(residual_[i]); +#endif // HAVE_VALGRIND + + if (bcTypes_().hasDirichlet()) + asImp_().evalDirichlet_(); + } + + /*! + * \brief Set the values of the Dirichlet boundary control volumes + * of the current element. + */ + void evalDirichlet_() + { + PrimaryVariables dirichletValues(0); + for (int scvIdx = 0; scvIdx < fvGeometry_().numVertices; ++scvIdx) { + const BoundaryTypes &bcTypes = bcTypes_(scvIdx); + + if (bcTypes.hasDirichlet()) { + // ask the problem for the dirichlet values + const VertexPointer vPtr = element_().template subEntity<dim>(scvIdx); + Valgrind::SetUndefined(dirichletValues); + asImp_().problem_().dirichlet(dirichletValues, *vPtr); + + // set the dirichlet conditions + for (int eqIdx = 0; eqIdx < numEq; ++eqIdx) { + if (bcTypes.isDirichlet(eqIdx)) { + int pvIdx = bcTypes.eqToDirichletIndex(eqIdx); + assert(0 <= pvIdx && pvIdx < numEq); + Valgrind::CheckDefined(dirichletValues[pvIdx]); + + residual_[scvIdx][eqIdx] = + curPriVar_(scvIdx, pvIdx) - dirichletValues[pvIdx]; + + storageTerm_[scvIdx][eqIdx] = 0.0; + } + } + } + } + } + + /*! + * \brief Add all Neumann and outflow boundary conditions to the local + * residual. + */ + void evalBoundaryFluxes_() + { + Dune::GeometryType geoType = element_().geometry().type(); + const ReferenceElement &refElement = ReferenceElements::general(geoType); + + IntersectionIterator isIt = gridView_().ibegin(element_()); + const IntersectionIterator &endIt = gridView_().iend(element_()); + for (; isIt != endIt; ++isIt) + { + // handle only faces on the boundary + if (isIt->boundary()) { + // Assemble the boundary for all vertices of the current + // face + int faceIdx = isIt->indexInInside(); + int numFaceVerts = refElement.size(faceIdx, 1, dim); + for (int faceVertIdx = 0; + faceVertIdx < numFaceVerts; + ++faceVertIdx) + { + int scvIdx = refElement.subEntity(faceIdx, + 1, + faceVertIdx, + dim); + + int boundaryFaceIdx = + fvGeometry_().boundaryFaceIndex(faceIdx, faceVertIdx); + + // add the residual of all vertices of the boundary + // segment + asImp_().evalNeumannSegment_(isIt, + scvIdx, + boundaryFaceIdx); + // evaluate the outflow conditions at the boundary face + // ATTENTION: This is so far a beta version that is only for the 2p2c and 2p2cni model + // available and not thoroughly tested. + asImp_().evalOutflowSegment_(isIt, + scvIdx, + boundaryFaceIdx); + } + } + } + } + + /*! + * \brief Add Neumann boundary conditions for a single sub-control + * volume face to the local residual. + */ + void evalNeumannSegment_(const IntersectionIterator &isIt, + const int scvIdx, + const int boundaryFaceIdx) + { + // temporary vector to store the neumann boundary fluxes + PrimaryVariables neumannFlux(0.0); + const BoundaryTypes &bcTypes = bcTypes_(scvIdx); + + // deal with neumann boundaries + if (bcTypes.hasNeumann()) { + Valgrind::SetUndefined(neumannFlux); + problem_().boxSDNeumann(neumannFlux, + element_(), + fvGeometry_(), + *isIt, + scvIdx, + boundaryFaceIdx, + curVolVars_()); + neumannFlux *= + fvGeometry_().boundaryFace[boundaryFaceIdx].area + * curVolVars_(scvIdx).extrusionFactor(); + Valgrind::CheckDefined(neumannFlux); + + // set the neumann conditions + for (int eqIdx = 0; eqIdx < numEq; ++eqIdx) { + if (!bcTypes.isNeumann(eqIdx)) + continue; + residual_[scvIdx][eqIdx] += neumannFlux[eqIdx]; + } + } + } + + /*! + * \brief Add outflow boundary conditions for a single sub-control + * volume face to the local residual. + * + * \param isIt The intersection iterator of current element + * \param scvIdx The index of the considered face of the sub-control volume + * \param boundaryFaceIdx The index of the considered boundary face of the sub control volume + */ + void evalOutflowSegment_(const IntersectionIterator &isIt, + const int scvIdx, + const int boundaryFaceIdx) + { + const BoundaryTypes &bcTypes = this->bcTypes_(scvIdx); + // deal with outflow boundaries + if (bcTypes.hasOutflow()) + { + //calculate outflow fluxes + PrimaryVariables values(0.0); + asImp_().computeFlux(values, boundaryFaceIdx, true); + Valgrind::CheckDefined(values); + + for (int equationIdx = 0; equationIdx < numEq; ++equationIdx) + { + if (!bcTypes.isOutflow(equationIdx) ) + continue; + // deduce outflow + this->residual_[scvIdx][equationIdx] += values[equationIdx]; + } + } + } + + /*! + * \brief Add the flux terms to the local residual of all + * sub-control volumes of the current element. + */ + void evalFluxes_() + { + // calculate the mass flux over the faces and subtract + // it from the local rates + for (int scvfIdx = 0; scvfIdx < fvGeometry_().numEdges; scvfIdx++) + { + int i = fvGeometry_().subContVolFace[scvfIdx].i; + int j = fvGeometry_().subContVolFace[scvfIdx].j; + + PrimaryVariables flux; + + Valgrind::SetUndefined(flux); + asImp_().computeFlux(flux, scvfIdx); + Valgrind::CheckDefined(flux); + + Scalar extrusionFactor = + (curVolVars_(i).extrusionFactor() + + curVolVars_(j).extrusionFactor()) + / 2; + flux *= extrusionFactor; + + // The balance equation for a finite volume is: + // + // dStorage/dt = Flux + Source + // + // where the 'Flux' and the 'Source' terms represent the + // mass per second which _ENTER_ the finite + // volume. Re-arranging this, we get + // + // dStorage/dt - Source - Flux = 0 + // + // Since the flux calculated by computeFlux() goes _OUT_ + // of sub-control volume i and _INTO_ sub-control volume + // j, we need to add the flux to finite volume i and + // subtract it from finite volume j + residual_[i] += flux; + residual_[j] -= flux; + } + } + + /*! + * \brief Set the local residual to the storage terms of all + * sub-control volumes of the current element. + */ + void evalStorage_() + { + storageTerm_.resize(fvGeometry_().numVertices); + storageTerm_ = 0; + + // calculate the amount of conservation each quantity inside + // all sub control volumes + for (int scvIdx = 0; scvIdx < fvGeometry_().numVertices; scvIdx++) { + Valgrind::SetUndefined(storageTerm_[scvIdx]); + asImp_().computeStorage(storageTerm_[scvIdx], scvIdx, /*isOldSol=*/false); + storageTerm_[scvIdx] *= + fvGeometry_().subContVol[scvIdx].volume + * curVolVars_(scvIdx).extrusionFactor(); + Valgrind::CheckDefined(storageTerm_[scvIdx]); + } + } + + /*! + * \brief Add the change in the storage terms and the source term + * to the local residual of all sub-control volumes of the + * current element. + */ + void evalVolumeTerms_() + { + // evaluate the volume terms (storage + source terms) + for (int scvIdx = 0; scvIdx < fvGeometry_().numVertices; scvIdx++) + { + Scalar extrusionFactor = + curVolVars_(scvIdx).extrusionFactor(); + + PrimaryVariables values(0.0); + + // mass balance within the element. this is the + // \f$\frac{m}{\partial t}\f$ term if using implicit + // euler as time discretization. + // + // TODO (?): we might need a more explicit way for + // doing the time discretization... + Valgrind::SetUndefined(storageTerm_[scvIdx]); + Valgrind::SetUndefined(values); + asImp_().computeStorage(storageTerm_[scvIdx], scvIdx, false); + asImp_().computeStorage(values, scvIdx, true); + Valgrind::CheckDefined(storageTerm_[scvIdx]); + Valgrind::CheckDefined(values); + + storageTerm_[scvIdx] -= values; + storageTerm_[scvIdx] *= + fvGeometry_().subContVol[scvIdx].volume + / problem_().timeManager().timeStepSize() + * extrusionFactor; + residual_[scvIdx] += storageTerm_[scvIdx]; + + // subtract the source term from the local rate + Valgrind::SetUndefined(values); + asImp_().computeSource(values, scvIdx); + Valgrind::CheckDefined(values); + values *= fvGeometry_().subContVol[scvIdx].volume * extrusionFactor; + residual_[scvIdx] -= values; + + // make sure that only defined quantities were used + // to calculate the residual. + Valgrind::CheckDefined(residual_[scvIdx]); + } + } + + /*! + * \brief Returns a reference to the problem. + */ + const Problem &problem_() const + { return *problemPtr_; }; + + /*! + * \brief Returns a reference to the model. + */ + const Model &model_() const + { return problem_().model(); }; + + /*! + * \brief Returns a reference to the vertex mapper. + */ + const VertexMapper &vertexMapper_() const + { return problem_().vertexMapper(); }; + + /*! + * \brief Returns a reference to the grid view. + */ + const GridView &gridView_() const + { return problem_().gridView(); } + + /*! + * \brief Returns a reference to the current element. + */ + const Element &element_() const + { + Valgrind::CheckDefined(elemPtr_); + return *elemPtr_; + } + + /*! + * \brief Returns a reference to the current element's finite + * volume geometry. + */ + const FVElementGeometry &fvGeometry_() const + { + Valgrind::CheckDefined(fvElemGeomPtr_); + return *fvElemGeomPtr_; + } + + /*! + * \brief Returns a reference to the primary variables of + * the last time step of the i'th + * sub-control volume of the current element. + */ + const PrimaryVariables &prevPriVars_(const int i) const + { + return prevVolVars_(i).priVars(); + } + + /*! + * \brief Returns a reference to the primary variables of the i'th + * sub-control volume of the current element. + */ + const PrimaryVariables &curPriVars_(const int i) const + { + return curVolVars_(i).priVars(); + } + + /*! + * \brief Returns the j'th primary of the i'th sub-control volume + * of the current element. + */ + Scalar curPriVar_(const int i, const int j) const + { + return curVolVars_(i).priVar(j); + } + + /*! + * \brief Returns a reference to the current volume variables of + * all sub-control volumes of the current element. + */ + const ElementVolumeVariables &curVolVars_() const + { + Valgrind::CheckDefined(curVolVarsPtr_); + return *curVolVarsPtr_; + } + + /*! + * \brief Returns a reference to the volume variables of the i-th + * sub-control volume of the current element. + */ + const VolumeVariables &curVolVars_(const int i) const + { + return curVolVars_()[i]; + } + + /*! + * \brief Returns a reference to the previous time step's volume + * variables of all sub-control volumes of the current + * element. + */ + const ElementVolumeVariables &prevVolVars_() const + { + Valgrind::CheckDefined(prevVolVarsPtr_); + return *prevVolVarsPtr_; + } + + /*! + * \brief Returns a reference to the previous time step's volume + * variables of the i-th sub-control volume of the current + * element. + */ + const VolumeVariables &prevVolVars_(const int i) const + { + return prevVolVars_()[i]; + } + + /*! + * \brief Returns a reference to the boundary types of all + * sub-control volumes of the current element. + */ + const ElementBoundaryTypes &bcTypes_() const + { + Valgrind::CheckDefined(bcTypesPtr_); + return *bcTypesPtr_; + } + + /*! + * \brief Returns a reference to the boundary types of the i-th + * sub-control volume of the current element. + */ + const BoundaryTypes &bcTypes_(const int i) const + { + return bcTypes_()[i]; + } + +protected: + ElementSolutionVector storageTerm_; + ElementSolutionVector residual_; + + // The problem we would like to solve + Problem *problemPtr_; + + const Element *elemPtr_; + const FVElementGeometry *fvElemGeomPtr_; + + // current and previous secondary variables for the element + const ElementVolumeVariables *prevVolVarsPtr_; + const ElementVolumeVariables *curVolVarsPtr_; + + const ElementBoundaryTypes *bcTypesPtr_; +}; + +} + +#endif diff --git a/dumux/implicit/box/boxmodel.hh b/dumux/implicit/box/boxmodel.hh new file mode 100644 index 0000000000..567d706dd7 --- /dev/null +++ b/dumux/implicit/box/boxmodel.hh @@ -0,0 +1,927 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \file + * + * \brief Base class for models using box discretization + */ +#ifndef DUMUX_BOX_MODEL_HH +#define DUMUX_BOX_MODEL_HH + +#include "boxproperties.hh" +#include "boxpropertydefaults.hh" + +#include "boxelementvolumevariables.hh" +#include "boxlocaljacobian.hh" +#include "boxlocalresidual.hh" + +#include <dumux/parallel/vertexhandles.hh> + +#include <dune/grid/common/geometry.hh> + +namespace Dumux +{ + +/*! + * \ingroup BoxModel + * + * \brief The base class for the vertex centered finite volume + * discretization scheme. + */ +template<class TypeTag> +class BoxModel +{ + typedef typename GET_PROP_TYPE(TypeTag, Model) Implementation; + typedef typename GET_PROP_TYPE(TypeTag, Problem) Problem; + typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; + typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; + typedef typename GET_PROP_TYPE(TypeTag, ElementMapper) ElementMapper; + typedef typename GET_PROP_TYPE(TypeTag, VertexMapper) VertexMapper; + typedef typename GET_PROP_TYPE(TypeTag, DofMapper) DofMapper; + typedef typename GET_PROP_TYPE(TypeTag, SolutionVector) SolutionVector; + typedef typename GET_PROP_TYPE(TypeTag, PrimaryVariables) PrimaryVariables; + typedef typename GET_PROP_TYPE(TypeTag, JacobianAssembler) JacobianAssembler; + typedef typename GET_PROP_TYPE(TypeTag, ElementVolumeVariables) ElementVolumeVariables; + typedef typename GET_PROP_TYPE(TypeTag, VolumeVariables) VolumeVariables; + + enum { + numEq = GET_PROP_VALUE(TypeTag, NumEq), + dim = GridView::dimension + }; + + typedef typename GET_PROP_TYPE(TypeTag, FVElementGeometry) FVElementGeometry; + typedef typename GET_PROP_TYPE(TypeTag, LocalJacobian) LocalJacobian; + typedef typename GET_PROP_TYPE(TypeTag, LocalResidual) LocalResidual; + typedef typename GET_PROP_TYPE(TypeTag, NewtonMethod) NewtonMethod; + typedef typename GET_PROP_TYPE(TypeTag, NewtonController) NewtonController; + + typedef typename GridView::ctype CoordScalar; + typedef typename GridView::template Codim<0>::Entity Element; + typedef typename GridView::template Codim<0>::Iterator ElementIterator; + typedef typename GridView::template Codim<dim>::Entity Vertex; + typedef typename GridView::template Codim<dim>::Iterator VertexIterator; + typedef typename GridView::IntersectionIterator IntersectionIterator; + + typedef typename Dune::GenericReferenceElements<CoordScalar, dim> ReferenceElements; + typedef typename Dune::GenericReferenceElement<CoordScalar, dim> ReferenceElement; + + // copying a model is not a good idea + BoxModel(const BoxModel &); + +public: + /*! + * \brief The constructor. + */ + BoxModel() + { + enableHints_ = GET_PARAM_FROM_GROUP(TypeTag, bool, Implicit, EnableHints); + } + + ~BoxModel() + { delete jacAsm_; } + + /*! + * \brief Apply the initial conditions to the model. + * + * \param problem The object representing the problem which needs to + * be simulated. + */ + void init(Problem &problem) + { + problemPtr_ = &problem; + + updateBoundaryIndices_(); + + int nDofs = asImp_().numDofs(); + uCur_.resize(nDofs); + uPrev_.resize(nDofs); + boxVolume_.resize(nDofs); + + localJacobian_.init(problem_()); + jacAsm_ = new JacobianAssembler(); + jacAsm_->init(problem_()); + + asImp_().applyInitialSolution_(); + + // resize the hint vectors + if (enableHints_) { + int nVerts = gridView_().size(dim); + curHints_.resize(nVerts); + prevHints_.resize(nVerts); + hintsUsable_.resize(nVerts); + std::fill(hintsUsable_.begin(), + hintsUsable_.end(), + false); + } + + // also set the solution of the "previous" time step to the + // initial solution. + uPrev_ = uCur_; + } + + void setHints(const Element &element, + ElementVolumeVariables &prevVolVars, + ElementVolumeVariables &curVolVars) const + { + if (!enableHints_) + return; + + int n = element.template count<dim>(); + prevVolVars.resize(n); + curVolVars.resize(n); + for (int i = 0; i < n; ++i) { + int globalIdx = vertexMapper().map(element, i, dim); + + if (!hintsUsable_[globalIdx]) { + curVolVars[i].setHint(NULL); + prevVolVars[i].setHint(NULL); + } + else { + curVolVars[i].setHint(&curHints_[globalIdx]); + prevVolVars[i].setHint(&prevHints_[globalIdx]); + } + } + } + + void setHints(const Element &element, + ElementVolumeVariables &curVolVars) const + { + if (!enableHints_) + return; + + int n = element.template count<dim>(); + curVolVars.resize(n); + for (int i = 0; i < n; ++i) { + int globalIdx = vertexMapper().map(element, i, dim); + + if (!hintsUsable_[globalIdx]) + curVolVars[i].setHint(NULL); + else + curVolVars[i].setHint(&curHints_[globalIdx]); + } + } + + void updatePrevHints() + { + if (!enableHints_) + return; + + prevHints_ = curHints_; + } + + void updateCurHints(const Element &element, + const ElementVolumeVariables &elemVolVars) const + { + if (!enableHints_) + return; + + for (unsigned int i = 0; i < elemVolVars.size(); ++i) { + int globalIdx = vertexMapper().map(element, i, dim); + curHints_[globalIdx] = elemVolVars[i]; + if (!hintsUsable_[globalIdx]) + prevHints_[globalIdx] = elemVolVars[i]; + hintsUsable_[globalIdx] = true; + } + } + + + /*! + * \brief Compute the global residual for an arbitrary solution + * vector. + * + * \param residual Stores the result + * \param u The solution for which the residual ought to be calculated + */ + Scalar globalResidual(SolutionVector &residual, + const SolutionVector &u) + { + SolutionVector tmp(curSol()); + curSol() = u; + Scalar res = globalResidual(residual); + curSol() = tmp; + return res; + } + + /*! + * \brief Compute the global residual for the current solution + * vector. + * + * \param residual Stores the result + */ + Scalar globalResidual(SolutionVector &residual) + { + residual = 0; + + ElementIterator elemIt = gridView_().template begin<0>(); + const ElementIterator elemEndIt = gridView_().template end<0>(); + for (; elemIt != elemEndIt; ++elemIt) { + localResidual().eval(*elemIt); + + for (int i = 0; i < elemIt->template count<dim>(); ++i) { + int globalI = vertexMapper().map(*elemIt, i, dim); + residual[globalI] += localResidual().residual(i); + } + } + + // calculate the square norm of the residual + Scalar result2 = residual.two_norm2(); + if (gridView_().comm().size() > 1) + result2 = gridView_().comm().sum(result2); + + // add up the residuals on the process borders + if (gridView_().comm().size() > 1) { + VertexHandleSum<PrimaryVariables, SolutionVector, VertexMapper> + sumHandle(residual, vertexMapper()); + gridView_().communicate(sumHandle, + Dune::InteriorBorder_InteriorBorder_Interface, + Dune::ForwardCommunication); + } + + return std::sqrt(result2); + } + + /*! + * \brief Compute the integral over the domain of the storage + * terms of all conservation quantities. + * + * \param storage Stores the result + */ + void globalStorage(PrimaryVariables &storage) + { + storage = 0; + + ElementIterator elemIt = gridView_().template begin<0>(); + const ElementIterator elemEndIt = gridView_().template end<0>(); + for (; elemIt != elemEndIt; ++elemIt) { + localResidual().evalStorage(*elemIt); + + for (int i = 0; i < elemIt->template count<dim>(); ++i) + storage += localResidual().storageTerm()[i]; + } + + if (gridView_().comm().size() > 1) + storage = gridView_().comm().sum(storage); + } + + /*! + * \brief Returns the volume \f$\mathrm{[m^3]}\f$ of a given control volume. + * + * \param globalIdx The global index of the control volume's + * associated vertex + */ + Scalar boxVolume(const int globalIdx) const + { return boxVolume_[globalIdx][0]; } + + /*! + * \brief Reference to the current solution as a block vector. + */ + const SolutionVector &curSol() const + { return uCur_; } + + /*! + * \brief Reference to the current solution as a block vector. + */ + SolutionVector &curSol() + { return uCur_; } + + /*! + * \brief Reference to the previous solution as a block vector. + */ + const SolutionVector &prevSol() const + { return uPrev_; } + + /*! + * \brief Reference to the previous solution as a block vector. + */ + SolutionVector &prevSol() + { return uPrev_; } + + /*! + * \brief Returns the operator assembler for the global jacobian of + * the problem. + */ + JacobianAssembler &jacobianAssembler() + { return *jacAsm_; } + + /*! + * \copydoc jacobianAssembler() + */ + const JacobianAssembler &jacobianAssembler() const + { return *jacAsm_; } + + /*! + * \brief Returns the local jacobian which calculates the local + * stiffness matrix for an arbitrary element. + * + * The local stiffness matrices of the element are used by + * the jacobian assembler to produce a global linerization of the + * problem. + */ + LocalJacobian &localJacobian() + { return localJacobian_; } + /*! + * \copydoc localJacobian() + */ + const LocalJacobian &localJacobian() const + { return localJacobian_; } + + /*! + * \brief Returns the local residual function. + */ + LocalResidual &localResidual() + { return localJacobian().localResidual(); } + /*! + * \copydoc localResidual() + */ + const LocalResidual &localResidual() const + { return localJacobian().localResidual(); } + + /*! + * \brief Returns the relative error between two vectors of + * primary variables. + * + * \param vertexIdx The global index of the control volume's + * associated vertex + * \param priVars1 The first vector of primary variables + * \param priVars2 The second vector of primary variables + * + * \todo The vertexIdx argument is pretty hacky. it is required by + * models with pseudo primary variables (i.e. the primary + * variable switching models). the clean solution would be + * to access the pseudo primary variables from the primary + * variables. + */ + Scalar relativeErrorVertex(const int vertexIdx, + const PrimaryVariables &priVars1, + const PrimaryVariables &priVars2) + { + Scalar result = 0.0; + for (int j = 0; j < numEq; ++j) { + Scalar eqErr = std::abs(priVars1[j] - priVars2[j]); + eqErr /= std::max<Scalar>(1.0, std::abs(priVars1[j] + priVars2[j])/2); + + result = std::max(result, eqErr); + } + return result; + } + + /*! + * \brief Try to progress the model to the next timestep. + * + * \param solver The non-linear solver + * \param controller The controller which specifies the behaviour + * of the non-linear solver + */ + bool update(NewtonMethod &solver, + NewtonController &controller) + { +#if HAVE_VALGRIND + for (size_t i = 0; i < curSol().size(); ++i) + Valgrind::CheckDefined(curSol()[i]); +#endif // HAVE_VALGRIND + + asImp_().updateBegin(); + + bool converged = solver.execute(controller); + if (converged) { + asImp_().updateSuccessful(); + } + else + asImp_().updateFailed(); + +#if HAVE_VALGRIND + for (size_t i = 0; i < curSol().size(); ++i) { + Valgrind::CheckDefined(curSol()[i]); + } +#endif // HAVE_VALGRIND + + return converged; + } + + + /*! + * \brief Called by the update() method before it tries to + * apply the newton method. This is primary a hook + * which the actual model can overload. + */ + void updateBegin() + { } + + + /*! + * \brief Called by the update() method if it was + * successful. This is primary a hook which the actual + * model can overload. + */ + void updateSuccessful() + { } + + /*! + * \brief Called by the update() method if it was + * unsuccessful. This is primary a hook which the actual + * model can overload. + */ + void updateFailed() + { + // Reset the current solution to the one of the + // previous time step so that we can start the next + // update at a physically meaningful solution. + uCur_ = uPrev_; + curHints_ = prevHints_; + + jacAsm_->reassembleAll(); + } + + /*! + * \brief Called by the problem if a time integration was + * successful, post processing of the solution is done and + * the result has been written to disk. + * + * This should prepare the model for the next time integration. + */ + void advanceTimeLevel() + { + // make the current solution the previous one. + uPrev_ = uCur_; + prevHints_ = curHints_; + + updatePrevHints(); + } + + /*! + * \brief Serializes the current state of the model. + * + * \tparam Restarter The type of the serializer class + * + * \param res The serializer object + */ + template <class Restarter> + void serialize(Restarter &res) + { res.template serializeEntities<dim>(asImp_(), this->gridView_()); } + + /*! + * \brief Deserializes the state of the model. + * + * \tparam Restarter The type of the serializer class + * + * \param res The serializer object + */ + template <class Restarter> + void deserialize(Restarter &res) + { + res.template deserializeEntities<dim>(asImp_(), this->gridView_()); + prevSol() = curSol(); + } + + /*! + * \brief Write the current solution for a vertex to a restart + * file. + * + * \param outstream The stream into which the vertex data should + * be serialized to + * \param vertex The DUNE Codim<dim> entity which's data should be + * serialized + */ + void serializeEntity(std::ostream &outstream, + const Vertex &vertex) + { + int vertIdx = dofMapper().map(vertex); + + // write phase state + if (!outstream.good()) { + DUNE_THROW(Dune::IOError, + "Could not serialize vertex " + << vertIdx); + } + + for (int eqIdx = 0; eqIdx < numEq; ++eqIdx) { + outstream << curSol()[vertIdx][eqIdx] << " "; + } + } + + /*! + * \brief Reads the current solution variables for a vertex from a + * restart file. + * + * \param instream The stream from which the vertex data should + * be deserialized from + * \param vertex The DUNE Codim<dim> entity which's data should be + * deserialized + */ + void deserializeEntity(std::istream &instream, + const Vertex &vertex) + { + int vertIdx = dofMapper().map(vertex); + for (int eqIdx = 0; eqIdx < numEq; ++eqIdx) { + if (!instream.good()) + DUNE_THROW(Dune::IOError, + "Could not deserialize vertex " + << vertIdx); + instream >> curSol()[vertIdx][eqIdx]; + } + } + + /*! + * \brief Returns the number of global degrees of freedoms (DOFs) + */ + size_t numDofs() const + { return gridView_().size(dim); } + + /*! + * \brief Mapper for the entities where degrees of freedoms are + * defined to indices. + * + * This usually means a mapper for vertices. + */ + const DofMapper &dofMapper() const + { return problem_().vertexMapper(); } + + /*! + * \brief Mapper for vertices to indices. + */ + const VertexMapper &vertexMapper() const + { return problem_().vertexMapper(); } + + /*! + * \brief Mapper for elements to indices. + */ + const ElementMapper &elementMapper() const + { return problem_().elementMapper(); } + + /*! + * \brief Resets the Jacobian matrix assembler, so that the + * boundary types can be altered. + */ + void resetJacobianAssembler () + { + delete jacAsm_; + jacAsm_ = new JacobianAssembler; + jacAsm_->init(problem_()); + } + + /*! + * \brief Update the weights of all primary variables within an + * element given the complete set of volume variables + * + * \param element The DUNE codim 0 entity + * \param volVars All volume variables for the element + */ + void updatePVWeights(const Element &element, + const ElementVolumeVariables &volVars) const + { } + + /*! + * \brief Add the vector fields for analysing the convergence of + * the newton method to the a VTK multi writer. + * + * \tparam MultiWriter The type of the VTK multi writer + * + * \param writer The VTK multi writer object on which the fields should be added. + * \param u The solution function + * \param deltaU The delta of the solution function before and after the Newton update + */ + template <class MultiWriter> + void addConvergenceVtkFields(MultiWriter &writer, + const SolutionVector &u, + const SolutionVector &deltaU) + { + typedef Dune::BlockVector<Dune::FieldVector<double, 1> > ScalarField; + + SolutionVector residual(u); + asImp_().globalResidual(residual, u); + + // create the required scalar fields + unsigned numVertices = this->gridView_().size(dim); + + // global defect of the two auxiliary equations + ScalarField* def[numEq]; + ScalarField* delta[numEq]; + ScalarField* x[numEq]; + for (int eqIdx = 0; eqIdx < numEq; ++eqIdx) { + x[eqIdx] = writer.allocateManagedBuffer(numVertices); + delta[eqIdx] = writer.allocateManagedBuffer(numVertices); + def[eqIdx] = writer.allocateManagedBuffer(numVertices); + } + + VertexIterator vIt = this->gridView_().template begin<dim>(); + VertexIterator vEndIt = this->gridView_().template end<dim>(); + for (; vIt != vEndIt; ++ vIt) + { + int globalIdx = vertexMapper().map(*vIt); + for (int eqIdx = 0; eqIdx < numEq; ++eqIdx) { + (*x[eqIdx])[globalIdx] = u[globalIdx][eqIdx]; + (*delta[eqIdx])[globalIdx] = - deltaU[globalIdx][eqIdx]; + (*def[eqIdx])[globalIdx] = residual[globalIdx][eqIdx]; + } + } + + for (int eqIdx = 0; eqIdx < numEq; ++eqIdx) { + std::ostringstream oss; + oss.str(""); oss << "x_" << eqIdx; + writer.attachVertexData(*x[eqIdx], oss.str()); + oss.str(""); oss << "delta_" << eqIdx; + writer.attachVertexData(*delta[eqIdx], oss.str()); + oss.str(""); oss << "defect_" << eqIdx; + writer.attachVertexData(*def[eqIdx], oss.str()); + } + + asImp_().addOutputVtkFields(u, writer); + } + + /*! + * \brief Add the quantities of a time step which ought to be written to disk. + * + * This should be overwritten by the actual model if any secondary + * variables should be written out. Read: This should _always_ be + * overwritten by well behaved models! + * + * \tparam MultiWriter The type of the VTK multi writer + * + * \param sol The global vector of primary variable values. + * \param writer The VTK multi writer where the fields should be added. + */ + template <class MultiWriter> + void addOutputVtkFields(const SolutionVector &sol, + MultiWriter &writer) + { + typedef Dune::BlockVector<Dune::FieldVector<Scalar, 1> > ScalarField; + + // create the required scalar fields + unsigned numVertices = this->gridView_().size(dim); + + // global defect of the two auxiliary equations + ScalarField* x[numEq]; + for (int eqIdx = 0; eqIdx < numEq; ++eqIdx) { + x[eqIdx] = writer.allocateManagedBuffer(numVertices); + } + + VertexIterator vIt = this->gridView_().template begin<dim>(); + VertexIterator vEndIt = this->gridView_().template end<dim>(); + for (; vIt != vEndIt; ++ vIt) + { + int globalIdx = vertexMapper().map(*vIt); + for (int eqIdx = 0; eqIdx < numEq; ++eqIdx) { + (*x[eqIdx])[globalIdx] = sol[globalIdx][eqIdx]; + } + } + + for (int eqIdx = 0; eqIdx < numEq; ++eqIdx) { + std::ostringstream oss; + oss << "primaryVar_" << eqIdx; + writer.attachVertexData(*x[eqIdx], oss.str()); + } + } + + /*! + * \brief Reference to the grid view of the spatial domain. + */ + const GridView &gridView() const + { return problem_().gridView(); } + + /*! + * \brief Returns true if the vertex with 'globalVertIdx' is + * located on the grid's boundary. + * + * \param globalVertIdx The global index of the control volume's + * associated vertex + */ + bool onBoundary(const int globalVertIdx) const + { return boundaryIndices_[globalVertIdx]; } + + /*! + * \brief Returns true if a vertex is located on the grid's + * boundary. + * + * \param element A DUNE Codim<0> entity which contains the control + * volume's associated vertex. + * \param vIdx The local vertex index inside element + */ + bool onBoundary(const Element &element, const int vIdx) const + { return onBoundary(vertexMapper().map(element, vIdx, dim)); } + + /*! + * \brief Fill the fluid state according to the primary variables. + * + * Taking the information from the primary variables, + * the fluid state is filled with every information that is + * necessary to evaluate the model's local residual. + * + * \param priVars The primary variables of the model. + * \param problem The problem at hand. + * \param element The current element. + * \param fvGeometry The finite volume element geometry. + * \param scvIdx The index of the subcontrol volume. + * \param fluidState The fluid state to fill. + */ + template <class FluidState> + static void completeFluidState(const PrimaryVariables& priVars, + const Problem& problem, + const Element& element, + const FVElementGeometry& fvGeometry, + const int scvIdx, + FluidState& fluidState) + { + VolumeVariables::completeFluidState(priVars, problem, element, + fvGeometry, scvIdx, fluidState); + } +protected: + /*! + * \brief A reference to the problem on which the model is applied. + */ + Problem &problem_() + { return *problemPtr_; } + /*! + * \copydoc problem_() + */ + const Problem &problem_() const + { return *problemPtr_; } + + /*! + * \brief Reference to the grid view of the spatial domain. + */ + const GridView &gridView_() const + { return problem_().gridView(); } + + /*! + * \brief Reference to the local residal object + */ + LocalResidual &localResidual_() + { return localJacobian_.localResidual(); } + + /*! + * \brief Applies the initial solution for all vertices of the grid. + */ + void applyInitialSolution_() + { + // first set the whole domain to zero + uCur_ = Scalar(0.0); + boxVolume_ = Scalar(0.0); + + FVElementGeometry fvGeometry; + + // iterate through leaf grid and evaluate initial + // condition at the center of each sub control volume + // + // TODO: the initial condition needs to be unique for + // each vertex. we should think about the API... + ElementIterator eIt = gridView_().template begin<0>(); + const ElementIterator &eEndIt = gridView_().template end<0>(); + for (; eIt != eEndIt; ++eIt) { + // deal with the current element + fvGeometry.update(gridView_(), *eIt); + + // loop over all element vertices, i.e. sub control volumes + for (int scvIdx = 0; scvIdx < fvGeometry.numVertices; scvIdx++) + { + // map the local vertex index to the global one + int globalIdx = vertexMapper().map(*eIt, + scvIdx, + dim); + + // let the problem do the dirty work of nailing down + // the initial solution. + PrimaryVariables initPriVars; + Valgrind::SetUndefined(initPriVars); + problem_().initial(initPriVars, + *eIt, + fvGeometry, + scvIdx); + Valgrind::CheckDefined(initPriVars); + + // add up the initial values of all sub-control + // volumes. If the initial values disagree for + // different sub control volumes, the initial value + // will be the arithmetic mean. + initPriVars *= fvGeometry.subContVol[scvIdx].volume; + boxVolume_[globalIdx] += fvGeometry.subContVol[scvIdx].volume; + uCur_[globalIdx] += initPriVars; + Valgrind::CheckDefined(uCur_[globalIdx]); + } + } + + // add up the primary variables and the volumes of the boxes + // which cross process borders + if (gridView_().comm().size() > 1) { + VertexHandleSum<Dune::FieldVector<Scalar, 1>, + Dune::BlockVector<Dune::FieldVector<Scalar, 1> >, + VertexMapper> sumVolumeHandle(boxVolume_, vertexMapper()); + gridView_().communicate(sumVolumeHandle, + Dune::InteriorBorder_InteriorBorder_Interface, + Dune::ForwardCommunication); + + VertexHandleSum<PrimaryVariables, SolutionVector, VertexMapper> + sumPVHandle(uCur_, vertexMapper()); + gridView_().communicate(sumPVHandle, + Dune::InteriorBorder_InteriorBorder_Interface, + Dune::ForwardCommunication); + } + + // divide all primary variables by the volume of their boxes + int n = gridView_().size(dim); + for (int i = 0; i < n; ++i) { + uCur_[i] /= boxVolume(i); + } + } + + /*! + * \brief Find all indices of boundary vertices. + * + * For this we need to loop over all intersections (which is slow + * in general). If the DUNE grid interface would provide a + * onBoundary() method for entities this could be done in a much + * nicer way (actually this would not be necessary) + */ + void updateBoundaryIndices_() + { + boundaryIndices_.resize(numDofs()); + std::fill(boundaryIndices_.begin(), boundaryIndices_.end(), false); + + ElementIterator eIt = gridView_().template begin<0>(); + ElementIterator eEndIt = gridView_().template end<0>(); + for (; eIt != eEndIt; ++eIt) { + Dune::GeometryType geoType = eIt->geometry().type(); + const ReferenceElement &refElement = ReferenceElements::general(geoType); + + IntersectionIterator isIt = gridView_().ibegin(*eIt); + IntersectionIterator isEndIt = gridView_().iend(*eIt); + for (; isIt != isEndIt; ++isIt) { + if (isIt->boundary()) { + // add all vertices on the intersection to the set of + // boundary vertices + int faceIdx = isIt->indexInInside(); + int numFaceVerts = refElement.size(faceIdx, 1, dim); + for (int faceVertIdx = 0; + faceVertIdx < numFaceVerts; + ++faceVertIdx) + { + int elemVertIdx = refElement.subEntity(faceIdx, + 1, + faceVertIdx, + dim); + int globalVertIdx = vertexMapper().map(*eIt, elemVertIdx, dim); + boundaryIndices_[globalVertIdx] = true; + } + } + } + } + } + + // the hint cache for the previous and the current volume + // variables + mutable std::vector<bool> hintsUsable_; + mutable std::vector<VolumeVariables> curHints_; + mutable std::vector<VolumeVariables> prevHints_; + + // the problem we want to solve. defines the constitutive + // relations, matxerial laws, etc. + Problem *problemPtr_; + + // calculates the local jacobian matrix for a given element + LocalJacobian localJacobian_; + // Linearizes the problem at the current time step using the + // local jacobian + JacobianAssembler *jacAsm_; + + // the set of all indices of vertices on the boundary + std::vector<bool> boundaryIndices_; + + // cur is the current iterative solution, prev the converged + // solution of the previous time step + SolutionVector uCur_; + SolutionVector uPrev_; + + Dune::BlockVector<Dune::FieldVector<Scalar, 1> > boxVolume_; + +private: + /*! + * \brief Returns whether messages should be printed + */ + bool verbose_() const + { return gridView_().comm().rank() == 0; } + + Implementation &asImp_() + { return *static_cast<Implementation*>(this); } + const Implementation &asImp_() const + { return *static_cast<const Implementation*>(this); } + + bool enableHints_; +}; +} + +#endif diff --git a/dumux/implicit/box/boxproblem.hh b/dumux/implicit/box/boxproblem.hh new file mode 100644 index 0000000000..c455443807 --- /dev/null +++ b/dumux/implicit/box/boxproblem.hh @@ -0,0 +1,835 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \file + * \brief Base class for all problems which use the box scheme + */ +#ifndef DUMUX_BOX_PROBLEM_HH +#define DUMUX_BOX_PROBLEM_HH + +#include "boxproperties.hh" + +#include <dumux/io/vtkmultiwriter.hh> +#include <dumux/io/restart.hh> + +namespace Dumux +{ +/*! + * \ingroup BoxModel + * \ingroup BoxBaseProblems + * \brief Base class for all problems which use the box scheme. + * + * \note All quantities are specified assuming a threedimensional + * world. Problems discretized using 2D grids are assumed to be + * extruded by \f$1 m\f$ and 1D grids are assumed to have a + * cross section of \f$1m \times 1m\f$. + */ +template<class TypeTag> +class BoxProblem +{ +private: + typedef typename GET_PROP_TYPE(TypeTag, Problem) Implementation; + typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; + + typedef Dumux::VtkMultiWriter<GridView> VtkMultiWriter; + + typedef typename GET_PROP_TYPE(TypeTag, NewtonMethod) NewtonMethod; + typedef typename GET_PROP_TYPE(TypeTag, NewtonController) NewtonController; + + typedef typename GET_PROP_TYPE(TypeTag, Model) Model; + typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; + typedef typename GET_PROP_TYPE(TypeTag, TimeManager) TimeManager; + + typedef typename GET_PROP_TYPE(TypeTag, VertexMapper) VertexMapper; + typedef typename GET_PROP_TYPE(TypeTag, ElementMapper) ElementMapper; + + typedef typename GET_PROP_TYPE(TypeTag, PrimaryVariables) PrimaryVariables; + typedef typename GET_PROP_TYPE(TypeTag, ElementVolumeVariables) ElementVolumeVariables; + typedef typename GET_PROP_TYPE(TypeTag, FVElementGeometry) FVElementGeometry; + typedef typename GET_PROP_TYPE(TypeTag, BoundaryTypes) BoundaryTypes; + + enum { + dim = GridView::dimension, + dimWorld = GridView::dimensionworld + }; + + typedef typename GridView::template Codim<0>::Entity Element; + typedef typename GridView::template Codim<dim>::Entity Vertex; + typedef typename GridView::template Codim<dim>::Iterator VertexIterator; + typedef typename GridView::Intersection Intersection; + + typedef typename GridView::Grid::ctype CoordScalar; + typedef Dune::FieldVector<CoordScalar, dimWorld> GlobalPosition; + + // copying a problem is not a good idea + BoxProblem(const BoxProblem &); + +public: + /*! + * \brief Constructor + * + * \param timeManager The TimeManager which is used by the simulation + * \param gridView The simulation's idea about physical space + */ + BoxProblem(TimeManager &timeManager, const GridView &gridView) + : gridView_(gridView) + , bboxMin_(std::numeric_limits<double>::max()) + , bboxMax_(-std::numeric_limits<double>::max()) + , elementMapper_(gridView) + , vertexMapper_(gridView) + , timeManager_(&timeManager) + , newtonMethod_(asImp_()) + , newtonCtl_(asImp_()) + { + // calculate the bounding box of the local partition of the grid view + VertexIterator vIt = gridView.template begin<dim>(); + const VertexIterator vEndIt = gridView.template end<dim>(); + for (; vIt!=vEndIt; ++vIt) { + for (int i=0; i<dim; i++) { + bboxMin_[i] = std::min(bboxMin_[i], vIt->geometry().corner(0)[i]); + bboxMax_[i] = std::max(bboxMax_[i], vIt->geometry().corner(0)[i]); + } + } + + // communicate to get the bounding box of the whole domain + if (gridView.comm().size() > 1) + for (int i = 0; i < dim; ++i) { + bboxMin_[i] = gridView.comm().min(bboxMin_[i]); + bboxMax_[i] = gridView.comm().max(bboxMax_[i]); + } + + // set a default name for the problem + simName_ = "sim"; + + resultWriter_ = NULL; + } + + ~BoxProblem() + { + delete resultWriter_; + }; + + + /*! + * \brief Called by the Dumux::TimeManager in order to + * initialize the problem. + * + * If you overload this method don't forget to call + * ParentType::init() + */ + void init() + { + // set the initial condition of the model + model().init(asImp_()); + } + + /*! + * \brief Specifies which kind of boundary condition should be + * used for which equation on a given boundary segment. + * + * \param values The boundary types for the conservation equations + * \param vertex The vertex for which the boundary type is set + */ + void boundaryTypes(BoundaryTypes &values, + const Vertex &vertex) const + { + // forward it to the method which only takes the global coordinate + asImp_().boundaryTypesAtPos(values, vertex.geometry().center()); + } + + /*! + * \brief Specifies which kind of boundary condition should be + * used for which equation on a given boundary segment. + * + * \param values The boundary types for the conservation equations + * \param pos The position of the finite volume in global coordinates + */ + void boundaryTypesAtPos(BoundaryTypes &values, + const GlobalPosition &pos) const + { + // Throw an exception (there is no reasonable default value + // for Dirichlet conditions) + DUNE_THROW(Dune::InvalidStateException, + "The problem does not provide " + "a boundaryTypes() method."); + } + + + /*! + * \brief Evaluate the boundary conditions for a dirichlet + * control volume. + * + * \param values The dirichlet values for the primary variables + * \param vertex The vertex representing the "half volume on the boundary" + * + * For this method, the \a values parameter stores primary variables. + */ + void dirichlet(PrimaryVariables &values, + const Vertex &vertex) const + { + // forward it to the method which only takes the global coordinate + asImp_().dirichletAtPos(values, vertex.geometry().center()); + } + + /*! + * \brief Evaluate the boundary conditions for a dirichlet + * control volume. + * + * \param values The dirichlet values for the primary variables + * \param pos The position of the center of the finite volume + * for which the dirichlet condition ought to be + * set in global coordinates + * + * For this method, the \a values parameter stores primary variables. + */ + void dirichletAtPos(PrimaryVariables &values, + const GlobalPosition &pos) const + { + // Throw an exception (there is no reasonable default value + // for Dirichlet conditions) + DUNE_THROW(Dune::InvalidStateException, + "The problem specifies that some boundary " + "segments are dirichlet, but does not provide " + "a dirichlet() method."); + } + + /*! + * \brief Evaluate the boundary conditions for a neumann + * boundary segment. + * + * This is the method for the case where the Neumann condition is + * potentially solution dependent and requires some box method + * specific things. + * + * \param values The neumann values for the conservation equations [kg / (m^2 *s )] + * \param element The finite element + * \param fvGeometry The finite-volume geometry in the box scheme + * \param is The intersection between element and boundary + * \param scvIdx The local vertex index + * \param boundaryFaceIdx The index of the boundary face + * \param elemVolVars All volume variables for the element + * + * For this method, the \a values parameter stores the mass flux + * in normal direction of each phase. Negative values mean influx. + */ + void boxSDNeumann(PrimaryVariables &values, + const Element &element, + const FVElementGeometry &fvGeometry, + const Intersection &is, + const int scvIdx, + const int boundaryFaceIdx, + const ElementVolumeVariables &elemVolVars) const + { + // forward it to the interface without the volume variables + asImp_().neumann(values, + element, + fvGeometry, + is, + scvIdx, + boundaryFaceIdx); + } + + /*! + * \brief Evaluate the boundary conditions for a neumann + * boundary segment. + * + * \param values The neumann values for the conservation equations [kg / (m^2 *s )] + * \param element The finite element + * \param fvGeometry The finite-volume geometry in the box scheme + * \param is The intersection between element and boundary + * \param scvIdx The local vertex index + * \param boundaryFaceIdx The index of the boundary face + * + * For this method, the \a values parameter stores the mass flux + * in normal direction of each phase. Negative values mean influx. + */ + void neumann(PrimaryVariables &values, + const Element &element, + const FVElementGeometry &fvGeometry, + const Intersection &is, + const int scvIdx, + const int boundaryFaceIdx) const + { + // forward it to the interface with only the global position + asImp_().neumannAtPos(values, fvGeometry.boundaryFace[boundaryFaceIdx].ipGlobal); + } + + /*! + * \brief Evaluate the boundary conditions for a neumann + * boundary segment. + * + * \param values The neumann values for the conservation equations [kg / (m^2 *s )] + * \param pos The position of the boundary face's integration point in global coordinates + * + * For this method, the \a values parameter stores the mass flux + * in normal direction of each phase. Negative values mean influx. + */ + void neumannAtPos(PrimaryVariables &values, + const GlobalPosition &pos) const + { + // Throw an exception (there is no reasonable default value + // for Neumann conditions) + DUNE_THROW(Dune::InvalidStateException, + "The problem specifies that some boundary " + "segments are neumann, but does not provide " + "a neumannAtPos() method."); + } + + /*! + * \brief Evaluate the source term for all phases within a given + * sub-control-volume. + * + * This is the method for the case where the source term is + * potentially solution dependent and requires some box method + * specific things. + * + * \param values The source and sink values for the conservation equations + * \param element The finite element + * \param fvGeometry The finite-volume geometry in the box scheme + * \param scvIdx The local vertex index + * \param elemVolVars All volume variables for the element + * + * For this method, the \a values parameter stores the rate mass + * generated or annihilate per volume unit. Positive values mean + * that mass is created, negative ones mean that it vanishes. + */ + void boxSDSource(PrimaryVariables &values, + const Element &element, + const FVElementGeometry &fvGeometry, + const int scvIdx, + const ElementVolumeVariables &elemVolVars) const + { + // forward to solution independent, box specific interface + asImp_().source(values, element, fvGeometry, scvIdx); + } + + /*! + * \brief Evaluate the source term for all phases within a given + * sub-control-volume. + * + * \param values The source and sink values for the conservation equations + * \param element The finite element + * \param fvGeometry The finite-volume geometry in the box scheme + * \param scvIdx The local vertex index + * + * For this method, the \a values parameter stores the rate mass + * generated or annihilate per volume unit. Positive values mean + * that mass is created, negative ones mean that it vanishes. + */ + void source(PrimaryVariables &values, + const Element &element, + const FVElementGeometry &fvGeometry, + const int scvIdx) const + { + // forward to generic interface + asImp_().sourceAtPos(values, fvGeometry.subContVol[scvIdx].global); + } + + /*! + * \brief Evaluate the source term for all phases within a given + * sub-control-volume. + * + * \param values The source and sink values for the conservation equations + * \param pos The position of the center of the finite volume + * for which the source term ought to be + * specified in global coordinates + * + * For this method, the \a values parameter stores the rate mass + * generated or annihilate per volume unit. Positive values mean + * that mass is created, negative ones mean that it vanishes. + */ + void sourceAtPos(PrimaryVariables &values, + const GlobalPosition &pos) const + { + DUNE_THROW(Dune::InvalidStateException, + "The problem does not provide " + "a sourceAtPos() method."); + } + + /*! + * \brief Evaluate the initial value for a control volume. + * + * \param values The initial values for the primary variables + * \param element The finite element + * \param fvGeometry The finite-volume geometry in the box scheme + * \param scvIdx The local vertex index + * + * For this method, the \a values parameter stores primary + * variables. + */ + void initial(PrimaryVariables &values, + const Element &element, + const FVElementGeometry &fvGeometry, + const int scvIdx) const + { + // forward to generic interface + asImp_().initialAtPos(values, fvGeometry.subContVol[scvIdx].global); + } + + /*! + * \brief Evaluate the initial value for a control volume. + * + * \param values The dirichlet values for the primary variables + * \param pos The position of the center of the finite volume + * for which the initial values ought to be + * set (in global coordinates) + * + * For this method, the \a values parameter stores primary variables. + */ + void initialAtPos(PrimaryVariables &values, + const GlobalPosition &pos) const + { + // Throw an exception (there is no reasonable default value + // for Dirichlet conditions) + DUNE_THROW(Dune::InvalidStateException, + "The problem does not provide " + "a initialAtPos() method."); + } + + /*! + * \brief Return how much the domain is extruded at a given sub-control volume. + * + * This means the factor by which a lower-dimensional (1D or 2D) + * entity needs to be expanded to get a full dimensional cell. The + * default is 1.0 which means that 1D problems are actually + * thought as pipes with a cross section of 1 m^2 and 2D problems + * are assumed to extend 1 m to the back. + */ + Scalar boxExtrusionFactor(const Element &element, + const FVElementGeometry &fvGeometry, + const int scvIdx) const + { + // forward to generic interface + return asImp_().extrusionFactorAtPos(fvGeometry.subContVol[scvIdx].global); + } + + /*! + * \brief Return how much the domain is extruded at a given position. + * + * This means the factor by which a lower-dimensional (1D or 2D) + * entity needs to be expanded to get a full dimensional cell. The + * default is 1.0 which means that 1D problems are actually + * thought as pipes with a cross section of 1 m^2 and 2D problems + * are assumed to extend 1 m to the back. + */ + Scalar extrusionFactorAtPos(const GlobalPosition &pos) const + { return 1.0; } + + /*! + * \brief If model coupling is used, this updates the parameters + * required to calculate the coupling fluxes between the + * sub-models. + * + * By default it does nothing + * + * \param element The DUNE Codim<0> entity for which the coupling + * parameters should be computed. + */ + void updateCouplingParams(const Element &element) const + {} + + /*! + * \name Simulation steering + */ + // \{ + + /*! + * \brief Called by the time manager before the time integration. + */ + void preTimeStep() + {} + + /*! + * \brief Called by Dumux::TimeManager in order to do a time + * integration on the model. + */ + void timeIntegration() + { + const int maxFails = 10; + for (int i = 0; i < maxFails; ++i) { + if (model_.update(newtonMethod_, newtonCtl_)) + return; + + Scalar dt = timeManager().timeStepSize(); + Scalar nextDt = dt / 2; + timeManager().setTimeStepSize(nextDt); + + // update failed + std::cout << "Newton solver did not converge with dt="<<dt<<" seconds. Retrying with time step of " + << nextDt << " seconds\n"; + } + + DUNE_THROW(Dune::MathError, + "Newton solver didn't converge after " + << maxFails + << " time-step divisions. dt=" + << timeManager().timeStepSize()); + } + + /*! + * \brief Returns the newton method object + */ + NewtonMethod &newtonMethod() + { return newtonMethod_; } + + /*! + * \copydoc newtonMethod() + */ + const NewtonMethod &newtonMethod() const + { return newtonMethod_; } + + /*! + * \brief Returns the newton contoller object + */ + NewtonController &newtonController() + { return newtonCtl_; } + + /*! + * \copydoc newtonController() + */ + const NewtonController &newtonController() const + { return newtonCtl_; } + + /*! + * \brief Called by Dumux::TimeManager whenever a solution for a + * time step has been computed and the simulation time has + * been updated. + * + * \param dt The current time-step size + */ + Scalar nextTimeStepSize(const Scalar dt) + { + return std::min(GET_PARAM_FROM_GROUP(TypeTag, Scalar, TimeManager, MaxTimeStepSize), + newtonCtl_.suggestTimeStepSize(dt)); + }; + + /*! + * \brief Returns true if a restart file should be written to + * disk. + * + * The default behavior is to write one restart file every 5 time + * steps. This file is intended to be overwritten by the + * implementation. + */ + bool shouldWriteRestartFile() const + { + return timeManager().timeStepIndex() > 0 && + (timeManager().timeStepIndex() % 10 == 0); + } + + /*! + * \brief Returns true if the current solution should be written to + * disk (i.e. as a VTK file) + * + * The default behavior is to write out every the solution for + * very time step. This file is intended to be overwritten by the + * implementation. + */ + bool shouldWriteOutput() const + { return true; } + + /*! + * \brief Called by the time manager after the time integration to + * do some post processing on the solution. + */ + void postTimeStep() + { } + + /*! + * \brief Called by the time manager after everything which can be + * done about the current time step is finished and the + * model should be prepared to do the next time integration. + */ + void advanceTimeLevel() + { + model_.advanceTimeLevel(); + } + + /*! + * \brief Called when the end of an simulation episode is reached. + * + * Typically a new episode should be started in this method. + */ + void episodeEnd() + { + std::cerr << "The end of an episode is reached, but the problem " + << "does not override the episodeEnd() method. " + << "Doing nothing!\n"; + }; + // \} + + /*! + * \brief The problem name. + * + * This is used as a prefix for files generated by the simulation. + * It could be either overwritten by the problem files, or simply + * declared over the setName() function in the application file. + */ + const char *name() const + { + return simName_.c_str(); + } + + /*! + * \brief Set the problem name. + * + * This static method sets the simulation name, which should be + * called before the application problem is declared! If not, the + * default name "sim" will be used. + * + * \param newName The problem's name + */ + void setName(const char *newName) + { + simName_ = newName; + } + + + /*! + * \brief Returns the number of the current VTK file. + */ + int currentVTKFileNumber() + { + createResultWriter_(); + return resultWriter_->curWriterNum(); + } + + /*! + * \brief The GridView which used by the problem. + */ + const GridView &gridView() const + { return gridView_; } + + /*! + * \brief The coordinate of the corner of the GridView's bounding + * box with the smallest values. + */ + const GlobalPosition &bboxMin() const + { return bboxMin_; } + + /*! + * \brief The coordinate of the corner of the GridView's bounding + * box with the largest values. + */ + const GlobalPosition &bboxMax() const + { return bboxMax_; } + + /*! + * \brief Returns the mapper for vertices to indices. + */ + const VertexMapper &vertexMapper() const + { return vertexMapper_; } + + /*! + * \brief Returns the mapper for elements to indices. + */ + const ElementMapper &elementMapper() const + { return elementMapper_; } + + /*! + * \brief Returns TimeManager object used by the simulation + */ + TimeManager &timeManager() + { return *timeManager_; } + + /*! + * \copydoc timeManager() + */ + const TimeManager &timeManager() const + { return *timeManager_; } + + /*! + * \brief Returns numerical model used for the problem. + */ + Model &model() + { return model_; } + + /*! + * \copydoc model() + */ + const Model &model() const + { return model_; } + // \} + + /*! + * \name Restart mechanism + */ + // \{ + + /*! + * \brief This method writes the complete state of the simulation + * to the harddisk. + * + * The file will start with the prefix returned by the name() + * method, has the current time of the simulation clock in it's + * name and uses the extension <tt>.drs</tt>. (Dumux ReStart + * file.) See Dumux::Restart for details. + */ + void serialize() + { + typedef Dumux::Restart Restarter; + Restarter res; + res.serializeBegin(asImp_()); + if (gridView().comm().rank() == 0) + std::cout << "Serialize to file '" << res.fileName() << "'\n"; + + timeManager().serialize(res); + asImp_().serialize(res); + res.serializeEnd(); + } + + /*! + * \brief This method writes the complete state of the problem + * to the harddisk. + * + * The file will start with the prefix returned by the name() + * method, has the current time of the simulation clock in it's + * name and uses the extension <tt>.drs</tt>. (Dumux ReStart + * file.) See Dumux::Restart for details. + * + * \tparam Restarter The serializer type + * + * \param res The serializer object + */ + template <class Restarter> + void serialize(Restarter &res) + { + createResultWriter_(); + resultWriter_->serialize(res); + model().serialize(res); + } + + /*! + * \brief Load a previously saved state of the whole simulation + * from disk. + * + * \param tRestart The simulation time on which the program was + * written to disk. + */ + void restart(const Scalar tRestart) + { + typedef Dumux::Restart Restarter; + + Restarter res; + + res.deserializeBegin(asImp_(), tRestart); + if (gridView().comm().rank() == 0) + std::cout << "Deserialize from file '" << res.fileName() << "'\n"; + timeManager().deserialize(res); + asImp_().deserialize(res); + res.deserializeEnd(); + } + + /*! + * \brief This method restores the complete state of the problem + * from disk. + * + * It is the inverse of the serialize() method. + * + * \tparam Restarter The deserializer type + * + * \param res The deserializer object + */ + template <class Restarter> + void deserialize(Restarter &res) + { + createResultWriter_(); + resultWriter_->deserialize(res); + model().deserialize(res); + } + + // \} + + /*! + * \brief Adds additional VTK output data to the VTKWriter. Function is called by writeOutput(). + */ + void addOutputVtkFields() + {} + + /*! + * \brief Write the relevant secondary variables of the current + * solution into an VTK output file. + */ + void writeOutput(const bool verbose = true) + { + // write the current result to disk + if (asImp_().shouldWriteOutput()) { + if (verbose && gridView().comm().rank() == 0) + std::cout << "Writing result file for \"" << asImp_().name() << "\"\n"; + + // calculate the time _after_ the time was updated + Scalar t = timeManager().time() + timeManager().timeStepSize(); + createResultWriter_(); + resultWriter_->beginWrite(t); + model().addOutputVtkFields(model().curSol(), *resultWriter_); + asImp_().addOutputVtkFields(); + resultWriter_->endWrite(); + } + } + +protected: + //! Returns the implementation of the problem (i.e. static polymorphism) + Implementation &asImp_() + { return *static_cast<Implementation *>(this); } + + //! \copydoc asImp_() + const Implementation &asImp_() const + { return *static_cast<const Implementation *>(this); } + + //! Returns the applied VTK-writer for the output + VtkMultiWriter& resultWriter() + { + createResultWriter_(); + return *resultWriter_; + } + //! \copydoc Dumux::IMPETProblem::resultWriter() + VtkMultiWriter& resultWriter() const + { + createResultWriter_(); + return *resultWriter_; + } + + +private: + // makes sure that the result writer exists + void createResultWriter_() + { if (!resultWriter_) resultWriter_ = new VtkMultiWriter(gridView_, asImp_().name()); }; + + std::string simName_; + const GridView gridView_; + + GlobalPosition bboxMin_; + GlobalPosition bboxMax_; + + ElementMapper elementMapper_; + VertexMapper vertexMapper_; + + TimeManager *timeManager_; + + Model model_; + + NewtonMethod newtonMethod_; + NewtonController newtonCtl_; + + VtkMultiWriter *resultWriter_; +}; + +} + +#endif diff --git a/dumux/implicit/box/boxproperties.hh b/dumux/implicit/box/boxproperties.hh new file mode 100644 index 0000000000..9fd7fffa05 --- /dev/null +++ b/dumux/implicit/box/boxproperties.hh @@ -0,0 +1,142 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +#ifndef DUMUX_BOX_PROPERTIES_HH +#define DUMUX_BOX_PROPERTIES_HH + +#include <dumux/common/propertysystem.hh> + +#include <dumux/common/basicproperties.hh> +#include <dumux/linear/linearsolverproperties.hh> +#include <dumux/nonlinear/newtonmethod.hh> + +/*! + * \ingroup Properties + * \ingroup BoxProperties + * \ingroup BoxModel + * \file + * \brief Specify the shape functions, operator assemblers, etc + * used for the BoxModel. + */ +namespace Dumux +{ + +namespace Properties +{ +/*! + * \ingroup BoxModel + */ +// \{ + +////////////////////////////////////////////////////////////////// +// Type tags +////////////////////////////////////////////////////////////////// + +//! The type tag for models based on the box-scheme +NEW_TYPE_TAG(BoxModel, INHERITS_FROM(NewtonMethod, LinearSolverTypeTag, ImplicitModel)); + +////////////////////////////////////////////////////////////////// +// Property tags +////////////////////////////////////////////////////////////////// + +NEW_PROP_TAG(Grid); //!< The type of the DUNE grid +NEW_PROP_TAG(GridView); //!< The type of the grid view + +NEW_PROP_TAG(FVElementGeometry); //! The type of the finite-volume geometry in the box scheme +NEW_PROP_TAG(EvalGradientsAtSCVCenter); //! Evaluate shape function gradients additionally at the sub-control volume center + +NEW_PROP_TAG(Problem); //!< The type of the problem +NEW_PROP_TAG(BaseModel); //!< The type of the base class of the model +NEW_PROP_TAG(Model); //!< The type of the model +NEW_PROP_TAG(NumEq); //!< Number of equations in the system of PDEs +NEW_PROP_TAG(BaseLocalResidual); //!< The type of the base class of the local residual +NEW_PROP_TAG(LocalResidual); //!< The type of the local residual function +NEW_PROP_TAG(LocalJacobian); //!< The type of the local jacobian operator + +NEW_PROP_TAG(JacobianAssembler); //!< Assembles the global jacobian matrix +NEW_PROP_TAG(JacobianMatrix); //!< Type of the global jacobian matrix +NEW_PROP_TAG(BoundaryTypes); //!< Stores the boundary types of a single degree of freedom +NEW_PROP_TAG(ElementBoundaryTypes); //!< Stores the boundary types on an element + +NEW_PROP_TAG(PrimaryVariables); //!< A vector of primary variables within a sub-control volume +NEW_PROP_TAG(SolutionVector); //!< Vector containing all primary variable vector of the grid +NEW_PROP_TAG(ElementSolutionVector); //!< A vector of primary variables within a sub-control volume + +NEW_PROP_TAG(VolumeVariables); //!< The secondary variables within a sub-control volume +NEW_PROP_TAG(ElementVolumeVariables); //!< The secondary variables of all sub-control volumes in an element +NEW_PROP_TAG(FluxVariables); //!< Data required to calculate a flux over a face +NEW_PROP_TAG(BoundaryVariables); //!< Data required to calculate fluxes over boundary faces (outflow) + +// high level simulation control +NEW_PROP_TAG(TimeManager); //!< Manages the simulation time +NEW_PROP_TAG(NewtonMethod); //!< The type of the newton method +NEW_PROP_TAG(NewtonController); //!< The type of the newton controller + +//! Specify whether the jacobian matrix of the last iteration of a +//! time step should be re-used as the jacobian of the first iteration +//! of the next time step. +NEW_PROP_TAG(ImplicitEnableJacobianRecycling); + +//! Specify whether the jacobian matrix should be only reassembled for +//! elements where at least one vertex is above the specified +//! tolerance +NEW_PROP_TAG(ImplicitEnablePartialReassemble); +/*! + * \brief Specify the maximum size of a time integration [s]. + * + * The default is to not limit the step size. + */ +NEW_PROP_TAG(TimeManagerMaxTimeStepSize); + +/*! + * \brief Specify which kind of method should be used to numerically + * calculate the partial derivatives of the residual. + * + * -1 means backward differences, 0 means central differences, 1 means + * forward differences. By default we use central differences. + */ +NEW_PROP_TAG(ImplicitNumericDifferenceMethod); + +/*! + * \brief Specify whether to use the already calculated solutions as + * starting values of the volume variables. + * + * This only makes sense if the calculation of the volume variables is + * very expensive (e.g. for non-linear fugacity functions where the + * solver converges faster). + */ +NEW_PROP_TAG(ImplicitEnableHints); + +//! indicates whether two-point flux should be used +NEW_PROP_TAG(ImplicitUseTwoPointFlux); + +// mappers from local to global indices + +//! maper for vertices +NEW_PROP_TAG(VertexMapper); +//! maper for elements +NEW_PROP_TAG(ElementMapper); +//! maper for degrees of freedom +NEW_PROP_TAG(DofMapper); + +} +} + +// \} + +#endif diff --git a/dumux/implicit/box/boxpropertydefaults.hh b/dumux/implicit/box/boxpropertydefaults.hh new file mode 100644 index 0000000000..50ec31e2c7 --- /dev/null +++ b/dumux/implicit/box/boxpropertydefaults.hh @@ -0,0 +1,207 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \ingroup Properties + * \ingroup BoxProperties + * \ingroup BoxModel + * \file + * + * \brief Default properties for box models + */ +#ifndef DUMUX_BOX_PROPERTY_DEFAULTS_HH +#define DUMUX_BOX_PROPERTY_DEFAULTS_HH + +#include <dumux/nonlinear/newtonmethod.hh> +#include <dumux/nonlinear/newtoncontroller.hh> + +#include "boxassembler.hh" +#include "boxmodel.hh" +#include "boxfvelementgeometry.hh" +#include "boxelementboundarytypes.hh" +#include "boxlocaljacobian.hh" +#include "boxlocalresidual.hh" +#include "boxelementvolumevariables.hh" +#include "boxvolumevariables.hh" + +#include <dumux/common/boundarytypes.hh> +#include <dumux/common/timemanager.hh> + +#include "boxproperties.hh" + +#include <limits> + +namespace Dumux { + +// forward declaration +template<class TypeTag> +class BoxModel; + +namespace Properties { +////////////////////////////////////////////////////////////////// +// Some defaults for very fundamental properties +////////////////////////////////////////////////////////////////// + +//! Set the default type for the time manager +SET_TYPE_PROP(BoxModel, TimeManager, Dumux::TimeManager<TypeTag>); + +////////////////////////////////////////////////////////////////// +// Properties +////////////////////////////////////////////////////////////////// + +//! Use the leaf grid view if not defined otherwise +SET_TYPE_PROP(BoxModel, + GridView, + typename GET_PROP_TYPE(TypeTag, Grid)::LeafGridView); + +//! Set the default for the FVElementGeometry +SET_TYPE_PROP(BoxModel, FVElementGeometry, Dumux::BoxFVElementGeometry<TypeTag>); + +//! Disable evaluation of shape function gradients at the sub-control volume center by default +// The shape function gradients at the sub-control volume center are currently only +// needed for the stokes and the linear elastic models +SET_BOOL_PROP(BoxModel, EvalGradientsAtSCVCenter, false); + +//! Set the default for the ElementBoundaryTypes +SET_TYPE_PROP(BoxModel, ElementBoundaryTypes, Dumux::BoxElementBoundaryTypes<TypeTag>); + +//! use the plain newton method for the box scheme by default +SET_TYPE_PROP(BoxModel, NewtonMethod, Dumux::NewtonMethod<TypeTag>); + +//! use the plain newton controller for the box scheme by default +SET_TYPE_PROP(BoxModel, NewtonController, Dumux::NewtonController<TypeTag>); + +//! Mapper for the grid view's vertices. +SET_TYPE_PROP(BoxModel, + VertexMapper, + Dune::MultipleCodimMultipleGeomTypeMapper<typename GET_PROP_TYPE(TypeTag, GridView), + Dune::MCMGVertexLayout>); + +//! Mapper for the grid view's elements. +SET_TYPE_PROP(BoxModel, + ElementMapper, + Dune::MultipleCodimMultipleGeomTypeMapper<typename GET_PROP_TYPE(TypeTag, GridView), + Dune::MCMGElementLayout>); + +//! Mapper for the degrees of freedoms. +SET_TYPE_PROP(BoxModel, DofMapper, typename GET_PROP_TYPE(TypeTag, VertexMapper)); + +//! Set the BaseLocalResidual to BoxLocalResidual +SET_TYPE_PROP(BoxModel, BaseLocalResidual, Dumux::BoxLocalResidual<TypeTag>); + +//! Set the BaseModel to BoxModel +SET_TYPE_PROP(BoxModel, BaseModel, Dumux::BoxModel<TypeTag>); + +//! The local jacobian operator for the box scheme +SET_TYPE_PROP(BoxModel, LocalJacobian, Dumux::BoxLocalJacobian<TypeTag>); + +/*! + * \brief The type of a solution for the whole grid at a fixed time. + */ +SET_TYPE_PROP(BoxModel, + SolutionVector, + Dune::BlockVector<typename GET_PROP_TYPE(TypeTag, PrimaryVariables)>); + +/*! + * \brief The type of a solution for a whole element. + */ +SET_TYPE_PROP(BoxModel, + ElementSolutionVector, + Dune::BlockVector<typename GET_PROP_TYPE(TypeTag, PrimaryVariables)>); + +/*! + * \brief A vector of primary variables. + */ +SET_TYPE_PROP(BoxModel, + PrimaryVariables, + Dune::FieldVector<typename GET_PROP_TYPE(TypeTag, Scalar), + GET_PROP_VALUE(TypeTag, NumEq)>); + +/*! + * \brief The volume variable class. + * + * This should almost certainly be overloaded by the model... + */ +SET_TYPE_PROP(BoxModel, VolumeVariables, Dumux::BoxVolumeVariables<TypeTag>); + +/*! + * \brief An array of secondary variable containers. + */ +SET_TYPE_PROP(BoxModel, ElementVolumeVariables, Dumux::BoxElementVolumeVariables<TypeTag>); + +/*! + * \brief Boundary types at a single degree of freedom. + */ +SET_TYPE_PROP(BoxModel, + BoundaryTypes, + Dumux::BoundaryTypes<GET_PROP_VALUE(TypeTag, NumEq)>); + +/*! + * \brief Assembler for the global jacobian matrix. + */ +SET_TYPE_PROP(BoxModel, JacobianAssembler, Dumux::BoxAssembler<TypeTag>); + +//! use an unlimited time step size by default +SET_SCALAR_PROP(BoxModel, TimeManagerMaxTimeStepSize, 1e100); + +//! use forward differences to calculate the jacobian by default +SET_INT_PROP(BoxModel, ImplicitNumericDifferenceMethod, +1); + +//! do not use hints by default +SET_BOOL_PROP(BoxModel, ImplicitEnableHints, false); + +// disable jacobian matrix recycling by default +SET_BOOL_PROP(BoxModel, ImplicitEnableJacobianRecycling, false); + +// disable partial reassembling by default +SET_BOOL_PROP(BoxModel, ImplicitEnablePartialReassemble, false); + +// disable two-point-flux by default +SET_BOOL_PROP(BoxModel, ImplicitUseTwoPointFlux, false); + +//! Set the type of a global jacobian matrix from the solution types +SET_PROP(BoxModel, JacobianMatrix) +{ +private: + typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; + enum { numEq = GET_PROP_VALUE(TypeTag, NumEq) }; + typedef typename Dune::FieldMatrix<Scalar, numEq, numEq> MatrixBlock; +public: + typedef typename Dune::BCRSMatrix<MatrixBlock> type; +}; + +// use the stabilized BiCG solver preconditioned by the ILU-0 by default +SET_TYPE_PROP(BoxModel, LinearSolver, Dumux::BoxBiCGStabILU0Solver<TypeTag> ); + +// if the deflection of the newton method is large, we do not +// need to solve the linear approximation accurately. Assuming +// that the initial value for the delta vector u is quite +// close to the final value, a reduction of 6 orders of +// magnitude in the defect should be sufficient... +SET_SCALAR_PROP(BoxModel, LinearSolverResidualReduction, 1e-6); + +//! set the default number of maximum iterations for the linear solver +SET_INT_PROP(BoxModel, LinearSolverMaxIterations, 250); + +//! set number of equations of the mathematical model as default +SET_INT_PROP(BoxModel, LinearSolverBlockSize, GET_PROP_VALUE(TypeTag, NumEq)); + +} // namespace Properties +} // namespace Dumux + +#endif diff --git a/dumux/implicit/box/boxvolumevariables.hh b/dumux/implicit/box/boxvolumevariables.hh new file mode 100644 index 0000000000..0c0bb6d8f6 --- /dev/null +++ b/dumux/implicit/box/boxvolumevariables.hh @@ -0,0 +1,190 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \file + * + * \brief Base class for the model specific class which provides + * access to all volume averaged quantities. + */ +#ifndef DUMUX_BOX_VOLUME_VARIABLES_HH +#define DUMUX_BOX_VOLUME_VARIABLES_HH + +#include "boxproperties.hh" + +#include <dumux/common/valgrind.hh> + +namespace Dumux +{ + +/*! + * \ingroup BoxModel + * \ingroup BoxVolumeVariables + * \brief Base class for the model specific class which provides + * access to all volume averaged quantities. + */ +template <class TypeTag> +class BoxVolumeVariables +{ + typedef typename GET_PROP_TYPE(TypeTag, VolumeVariables) Implementation; + + typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; + typedef typename GET_PROP_TYPE(TypeTag, Problem) Problem; + typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; + typedef typename GridView::template Codim<0>::Entity Element; + typedef typename GET_PROP_TYPE(TypeTag, FVElementGeometry) FVElementGeometry; + + typedef typename GET_PROP_TYPE(TypeTag, PrimaryVariables) PrimaryVariables; + +public: + // default constructor + BoxVolumeVariables() + { evalPoint_ = 0; }; + + // copy constructor + BoxVolumeVariables(const BoxVolumeVariables &v) + { + evalPoint_ = 0; + priVars_ = v.priVars_; + extrusionFactor_ = v.extrusionFactor_; + }; + + /*! + * \brief Assignment operator + */ + BoxVolumeVariables &operator=(const BoxVolumeVariables &v) + { + evalPoint_ = 0; + priVars_ = v.priVars_; + extrusionFactor_ = v.extrusionFactor_; + + return *this; + }; + + /*! + * \brief Sets the evaluation point used by the local jacobian. + * + * The evaluation point is only used by semi-smooth models. + */ + void setEvalPoint(const Implementation *ep) + { + evalPoint_ = ep; + Valgrind::CheckDefined(evalPoint_); + } + + /*! + * \brief Returns the evaluation point used by the local jacobian. + * + * The evaluation point is only used by semi-smooth models. + */ + const Implementation &evalPoint() const + { return (evalPoint_ == 0)?asImp_():*evalPoint_; } + + /*! + * \brief Set the volume variables which should be used as initial + * conditions for complex calculations. + */ + void setHint(const Implementation *hint) + {}; + + /*! + * \brief Update all quantities for a given control volume + * + * \param priVars A vector containing the primary variables for the control volume + * \param problem The object specifying the problem which ought to + * be simulated + * \param element An element which contains part of the control volume + * \param fvGeometry The finite volume geometry for the element + * \param scvIdx Local index of the sub control volume which is inside the element + * \param isOldSol Specifies whether this is the previous solution or the current one + * + * \todo Eliminate the 'isOldSol' parameter. This implies that the + * 'pseudo-primary variables' must be somehow be stored + * inside the PrimaryVariables. (e.g. we need to know the + * phase state in the 2p2c model) + */ + void update(const PrimaryVariables &priVars, + const Problem &problem, + const Element &element, + const FVElementGeometry &fvGeometry, + const int scvIdx, + const bool isOldSol) + { + Valgrind::CheckDefined(priVars); + priVars_ = priVars; + extrusionFactor_ = problem.boxExtrusionFactor(element, fvGeometry, scvIdx); + } + + /*! + * \brief Return the vector of primary variables + */ + const PrimaryVariables &priVars() const + { return priVars_; } + + /*! + * \brief Return a component of primary variable vector + * + * \param pvIdx The index of the primary variable of interest + */ + Scalar priVar(const int pvIdx) const + { + return priVars_[pvIdx]; + } + + /*! + * \brief Return how much the sub-control volume is extruded. + * + * This means the factor by which a lower-dimensional (1D or 2D) + * entity needs to be expanded to get a full dimensional cell. The + * default is 1.0 which means that 1D problems are actually + * thought as pipes with a cross section of 1 m^2 and 2D problems + * are assumed to extend 1 m to the back. + */ + Scalar extrusionFactor() const + { return extrusionFactor_; } + + /*! + * \brief If running in valgrind this makes sure that all + * quantities in the volume variables are defined. + */ + void checkDefined() const + { +#if !defined NDEBUG && HAVE_VALGRIND + Valgrind::CheckDefined(priVars_); + Valgrind::CheckDefined(evalPoint_); + if (evalPoint_ && evalPoint_ != this) + evalPoint_->checkDefined(); +#endif + }; + +protected: + const Implementation &asImp_() const + { return *static_cast<const Implementation*>(this); } + Implementation &asImp_() + { return *static_cast<Implementation*>(this); } + + // the evaluation point of the local jacobian + const Implementation *evalPoint_; + + PrimaryVariables priVars_; + Scalar extrusionFactor_; +}; + +} // end namepace + +#endif diff --git a/dumux/implicit/box/intersectiontovertexbc.hh b/dumux/implicit/box/intersectiontovertexbc.hh new file mode 100644 index 0000000000..f57fddb434 --- /dev/null +++ b/dumux/implicit/box/intersectiontovertexbc.hh @@ -0,0 +1,112 @@ +/***************************************************************************** + * Copyright (C) 2010 by Bernd Flemisch * + * Institute for Modelling Hydraulic and Environmental Systems * + * University of Stuttgart, Germany * + * email: <givenname>.<name>@iws.uni-stuttgart.de * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \file + * \brief Base class for all problems which use the box scheme + */ +#ifndef DUMUX_INTERSECTIONTOVERTEXBC_HH +#define DUMUX_INTERSECTIONTOVERTEXBC_HH + +#include <dumux/boxmodels/common/boxproperties.hh> + +namespace Dumux +{ + +template<typename TypeTag> +class IntersectionToVertexBC +{ + typedef typename GET_PROP_TYPE(TypeTag, BoundaryTypes) BoundaryTypes; + typedef typename GET_PROP_TYPE(TypeTag, Problem) Problem; + typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; + typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; + + enum { + numEq = GET_PROP_VALUE(TypeTag, NumEq), + dim = GridView::dimension, + }; + + typedef typename GridView::template Codim<0>::Iterator ElementIterator; + typedef typename GridView::IntersectionIterator IntersectionIterator; + typedef typename GridView::template Codim<dim>::Entity Vertex; + + typedef typename Dune::GenericReferenceElements<Scalar, dim> ReferenceElements; + typedef typename Dune::GenericReferenceElement<Scalar, dim> ReferenceElement; + +public: + IntersectionToVertexBC(const Problem& problem) + : problem_(problem) + { + vertexBC.resize(problem.vertexMapper().size()); + for (int vertIdx = 0; vertIdx < vertexBC.size(); vertIdx++) + vertexBC[vertIdx].setAllNeumann(); + + ElementIterator eIt = problem.gridView().template begin<0>(); + const ElementIterator elemEndIt = problem.gridView().template end<0>(); + for (; eIt != elemEndIt; ++eIt) { + Dune::GeometryType geoType = eIt->geometry().type(); + const ReferenceElement &refElem = ReferenceElements::general(geoType); + + IntersectionIterator isIt = problem.gridView().ibegin(*eIt); + IntersectionIterator isEndIt = problem.gridView().iend(*eIt); + for (; isIt != isEndIt; ++isIt) { + if (!isIt->boundary()) + continue; + + BoundaryTypes bcTypes; + problem.boundaryTypes(bcTypes, *isIt); + + if (!bcTypes.hasDirichlet()) + continue; + + int faceIdx = isIt->indexInInside(); + int numFaceVerts = refElem.size(faceIdx, 1, dim); + for (int faceVertIdx = 0; faceVertIdx < numFaceVerts; ++faceVertIdx) + { + int elemVertIdx = refElem.subEntity(faceIdx, 1, faceVertIdx, dim); + int globalVertIdx = problem.vertexMapper().map(*eIt, elemVertIdx, dim); + + for (int eqIdx = 0; eqIdx < numEq; eqIdx++) + if (bcTypes.isDirichlet(eqIdx)) + vertexBC[globalVertIdx].setDirichlet(eqIdx); + } + } + } + } + + void boundaryTypes(BoundaryTypes& values, const Vertex& vertex) const + { + values.setAllNeumann(); + + int vertIdx = problem_.vertexMapper().map(vertex); + const BoundaryTypes& bcTypes = vertexBC[vertIdx]; + + for (int eqIdx = 0; eqIdx < numEq; eqIdx++) + if (bcTypes.isDirichlet(eqIdx)) + values.setDirichlet(eqIdx); + } + +private: + const Problem& problem_; + std::vector<BoundaryTypes> vertexBC; +}; +} + +#endif + diff --git a/dumux/implicit/box/porousmediaboxproblem.hh b/dumux/implicit/box/porousmediaboxproblem.hh new file mode 100644 index 0000000000..151f953703 --- /dev/null +++ b/dumux/implicit/box/porousmediaboxproblem.hh @@ -0,0 +1,197 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * 2012 by Bernd Flemisch * + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \file + * \brief Base class for all problems which use the two-phase box model + */ +#ifndef DUMUX_POROUS_MEDIA_BOX_PROBLEM_HH +#define DUMUX_POROUS_MEDIA_BOX_PROBLEM_HH + +#include "boxproperties.hh" + +#include <dumux/boxmodels/common/boxproblem.hh> + +namespace Dumux +{ +namespace Properties +{ +NEW_PROP_TAG(SpatialParams); //!< The type of the spatial parameters object +NEW_PROP_TAG(ProblemEnableGravity); //!< Returns whether gravity is considered in the problem +} + +/*! + * \ingroup BoxBaseProblems + * \brief Base class for all porous media box problems + */ +template<class TypeTag> +class PorousMediaBoxProblem : public BoxProblem<TypeTag> +{ + typedef BoxProblem<TypeTag> ParentType; + + typedef typename GET_PROP_TYPE(TypeTag, Problem) Implementation; + typedef typename GET_PROP_TYPE(TypeTag, TimeManager) TimeManager; + typedef typename GET_PROP_TYPE(TypeTag, FVElementGeometry) FVElementGeometry; + typedef typename GET_PROP_TYPE(TypeTag, SpatialParams) SpatialParams; + + typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; + typedef typename GridView::template Codim<0>::Entity Element; + enum { + dim = GridView::dimension, + dimWorld = GridView::dimensionworld + }; + + typedef typename GridView::ctype CoordScalar; + typedef Dune::FieldVector<CoordScalar, dimWorld> GlobalPosition; + typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; + typedef Dune::FieldVector<Scalar, dim> DimVector; + +public: + /*! + * \brief The constructor + * + * \param timeManager The time manager + * \param gridView The grid view + * \param verbose Turn verbosity on or off + */ + PorousMediaBoxProblem(TimeManager &timeManager, + const GridView &gridView, + const bool verbose = true) + : ParentType(timeManager, gridView), + gravity_(0) + { + newSpatialParams_ = true; + spatialParams_ = new SpatialParams(gridView); + + if (GET_PARAM_FROM_GROUP(TypeTag, bool, Problem, EnableGravity)) + gravity_[dim-1] = -9.81; + } + + ~PorousMediaBoxProblem() + { + if (newSpatialParams_) + delete spatialParams_; + } + + /*! + * \name Problem parameters + */ + // \{ + + /*! + * \brief Returns the temperature \f$\mathrm{[K]}\f$ within a control volume. + * + * This is the discretization specific interface for the box + * method. By default it just calls temperature(pos). + * + * \param element The DUNE Codim<0> enitiy which intersects with + * the finite volume. + * \param fvGeometry The finite volume geometry of the element. + * \param scvIdx The local index of the sub control volume inside the element + */ + Scalar boxTemperature(const Element &element, + const FVElementGeometry fvGeometry, + const int scvIdx) const + { return asImp_().temperatureAtPos(fvGeometry.subContVol[scvIdx].global); } + + /*! + * \brief Returns the temperature \f$\mathrm{[K]}\f$ at a given global position. + * + * This is not specific to the discretization. By default it just + * calls temperature(). + * + * \param pos The position in global coordinates where the temperature should be specified. + */ + Scalar temperatureAtPos(const GlobalPosition &pos) const + { return asImp_().temperature(); } + + /*! + * \brief Returns the temperature \f$\mathrm{[K]}\f$ for an isothermal problem. + * + * This is not specific to the discretization. By default it just + * throws an exception so it must be overloaded by the problem if + * no energy equation is used. + */ + Scalar temperature() const + { DUNE_THROW(Dune::NotImplemented, "temperature() method not implemented by the actual problem"); }; + + /*! + * \brief Returns the acceleration due to gravity \f$\mathrm{[m/s^2]}\f$. + * + * This is the box discretization specific interface. By default + * it just calls gravityAtPos(). + */ + const DimVector &boxGravity(const Element &element, + const FVElementGeometry &fvGeometry, + const int scvIdx) const + { return asImp_().gravityAtPos(fvGeometry.subContVol[scvIdx].global); } + + /*! + * \brief Returns the acceleration due to gravity \f$\mathrm{[m/s^2]}\f$. + * + * This is discretization independent interface. By default it + * just calls gravity(). + */ + const DimVector &gravityAtPos(const GlobalPosition &pos) const + { return asImp_().gravity(); } + + /*! + * \brief Returns the acceleration due to gravity \f$\mathrm{[m/s^2]}\f$. + * + * This method is used for problems where the gravitational + * acceleration does not depend on the spatial position. The + * default behaviour is that if the <tt>EnableGravity</tt> + * property is true, \f$\boldsymbol{g} = ( 0,\dots,\ -9.81)^T \f$ holds, + * else \f$\boldsymbol{g} = ( 0,\dots, 0)^T \f$. + */ + const DimVector &gravity() const + { return gravity_; } + + /*! + * \brief Returns the spatial parameters object. + */ + SpatialParams &spatialParams() + { return *spatialParams_; } + + /*! + * \brief Returns the spatial parameters object. + */ + const SpatialParams &spatialParams() const + { return *spatialParams_; } + + // \} + +protected: + //! Returns the implementation of the problem (i.e. static polymorphism) + Implementation &asImp_() + { return *static_cast<Implementation *>(this); } + //! \copydoc asImp_() + const Implementation &asImp_() const + { return *static_cast<const Implementation *>(this); } + + DimVector gravity_; + + // fluids and material properties + SpatialParams* spatialParams_; + bool newSpatialParams_; +}; + +} + +#endif diff --git a/dumux/implicit/cellcentered/Makefile.am b/dumux/implicit/cellcentered/Makefile.am new file mode 100644 index 0000000000..79882bb6f4 --- /dev/null +++ b/dumux/implicit/cellcentered/Makefile.am @@ -0,0 +1,4 @@ +commondir = $(includedir)/dumux/ccmodels/common +common_HEADERS = *.hh + +include $(top_srcdir)/am/global-rules diff --git a/dumux/implicit/cellcentered/ccassembler.hh b/dumux/implicit/cellcentered/ccassembler.hh new file mode 100644 index 0000000000..b4a761a169 --- /dev/null +++ b/dumux/implicit/cellcentered/ccassembler.hh @@ -0,0 +1,683 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * Copyright (C) 2010-2011 by Andreas Lauser * + * Copyright (C) 2009-2010 by Bernd Flemisch * + * Institute for Modelling Hydraulic and Environmental Systems * + * University of Stuttgart, Germany * + * email: <givenname>.<name>@iws.uni-stuttgart.de * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \file + * + * \brief An assembler for the global Jacobian matrix for models using the box discretization. + */ +#ifndef DUMUX_CC_ASSEMBLER_HH +#define DUMUX_CC_ASSEMBLER_HH + +#include <dune/grid/common/gridenums.hh> + +#include <dumux/ccmodels/common/ccproperties.hh> +#include <dumux/linear/vertexborderlistfromgrid.hh> +#include <dumux/linear/foreignoverlapfrombcrsmatrix.hh> +#include <dumux/parallel/vertexhandles.hh> + +namespace Dumux { + +/*! + * \brief An assembler for the global Jacobian matrix for models using the box discretization. + */ +template<class TypeTag> +class CCAssembler +{ + typedef typename GET_PROP_TYPE(TypeTag, Model) Model; + typedef typename GET_PROP_TYPE(TypeTag, Problem) Problem; + typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; + typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; + typedef typename GET_PROP_TYPE(TypeTag, VertexMapper) VertexMapper; + typedef typename GET_PROP_TYPE(TypeTag, ElementMapper) ElementMapper; + + typedef typename GET_PROP_TYPE(TypeTag, SolutionVector) SolutionVector; + typedef typename GET_PROP_TYPE(TypeTag, JacobianMatrix) JacobianMatrix; + typedef typename GET_PROP_TYPE(TypeTag, PrimaryVariables) PrimaryVariables; + + enum{ dim = GridView::dimension }; + typedef typename GridView::template Codim<0>::Entity Element; + typedef typename GridView::template Codim<0>::Iterator ElementIterator; + typedef typename GridView::template Codim<dim>::EntityPointer VertexPointer; + typedef typename GridView::IntersectionIterator IntersectionIterator; + + enum { numEq = GET_PROP_VALUE(TypeTag, NumEq) }; + typedef Dune::FieldMatrix<Scalar, numEq, numEq> MatrixBlock; + typedef Dune::FieldVector<Scalar, numEq> VectorBlock; + + // copying the jacobian assembler is not a good idea + CCAssembler(const CCAssembler &); + +public: + /*! + * \brief The colors of elements required for partial + * Jacobian reassembly. + */ + enum EntityColor { + /*! + * Element that needs to be reassembled because some + * relative error is above the tolerance + */ + Red = 0, + + /*! + * Element that does not need to be reassembled + */ + Green = 3 + }; + + CCAssembler() + { + problemPtr_ = 0; + matrix_ = 0; + + // set reassemble accuracy to 0, so that if partial reassembly + // of the jacobian matrix is disabled, the reassemble accuracy + // is always smaller than the current relative tolerance + reassembleAccuracy_ = 0.0; + } + + ~CCAssembler() + { + delete matrix_; + } + + /*! + * \brief Initialize the jacobian assembler. + * + * At this point we can assume that all objects in the problem and + * the model have been allocated. We can not assume that they are + * fully initialized, though. + * + * \param problem The problem object + */ + void init(Problem& problem) + { + problemPtr_ = &problem; + + // initialize the BCRS matrix + createMatrix_(); + + // initialize the jacobian matrix and the right hand side + // vector + *matrix_ = 0; + reuseMatrix_ = false; + + int numElems = gridView_().size(0); + + residual_.resize(numElems); + + // initialize the storage part of the Jacobian matrix. Since + // we only need this if Jacobian matrix recycling is enabled, + // we do not waste space if it is disabled + if (enableJacobianRecycling_()) { + storageJacobian_.resize(numElems); + storageTerm_.resize(numElems); + } + + if (gridView_().comm().size() > 1) + totalElems_ = gridView_().comm().sum(numElems); + else + totalElems_ = numElems; + + // initialize data needed for partial reassembly + if (enablePartialReassemble_()) + { + elementColor_.resize(numElems); + elementDelta_.resize(numElems); + } + + reassembleAll(); + } + + /*! + * \brief Assemble the global Jacobian of the residual and the residual for the current solution. + * + * The current state of affairs (esp. the previous and the current + * solutions) is represented by the model object. + */ + void assemble() + { + bool printReassembleStatistics = enablePartialReassemble_() && !reuseMatrix_; + int succeeded; + try { + assemble_(); + succeeded = 1; + if (gridView_().comm().size() > 1) + succeeded = gridView_().comm().min(succeeded); + } + catch (Dumux::NumericalProblem &e) + { + std::cout << "rank " << problem_().gridView().comm().rank() + << " caught an exception while assembling:" << e.what() + << "\n"; + succeeded = 0; + if (gridView_().comm().size() > 1) + succeeded = gridView_().comm().min(succeeded); + } + + if (!succeeded) { + DUNE_THROW(NumericalProblem, + "A process did not succeed in linearizing the system"); + } + + if (printReassembleStatistics) + { + if (gridView_().comm().size() > 1) + { + greenElems_ = gridView_().comm().sum(greenElems_); + reassembleAccuracy_ = gridView_().comm().max(nextReassembleAccuracy_); + } + else + { + reassembleAccuracy_ = nextReassembleAccuracy_; + } + + problem_().newtonController().endIterMsg() + << ", reassembled " + << totalElems_ - greenElems_ << "/" << totalElems_ + << " (" << 100*Scalar(totalElems_ - greenElems_)/totalElems_ << "%) elems @accuracy=" + << reassembleAccuracy_; + } + } + + /*! + * \brief If Jacobian matrix recycling is enabled, this method + * specifies whether the next call to assemble() just + * rescales the storage term or does a full reassembly + * + * \param yesno If true, only rescale; else do full Jacobian assembly. + */ + void setMatrixReuseable(bool yesno = true) + { + if (enableJacobianRecycling_()) + reuseMatrix_ = yesno; + } + + /*! + * \brief If partial Jacobian matrix reassembly is enabled, this + * method causes all elements to be reassembled in the next + * assemble() call. + */ + void reassembleAll() + { + // do not reuse the current linearization + reuseMatrix_ = false; + + // do not use partial reassembly for the next iteration + nextReassembleAccuracy_ = 0.0; + if (enablePartialReassemble_()) { + std::fill(elementColor_.begin(), + elementColor_.end(), + Red); + std::fill(elementDelta_.begin(), + elementDelta_.end(), + 0.0); + } + } + + /*! + * \brief Returns the largest relative error of a "green" vertex + * for the most recent call of the assemble() method. + * + * This only has an effect if partial Jacobian reassembly is + * enabled. If it is disabled, then this method always returns 0. + * + * This returns the _actual_ relative computed seen by + * computeColors(), not the tolerance which it was given. + */ + Scalar reassembleAccuracy() const + { return reassembleAccuracy_; } + + /*! + * \brief Update the distance where the non-linear system was + * originally insistently linearized and the point where it + * will be linerized the next time. + * + * This only has an effect if partial reassemble is enabled. + */ + void updateDiscrepancy(const SolutionVector &u, + const SolutionVector &uDelta) + { + if (!enablePartialReassemble_()) + return; + + // update the vector with the distances of the current + // evaluation point used for linearization from the original + // evaluation point + for (unsigned int i = 0; i < elementDelta_.size(); ++i) { + PrimaryVariables currentPriVars(u[i]); + PrimaryVariables nextPriVars(currentPriVars); + nextPriVars -= uDelta[i]; + + // we need to add the distance the solution was moved for + // this vertex + Scalar dist = model_().relativeErrorVertex(i, + currentPriVars, + nextPriVars); + elementDelta_[i] += std::abs(dist); + } + } + + /*! + * \brief Determine the colors of elements for partial + * reassembly given a relative tolerance. + * + * The following approach is used: + * + * - Set all elements to 'green' + * - Mark all elements as 'red' which exhibit an relative error above + * the tolerance + * - Mark all neighbors of 'red' elements also 'red' + * + * \param relTol The relative error below which an element won't be + * reassembled. Note that this specifies the + * worst-case relative error between the last + * linearization point and the current solution and + * _not_ the delta vector of the Newton iteration! + */ + void computeColors(Scalar relTol) + { + if (!enablePartialReassemble_()) + return; + + ElementIterator elemIt = gridView_().template begin<0>(); + ElementIterator elemEndIt = gridView_().template end<0>(); + + // mark the red elements and update the tolerance of the + // linearization which actually will get achieved + nextReassembleAccuracy_ = 0; + for (; elemIt != elemEndIt; ++elemIt) { + int elemIdx = this->elementMapper_().map(*elemIt); + if (elementDelta_[elemIdx] > relTol) + { + // mark element as red if discrepancy is larger than + // the relative tolerance + elementColor_[elemIdx] = Red; + } + else + { + elementColor_[elemIdx] = Green; + nextReassembleAccuracy_ = + std::max(nextReassembleAccuracy_, elementDelta_[elemIdx]); + } + } + + // mark the neighbors also red + elemIt = gridView_().template begin<0>(); + for (; elemIt != elemEndIt; ++elemIt) { + int elemIdx = this->elementMapper_().map(*elemIt); + if (elementColor_[elemIdx] == Red) + continue; // element is red already! + + if (elementDelta_[elemIdx] > relTol) + { + // also mark the neighbors + IntersectionIterator endIsIt = gridView_().iend(*elemIt); + for (IntersectionIterator isIt = gridView_().ibegin(*elemIt); isIt != endIsIt; ++isIt) + { + if (isIt->neighbor()) + { + int neighborIdx = this->elementMapper_().map(*isIt->outside()); + elementColor_[neighborIdx] = Red; + } + } + } + } + + // set the discrepancy of the red elements to zero + for (int i = 0; i < elementDelta_.size(); i++) + if (elementColor_[i] == Red) + elementDelta_[i] = 0; + } + + /*! + * \brief Returns the Jacobian reassemble color of an element + * + * \param element The Codim-0 DUNE entity + */ + int elementColor(const Element &element) const + { + if (!enablePartialReassemble_()) + return Red; // reassemble unconditionally! + + int globalIdx = elementMapper_().map(element); + return elementColor_[globalIdx]; + } + + /*! + * \brief Returns the Jacobian reassemble color of an element + * + * \param globalElementIdx The global index of the element. + */ + int elementColor(const int globalElementIdx) const + { + if (!enablePartialReassemble_()) + return Red; // reassemble unconditionally! + return elementColor_[globalElementIdx]; + } + + /*! + * \brief Return constant reference to global Jacobian matrix. + */ + const JacobianMatrix& matrix() const + { return *matrix_; } + JacobianMatrix& matrix() + { return *matrix_; } + + /*! + * \brief Return constant reference to global residual vector. + */ + const SolutionVector& residual() const + { return residual_; } + SolutionVector& residual() + { return residual_; } + + // functions needed for interface consistence + void markVertexRed(const int globalVertIdx) {} + +private: + static bool enableJacobianRecycling_() + { return GET_PARAM_FROM_GROUP(TypeTag, bool, Implicit, EnableJacobianRecycling); } + static bool enablePartialReassemble_() + { return GET_PARAM_FROM_GROUP(TypeTag, bool, Implicit, EnablePartialReassemble); } + + // Construct the BCRS matrix for the global jacobian + void createMatrix_() + { + int nElems = gridView_().size(0); + + // allocate raw matrix + matrix_ = new JacobianMatrix(nElems, nElems, JacobianMatrix::random); + + // find out the global indices of the neighboring elements of + // each element + typedef std::set<int> NeighborSet; + std::vector<NeighborSet> neighbors(nElems); + ElementIterator eIt = gridView_().template begin<0>(); + const ElementIterator eEndIt = gridView_().template end<0>(); + for (; eIt != eEndIt; ++eIt) { + const Element &elem = *eIt; + + int globalI = elementMapper_().map(elem); + neighbors[globalI].insert(globalI); + + // if the element is ghost, + // all dofs just contain main-diagonal entries + //if (elem.partitionType() == Dune::GhostEntity) + // continue; + + // loop over all neighbors + IntersectionIterator isIt = gridView_().ibegin(elem); + const IntersectionIterator &endIt = gridView_().iend(elem); + for (; isIt != endIt; ++isIt) + { + if (isIt->neighbor()) + { + int globalJ = elementMapper_().map(*(isIt->outside())); + neighbors[globalI].insert(globalJ); + } + } + } + + // allocate space for the rows of the matrix + for (int i = 0; i < nElems; ++i) { + matrix_->setrowsize(i, neighbors[i].size()); + } + matrix_->endrowsizes(); + + // fill the rows with indices. each element talks to all of its + // neighbors and itself. + for (int i = 0; i < nElems; ++i) { + typename NeighborSet::iterator nIt = neighbors[i].begin(); + typename NeighborSet::iterator nEndIt = neighbors[i].end(); + for (; nIt != nEndIt; ++nIt) { + matrix_->addindex(i, *nIt); + } + } + matrix_->endindices(); + } + + // reset the global linear system of equations. if partial + // reassemble is enabled, this means that the jacobian matrix must + // only be erased partially! + void resetSystem_() + { + // do not do anything if we can re-use the current linearization + if (reuseMatrix_) + return; + + // reset the right hand side. + residual_ = 0.0; + + if (!enablePartialReassemble_()) { + // If partial reassembly of the jacobian is not enabled, + // we can just reset everything! + (*matrix_) = 0; + + // reset the parts needed for Jacobian recycling + if (enableJacobianRecycling_()) { + int numElementsGlobal = matrix_->N(); + for (int i=0; i < numElementsGlobal; ++ i) { + storageJacobian_[i] = 0; + storageTerm_[i] = 0; + } + } + + return; + } + + // reset all entries corrosponding to a red or yellow vertex + for (unsigned int rowIdx = 0; rowIdx < matrix_->N(); ++rowIdx) { + if (elementColor_[rowIdx] == Green) + continue; // the equations for this control volume are + // already below the treshold + + // reset the parts needed for Jacobian recycling + if (enableJacobianRecycling_()) { + storageJacobian_[rowIdx] = 0; + storageTerm_[rowIdx] = 0; + } + + // set all matrix entries in the row to 0 + typedef typename JacobianMatrix::ColIterator ColIterator; + ColIterator colIt = (*matrix_)[rowIdx].begin(); + const ColIterator &colEndIt = (*matrix_)[rowIdx].end(); + for (; colIt != colEndIt; ++colIt) { + (*colIt) = 0.0; + } + } + } + + // linearize the whole system + void assemble_() + { + resetSystem_(); + + // if we can "recycle" the current linearization, we do it + // here and be done with it... + Scalar curDt = problem_().timeManager().timeStepSize(); + if (reuseMatrix_) { + int numElementsGlobal = storageJacobian_.size(); + for (int i = 0; i < numElementsGlobal; ++i) { + // rescale the mass term of the jacobian matrix + MatrixBlock &J_i_i = (*matrix_)[i][i]; + + J_i_i -= storageJacobian_[i]; + storageJacobian_[i] *= oldDt_/curDt; + J_i_i += storageJacobian_[i]; + + // use the flux term plus the source term as the new + // residual (since the delta in the d(storage)/dt is 0 + // for the first iteration and the residual is + // approximately 0 in the last iteration, the flux + // term plus the source term must be equal to the + // negative change of the storage term of the last + // iteration of the last time step...) + residual_[i] = storageTerm_[i]; + residual_[i] *= -1; + } + + reuseMatrix_ = false; + oldDt_ = curDt; + return; + } + + oldDt_ = curDt; + greenElems_ = 0; + + // reassemble the elements... + ElementIterator elemIt = gridView_().template begin<0>(); + ElementIterator elemEndIt = gridView_().template end<0>(); + for (; elemIt != elemEndIt; ++elemIt) { + const Element &elem = *elemIt; + if (elem.partitionType() == Dune::GhostEntity) + { + assembleGhostElement_(elem); + } + else + { + assembleElement_(elem); + } + } + } + + // assemble a non-ghost element + void assembleElement_(const Element &elem) + { + if (enablePartialReassemble_()) { + int globalElemIdx = model_().elementMapper().map(elem); + if (elementColor_[globalElemIdx] == Green) { + ++greenElems_; + + assembleGreenElement_(elem); + return; + } + } + + model_().localJacobian().assemble(elem); + + int globalI = elementMapper_().map(elem); + + // update the right hand side + residual_[globalI] = model_().localJacobian().residual(0); + for (int j = 0; j < residual_[globalI].dimension; ++j) + assert(std::isfinite(residual_[globalI][j])); + if (enableJacobianRecycling_()) { + storageTerm_[globalI] += + model_().localJacobian().storageTerm(0); + } + + if (enableJacobianRecycling_()) + storageJacobian_[globalI] += + model_().localJacobian().storageJacobian(0); + + // update the diagonal entry + (*matrix_)[globalI][globalI] = model_().localJacobian().mat(0,0); + + IntersectionIterator isIt = gridView_().ibegin(elem); + const IntersectionIterator &endIt = gridView_().iend(elem); + for (int j = 0; isIt != endIt; ++isIt) + { + if (isIt->neighbor()) + { + int globalJ = elementMapper_().map(*(isIt->outside())); + (*matrix_)[globalI][globalJ] = model_().localJacobian().mat(0,++j); + } + } + } + + // "assemble" a green element. green elements only get the + // residual updated, but the jacobian is left alone... + void assembleGreenElement_(const Element &elem) + { + model_().localResidual().eval(elem); + + int globalI = elementMapper_().map(elem); + + // update the right hand side + residual_[globalI] += model_().localResidual().residual(0); + if (enableJacobianRecycling_()) + storageTerm_[globalI] += model_().localResidual().storageTerm(0); + } + + // "assemble" a ghost element + void assembleGhostElement_(const Element &elem) + { + int globalI = elementMapper_().map(elem); + + // update the right hand side + residual_[globalI] = 0.0; + + // update the diagonal entry + typedef typename JacobianMatrix::block_type BlockType; + BlockType &J = (*matrix_)[globalI][globalI]; + for (int j = 0; j < BlockType::rows; ++j) + J[j][j] = 1.0; + } + + + Problem &problem_() + { return *problemPtr_; } + const Problem &problem_() const + { return *problemPtr_; } + const Model &model_() const + { return problem_().model(); } + Model &model_() + { return problem_().model(); } + const GridView &gridView_() const + { return problem_().gridView(); } + const VertexMapper &vertexMapper_() const + { return problem_().vertexMapper(); } + const ElementMapper &elementMapper_() const + { return problem_().elementMapper(); } + + Problem *problemPtr_; + + // the jacobian matrix + JacobianMatrix *matrix_; + // the right-hand side + SolutionVector residual_; + + // attributes required for jacobian matrix recycling + bool reuseMatrix_; + // The storage part of the local Jacobian + std::vector<MatrixBlock> storageJacobian_; + std::vector<VectorBlock> storageTerm_; + // time step size of last assembly + Scalar oldDt_; + + + // attributes required for partial jacobian reassembly + std::vector<EntityColor> elementColor_; + std::vector<Scalar> elementDelta_; + + int totalElems_; + int greenElems_; + + Scalar nextReassembleAccuracy_; + Scalar reassembleAccuracy_; +}; + +} // namespace Dumux + +#endif diff --git a/dumux/implicit/cellcentered/ccelementboundarytypes.hh b/dumux/implicit/cellcentered/ccelementboundarytypes.hh new file mode 100644 index 0000000000..dd4c7ff2b5 --- /dev/null +++ b/dumux/implicit/cellcentered/ccelementboundarytypes.hh @@ -0,0 +1,150 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * Copyright (C) 2010 by Andreas Lauser * + * Institute for Modelling Hydraulic and Environmental Systems * + * University of Stuttgart, Germany * + * email: <givenname>.<name>@iws.uni-stuttgart.de * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \file + * + * \brief Boundary types gathered on an element + */ +#ifndef DUMUX_CC_ELEMENT_BOUNDARY_TYPES_HH +#define DUMUX_CC_ELEMENT_BOUNDARY_TYPES_HH + +#include "ccproperties.hh" + +#include <dumux/common/valgrind.hh> + +namespace Dumux +{ + +/*! + * \ingroup CCModel + * \ingroup CCBoundaryTypes + * + * \brief This class stores an array of BoundaryTypes objects + */ +template<class TypeTag> +class CCElementBoundaryTypes : public std::vector<typename GET_PROP_TYPE(TypeTag, BoundaryTypes) > +{ + typedef typename GET_PROP_TYPE(TypeTag, BoundaryTypes) BoundaryTypes; + typedef std::vector<BoundaryTypes> ParentType; + + typedef typename GET_PROP_TYPE(TypeTag, Problem) Problem; + typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; + typedef typename GET_PROP_TYPE(TypeTag, FVElementGeometry) FVElementGeometry; + + enum { dim = GridView::dimension }; + typedef typename GridView::template Codim<0>::Entity Element; + typedef typename GridView::template Codim<dim>::EntityPointer VertexPointer; + typedef typename GridView::IntersectionIterator IntersectionIterator; + +public: + /*! + * \brief Copy constructor. + * + * Copying a the boundary types of an element should be explicitly + * requested + */ + explicit CCElementBoundaryTypes(const CCElementBoundaryTypes &v) + : ParentType(v) + {} + + /*! + * \brief Default constructor. + */ + CCElementBoundaryTypes() + { + hasDirichlet_ = false; + hasNeumann_ = false; + hasOutflow_ = false; + } + + /*! + * \brief Update the boundary types for all vertices of an element. + * + * \param problem The problem object which needs to be simulated + * \param element The DUNE Codim<0> entity for which the boundary + * types should be collected + */ + void update(const Problem &problem, + const Element &element) + { + this->resize(1); + + hasDirichlet_ = false; + hasNeumann_ = false; + hasOutflow_ = false; + + (*this)[0].reset(); + + if (!problem.model().onBoundary(element)) + return; + + IntersectionIterator isIt = problem.gridView().ibegin(element); + IntersectionIterator isEndIt = problem.gridView().iend(element); + for (; isIt != isEndIt; ++isIt) { + if (!isIt->boundary()) + continue; + + problem.boundaryTypes((*this)[0], *isIt); + + hasDirichlet_ = hasDirichlet_ || (*this)[0].hasDirichlet(); + hasNeumann_ = hasNeumann_ || (*this)[0].hasNeumann(); + hasOutflow_ = hasOutflow_ || (*this)[0].hasOutflow(); + } + } + + void update(const Problem &problem, + const Element &element, + const FVElementGeometry &fvGeometry) + { + update(problem, element); + } + + /*! + * \brief Returns whether the element has a vertex which contains + * a Dirichlet value. + */ + bool hasDirichlet() const + { return hasDirichlet_; } + + /*! + * \brief Returns whether the element potentially features a + * Neumann boundary segment. + */ + bool hasNeumann() const + { return hasNeumann_; } + + /*! + * \brief Returns whether the element potentially features an + * outflow boundary segment. + */ + bool hasOutflow() const + { return hasOutflow_; } + +protected: + bool hasDirichlet_; + bool hasNeumann_; + bool hasOutflow_; +}; + +} // namespace Dumux + +#endif diff --git a/dumux/implicit/cellcentered/ccelementvolumevariables.hh b/dumux/implicit/cellcentered/ccelementvolumevariables.hh new file mode 100644 index 0000000000..6700444b86 --- /dev/null +++ b/dumux/implicit/cellcentered/ccelementvolumevariables.hh @@ -0,0 +1,107 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * Copyright (C) 2010 by Andreas Lauser * + * Institute for Modelling Hydraulic and Environmental Systems * + * University of Stuttgart, Germany * + * email: <givenname>.<name>@iws.uni-stuttgart.de * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \file + * + * \brief Volume variables gathered on an element + */ +#ifndef DUMUX_CC_ELEMENT_VOLUME_VARIABLES_HH +#define DUMUX_CC_ELEMENT_VOLUME_VARIABLES_HH + +#include "ccproperties.hh" + + +namespace Dumux +{ + +/*! + * \ingroup CCModel + * + * \brief This class stores an array of VolumeVariables objects, one + * volume variables object for each of the element's vertices + */ +template<class TypeTag> +class CCElementVolumeVariables : public std::vector<typename GET_PROP_TYPE(TypeTag, VolumeVariables) > +{ + typedef typename GET_PROP_TYPE(TypeTag, Problem) Problem; + typedef typename GET_PROP_TYPE(TypeTag, FVElementGeometry) FVElementGeometry; + typedef typename GET_PROP_TYPE(TypeTag, SolutionVector) SolutionVector; + typedef typename GET_PROP_TYPE(TypeTag, VertexMapper) VertexMapper; + typedef typename GET_PROP_TYPE(TypeTag, PrimaryVariables) PrimaryVariables; + + typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; + typedef typename GridView::template Codim<0>::Entity Element; + + enum { dim = GridView::dimension }; + enum { numEq = GET_PROP_VALUE(TypeTag, NumEq) }; + +public: + /*! + * \brief The constructor. + */ + CCElementVolumeVariables() + { } + + /*! + * \brief Construct the volume variables for all of vertices of an element. + * + * \param problem The problem which needs to be simulated. + * \param element The DUNE Codim<0> entity for which the volume variables ought to be calculated + * \param fvElemGeom The finite volume geometry of the element + * \param oldSol Tells whether the model's previous or current solution should be used. + */ + void update(const Problem &problem, + const Element &element, + const FVElementGeometry &fvElemGeom, + bool oldSol) + { + const SolutionVector &globalSol = + oldSol? + problem.model().prevSol(): + problem.model().curSol(); + + int numNeighbors = fvElemGeom.numNeighbors; + this->resize(numNeighbors); + + for (int i = 0; i < numNeighbors; i++) + { + const Element& neighbor = *(fvElemGeom.neighbors[i]); + + const PrimaryVariables &solI + = globalSol[problem.elementMapper().map(neighbor)]; + + FVElementGeometry neighborFVGeom; + neighborFVGeom.updateInner(problem.gridView(), neighbor); + + (*this)[i].update(solI, + problem, + neighbor, + neighborFVGeom, + /*scvIdx=*/0, + oldSol); + } + } +}; + +} // namespace Dumux + +#endif diff --git a/dumux/implicit/cellcentered/ccfvelementgeometry.hh b/dumux/implicit/cellcentered/ccfvelementgeometry.hh new file mode 100644 index 0000000000..9c9020db23 --- /dev/null +++ b/dumux/implicit/cellcentered/ccfvelementgeometry.hh @@ -0,0 +1,219 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * Copyright (C) 2008-2012 by Bernd Flemisch, Andreas Lauser * + * Institute for Modelling Hydraulic and Environmental Systems * + * University of Stuttgart, Germany * + * email: <givenname>.<name>@iws.uni-stuttgart.de * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \file + * + * \brief Represents the finite volume geometry of a single element in + * the box scheme. + */ +#ifndef DUMUX_CC_FV_ELEMENTGEOMETRY_HH +#define DUMUX_CC_FV_ELEMENTGEOMETRY_HH + +#include <dune/common/version.hh> +#include <dune/grid/common/intersectioniterator.hh> + +#if DUNE_VERSION_NEWER_REV(DUNE_COMMON, 2, 2, 0) +// dune 2.2 +#include <dune/geometry/referenceelements.hh> +#else +// dune 2.1 +#include <dune/geometry/referenceelements.hh> +#endif + +#include <dune/localfunctions/lagrange/pqkfactory.hh> + +#include <dumux/common/propertysystem.hh> + +namespace Dumux +{ +namespace Properties +{ +NEW_PROP_TAG(GridView); +NEW_PROP_TAG(Scalar); +} + +/*! + * \brief Represents the finite volume geometry of a single element in + * the cell centered fv scheme. + */ +template<class TypeTag> +class CCFVElementGeometry +{ + typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; + enum{dim = GridView::dimension}; + enum{dimWorld = GridView::dimensionworld}; + + enum{maxNFAP = 2}; + enum{maxNE = (dim < 3 ? 4 : 12)}; + enum{maxNF = (dim < 3 ? 1 : 6)}; + enum{maxCOS = (dim < 3 ? 2 : 4)}; + enum{maxBF = (dim < 3 ? 8 : 24)}; + typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; + typedef typename GridView::ctype CoordScalar; + typedef typename GridView::Traits::template Codim<0>::Entity Element; + typedef typename GridView::Traits::template Codim<0>::EntityPointer ElementPointer; + typedef typename Element::Geometry Geometry; + typedef Dune::FieldVector<Scalar,dimWorld> Vector; + typedef Dune::FieldVector<CoordScalar,dimWorld> GlobalPosition; + typedef Dune::FieldVector<CoordScalar,dim> LocalPosition; + typedef typename GridView::IntersectionIterator IntersectionIterator; + +public: + struct SubControlVolume //! FV intersected with element + { + LocalPosition local; //!< local vert position + GlobalPosition global; //!< global vert position + Scalar volume; //!< volume of scv + bool inner; + }; + + struct SubControlVolumeFace //! interior face of a sub control volume + { + int i,j; //!< scvf seperates corner i and j of elem + LocalPosition ipLocal; //!< integration point in local coords + GlobalPosition ipGlobal; //!< integration point in global coords + Vector normal; //!< normal on face pointing to CV j or outward of the domain with length equal to |scvf| + Scalar area; //!< area of face + Dune::FieldVector<Vector, maxNFAP> grad; //!< derivatives of shape functions at ip + Dune::FieldVector<Scalar, maxNFAP> shapeValue; //!< value of shape functions at ip + Dune::FieldVector<int, maxNFAP> fapIndices; //!< indices w.r.t.neighbors of the flux approximation points + }; + + typedef SubControlVolumeFace BoundaryFace; //!< compatibility typedef + + LocalPosition elementLocal; //!< local coordinate of element center + GlobalPosition elementGlobal; //!< global coordinate of element center + Scalar elementVolume; //!< element volume + SubControlVolume subContVol[1]; //!< data of the sub control volumes + SubControlVolumeFace subContVolFace[maxNE]; //!< data of the sub control volume faces + BoundaryFace boundaryFace[maxBF]; //!< data of the boundary faces + int numVertices; //!< number of verts + int numEdges; //!< number of edges + int numFaces; //!< number of faces (0 in < 3D) + int numSCV; //!< number of subcontrol volumes + int numNeighbors; //!< number of neighboring elements including the element itself + int numFAP; //!< number of flux approximation points + std::vector<ElementPointer> neighbors; //!< stores pointers for the neighboring elements + + void updateInner(const GridView& gridView, const Element& e) + { + const Geometry& geometry = e.geometry(); + + elementVolume = geometry.volume(); + elementGlobal = geometry.center(); + elementLocal = geometry.local(elementGlobal); + + numVertices = e.template count<dim>(); + numEdges = e.template count<dim-1>(); + numFaces = (dim<3)? 0 : e.template count<1>(); + numSCV = 1; + numFAP = 2; + + subContVol[0].local = elementLocal; + subContVol[0].global = elementGlobal; + subContVol[0].inner = true; + subContVol[0].volume = elementVolume; + + // initialize neighbors list with self: + numNeighbors = 1; + neighbors.clear(); + neighbors.reserve(maxNE); + ElementPointer elementPointer(e); + neighbors.push_back(elementPointer); + } + + void update(const GridView& gridView, const Element& e) + { + updateInner(gridView, e); + + const Geometry& geometry = e.geometry(); + + // fill neighbor information and control volume face data: + IntersectionIterator endit = gridView.iend(e); + for (IntersectionIterator it = gridView.ibegin(e); it != endit; ++it) + { + // neighbor information and inner cvf data: + if (it->neighbor()) + { + numNeighbors++; + ElementPointer elementPointer(it->outside()); + neighbors.push_back(elementPointer); + + int k = numNeighbors - 2; + + subContVolFace[k].i = 0; + subContVolFace[k].j = k+1; + + subContVolFace[k].ipGlobal = it->geometry().center(); + subContVolFace[k].ipLocal = geometry.local(subContVolFace[k].ipGlobal); + subContVolFace[k].normal = it->centerUnitOuterNormal(); + subContVolFace[k].normal *= it->geometry().volume(); + subContVolFace[k].area = it->geometry().volume(); + + GlobalPosition distVec = elementGlobal; + distVec -= neighbors[k+1]->geometry().center(); + distVec /= distVec.two_norm2(); + + // gradients using a two-point flux approximation + for (int idx = 0; idx < 2; idx++) + { + subContVolFace[k].grad[idx] = distVec; + subContVolFace[k].shapeValue[idx] = 0.5; + } + subContVolFace[k].grad[1] *= -1.0; + + subContVolFace[k].fapIndices[0] = subContVolFace[k].i; + subContVolFace[k].fapIndices[1] = subContVolFace[k].j; + } + + // boundary cvf data + if (it->boundary()) + { + int bfIdx = it->indexInInside(); + boundaryFace[bfIdx].ipGlobal = it->geometry().center(); + boundaryFace[bfIdx].ipLocal = geometry.local(boundaryFace[bfIdx].ipGlobal); + boundaryFace[bfIdx].normal = it->centerUnitOuterNormal(); + boundaryFace[bfIdx].normal *= it->geometry().volume(); + boundaryFace[bfIdx].area = it->geometry().volume(); + boundaryFace[bfIdx].i = 0; + boundaryFace[bfIdx].j = 0; + + GlobalPosition distVec = elementGlobal; + distVec -= boundaryFace[bfIdx].ipGlobal; + distVec /= distVec.two_norm2(); + + // gradients using a two-point flux approximation + for (int idx = 0; idx < 2; idx++) + { + boundaryFace[bfIdx].grad[idx] = distVec; + boundaryFace[bfIdx].shapeValue[idx] = 0.5; + } + boundaryFace[bfIdx].grad[1] *= -1.0; + } + } + } +}; + +} + +#endif + diff --git a/dumux/implicit/cellcentered/cclocaljacobian.hh b/dumux/implicit/cellcentered/cclocaljacobian.hh new file mode 100644 index 0000000000..2da777b72a --- /dev/null +++ b/dumux/implicit/cellcentered/cclocaljacobian.hh @@ -0,0 +1,541 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * Copyright (C) 2008-2009 by Andreas Lauser * + * Copyright (C) 2007-2009 by Bernd Flemisch * + * Institute for Modelling Hydraulic and Environmental Systems * + * University of Stuttgart, Germany * + * email: <givenname>.<name>@iws.uni-stuttgart.de * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \file + * \brief Caculates the Jacobian of the local residual for box models + */ +#ifndef DUMUX_CC_LOCAL_JACOBIAN_HH +#define DUMUX_CC_LOCAL_JACOBIAN_HH + +#include <dune/istl/matrix.hh> + +#include <dumux/common/math.hh> +#include "ccelementboundarytypes.hh" + +namespace Dumux +{ +/*! + * \ingroup CCModel + * \ingroup CCLocalJacobian + * \brief Calculates the Jacobian of the local residual for box models + * + * The default behavior is to use numeric differentiation, i.e. + * forward or backward differences (2nd order), or central + * differences (3rd order). The method used is determined by the + * "NumericDifferenceMethod" property: + * + * - if the value of this property is smaller than 0, backward + * differences are used, i.e.: + * \f[ + \frac{\partial f(x)}{\partial x} \approx \frac{f(x) - f(x - \epsilon)}{\epsilon} + * \f] + * + * - if the value of this property is 0, central + * differences are used, i.e.: + * \f[ + \frac{\partial f(x)}{\partial x} \approx \frac{f(x + \epsilon) - f(x - \epsilon)}{2 \epsilon} + * \f] + * + * - if the value of this property is larger than 0, forward + * differences are used, i.e.: + * \f[ + \frac{\partial f(x)}{\partial x} \approx \frac{f(x + \epsilon) - f(x)}{\epsilon} + * \f] + * + * Here, \f$ f \f$ is the residual function for all equations, \f$x\f$ + * is the value of a sub-control volume's primary variable at the + * evaluation point and \f$\epsilon\f$ is a small value larger than 0. + */ +template<class TypeTag> +class CCLocalJacobian +{ +private: + typedef typename GET_PROP_TYPE(TypeTag, LocalJacobian) Implementation; + typedef typename GET_PROP_TYPE(TypeTag, LocalResidual) LocalResidual; + typedef typename GET_PROP_TYPE(TypeTag, Problem) Problem; + typedef typename GET_PROP_TYPE(TypeTag, Model) Model; + typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; + typedef typename GridView::template Codim<0>::Entity Element; + typedef typename GET_PROP_TYPE(TypeTag, JacobianAssembler) JacobianAssembler; + + enum { + numEq = GET_PROP_VALUE(TypeTag, NumEq), + dim = GridView::dimension + }; + + typedef typename GET_PROP_TYPE(TypeTag, FVElementGeometry) FVElementGeometry; + typedef typename GET_PROP_TYPE(TypeTag, VertexMapper) VertexMapper; + typedef typename GET_PROP_TYPE(TypeTag, ElementSolutionVector) ElementSolutionVector; + typedef typename GET_PROP_TYPE(TypeTag, PrimaryVariables) PrimaryVariables; + + typedef typename GET_PROP_TYPE(TypeTag, VolumeVariables) VolumeVariables; + typedef typename GET_PROP_TYPE(TypeTag, ElementVolumeVariables) ElementVolumeVariables; + typedef typename GET_PROP_TYPE(TypeTag, ElementBoundaryTypes) ElementBoundaryTypes; + + typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; + typedef Dune::FieldMatrix<Scalar, numEq, numEq> MatrixBlock; + typedef Dune::Matrix<MatrixBlock> LocalBlockMatrix; + + // copying a local jacobian is not a good idea + CCLocalJacobian(const CCLocalJacobian &); + +public: + CCLocalJacobian() + { + numericDifferenceMethod_ = GET_PARAM(TypeTag, int, ImplicitNumericDifferenceMethod); + Valgrind::SetUndefined(problemPtr_); + } + + + /*! + * \brief Initialize the local Jacobian object. + * + * At this point we can assume that everything has been allocated, + * although some objects may not yet be completely initialized. + * + * \param prob The problem which we want to simulate. + */ + void init(Problem &prob) + { + problemPtr_ = &prob; + localResidual_.init(prob); + + // assume quadrilinears as elements with most vertices + A_.setSize(1, 2<<dim); + storageJacobian_.resize(1); + } + + /*! + * \brief Assemble an element's local Jacobian matrix of the + * defect. + * + * \param element The DUNE Codim<0> entity which we look at. + */ + void assemble(const Element &element) + { + // set the current grid element and update the element's + // finite volume geometry + elemPtr_ = &element; + fvElemGeom_.update(gridView_(), element); + reset_(); + + bcTypes_.update(problem_(), elem_(), fvElemGeom_); + + // this is pretty much a HACK because the internal state of + // the problem is not supposed to be changed during the + // evaluation of the residual. (Reasons: It is a violation of + // abstraction, makes everything more prone to errors and is + // not thread save.) The real solution are context objects! + problem_().updateCouplingParams(elem_()); + + // set the hints for the volume variables + model_().setHints(element, prevVolVars_, curVolVars_); + + // update the secondary variables for the element at the last + // and the current time levels + prevVolVars_.update(problem_(), + elem_(), + fvElemGeom_, + true /* isOldSol? */); + + curVolVars_.update(problem_(), + elem_(), + fvElemGeom_, + false /* isOldSol? */); + + // update the hints of the model + model_().updateCurHints(element, curVolVars_); + + // calculate the local residual + localResidual().eval(elem_(), + fvElemGeom_, + prevVolVars_, + curVolVars_, + bcTypes_); + residual_ = localResidual().residual(); + storageTerm_ = localResidual().storageTerm(); + + model_().updatePVWeights(elem_(), curVolVars_); + + // calculate the local jacobian matrix + int numNeighbors = fvElemGeom_.numNeighbors; + ElementSolutionVector partialDeriv(1); + PrimaryVariables storageDeriv(0.0); + for (int j = 0; j < numNeighbors; j++) { + for (int pvIdx = 0; pvIdx < numEq; pvIdx++) { + asImp_().evalPartialDerivative_(partialDeriv, + storageDeriv, + j, + pvIdx); + + // update the local stiffness matrix with the current partial + // derivatives + updateLocalJacobian_(j, + pvIdx, + partialDeriv, + storageDeriv); + } + } + } + + /*! + * \brief Returns a reference to the object which calculates the + * local residual. + */ + const LocalResidual &localResidual() const + { return localResidual_; } + + /*! + * \brief Returns a reference to the object which calculates the + * local residual. + */ + LocalResidual &localResidual() + { return localResidual_; } + + /*! + * \brief Returns the Jacobian of the equations at vertex i to the + * primary variables at vertex j. + * + * \param i The local vertex (or sub-control volume) index on which + * the equations are defined + * \param j The local vertex (or sub-control volume) index which holds + * primary variables + */ + const MatrixBlock &mat(int i, int j) const + { return A_[i][j]; } + + /*! + * \brief Returns the Jacobian of the storage term at vertex i. + * + * \param i The local vertex (or sub-control volume) index + */ + const MatrixBlock &storageJacobian(int i) const + { return storageJacobian_[i]; } + + /*! + * \brief Returns the residual of the equations at vertex i. + * + * \param i The local vertex (or sub-control volume) index on which + * the equations are defined + */ + const PrimaryVariables &residual(int i) const + { return residual_[i]; } + + /*! + * \brief Returns the storage term for vertex i. + * + * \param i The local vertex (or sub-control volume) index on which + * the equations are defined + */ + const PrimaryVariables &storageTerm(int i) const + { return storageTerm_[i]; } + + /*! + * \brief Returns the epsilon value which is added and removed + * from the current solution. + * + * \param scvIdx The local index of the element's vertex for + * which the local derivative ought to be calculated. + * \param pvIdx The index of the primary variable which gets varied + */ + Scalar numericEpsilon(int scvIdx, + int pvIdx) const + { + // define the base epsilon as the geometric mean of 1 and the + // resolution of the scalar type. E.g. for standard 64 bit + // floating point values, the resolution is about 10^-16 and + // the base epsilon is thus approximately 10^-8. + /* + static const Scalar baseEps + = Dumux::geometricMean<Scalar>(std::numeric_limits<Scalar>::epsilon(), 1.0); + */ + static const Scalar baseEps = 1e-10; + assert(std::numeric_limits<Scalar>::epsilon()*1e4 < baseEps); + // the epsilon value used for the numeric differentiation is + // now scaled by the absolute value of the primary variable... + Scalar pv = this->curVolVars_[scvIdx].priVar(pvIdx); + return baseEps*(std::abs(pv) + 1.0); + } + +protected: + Implementation &asImp_() + { return *static_cast<Implementation*>(this); } + const Implementation &asImp_() const + { return *static_cast<const Implementation*>(this); } + + /*! + * \brief Returns a reference to the problem. + */ + const Problem &problem_() const + { + Valgrind::CheckDefined(problemPtr_); + return *problemPtr_; + }; + + /*! + * \brief Returns a reference to the grid view. + */ + const GridView &gridView_() const + { return problem_().gridView(); } + + /*! + * \brief Returns a reference to the element. + */ + const Element &elem_() const + { + Valgrind::CheckDefined(elemPtr_); + return *elemPtr_; + }; + + /*! + * \brief Returns a reference to the model. + */ + const Model &model_() const + { return problem_().model(); }; + + /*! + * \brief Returns a reference to the jacobian assembler. + */ + const JacobianAssembler &jacAsm_() const + { return model_().jacobianAssembler(); } + + /*! + * \brief Returns a reference to the vertex mapper. + */ + const VertexMapper &vertexMapper_() const + { return problem_().vertexMapper(); }; + + /*! + * \brief Reset the local jacobian matrix to 0 + */ + void reset_() + { + for (int i = 0; i < 1; ++ i) { + storageJacobian_[i] = 0.0; + for (int j = 0; j < A_.M(); ++ j) { + A_[i][j] = 0.0; + } + } + } + + /*! + * \brief Compute the partial derivatives to a primary variable at + * an degree of freedom. + * + * This method can be overwritten by the implementation if a + * better scheme than numerical differentiation is available. + * + * The default implementation of this method uses numeric + * differentiation, i.e. forward or backward differences (2nd + * order), or central differences (3rd order). The method used is + * determined by the "NumericDifferenceMethod" property: + * + * - if the value of this property is smaller than 0, backward + * differences are used, i.e.: + * \f[ + \frac{\partial f(x)}{\partial x} \approx \frac{f(x) - f(x - \epsilon)}{\epsilon} + * \f] + * + * - if the value of this property is 0, central + * differences are used, i.e.: + * \f[ + \frac{\partial f(x)}{\partial x} \approx \frac{f(x + \epsilon) - f(x - \epsilon)}{2 \epsilon} + * \f] + * + * - if the value of this property is larger than 0, forward + * differences are used, i.e.: + * \f[ + \frac{\partial f(x)}{\partial x} \approx \frac{f(x + \epsilon) - f(x)}{\epsilon} + * \f] + * + * Here, \f$ f \f$ is the residual function for all equations, \f$x\f$ + * is the value of a sub-control volume's primary variable at the + * evaluation point and \f$\epsilon\f$ is a small value larger than 0. + * + * \param dest The vector storing the partial derivatives of all + * equations + * \param destStorage the mass matrix contributions + * \param scvIdx The sub-control volume index of the current + * finite element for which the partial derivative + * ought to be calculated + * \param pvIdx The index of the primary variable at the scvIdx' + * sub-control volume of the current finite element + * for which the partial derivative ought to be + * calculated + */ + void evalPartialDerivative_(ElementSolutionVector &dest, + PrimaryVariables &destStorage, + int neighborIdx, + int pvIdx) + { + const Element& neighbor = *(fvElemGeom_.neighbors[neighborIdx]); + FVElementGeometry neighborFVGeom; + neighborFVGeom.updateInner(problemPtr_->gridView(), neighbor); + + int globalIdx = problemPtr_->elementMapper().map(neighbor); + + PrimaryVariables priVars(model_().curSol()[globalIdx]); + VolumeVariables origVolVars(curVolVars_[neighborIdx]); + + curVolVars_[neighborIdx].setEvalPoint(&origVolVars); + Scalar eps = asImp_().numericEpsilon(neighborIdx, pvIdx); + Scalar delta = 0; + + if (numericDifferenceMethod_ >= 0) { + // we are not using backward differences, i.e. we need to + // calculate f(x + \epsilon) + + // deflect primary variables + priVars[pvIdx] += eps; + delta += eps; + + // calculate the residual + curVolVars_[neighborIdx].update(priVars, + problem_(), + neighbor, + neighborFVGeom, + /*scvIdx=*/0, + false); + localResidual().eval(elem_(), + fvElemGeom_, + prevVolVars_, + curVolVars_, + bcTypes_); + + // store the residual and the storage term + dest = localResidual().residual(); + if (neighborIdx == 0) + destStorage = localResidual().storageTerm()[neighborIdx]; + } + else { + // we are using backward differences, i.e. we don't need + // to calculate f(x + \epsilon) and we can recycle the + // (already calculated) residual f(x) + dest = residual_; + if (neighborIdx == 0) + destStorage = storageTerm_[neighborIdx]; + } + + + if (numericDifferenceMethod_ <= 0) { + // we are not using forward differences, i.e. we don't + // need to calculate f(x - \epsilon) + + // deflect the primary variables + priVars[pvIdx] -= delta + eps; + delta += eps; + + // calculate residual again + curVolVars_[neighborIdx].update(priVars, + problem_(), + neighbor, + neighborFVGeom, + /*scvIdx=*/0, + false); + localResidual().eval(elem_(), + fvElemGeom_, + prevVolVars_, + curVolVars_, + bcTypes_); + dest -= localResidual().residual(); + if (neighborIdx == 0) + destStorage -= localResidual().storageTerm()[neighborIdx]; + } + else { + // we are using forward differences, i.e. we don't need to + // calculate f(x - \epsilon) and we can recycle the + // (already calculated) residual f(x) + dest -= residual_; + if (neighborIdx == 0) + destStorage -= storageTerm_[neighborIdx]; + } + + // divide difference in residuals by the magnitude of the + // deflections between the two function evaluation + dest /= delta; + destStorage /= delta; + + // restore the original state of the element's volume variables + curVolVars_[neighborIdx] = origVolVars; + +#if HAVE_VALGRIND + for (unsigned i = 0; i < dest.size(); ++i) + Valgrind::CheckDefined(dest[i]); +#endif + } + + /*! + * \brief Updates the current local Jacobian matrix with the + * partial derivatives of all equations in regard to the + * primary variable 'pvIdx' at vertex 'scvIdx' . + */ + void updateLocalJacobian_(int neighborIdx, + int pvIdx, + const ElementSolutionVector &deriv, + const PrimaryVariables &storageDeriv) + { + // store the derivative of the storage term + if (neighborIdx == 0) + for (int eqIdx = 0; eqIdx < numEq; eqIdx++) { + storageJacobian_[neighborIdx][eqIdx][pvIdx] = storageDeriv[eqIdx]; + } + + for (int i = 0; i < 1; i++) + { + for (int eqIdx = 0; eqIdx < numEq; eqIdx++) { + // A[i][scvIdx][eqIdx][pvIdx] is the rate of change of + // the residual of equation 'eqIdx' at vertex 'i' + // depending on the primary variable 'pvIdx' at vertex + // 'scvIdx'. + this->A_[i][neighborIdx][eqIdx][pvIdx] = deriv[i][eqIdx]; + Valgrind::CheckDefined(this->A_[i][neighborIdx][eqIdx][pvIdx]); + } + } + } + + const Element *elemPtr_; + FVElementGeometry fvElemGeom_; + + ElementBoundaryTypes bcTypes_; + + // The problem we would like to solve + Problem *problemPtr_; + + // secondary variables at the previous and at the current time + // levels + ElementVolumeVariables prevVolVars_; + ElementVolumeVariables curVolVars_; + + LocalResidual localResidual_; + + LocalBlockMatrix A_; + std::vector<MatrixBlock> storageJacobian_; + + ElementSolutionVector residual_; + ElementSolutionVector storageTerm_; + + int numericDifferenceMethod_; +}; +} + +#endif diff --git a/dumux/implicit/cellcentered/cclocalresidual.hh b/dumux/implicit/cellcentered/cclocalresidual.hh new file mode 100644 index 0000000000..cdc914e8b2 --- /dev/null +++ b/dumux/implicit/cellcentered/cclocalresidual.hh @@ -0,0 +1,710 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * Copyright (C) 2008-2011 by Andreas Lauser * + * Copyright (C) 2007-2009 by Bernd Flemisch * + * Institute for Modelling Hydraulic and Environmental Systems * + * University of Stuttgart, Germany * + * email: <givenname>.<name>@iws.uni-stuttgart.de * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \file + * \brief Calculates the residual of models based on the box scheme element-wise. + */ +#ifndef DUMUX_CC_LOCAL_RESIDUAL_HH +#define DUMUX_CC_LOCAL_RESIDUAL_HH + +#include <dune/istl/matrix.hh> +#include <dune/grid/common/geometry.hh> + +#include <dumux/common/valgrind.hh> + +#include "ccproperties.hh" + +namespace Dumux +{ +/*! + * \ingroup CCModel + * \ingroup CCLocalResidual + * \brief Element-wise calculation of the residual matrix for models + * based on the box scheme. + * + * \todo Please doc me more! + */ +template<class TypeTag> +class CCLocalResidual +{ +private: + typedef typename GET_PROP_TYPE(TypeTag, LocalResidual) Implementation; + typedef typename GET_PROP_TYPE(TypeTag, Problem) Problem; + typedef typename GET_PROP_TYPE(TypeTag, Model) Model; + typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; + typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; + + enum { + numEq = GET_PROP_VALUE(TypeTag, NumEq), + dim = GridView::dimension + }; + + typedef typename GridView::template Codim<0>::Entity Element; + typedef typename GridView::template Codim<dim>::EntityPointer VertexPointer; + typedef typename GridView::IntersectionIterator IntersectionIterator; + + typedef typename GET_PROP_TYPE(TypeTag, FVElementGeometry) FVElementGeometry; + typedef typename GET_PROP_TYPE(TypeTag, VertexMapper) VertexMapper; + typedef typename GET_PROP_TYPE(TypeTag, ElementSolutionVector) ElementSolutionVector; + typedef typename GET_PROP_TYPE(TypeTag, PrimaryVariables) PrimaryVariables; + typedef typename GET_PROP_TYPE(TypeTag, BoundaryTypes) BoundaryTypes; + typedef typename GET_PROP_TYPE(TypeTag, ElementBoundaryTypes) ElementBoundaryTypes; + typedef typename GET_PROP_TYPE(TypeTag, VolumeVariables) VolumeVariables; + typedef typename GET_PROP_TYPE(TypeTag, ElementVolumeVariables) ElementVolumeVariables; + + // copying the local residual class is not a good idea + CCLocalResidual(const CCLocalResidual &); + +public: + CCLocalResidual() + { } + + ~CCLocalResidual() + { } + + /*! + * \brief Initialize the local residual. + * + * This assumes that all objects of the simulation have been fully + * allocated but not necessarily initialized completely. + * + * \param prob The representation of the physical problem to be + * solved. + */ + void init(Problem &prob) + { problemPtr_ = &prob; } + + /*! + * \brief Compute the local residual, i.e. the deviation of the + * equations from zero. + * + * \param element The DUNE Codim<0> entity for which the residual + * ought to be calculated + */ + void eval(const Element &element) + { + FVElementGeometry fvElemGeom; + + fvElemGeom.update(gridView_(), element); + fvElemGeomPtr_ = &fvElemGeom; + + ElementVolumeVariables volVarsPrev, volVarsCur; + // update the hints + model_().setHints(element, volVarsPrev, volVarsCur); + + volVarsPrev.update(problem_(), + element, + fvGeometry_(), + true /* oldSol? */); + volVarsCur.update(problem_(), + element, + fvGeometry_(), + false /* oldSol? */); + + ElementBoundaryTypes bcTypes; + bcTypes.update(problem_(), element, fvGeometry_()); + + // this is pretty much a HACK because the internal state of + // the problem is not supposed to be changed during the + // evaluation of the residual. (Reasons: It is a violation of + // abstraction, makes everything more prone to errors and is + // not thread save.) The real solution are context objects! + problem_().updateCouplingParams(element); + + asImp_().eval(element, fvGeometry_(), volVarsPrev, volVarsCur, bcTypes); + } + + /*! + * \brief Compute the storage term for the current solution. + * + * This can be used to figure out how much of each conservation + * quantity is inside the element. + * + * \param element The DUNE Codim<0> entity for which the storage + * term ought to be calculated + */ + void evalStorage(const Element &element) + { + elemPtr_ = &element; + + FVElementGeometry fvElemGeom; + fvElemGeom.update(gridView_(), element); + fvElemGeomPtr_ = &fvElemGeom; + + ElementBoundaryTypes bcTypes; + bcTypes.update(problem_(), element, fvGeometry_()); + bcTypesPtr_ = &bcTypes; + + // no previous volume variables! + prevVolVarsPtr_ = 0; + + ElementVolumeVariables volVars; + + // update the hints + model_().setHints(element, volVars); + + // calculate volume current variables + volVars.update(problem_(), element, fvGeometry_(), false); + curVolVarsPtr_ = &volVars; + + asImp_().evalStorage_(); + } + + /*! + * \brief Compute the flux term for the current solution. + * + * \param element The DUNE Codim<0> entity for which the residual + * ought to be calculated + * \param curVolVars The volume averaged variables for all + * sub-contol volumes of the element + */ + void evalFluxes(const Element &element, + const ElementVolumeVariables &curVolVars) + { + elemPtr_ = &element; + + FVElementGeometry fvElemGeom; + fvElemGeom.update(gridView_(), element); + fvElemGeomPtr_ = &fvElemGeom; + + ElementBoundaryTypes bcTypes; + bcTypes.update(problem_(), element, fvGeometry_()); + + residual_.resize(1); + residual_ = 0; + + bcTypesPtr_ = &bcTypes; + prevVolVarsPtr_ = 0; + curVolVarsPtr_ = &curVolVars; + asImp_().evalFluxes_(); + } + + /*! + * \brief Compute the local residual, i.e. the deviation of the + * equations from zero. + * + * \param element The DUNE Codim<0> entity for which the residual + * ought to be calculated + * \param fvElemGeom The finite-volume geometry of the element + * \param prevVolVars The volume averaged variables for all + * sub-control volumes of the element at the previous + * time level + * \param curVolVars The volume averaged variables for all + * sub-control volumes of the element at the current + * time level + * \param bcTypes The types of the boundary conditions for all + * vertices of the element + */ + void eval(const Element &element, + const FVElementGeometry &fvElemGeom, + const ElementVolumeVariables &prevVolVars, + const ElementVolumeVariables &curVolVars, + const ElementBoundaryTypes &bcTypes) + { + Valgrind::CheckDefined(prevVolVars); + Valgrind::CheckDefined(curVolVars); + +#if !defined NDEBUG && HAVE_VALGRIND + for (int i=0; i < fvElemGeom.numNeighbors; i++) { + prevVolVars[i].checkDefined(); + curVolVars[i].checkDefined(); + } +#endif // HAVE_VALGRIND + + elemPtr_ = &element; + fvElemGeomPtr_ = &fvElemGeom; + bcTypesPtr_ = &bcTypes; + prevVolVarsPtr_ = &prevVolVars; + curVolVarsPtr_ = &curVolVars; + + // resize the vectors for all terms + residual_.resize(1); + storageTerm_.resize(1); + + residual_ = 0.0; + storageTerm_ = 0.0; + + asImp_().evalFluxes_(); + +#if !defined NDEBUG && HAVE_VALGRIND + Valgrind::CheckDefined(residual_[0]); +#endif // HAVE_VALGRIND + + asImp_().evalVolumeTerms_(); + +#if !defined NDEBUG && HAVE_VALGRIND + Valgrind::CheckDefined(residual_[0]); +#endif // HAVE_VALGRIND + + // evaluate the boundary conditions + asImp_().evalBoundary_(); + +#if !defined NDEBUG && HAVE_VALGRIND + Valgrind::CheckDefined(residual_[0]); +#endif // HAVE_VALGRIND + } + + /*! + * \brief Add Outflow boundary conditions for a single sub-control + * volume face to the local residual. + * + * \note This is so far an empty method doing not more than + * nothing. A beta version is available for the 2p2c and + * 2p2cni model. There you can find a sample implementation. + */ + void evalOutflowSegment(const IntersectionIterator &isIt, + int scvIdx, + int boundaryFaceIdx) + {} + + /*! + * \brief Returns the local residual for all sub-control + * volumes of the element. + */ + const ElementSolutionVector &residual() const + { return residual_; } + + /*! + * \brief Returns the local residual for a given sub-control + * volume of the element. + * + * \param scvIdx The local index of the sub-control volume + * (i.e. the element's local vertex index) + */ + const PrimaryVariables &residual(int scvIdx) const + { return residual_[scvIdx]; } + + /*! + * \brief Returns the storage term for all sub-control volumes of the + * element. + */ + const ElementSolutionVector &storageTerm() const + { return storageTerm_; } + + /*! + * \brief Returns the storage term for a given sub-control volumes + * of the element. + */ + const PrimaryVariables &storageTerm(int scvIdx) const + { return storageTerm_[scvIdx]; } + + void evalOutflowSegment(const IntersectionIterator &isIt) + { + const BoundaryTypes &bcTypes = this->bcTypes_(0); + + // deal with outflow boundaries + if (bcTypes.hasOutflow()) + { + PrimaryVariables values(0.0); + asImp_().computeFlux(values, + /*boundaryFaceIdx=*/isIt->indexInInside(), + true); + Valgrind::CheckDefined(values); + + for (int eqIdx = 0; eqIdx < numEq; ++eqIdx) + { + if (!bcTypes.isOutflow(eqIdx) ) + continue; + this->residual_[0][eqIdx] += values[eqIdx]; + } + } + } +protected: + Implementation &asImp_() + { + assert(static_cast<Implementation*>(this) != 0); + return *static_cast<Implementation*>(this); + } + + const Implementation &asImp_() const + { + assert(static_cast<const Implementation*>(this) != 0); + return *static_cast<const Implementation*>(this); + } + + /*! + * \brief Evaluate the boundary conditions + * of the current element. + */ + void evalBoundary_() + { + if (bcTypes_().hasNeumann() || bcTypes_().hasOutflow()) + asImp_().evalBoundaryFluxes_(); + +#if !defined NDEBUG && HAVE_VALGRIND + Valgrind::CheckDefined(residual_[0]); +#endif // HAVE_VALGRIND + + if (bcTypes_().hasDirichlet()) + asImp_().evalDirichlet_(); + } + + /*! + * \brief Set the values of the Dirichlet boundary control volumes + * of the current element. + */ + void evalDirichlet_() + { + PrimaryVariables tmp(0); + + const BoundaryTypes &bcTypes = bcTypes_(0); + if (! bcTypes.hasDirichlet()) + return; + + Valgrind::SetUndefined(tmp); + // HACK: ask for Dirichlet value at element center + asImp_().problem_().dirichletAtPos(tmp, element_().geometry().center()); + + // set the dirichlet conditions + for (int eqIdx = 0; eqIdx < numEq; ++eqIdx) { + if (!bcTypes.isDirichlet(eqIdx)) + continue; + int pvIdx = bcTypes.eqToDirichletIndex(eqIdx); + assert(0 <= pvIdx && pvIdx < numEq); + Valgrind::CheckDefined(tmp[pvIdx]); + + residual_[0][eqIdx] = + (curPriVar_(0, pvIdx) - tmp[pvIdx]); + + storageTerm_[0][eqIdx] = 0.0; + } + } + + /*! + * \brief Add all Neumann and outflow boundary conditions to the local + * residual. + */ + void evalBoundaryFluxes_() + { + IntersectionIterator isIt = gridView_().ibegin(element_()); + const IntersectionIterator &endIt = gridView_().iend(element_()); + for (; isIt != endIt; ++isIt) + { + // handle only faces on the boundary + if (!isIt->boundary()) + continue; + + // add the residual of all vertices of the boundary + // segment + asImp_().evalNeumannSegment_(isIt); + + // evaluate the outflow conditions at the boundary face + // ATTENTION: This is so far a beta version that is only for the 2p2c and 2p2cni model + // available and not thoroughly tested. + asImp_().evalOutflowSegment(isIt); + } + } + + /*! + * \brief Add Neumann boundary conditions for a single sub-control + * volume face to the local residual. + */ + void evalNeumannSegment_(const IntersectionIterator &isIt) + { + // temporary vector to store the neumann boundary fluxes + PrimaryVariables values(0.0); + + BoundaryTypes bcTypes; + problem_().boundaryTypes(bcTypes, *isIt); + + // deal with neumann boundaries + if (bcTypes.hasNeumann()) { + Valgrind::SetUndefined(values); + problem_().boxSDNeumann(values, + element_(), + fvGeometry_(), + *isIt, + /*scvIdx=*/0, + /*boundaryFaceIdx=*/isIt->indexInInside(), + curVolVars_()); + values *= isIt->geometry().volume() + * curVolVars_(0).extrusionFactor(); + Valgrind::CheckDefined(values); + + // set the neumann conditions + for (int eqIdx = 0; eqIdx < numEq; ++eqIdx) { + if (!bcTypes.isNeumann(eqIdx)) + continue; + residual_[0][eqIdx] += values[eqIdx]; + } + } + } + + /*! + * \brief Add the flux terms to the local residual of all + * sub-control volumes of the current element. + */ + void evalFluxes_() + { + // calculate the mass flux over the faces and subtract + // it from the local rates + int faceIdx = -1; + IntersectionIterator isIt = gridView_().ibegin(element_()); + IntersectionIterator isEndIt = gridView_().iend(element_()); + for (; isIt != isEndIt; ++isIt) { + if (!isIt->neighbor()) + continue; + + faceIdx++; + PrimaryVariables flux; + + Valgrind::SetUndefined(flux); + asImp_().computeFlux(flux, faceIdx); + Valgrind::CheckDefined(flux); + + flux *= curVolVars_(0).extrusionFactor(); + + // The balance equation for a finite volume is: + // + // dStorage/dt = Flux + Source + // + // where the 'Flux' and the 'Source' terms represent the + // mass per second which _ENTER_ the finite + // volume. Re-arranging this, we get + // + // dStorage/dt - Source - Flux = 0 + // + // Since the flux calculated by computeFlux() goes _OUT_ + // of sub-control volume i and _INTO_ sub-control volume + // j, we need to add the flux to finite volume i and + // subtract it from finite volume j + residual_[0] += flux; + } + } + + /*! + * \brief Set the local residual to the storage terms of all + * sub-control volumes of the current element. + */ + void evalStorage_() + { + storageTerm_.resize(1); + storageTerm_ = 0; + + // calculate the amount of conservation each quantity inside + // all sub control volumes + Valgrind::SetUndefined(storageTerm_[0]); + asImp_().computeStorage(storageTerm_[0], 0, /*isOldSol=*/false); + storageTerm_[0] *= fvGeometry_().subContVol[0].volume + * curVolVars_(0).extrusionFactor(); + Valgrind::CheckDefined(storageTerm_[0]); + } + + /*! + * \brief Add the change in the storage terms and the source term + * to the local residual of all sub-control volumes of the + * current element. + */ + void evalVolumeTerms_() + { + // evaluate the volume terms (storage + source terms) + for (int i=0; i < 1; i++) + { + Scalar extrusionFactor = + curVolVars_(i).extrusionFactor(); + + PrimaryVariables tmp(0.); + + // mass balance within the element. this is the + // \f$\frac{m}{\partial t}\f$ term if using implicit + // euler as time discretization. + // + // TODO (?): we might need a more explicit way for + // doing the time discretization... + Valgrind::SetUndefined(storageTerm_[i]); + Valgrind::SetUndefined(tmp); + asImp_().computeStorage(storageTerm_[i], i, false); + asImp_().computeStorage(tmp, i, true); + Valgrind::CheckDefined(storageTerm_[i]); + Valgrind::CheckDefined(tmp); + + storageTerm_[i] -= tmp; + storageTerm_[i] *= + fvGeometry_().subContVol[i].volume + / problem_().timeManager().timeStepSize() + * extrusionFactor; + residual_[i] += storageTerm_[i]; + + // subtract the source term from the local rate + Valgrind::SetUndefined(tmp); + asImp_().computeSource(tmp, i); + Valgrind::CheckDefined(tmp); + tmp *= fvGeometry_().subContVol[i].volume * extrusionFactor; + residual_[i] -= tmp; + + // make sure that only defined quantities were used + // to calculate the residual. + Valgrind::CheckDefined(residual_[i]); + } + } + + /*! + * \brief Returns a reference to the problem. + */ + const Problem &problem_() const + { return *problemPtr_; }; + + /*! + * \brief Returns a reference to the model. + */ + const Model &model_() const + { return problem_().model(); }; + + /*! + * \brief Returns a reference to the vertex mapper. + */ + const VertexMapper &vertexMapper_() const + { return problem_().vertexMapper(); }; + + /*! + * \brief Returns a reference to the grid view. + */ + const GridView &gridView_() const + { return problem_().gridView(); } + + /*! + * \brief Returns a reference to the current element. + */ + const Element &element_() const + { + Valgrind::CheckDefined(elemPtr_); + return *elemPtr_; + } + + /*! + * \brief Returns a reference to the current element's finite + * volume geometry. + */ + const FVElementGeometry &fvGeometry_() const + { + Valgrind::CheckDefined(fvElemGeomPtr_); + return *fvElemGeomPtr_; + } + + /*! + * \brief Returns a reference to the primary variables of + * the last time step of the i'th + * sub-control volume of the current element. + */ + const PrimaryVariables &prevPriVars_(int i) const + { + return prevVolVars_(i).priVars(); + } + + /*! + * \brief Returns a reference to the primary variables of the i'th + * sub-control volume of the current element. + */ + const PrimaryVariables &curPriVars_(int i) const + { + return curVolVars_(i).priVars(); + } + + /*! + * \brief Returns the j'th primary of the i'th sub-control volume + * of the current element. + */ + Scalar curPriVar_(int i, int j) const + { + return curVolVars_(i).priVar(j); + } + + /*! + * \brief Returns a reference to the current volume variables of + * all sub-control volumes of the current element. + */ + const ElementVolumeVariables &curVolVars_() const + { + Valgrind::CheckDefined(curVolVarsPtr_); + return *curVolVarsPtr_; + } + + /*! + * \brief Returns a reference to the volume variables of the i-th + * sub-control volume of the current element. + */ + const VolumeVariables &curVolVars_(int i) const + { + return curVolVars_()[i]; + } + + /*! + * \brief Returns a reference to the previous time step's volume + * variables of all sub-control volumes of the current + * element. + */ + const ElementVolumeVariables &prevVolVars_() const + { + Valgrind::CheckDefined(prevVolVarsPtr_); + return *prevVolVarsPtr_; + } + + /*! + * \brief Returns a reference to the previous time step's volume + * variables of the i-th sub-control volume of the current + * element. + */ + const VolumeVariables &prevVolVars_(int i) const + { + return prevVolVars_()[i]; + } + + /*! + * \brief Returns a reference to the boundary types of all + * sub-control volumes of the current element. + */ + const ElementBoundaryTypes &bcTypes_() const + { + Valgrind::CheckDefined(bcTypesPtr_); + return *bcTypesPtr_; + } + + /*! + * \brief Returns a reference to the boundary types of the i-th + * sub-control volume of the current element. + */ + const BoundaryTypes &bcTypes_(int i) const + { + return bcTypes_()[i]; + } + +protected: + ElementSolutionVector storageTerm_; + ElementSolutionVector residual_; + + // The problem we would like to solve + Problem *problemPtr_; + + const Element *elemPtr_; + const FVElementGeometry *fvElemGeomPtr_; + + // current and previous secondary variables for the element + const ElementVolumeVariables *prevVolVarsPtr_; + const ElementVolumeVariables *curVolVarsPtr_; + + const ElementBoundaryTypes *bcTypesPtr_; +}; + +} + +#endif diff --git a/dumux/implicit/cellcentered/ccmodel.hh b/dumux/implicit/cellcentered/ccmodel.hh new file mode 100644 index 0000000000..0f29321213 --- /dev/null +++ b/dumux/implicit/cellcentered/ccmodel.hh @@ -0,0 +1,755 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * Copyright (C) 2008-2010 by Andreas Lauser * + * Copyright (C) 2008-2010 by Bernd Flemisch * + * Institute for Modelling Hydraulic and Environmental Systems * + * University of Stuttgart, Germany * + * email: <givenname>.<name>@iws.uni-stuttgart.de * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \file + * + * \brief Base class for models using box discretization + */ +#ifndef DUMUX_CC_MODEL_HH +#define DUMUX_CC_MODEL_HH + +#include "ccproperties.hh" +#include "ccpropertydefaults.hh" + +#include "ccelementvolumevariables.hh" +#include "cclocaljacobian.hh" +#include "cclocalresidual.hh" + +#include <dumux/parallel/vertexhandles.hh> + +#include <dune/grid/common/geometry.hh> + +namespace Dumux +{ + +/*! + * \ingroup CCModel + * + * \brief The base class for the vertex centered finite volume + * discretization scheme. + */ +template<class TypeTag> +class CCModel +{ + typedef typename GET_PROP_TYPE(TypeTag, Model) Implementation; + typedef typename GET_PROP_TYPE(TypeTag, Problem) Problem; + typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; + typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; + typedef typename GET_PROP_TYPE(TypeTag, ElementMapper) ElementMapper; + typedef typename GET_PROP_TYPE(TypeTag, VertexMapper) VertexMapper; + typedef typename GET_PROP_TYPE(TypeTag, DofMapper) DofMapper; + typedef typename GET_PROP_TYPE(TypeTag, SolutionVector) SolutionVector; + typedef typename GET_PROP_TYPE(TypeTag, PrimaryVariables) PrimaryVariables; + typedef typename GET_PROP_TYPE(TypeTag, JacobianAssembler) JacobianAssembler; + typedef typename GET_PROP_TYPE(TypeTag, ElementVolumeVariables) ElementVolumeVariables; + typedef typename GET_PROP_TYPE(TypeTag, VolumeVariables) VolumeVariables; + + enum { + numEq = GET_PROP_VALUE(TypeTag, NumEq), + dim = GridView::dimension + }; + + typedef typename GET_PROP_TYPE(TypeTag, FVElementGeometry) FVElementGeometry; + typedef typename GET_PROP_TYPE(TypeTag, LocalJacobian) LocalJacobian; + typedef typename GET_PROP_TYPE(TypeTag, LocalResidual) LocalResidual; + typedef typename GET_PROP_TYPE(TypeTag, NewtonMethod) NewtonMethod; + typedef typename GET_PROP_TYPE(TypeTag, NewtonController) NewtonController; + + typedef typename GridView::ctype CoordScalar; + typedef typename GridView::template Codim<0>::Entity Element; + typedef typename GridView::template Codim<0>::Iterator ElementIterator; + typedef typename GridView::template Codim<dim>::Entity Vertex; + typedef typename GridView::template Codim<dim>::Iterator VertexIterator; + typedef typename GridView::IntersectionIterator IntersectionIterator; + + typedef typename Dune::GenericReferenceElements<CoordScalar, dim> ReferenceElements; + typedef typename Dune::GenericReferenceElement<CoordScalar, dim> ReferenceElement; + + // copying a model is not a good idea + CCModel(const CCModel &); + +public: + /*! + * \brief The constructor. + */ + CCModel() + {} + + ~CCModel() + { delete jacAsm_; } + + /*! + * \brief Apply the initial conditions to the model. + * + * \param prob The object representing the problem which needs to + * be simulated. + */ + void init(Problem &prob) + { + problemPtr_ = &prob; + + updateBoundaryIndices_(); + + int nDofs = asImp_().numDofs(); + uCur_.resize(nDofs); + uPrev_.resize(nDofs); + + localJacobian_.init(problem_()); + jacAsm_ = new JacobianAssembler(); + jacAsm_->init(problem_()); + + asImp_().applyInitialSolution_(); + + // also set the solution of the "previous" time step to the + // initial solution. + uPrev_ = uCur_; + } + + // functions for interface consistence + void setHints(const Element &elem, + ElementVolumeVariables &prevVolVars, + ElementVolumeVariables &curVolVars) const + {} + + void setHints(const Element &elem, + ElementVolumeVariables &curVolVars) const + {} + + void updatePrevHints() + {} + + void updateCurHints(const Element &elem, + const ElementVolumeVariables &ev) const + {} + + /*! + * \brief Compute the global residual for an arbitrary solution + * vector. + * + * \param dest Stores the result + * \param u The solution for which the residual ought to be calculated + */ + Scalar globalResidual(SolutionVector &dest, + const SolutionVector &u) + { + SolutionVector tmp(curSol()); + curSol() = u; + Scalar res = globalResidual(dest); + curSol() = tmp; + return res; + } + + /*! + * \brief Compute the global residual for the current solution + * vector. + * + * \param dest Stores the result + */ + Scalar globalResidual(SolutionVector &dest) + { + dest = 0; + + ElementIterator elemIt = gridView_().template begin<0>(); + const ElementIterator elemEndIt = gridView_().template end<0>(); + for (; elemIt != elemEndIt; ++elemIt) { + localResidual().eval(*elemIt); + + int globalI = elementMapper().map(*elemIt); + dest[globalI] = localResidual().residual(0); + } + + // calculate the square norm of the residual + Scalar result2 = dest.two_norm2(); + if (gridView_().comm().size() > 1) + result2 = gridView_().comm().sum(result2); + + return std::sqrt(result2); + } + + /*! + * \brief Compute the integral over the domain of the storage + * terms of all conservation quantities. + * + * \param dest Stores the result + */ + void globalStorage(PrimaryVariables &dest) + { + dest = 0; + + ElementIterator elemIt = gridView_().template begin<0>(); + const ElementIterator elemEndIt = gridView_().template end<0>(); + for (; elemIt != elemEndIt; ++elemIt) { + localResidual().evalStorage(*elemIt); + + dest += localResidual().storageTerm()[0]; + }; + + if (gridView_().comm().size() > 1) + dest = gridView_().comm().sum(dest); + } + + /*! + * \brief Reference to the current solution as a block vector. + */ + const SolutionVector &curSol() const + { return uCur_; } + + /*! + * \brief Reference to the current solution as a block vector. + */ + SolutionVector &curSol() + { return uCur_; } + + /*! + * \brief Reference to the previous solution as a block vector. + */ + const SolutionVector &prevSol() const + { return uPrev_; } + + /*! + * \brief Reference to the previous solution as a block vector. + */ + SolutionVector &prevSol() + { return uPrev_; } + + /*! + * \brief Returns the operator assembler for the global jacobian of + * the problem. + */ + JacobianAssembler &jacobianAssembler() + { return *jacAsm_; } + + /*! + * \copydoc jacobianAssembler() + */ + const JacobianAssembler &jacobianAssembler() const + { return *jacAsm_; } + + /*! + * \brief Returns the local jacobian which calculates the local + * stiffness matrix for an arbitrary element. + * + * The local stiffness matrices of the element are used by + * the jacobian assembler to produce a global linerization of the + * problem. + */ + LocalJacobian &localJacobian() + { return localJacobian_; } + /*! + * \copydoc localJacobian() + */ + const LocalJacobian &localJacobian() const + { return localJacobian_; } + + /*! + * \brief Returns the local residual function. + */ + LocalResidual &localResidual() + { return localJacobian().localResidual(); } + /*! + * \copydoc localResidual() + */ + const LocalResidual &localResidual() const + { return localJacobian().localResidual(); } + + /*! + * \brief Returns the relative weight of a primary variable for + * calculating relative errors. + * + * \param dofIdx The global index of the control volume + * \param pvIdx The index of the primary variable + */ + Scalar primaryVarWeight(int dofIdx, int pvIdx) const + { + return 1.0/std::max(std::abs(this->prevSol()[dofIdx][pvIdx]), 1.0); + } + + /*! + * \brief Returns the relative error between two vectors of + * primary variables. + * + * \param vertexIdx The global index of the control volume's + * associated vertex + * \param pv1 The first vector of primary variables + * \param pv2 The second vector of primary variables + * + * \todo The vertexIdx argument is pretty hacky. it is required by + * models with pseudo primary variables (i.e. the primary + * variable switching models). the clean solution would be + * to access the pseudo primary variables from the primary + * variables. + */ + Scalar relativeErrorVertex(int vertexIdx, + const PrimaryVariables &pv1, + const PrimaryVariables &pv2) + { + Scalar result = 0.0; + for (int j = 0; j < numEq; ++j) { + //Scalar weight = asImp_().primaryVarWeight(vertexIdx, j); + //Scalar eqErr = std::abs(pv1[j] - pv2[j])*weight; + Scalar eqErr = std::abs(pv1[j] - pv2[j]); + eqErr /= std::max<Scalar>(1.0, std::abs(pv1[j] + pv2[j])/2); + + result = std::max(result, eqErr); + } + return result; + } + + /*! + * \brief Try to progress the model to the next timestep. + * + * \param solver The non-linear solver + * \param controller The controller which specifies the behaviour + * of the non-linear solver + */ + bool update(NewtonMethod &solver, + NewtonController &controller) + { +#if HAVE_VALGRIND + for (size_t i = 0; i < curSol().size(); ++i) + Valgrind::CheckDefined(curSol()[i]); +#endif // HAVE_VALGRIND + + asImp_().updateBegin(); + + bool converged = solver.execute(controller); + if (converged) { + asImp_().updateSuccessful(); + } + else + asImp_().updateFailed(); + +#if HAVE_VALGRIND + for (size_t i = 0; i < curSol().size(); ++i) { + Valgrind::CheckDefined(curSol()[i]); + } +#endif // HAVE_VALGRIND + + return converged; + } + + + /*! + * \brief Called by the update() method before it tries to + * apply the newton method. This is primary a hook + * which the actual model can overload. + */ + void updateBegin() + { } + + + /*! + * \brief Called by the update() method if it was + * successful. This is primary a hook which the actual + * model can overload. + */ + void updateSuccessful() + { }; + + /*! + * \brief Called by the update() method if it was + * unsuccessful. This is primary a hook which the actual + * model can overload. + */ + void updateFailed() + { + // Reset the current solution to the one of the + // previous time step so that we can start the next + // update at a physically meaningful solution. + uCur_ = uPrev_; + + jacAsm_->reassembleAll(); + }; + + /*! + * \brief Called by the problem if a time integration was + * successful, post processing of the solution is done and + * the result has been written to disk. + * + * This should prepare the model for the next time integration. + */ + void advanceTimeLevel() + { + // make the current solution the previous one. + uPrev_ = uCur_; + + updatePrevHints(); + } + + /*! + * \brief Serializes the current state of the model. + * + * \tparam Restarter The type of the serializer class + * + * \param res The serializer object + */ + template <class Restarter> + void serialize(Restarter &res) + { res.template serializeEntities<0>(asImp_(), this->gridView_()); } + + /*! + * \brief Deserializes the state of the model. + * + * \tparam Restarter The type of the serializer class + * + * \param res The serializer object + */ + template <class Restarter> + void deserialize(Restarter &res) + { + res.template deserializeEntities<0>(asImp_(), this->gridView_()); + prevSol() = curSol(); + } + + /*! + * \brief Write the current solution for a vertex to a restart + * file. + * + * \param outstream The stream into which the vertex data should + * be serialized to + * \param vert The DUNE Codim<dim> entity which's data should be + * serialized + */ + void serializeEntity(std::ostream &outstream, + const Element &element) + { + int elemIdx = dofMapper().map(element); + + // write phase state + if (!outstream.good()) { + DUNE_THROW(Dune::IOError, + "Could not serialize element " + << elemIdx); + } + + for (int eqIdx = 0; eqIdx < numEq; ++eqIdx) { + outstream << curSol()[elemIdx][eqIdx] << " "; + } + } + + /*! + * \brief Reads the current solution variables for a vertex from a + * restart file. + * + * \param instream The stream from which the vertex data should + * be deserialized from + * \param vert The DUNE Codim<dim> entity which's data should be + * deserialized + */ + void deserializeEntity(std::istream &instream, + const Element &element) + { + int elemIdx = dofMapper().map(element); + for (int eqIdx = 0; eqIdx < numEq; ++eqIdx) { + if (!instream.good()) + DUNE_THROW(Dune::IOError, + "Could not deserialize element " + << elemIdx); + instream >> curSol()[elemIdx][eqIdx]; + } + } + + /*! + * \brief Returns the number of global degrees of freedoms (DOFs) + */ + size_t numDofs() const + { return gridView_().size(0); } + + /*! + * \brief Mapper for the entities where degrees of freedoms are + * defined to indices. + * + * Here, this means a mapper for elements. + */ + const DofMapper &dofMapper() const + { return problem_().elementMapper(); }; + + /*! + * \brief Mapper for vertices to indices. + */ + const VertexMapper &vertexMapper() const + { return problem_().vertexMapper(); }; + + /*! + * \brief Mapper for elements to indices. + */ + const ElementMapper &elementMapper() const + { return problem_().elementMapper(); }; + + /*! + * \brief Resets the Jacobian matrix assembler, so that the + * boundary types can be altered. + */ + void resetJacobianAssembler () + { + delete jacAsm_; + jacAsm_ = new JacobianAssembler; + jacAsm_->init(problem_()); + } + + /*! + * \brief Update the weights of all primary variables within an + * element given the complete set of volume variables + * + * \param element The DUNE codim 0 entity + * \param volVars All volume variables for the element + */ + void updatePVWeights(const Element &element, + const ElementVolumeVariables &volVars) const + { }; + + /*! + * \brief Add the vector fields for analysing the convergence of + * the newton method to the a VTK multi writer. + * + * \tparam MultiWriter The type of the VTK multi writer + * + * \param writer The VTK multi writer object on which the fields should be added. + * \param u The solution function + * \param deltaU The delta of the solution function before and after the Newton update + */ + template <class MultiWriter> + void addConvergenceVtkFields(MultiWriter &writer, + const SolutionVector &u, + const SolutionVector &deltaU) + {} + + /*! + * \brief Add the quantities of a time step which ought to be written to disk. + * + * This should be overwritten by the actual model if any secondary + * variables should be written out. Read: This should _always_ be + * overwritten by well behaved models! + * + * \tparam MultiWriter The type of the VTK multi writer + * + * \param sol The global vector of primary variable values. + * \param writer The VTK multi writer where the fields should be added. + */ + template <class MultiWriter> + void addOutputVtkFields(const SolutionVector &sol, + MultiWriter &writer) + { + typedef Dune::BlockVector<Dune::FieldVector<Scalar, 1> > ScalarField; + + // create the required scalar fields + unsigned numElements = this->gridView_().size(0); + + // global defect of the two auxiliary equations + ScalarField* x[numEq]; + for (int i = 0; i < numEq; ++i) { + x[i] = writer.allocateManagedBuffer(numElements); + } + + ElementIterator eIt = this->gridView_().template begin<0>(); + ElementIterator eEndIt = this->gridView_().template end<0>(); + for (; eIt != eEndIt; ++ eIt) + { + int globalIdx = elementMapper().map(*eIt); + for (int i = 0; i < numEq; ++i) { + (*x[i])[globalIdx] = sol[globalIdx][i]; + } + } + + for (int i = 0; i < numEq; ++i) { + std::ostringstream oss; + oss << "primaryVar_" << i; + writer.attachCellData(*x[i], oss.str()); + } + } + + /*! + * \brief Reference to the grid view of the spatial domain. + */ + const GridView &gridView() const + { return problem_().gridView(); } + + /*! + * \brief Returns true if the control volume touches + * the grid's boundary. + * + * \param globalIdx The global index of the control volume + */ + bool onBoundary(int globalIdx) const + { return boundaryIndices_[globalIdx]; } + + /*! + * \brief Returns true if the control volume touches + * the grid's boundary. + * + * \param elem A DUNE Codim<0> entity coinciding with the control + * volume. + */ + bool onBoundary(const Element &elem) const + { return onBoundary(elementMapper().map(elem)); } + + /*! + * \brief Fill the fluid state according to the primary variables. + * + * Taking the information from the primary variables, + * the fluid state is filled with every information that is + * necessary to evaluate the model's local residual. + * + * \param primaryVariables The primary variables of the model. + * \param problem The problem at hand. + * \param element The current element. + * \param elementGeometry The finite volume element geometry. + * \param scvIdx The index of the subcontrol volume. + * \param fluidState The fluid state to fill. + */ + template <class FluidState> + static void completeFluidState(const PrimaryVariables& primaryVariables, + const Problem& problem, + const Element& element, + const FVElementGeometry& elementGeometry, + FluidState& fluidState) + { + VolumeVariables::completeFluidState(primaryVariables, problem, element, + elementGeometry, fluidState); + } +protected: + /*! + * \brief A reference to the problem on which the model is applied. + */ + Problem &problem_() + { return *problemPtr_; } + /*! + * \copydoc problem_() + */ + const Problem &problem_() const + { return *problemPtr_; } + + /*! + * \brief Reference to the grid view of the spatial domain. + */ + const GridView &gridView_() const + { return problem_().gridView(); } + + /*! + * \brief Reference to the local residal object + */ + LocalResidual &localResidual_() + { return localJacobian_.localResidual(); } + + /*! + * \brief Applies the initial solution for all vertices of the grid. + */ + void applyInitialSolution_() + { + // first set the whole domain to zero + uCur_ = Scalar(0.0); + + FVElementGeometry fvElemGeom; + + // iterate through leaf grid and evaluate initial + // condition at the center of each sub control volume + // + // TODO: the initial condition needs to be unique for + // each vertex. we should think about the API... + ElementIterator it = gridView_().template begin<0>(); + const ElementIterator &eendit = gridView_().template end<0>(); + for (; it != eendit; ++it) { + // deal with the current element + fvElemGeom.update(gridView_(), *it); + + // map the local vertex index to the global one + int globalIdx = elementMapper().map(*it); + + // let the problem do the dirty work of nailing down + // the initial solution. + PrimaryVariables initVal; + Valgrind::SetUndefined(initVal); + problem_().initial(initVal, + *it, + fvElemGeom, + 0); + Valgrind::CheckDefined(initVal); + + uCur_[globalIdx] = initVal; + Valgrind::CheckDefined(uCur_[globalIdx]); + } + } + + /*! + * \brief Find all indices of boundary vertices. + * + * For this we need to loop over all intersections (which is slow + * in general). If the DUNE grid interface would provide a + * onBoundary() method for entities this could be done in a much + * nicer way (actually this would not be necessary) + */ + void updateBoundaryIndices_() + { + boundaryIndices_.resize(numDofs()); + std::fill(boundaryIndices_.begin(), boundaryIndices_.end(), false); + + ElementIterator eIt = gridView_().template begin<0>(); + ElementIterator eEndIt = gridView_().template end<0>(); + for (; eIt != eEndIt; ++eIt) { + IntersectionIterator isIt = gridView_().ibegin(*eIt); + IntersectionIterator isEndIt = gridView_().iend(*eIt); + for (; isIt != isEndIt; ++isIt) { + if (!isIt->boundary()) + continue; + + int globalIdx = elementMapper().map(*eIt); + boundaryIndices_[globalIdx] = true; + } + } + } + + // the problem we want to solve. defines the constitutive + // relations, matxerial laws, etc. + Problem *problemPtr_; + + // calculates the local jacobian matrix for a given element + LocalJacobian localJacobian_; + // Linearizes the problem at the current time step using the + // local jacobian + JacobianAssembler *jacAsm_; + + // the set of all indices of vertices on the boundary + std::vector<bool> boundaryIndices_; + + // cur is the current iterative solution, prev the converged + // solution of the previous time step + SolutionVector uCur_; + SolutionVector uPrev_; + +private: + /*! + * \brief Returns whether messages should be printed + */ + bool verbose_() const + { return gridView_().comm().rank() == 0; }; + + Implementation &asImp_() + { return *static_cast<Implementation*>(this); } + const Implementation &asImp_() const + { return *static_cast<const Implementation*>(this); } + + bool enableHints_; +}; +} + +#endif diff --git a/dumux/implicit/cellcentered/ccproblem.hh b/dumux/implicit/cellcentered/ccproblem.hh new file mode 100644 index 0000000000..ed4dbeb073 --- /dev/null +++ b/dumux/implicit/cellcentered/ccproblem.hh @@ -0,0 +1,849 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * Copyright (C) 2009 by Andreas Lauser * + * Institute for Modelling Hydraulic and Environmental Systems * + * University of Stuttgart, Germany * + * email: <givenname>.<name>@iws.uni-stuttgart.de * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \file + * \brief Base class for all problems which use the box scheme + */ +#ifndef DUMUX_CC_PROBLEM_HH +#define DUMUX_CC_PROBLEM_HH + +#include "ccproperties.hh" + +#include <dumux/io/vtkmultiwriter.hh> +#include <dumux/io/restart.hh> + +namespace Dumux +{ +/*! + * \ingroup CCModel + * \ingroup CCBaseProblems + * \brief Base class for all problems which use the box scheme. + * + * \note All quantities are specified assuming a threedimensional + * world. Problems discretized using 2D grids are assumed to be + * extruded by \f$1 m\f$ and 1D grids are assumed to have a + * cross section of \f$1m \times 1m\f$. + */ +template<class TypeTag> +class CCProblem +{ +private: + typedef typename GET_PROP_TYPE(TypeTag, Problem) Implementation; + typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; + + typedef Dumux::VtkMultiWriter<GridView> VtkMultiWriter; + + typedef typename GET_PROP_TYPE(TypeTag, NewtonMethod) NewtonMethod; + typedef typename GET_PROP_TYPE(TypeTag, NewtonController) NewtonController; + + typedef typename GET_PROP_TYPE(TypeTag, Model) Model; + typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; + typedef typename GET_PROP_TYPE(TypeTag, TimeManager) TimeManager; + + typedef typename GET_PROP_TYPE(TypeTag, VertexMapper) VertexMapper; + typedef typename GET_PROP_TYPE(TypeTag, ElementMapper) ElementMapper; + + typedef typename GET_PROP_TYPE(TypeTag, PrimaryVariables) PrimaryVariables; + typedef typename GET_PROP_TYPE(TypeTag, ElementVolumeVariables) ElementVolumeVariables; + typedef typename GET_PROP_TYPE(TypeTag, FVElementGeometry) FVElementGeometry; + typedef typename GET_PROP_TYPE(TypeTag, BoundaryTypes) BoundaryTypes; + + enum { + dim = GridView::dimension, + dimWorld = GridView::dimensionworld + }; + + typedef typename GridView::template Codim<0>::Entity Element; + typedef typename GridView::template Codim<dim>::Entity Vertex; + typedef typename GridView::template Codim<dim>::Iterator VertexIterator; + typedef typename GridView::template Codim<0>::Iterator ElementIterator; + typedef typename GridView::Intersection Intersection; + + typedef typename GridView::Grid::ctype CoordScalar; + typedef Dune::FieldVector<CoordScalar, dimWorld> GlobalPosition; + + // copying a problem is not a good idea + CCProblem(const CCProblem &); + +public: + /*! + * \brief Constructor + * + * \param timeManager The TimeManager which is used by the simulation + * \param gridView The simulation's idea about physical space + */ + CCProblem(TimeManager &timeManager, const GridView &gridView) + : gridView_(gridView) + , bboxMin_(std::numeric_limits<double>::max()) + , bboxMax_(-std::numeric_limits<double>::max()) + , elementMapper_(gridView) + , vertexMapper_(gridView) + , timeManager_(&timeManager) + , newtonMethod_(asImp_()) + , newtonCtl_(asImp_()) + { + // calculate the bounding box of the local partition of the grid view +// VertexIterator vIt = gridView.template begin<dim>(); +// const VertexIterator vEndIt = gridView.template end<dim>(); +// for (; vIt!=vEndIt; ++vIt) { +// for (int i=0; i<dim; i++) { +// bboxMin_[i] = std::min(bboxMin_[i], vIt->geometry().corner(0)[i]); +// bboxMax_[i] = std::max(bboxMax_[i], vIt->geometry().corner(0)[i]); +// } +// } + + // HACK due to current handling of Dirichlet boundary conditions + ElementIterator it = gridView.template begin<0>(); + const ElementIterator endIt = gridView.template end<0>(); + for (; it!=endIt; ++it) { + for (int i=0; i<dim; i++) { + bboxMin_[i] = std::min(bboxMin_[i], it->geometry().center()[i]); + bboxMax_[i] = std::max(bboxMax_[i], it->geometry().center()[i]); + } + } + + // communicate to get the bounding box of the whole domain + if (gridView.comm().size() > 1) + for (int i = 0; i < dim; ++i) { + bboxMin_[i] = gridView.comm().min(bboxMin_[i]); + bboxMax_[i] = gridView.comm().max(bboxMax_[i]); + } + + // set a default name for the problem + simName_ = "sim"; + + resultWriter_ = NULL; + } + + ~CCProblem() + { + delete resultWriter_; + }; + + + /*! + * \brief Called by the Dumux::TimeManager in order to + * initialize the problem. + * + * If you overload this method don't forget to call + * ParentType::init() + */ + void init() + { + // set the initial condition of the model + model().init(asImp_()); + } + + /*! + * \brief Specifies which kind of boundary condition should be + * used for which equation on a given boundary segment. + * + * \param values The boundary types for the conservation equations + * \param intersection The intersection for which the boundary type is set + */ + void boundaryTypes(BoundaryTypes &values, + const Intersection &intersection) const + { + // forward it to the method which only takes the global coordinate + asImp_().boundaryTypesAtPos(values, intersection.geometry().center()); + } + + /*! + * \brief Specifies which kind of boundary condition should be + * used for which equation on a given boundary segment. + * + * \param values The boundary types for the conservation equations + * \param pos The position of the finite volume in global coordinates + */ + void boundaryTypesAtPos(BoundaryTypes &values, + const GlobalPosition &pos) const + { + // Throw an exception (there is no reasonable default value + // for Dirichlet conditions) + DUNE_THROW(Dune::InvalidStateException, + "The problem does not provide " + "a boundaryTypes() method."); + } + + + /*! + * \brief Evaluate the boundary conditions for a dirichlet + * control volume. + * + * \param values The dirichlet values for the primary variables + * \param intersection The intersection representing the "half volume on the boundary" + * + * For this method, the \a values parameter stores primary variables. + */ + void dirichlet(PrimaryVariables &values, + const Intersection &intersection) const + { + // forward it to the method which only takes the global coordinate + asImp_().dirichletAtPos(values, intersection.geometry().center()); + } + + /*! + * \brief Evaluate the boundary conditions for a dirichlet + * control volume. + * + * \param values The dirichlet values for the primary variables + * \param pos The position of the center of the finite volume + * for which the dirichlet condition ought to be + * set in global coordinates + * + * For this method, the \a values parameter stores primary variables. + */ + void dirichletAtPos(PrimaryVariables &values, + const GlobalPosition &pos) const + { + // Throw an exception (there is no reasonable default value + // for Dirichlet conditions) + DUNE_THROW(Dune::InvalidStateException, + "The problem specifies that some boundary " + "segments are dirichlet, but does not provide " + "a dirichlet() method."); + } + + /*! + * \brief Evaluate the boundary conditions for a neumann + * boundary segment. + * + * This is the method for the case where the Neumann condition is + * potentially solution dependent and requires some box method + * specific things. + * + * \param values The neumann values for the conservation equations [kg / (m^2 *s )] + * \param element The finite element + * \param fvElemGeom The finite-volume geometry in the box scheme + * \param is The intersection between element and boundary + * \param scvIdx The local vertex index + * \param boundaryFaceIdx The index of the boundary face + * \param elemVolVars All volume variables for the element + * + * For this method, the \a values parameter stores the mass flux + * in normal direction of each phase. Negative values mean influx. + */ + void boxSDNeumann(PrimaryVariables &values, + const Element &element, + const FVElementGeometry &fvElemGeom, + const Intersection &is, + int scvIdx, + int boundaryFaceIdx, + const ElementVolumeVariables &elemVolVars) const + { + // forward it to the interface without the volume variables + asImp_().neumann(values, + element, + fvElemGeom, + is, + scvIdx, + boundaryFaceIdx); + } + + /*! + * \brief Evaluate the boundary conditions for a neumann + * boundary segment. + * + * \param values The neumann values for the conservation equations [kg / (m^2 *s )] + * \param element The finite element + * \param fvElemGeom The finite-volume geometry in the box scheme + * \param is The intersection between element and boundary + * \param scvIdx The local vertex index + * \param boundaryFaceIdx The index of the boundary face + * + * For this method, the \a values parameter stores the mass flux + * in normal direction of each phase. Negative values mean influx. + */ + void neumann(PrimaryVariables &values, + const Element &element, + const FVElementGeometry &fvElemGeom, + const Intersection &is, + int scvIdx, + int boundaryFaceIdx) const + { + // forward it to the interface with only the global position + asImp_().neumannAtPos(values, fvElemGeom.boundaryFace[boundaryFaceIdx].ipGlobal); + } + + /*! + * \brief Evaluate the boundary conditions for a neumann + * boundary segment. + * + * \param values The neumann values for the conservation equations [kg / (m^2 *s )] + * \param pos The position of the boundary face's integration point in global coordinates + * + * For this method, the \a values parameter stores the mass flux + * in normal direction of each phase. Negative values mean influx. + */ + void neumannAtPos(PrimaryVariables &values, + const GlobalPosition &pos) const + { + // Throw an exception (there is no reasonable default value + // for Neumann conditions) + DUNE_THROW(Dune::InvalidStateException, + "The problem specifies that some boundary " + "segments are neumann, but does not provide " + "a neumannAtPos() method."); + } + + /*! + * \brief Evaluate the source term for all phases within a given + * sub-control-volume. + * + * This is the method for the case where the source term is + * potentially solution dependent and requires some box method + * specific things. + * + * \param values The source and sink values for the conservation equations + * \param element The finite element + * \param fvElemGeom The finite-volume geometry in the box scheme + * \param scvIdx The local vertex index + * \param elemVolVars All volume variables for the element + * + * For this method, the \a values parameter stores the rate mass + * generated or annihilate per volume unit. Positive values mean + * that mass is created, negative ones mean that it vanishes. + */ + void boxSDSource(PrimaryVariables &values, + const Element &element, + const FVElementGeometry &fvElemGeom, + int scvIdx, + const ElementVolumeVariables &elemVolVars) const + { + // forward to solution independent, box specific interface + asImp_().source(values, element, fvElemGeom, scvIdx); + } + + /*! + * \brief Evaluate the source term for all phases within a given + * sub-control-volume. + * + * \param values The source and sink values for the conservation equations + * \param element The finite element + * \param fvElemGeom The finite-volume geometry in the box scheme + * \param scvIdx The local vertex index + * + * For this method, the \a values parameter stores the rate mass + * generated or annihilate per volume unit. Positive values mean + * that mass is created, negative ones mean that it vanishes. + */ + void source(PrimaryVariables &values, + const Element &element, + const FVElementGeometry &fvElemGeom, + int scvIdx) const + { + // forward to generic interface + asImp_().sourceAtPos(values, fvElemGeom.subContVol[scvIdx].global); + } + + /*! + * \brief Evaluate the source term for all phases within a given + * sub-control-volume. + * + * \param values The source and sink values for the conservation equations + * \param pos The position of the center of the finite volume + * for which the source term ought to be + * specified in global coordinates + * + * For this method, the \a values parameter stores the rate mass + * generated or annihilate per volume unit. Positive values mean + * that mass is created, negative ones mean that it vanishes. + */ + void sourceAtPos(PrimaryVariables &values, + const GlobalPosition &pos) const + { + DUNE_THROW(Dune::InvalidStateException, + "The problem does not provide " + "a sourceAtPos() method."); + } + + /*! + * \brief Evaluate the initial value for a control volume. + * + * \param values The initial values for the primary variables + * \param element The finite element + * \param fvElemGeom The finite-volume geometry in the box scheme + * \param scvIdx The local vertex index + * + * For this method, the \a values parameter stores primary + * variables. + */ + void initial(PrimaryVariables &values, + const Element &element, + const FVElementGeometry &fvElemGeom, + int scvIdx) const + { + // forward to generic interface + asImp_().initialAtPos(values, fvElemGeom.subContVol[scvIdx].global); + } + + /*! + * \brief Evaluate the initial value for a control volume. + * + * \param values The dirichlet values for the primary variables + * \param pos The position of the center of the finite volume + * for which the initial values ought to be + * set (in global coordinates) + * + * For this method, the \a values parameter stores primary variables. + */ + void initialAtPos(PrimaryVariables &values, + const GlobalPosition &pos) const + { + // Throw an exception (there is no reasonable default value + // for Dirichlet conditions) + DUNE_THROW(Dune::InvalidStateException, + "The problem does not provide " + "a initialAtPos() method."); + } + + /*! + * \brief Return how much the domain is extruded at a given sub-control volume. + * + * This means the factor by which a lower-dimensional (1D or 2D) + * entity needs to be expanded to get a full dimensional cell. The + * default is 1.0 which means that 1D problems are actually + * thought as pipes with a cross section of 1 m^2 and 2D problems + * are assumed to extend 1 m to the back. + */ + Scalar boxExtrusionFactor(const Element &element, + const FVElementGeometry &fvElemGeom, + int scvIdx) const + { + // forward to generic interface + return asImp_().extrusionFactorAtPos(fvElemGeom.subContVol[scvIdx].global); + } + + /*! + * \brief Return how much the domain is extruded at a given position. + * + * This means the factor by which a lower-dimensional (1D or 2D) + * entity needs to be expanded to get a full dimensional cell. The + * default is 1.0 which means that 1D problems are actually + * thought as pipes with a cross section of 1 m^2 and 2D problems + * are assumed to extend 1 m to the back. + */ + Scalar extrusionFactorAtPos(const GlobalPosition &pos) const + { return 1.0; } + + /*! + * \brief If model coupling is used, this updates the parameters + * required to calculate the coupling fluxes between the + * sub-models. + * + * By default it does nothing + * + * \param element The DUNE Codim<0> entity for which the coupling + * parameters should be computed. + */ + void updateCouplingParams(const Element &element) const + {} + + /*! + * \name Simulation steering + */ + // \{ + + /*! + * \brief Called by the time manager before the time integration. + */ + void preTimeStep() + {} + + /*! + * \brief Called by Dumux::TimeManager in order to do a time + * integration on the model. + */ + void timeIntegration() + { + const int maxFails = 10; + for (int i = 0; i < maxFails; ++i) { + if (model_.update(newtonMethod_, newtonCtl_)) + return; + + Scalar dt = timeManager().timeStepSize(); + Scalar nextDt = dt / 2; + timeManager().setTimeStepSize(nextDt); + + // update failed + std::cout << "Newton solver did not converge with dt="<<dt<<" seconds. Retrying with time step of " + << nextDt << " seconds\n"; + } + + DUNE_THROW(Dune::MathError, + "Newton solver didn't converge after " + << maxFails + << " time-step divisions. dt=" + << timeManager().timeStepSize()); + } + + /*! + * \brief Returns the newton method object + */ + NewtonMethod &newtonMethod() + { return newtonMethod_; } + + /*! + * \copydoc newtonMethod() + */ + const NewtonMethod &newtonMethod() const + { return newtonMethod_; } + + /*! + * \brief Returns the newton contoller object + */ + NewtonController &newtonController() + { return newtonCtl_; } + + /*! + * \copydoc newtonController() + */ + const NewtonController &newtonController() const + { return newtonCtl_; } + + /*! + * \brief Called by Dumux::TimeManager whenever a solution for a + * time step has been computed and the simulation time has + * been updated. + * + * \param dt The current time-step size + */ + Scalar nextTimeStepSize(Scalar dt) + { + return std::min(GET_PARAM_FROM_GROUP(TypeTag, Scalar, TimeManager, MaxTimeStepSize), + newtonCtl_.suggestTimeStepSize(dt)); + }; + + /*! + * \brief Returns true if a restart file should be written to + * disk. + * + * The default behavior is to write one restart file every 5 time + * steps. This file is intended to be overwritten by the + * implementation. + */ + bool shouldWriteRestartFile() const + { + return timeManager().timeStepIndex() > 0 && + (timeManager().timeStepIndex() % 10 == 0); + } + + /*! + * \brief Returns true if the current solution should be written to + * disk (i.e. as a VTK file) + * + * The default behavior is to write out every the solution for + * very time step. This file is intended to be overwritten by the + * implementation. + */ + bool shouldWriteOutput() const + { return true; } + + /*! + * \brief Called by the time manager after the time integration to + * do some post processing on the solution. + */ + void postTimeStep() + { } + + /*! + * \brief Called by the time manager after everything which can be + * done about the current time step is finished and the + * model should be prepared to do the next time integration. + */ + void advanceTimeLevel() + { + model_.advanceTimeLevel(); + } + + /*! + * \brief Called when the end of an simulation episode is reached. + * + * Typically a new episode should be started in this method. + */ + void episodeEnd() + { + std::cerr << "The end of an episode is reached, but the problem " + << "does not override the episodeEnd() method. " + << "Doing nothing!\n"; + }; + // \} + + /*! + * \brief The problem name. + * + * This is used as a prefix for files generated by the simulation. + * It could be either overwritten by the problem files, or simply + * declared over the setName() function in the application file. + */ + const char *name() const + { + return simName_.c_str(); + } + + /*! + * \brief Set the problem name. + * + * This static method sets the simulation name, which should be + * called before the application problem is declared! If not, the + * default name "sim" will be used. + * + * \param newName The problem's name + */ + void setName(const char *newName) + { + simName_ = newName; + } + + + /*! + * \brief Returns the number of the current VTK file. + */ + int currentVTKFileNumber() + { + createResultWriter_(); + return resultWriter_->curWriterNum(); + } + + /*! + * \brief The GridView which used by the problem. + */ + const GridView &gridView() const + { return gridView_; } + + /*! + * \brief The coordinate of the corner of the GridView's bounding + * box with the smallest values. + */ + const GlobalPosition &bboxMin() const + { return bboxMin_; } + + /*! + * \brief The coordinate of the corner of the GridView's bounding + * box with the largest values. + */ + const GlobalPosition &bboxMax() const + { return bboxMax_; } + + /*! + * \brief Returns the mapper for vertices to indices. + */ + const VertexMapper &vertexMapper() const + { return vertexMapper_; } + + /*! + * \brief Returns the mapper for elements to indices. + */ + const ElementMapper &elementMapper() const + { return elementMapper_; } + + /*! + * \brief Returns TimeManager object used by the simulation + */ + TimeManager &timeManager() + { return *timeManager_; } + + /*! + * \copydoc timeManager() + */ + const TimeManager &timeManager() const + { return *timeManager_; } + + /*! + * \brief Returns numerical model used for the problem. + */ + Model &model() + { return model_; } + + /*! + * \copydoc model() + */ + const Model &model() const + { return model_; } + // \} + + /*! + * \name Restart mechanism + */ + // \{ + + /*! + * \brief This method writes the complete state of the simulation + * to the harddisk. + * + * The file will start with the prefix returned by the name() + * method, has the current time of the simulation clock in it's + * name and uses the extension <tt>.drs</tt>. (Dumux ReStart + * file.) See Dumux::Restart for details. + */ + void serialize() + { + typedef Dumux::Restart Restarter; + Restarter res; + res.serializeBegin(asImp_()); + if (gridView().comm().rank() == 0) + std::cout << "Serialize to file '" << res.fileName() << "'\n"; + + timeManager().serialize(res); + asImp_().serialize(res); + res.serializeEnd(); + } + + /*! + * \brief This method writes the complete state of the problem + * to the harddisk. + * + * The file will start with the prefix returned by the name() + * method, has the current time of the simulation clock in it's + * name and uses the extension <tt>.drs</tt>. (Dumux ReStart + * file.) See Dumux::Restart for details. + * + * \tparam Restarter The serializer type + * + * \param res The serializer object + */ + template <class Restarter> + void serialize(Restarter &res) + { + createResultWriter_(); + resultWriter_->serialize(res); + model().serialize(res); + } + + /*! + * \brief Load a previously saved state of the whole simulation + * from disk. + * + * \param tRestart The simulation time on which the program was + * written to disk. + */ + void restart(Scalar tRestart) + { + typedef Dumux::Restart Restarter; + + Restarter res; + + res.deserializeBegin(asImp_(), tRestart); + if (gridView().comm().rank() == 0) + std::cout << "Deserialize from file '" << res.fileName() << "'\n"; + timeManager().deserialize(res); + asImp_().deserialize(res); + res.deserializeEnd(); + } + + /*! + * \brief This method restores the complete state of the problem + * from disk. + * + * It is the inverse of the serialize() method. + * + * \tparam Restarter The deserializer type + * + * \param res The deserializer object + */ + template <class Restarter> + void deserialize(Restarter &res) + { + createResultWriter_(); + resultWriter_->deserialize(res); + model().deserialize(res); + } + + // \} + + /*! + * \brief Adds additional VTK output data to the VTKWriter. Function is called by writeOutput(). + */ + void addOutputVtkFields() + {} + + /*! + * \brief Write the relevant secondary variables of the current + * solution into an VTK output file. + */ + void writeOutput(bool verbose = true) + { + // write the current result to disk + if (asImp_().shouldWriteOutput()) { + if (verbose && gridView().comm().rank() == 0) + std::cout << "Writing result file for \"" << asImp_().name() << "\"\n"; + + // calculate the time _after_ the time was updated + Scalar t = timeManager().time() + timeManager().timeStepSize(); + createResultWriter_(); + resultWriter_->beginWrite(t); + model().addOutputVtkFields(model().curSol(), *resultWriter_); + asImp_().addOutputVtkFields(); + resultWriter_->endWrite(); + } + } + +protected: + //! Returns the implementation of the problem (i.e. static polymorphism) + Implementation &asImp_() + { return *static_cast<Implementation *>(this); } + + //! \copydoc asImp_() + const Implementation &asImp_() const + { return *static_cast<const Implementation *>(this); } + + //! Returns the applied VTK-writer for the output + VtkMultiWriter& resultWriter() + { + createResultWriter_(); + return *resultWriter_; + } + //! \copydoc Dumux::IMPETProblem::resultWriter() + VtkMultiWriter& resultWriter() const + { + createResultWriter_(); + return *resultWriter_; + } + + +private: + // makes sure that the result writer exists + void createResultWriter_() + { if (!resultWriter_) resultWriter_ = new VtkMultiWriter(gridView_, asImp_().name()); }; + + std::string simName_; + const GridView gridView_; + + GlobalPosition bboxMin_; + GlobalPosition bboxMax_; + + ElementMapper elementMapper_; + VertexMapper vertexMapper_; + + TimeManager *timeManager_; + + Model model_; + + NewtonMethod newtonMethod_; + NewtonController newtonCtl_; + + VtkMultiWriter *resultWriter_; +}; + +} + +#endif diff --git a/dumux/implicit/cellcentered/ccproperties.hh b/dumux/implicit/cellcentered/ccproperties.hh new file mode 100644 index 0000000000..7666f5d4c8 --- /dev/null +++ b/dumux/implicit/cellcentered/ccproperties.hh @@ -0,0 +1,142 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * Copyright (C) 2009 by Andreas Lauser * + * Copyright (C) 2008 by Bernd Flemisch * + * Institute for Modelling Hydraulic and Environmental Systems * + * University of Stuttgart, Germany * + * email: <givenname>.<name>@iws.uni-stuttgart.de * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +#ifndef DUMUX_CC_PROPERTIES_HH +#define DUMUX_CC_PROPERTIES_HH + +#include <dumux/common/propertysystem.hh> + +#include <dumux/common/basicproperties.hh> +#include <dumux/linear/linearsolverproperties.hh> +#include <dumux/nonlinear/newtonmethod.hh> + +/*! + * \ingroup Properties + * \ingroup CCProperties + * \ingroup CCModel + * \file + * \brief Specify the shape functions, operator assemblers, etc + * used for the CCModel. + */ +namespace Dumux +{ + +namespace Properties +{ +/*! + * \ingroup CCModel + */ +// \{ + +////////////////////////////////////////////////////////////////// +// Type tags +////////////////////////////////////////////////////////////////// + +//! The type tag for models based on the box-scheme +NEW_TYPE_TAG(CCModel, INHERITS_FROM(NewtonMethod, LinearSolverTypeTag, ImplicitModel)); + +////////////////////////////////////////////////////////////////// +// Property tags +////////////////////////////////////////////////////////////////// + +NEW_PROP_TAG(Grid); //!< The type of the DUNE grid +NEW_PROP_TAG(GridView); //!< The type of the grid view + +NEW_PROP_TAG(FVElementGeometry); //! The type of the finite-volume geometry in the box scheme + +NEW_PROP_TAG(Problem); //!< The type of the problem +NEW_PROP_TAG(BaseModel); //!< The type of the base class of the model +NEW_PROP_TAG(Model); //!< The type of the model +NEW_PROP_TAG(NumEq); //!< Number of equations in the system of PDEs +NEW_PROP_TAG(BaseLocalResidual); //!< The type of the base class of the local residual +NEW_PROP_TAG(LocalResidual); //!< The type of the local residual function +NEW_PROP_TAG(LocalJacobian); //!< The type of the local jacobian operator + +NEW_PROP_TAG(JacobianAssembler); //!< Assembles the global jacobian matrix +NEW_PROP_TAG(JacobianMatrix); //!< Type of the global jacobian matrix +NEW_PROP_TAG(BoundaryTypes); //!< Stores the boundary types of a single degree of freedom +NEW_PROP_TAG(ElementBoundaryTypes); //!< Stores the boundary types on an element + +NEW_PROP_TAG(PrimaryVariables); //!< A vector of primary variables within a sub-control volume +NEW_PROP_TAG(SolutionVector); //!< Vector containing all primary variable vector of the grid +NEW_PROP_TAG(ElementSolutionVector); //!< A vector of primary variables within a sub-control volume + +NEW_PROP_TAG(VolumeVariables); //!< The secondary variables within a sub-control volume +NEW_PROP_TAG(ElementVolumeVariables); //!< The secondary variables of all sub-control volumes in an element +NEW_PROP_TAG(FluxVariables); //!< Data required to calculate a flux over a face +NEW_PROP_TAG(BoundaryVariables); //!< Data required to calculate fluxes over boundary faces (outflow) + +// high level simulation control +NEW_PROP_TAG(TimeManager); //!< Manages the simulation time +NEW_PROP_TAG(NewtonMethod); //!< The type of the newton method +NEW_PROP_TAG(NewtonController); //!< The type of the newton controller + +//! Specify whether the jacobian matrix of the last iteration of a +//! time step should be re-used as the jacobian of the first iteration +//! of the next time step. +NEW_PROP_TAG(ImplicitEnableJacobianRecycling); + +//! Specify whether the jacobian matrix should be only reassembled for +//! elements where at least one vertex is above the specified +//! tolerance +NEW_PROP_TAG(ImplicitEnablePartialReassemble); + +/*! + * \brief Specify the maximum size of a time integration [s]. + * + * The default is to not limit the step size. + */ +NEW_PROP_TAG(TimeManagerMaxTimeStepSize); + +/*! + * \brief Specify which kind of method should be used to numerically + * calculate the partial derivatives of the residual. + * + * -1 means backward differences, 0 means central differences, 1 means + * forward differences. By default we use central differences. + */ +NEW_PROP_TAG(ImplicitNumericDifferenceMethod); + +/*! + * \brief Specify whether to use the already calculated solutions as + * starting values of the volume variables. + * + * This only makes sense if the calculation of the volume variables is + * very expensive (e.g. for non-linear fugacity functions where the + * solver converges faster). + */ +NEW_PROP_TAG(ImplicitEnableHints); + +// mappers from local to global indices + +//! maper for vertices +NEW_PROP_TAG(VertexMapper); +//! maper for elements +NEW_PROP_TAG(ElementMapper); +//! maper for degrees of freedom +NEW_PROP_TAG(DofMapper); +} +} + +// \} + +#endif diff --git a/dumux/implicit/cellcentered/ccpropertydefaults.hh b/dumux/implicit/cellcentered/ccpropertydefaults.hh new file mode 100644 index 0000000000..8affb6f501 --- /dev/null +++ b/dumux/implicit/cellcentered/ccpropertydefaults.hh @@ -0,0 +1,209 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * Copyright (C) 2008-2010 by Andreas Lauser * + * Copyright (C) 2008-2010 by Bernd Flemisch * + * Institute for Modelling Hydraulic and Environmental Systems * + * University of Stuttgart, Germany * + * email: <givenname>.<name>@iws.uni-stuttgart.de * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \ingroup Properties + * \ingroup CCProperties + * \ingroup CCModel + * \file + * + * \brief Default properties for box models + */ +#ifndef DUMUX_CC_PROPERTY_DEFAULTS_HH +#define DUMUX_CC_PROPERTY_DEFAULTS_HH + +#include <dumux/nonlinear/newtonmethod.hh> +#include <dumux/nonlinear/newtoncontroller.hh> + +#include "ccassembler.hh" +#include "ccmodel.hh" +#include "ccfvelementgeometry.hh" +#include "ccelementboundarytypes.hh" +#include "cclocaljacobian.hh" +#include "cclocalresidual.hh" +#include "ccelementvolumevariables.hh" +#include <dumux/boxmodels/common/boxvolumevariables.hh> + +#include <dumux/common/boundarytypes.hh> +#include <dumux/common/timemanager.hh> + +#include "ccproperties.hh" + +#include <limits> + +namespace Dumux { + +// forward declaration +template<class TypeTag> +class CCModel; + +namespace Properties { +////////////////////////////////////////////////////////////////// +// Some defaults for very fundamental properties +////////////////////////////////////////////////////////////////// + +//! Set the default type for the time manager +SET_TYPE_PROP(CCModel, TimeManager, Dumux::TimeManager<TypeTag>); + +////////////////////////////////////////////////////////////////// +// Properties +////////////////////////////////////////////////////////////////// + +//! Use the leaf grid view if not defined otherwise +SET_TYPE_PROP(CCModel, + GridView, + typename GET_PROP_TYPE(TypeTag, Grid)::LeafGridView); + +//! Set the default for the FVElementGeometry +SET_TYPE_PROP(CCModel, FVElementGeometry, Dumux::CCFVElementGeometry<TypeTag>); + +//! Set the default for the ElementBoundaryTypes +SET_TYPE_PROP(CCModel, ElementBoundaryTypes, Dumux::CCElementBoundaryTypes<TypeTag>); + +//! use the plain newton method for the box scheme by default +SET_TYPE_PROP(CCModel, NewtonMethod, Dumux::NewtonMethod<TypeTag>); + +//! use the plain newton controller for the box scheme by default +SET_TYPE_PROP(CCModel, NewtonController, Dumux::NewtonController<TypeTag>); + +//! Mapper for the grid view's vertices. +SET_TYPE_PROP(CCModel, + VertexMapper, + Dune::MultipleCodimMultipleGeomTypeMapper<typename GET_PROP_TYPE(TypeTag, GridView), + Dune::MCMGVertexLayout>); + +//! Mapper for the grid view's elements. +SET_TYPE_PROP(CCModel, + ElementMapper, + Dune::MultipleCodimMultipleGeomTypeMapper<typename GET_PROP_TYPE(TypeTag, GridView), + Dune::MCMGElementLayout>); + +//! Mapper for the degrees of freedoms. +SET_TYPE_PROP(CCModel, DofMapper, typename GET_PROP_TYPE(TypeTag, ElementMapper)); + +//! Set the BaseLocalResidual to CCLocalResidual +SET_TYPE_PROP(CCModel, BaseLocalResidual, Dumux::CCLocalResidual<TypeTag>); + +//! Set the BaseModel to CCModel +SET_TYPE_PROP(CCModel, BaseModel, Dumux::CCModel<TypeTag>); + +//! The local jacobian operator for the box scheme +SET_TYPE_PROP(CCModel, LocalJacobian, Dumux::CCLocalJacobian<TypeTag>); + +/*! + * \brief The type of a solution for the whole grid at a fixed time. + */ +SET_TYPE_PROP(CCModel, + SolutionVector, + Dune::BlockVector<typename GET_PROP_TYPE(TypeTag, PrimaryVariables)>); + +/*! + * \brief The type of a solution for a whole element. + */ +SET_TYPE_PROP(CCModel, + ElementSolutionVector, + Dune::BlockVector<typename GET_PROP_TYPE(TypeTag, PrimaryVariables)>); + +/*! + * \brief A vector of primary variables. + */ +SET_TYPE_PROP(CCModel, + PrimaryVariables, + Dune::FieldVector<typename GET_PROP_TYPE(TypeTag, Scalar), + GET_PROP_VALUE(TypeTag, NumEq)>); + +/*! + * \brief The volume variable class. + * + * This should almost certainly be overloaded by the model... + */ +SET_TYPE_PROP(CCModel, VolumeVariables, Dumux::BoxVolumeVariables<TypeTag>); + +/*! + * \brief An array of secondary variable containers. + */ +SET_TYPE_PROP(CCModel, ElementVolumeVariables, Dumux::CCElementVolumeVariables<TypeTag>); + +/*! + * \brief Boundary types at a single degree of freedom. + */ +SET_TYPE_PROP(CCModel, + BoundaryTypes, + Dumux::BoundaryTypes<GET_PROP_VALUE(TypeTag, NumEq)>); + +/*! + * \brief Assembler for the global jacobian matrix. + */ +SET_TYPE_PROP(CCModel, JacobianAssembler, Dumux::CCAssembler<TypeTag>); + +//! use an unlimited time step size by default +#if 0 +// requires GCC 4.6 and above to call the constexpr function of +// numeric_limits +SET_SCALAR_PROP(CCModel, TimeManagerMaxTimeStepSize, std::numeric_limits<Scalar>::infinity()); +#else +SET_SCALAR_PROP(CCModel, TimeManagerMaxTimeStepSize, 1e100); +#endif + +//! use forward differences to calculate the jacobian by default +SET_INT_PROP(CCModel, ImplicitNumericDifferenceMethod, +1); + +//! do not use hints by default +SET_BOOL_PROP(CCModel, ImplicitEnableHints, false); + +// disable jacobian matrix recycling by default +SET_BOOL_PROP(CCModel, ImplicitEnableJacobianRecycling, false); + +// disable partial reassembling by default +SET_BOOL_PROP(CCModel, ImplicitEnablePartialReassemble, false); + +//! Set the type of a global jacobian matrix from the solution types +SET_PROP(CCModel, JacobianMatrix) +{ +private: + typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; + enum { numEq = GET_PROP_VALUE(TypeTag, NumEq) }; + typedef typename Dune::FieldMatrix<Scalar, numEq, numEq> MatrixBlock; +public: + typedef typename Dune::BCRSMatrix<MatrixBlock> type; +}; + +// use the stabilized BiCG solver preconditioned by the ILU-0 by default +SET_TYPE_PROP(CCModel, LinearSolver, Dumux::BoxBiCGStabILU0Solver<TypeTag> ); + +// if the deflection of the newton method is large, we do not +// need to solve the linear approximation accurately. Assuming +// that the initial value for the delta vector u is quite +// close to the final value, a reduction of 6 orders of +// magnitude in the defect should be sufficient... +SET_SCALAR_PROP(CCModel, LinearSolverResidualReduction, 1e-6); + +//! set the default number of maximum iterations for the linear solver +SET_INT_PROP(CCModel, LinearSolverMaxIterations, 250); + +//! set number of equations of the mathematical model as default +SET_INT_PROP(CCModel, LinearSolverBlockSize, GET_PROP_VALUE(TypeTag, NumEq)); + +} // namespace Properties +} // namespace Dumux + +#endif diff --git a/dumux/implicit/cellcentered/porousmediaccproblem.hh b/dumux/implicit/cellcentered/porousmediaccproblem.hh new file mode 100644 index 0000000000..91dcd6352d --- /dev/null +++ b/dumux/implicit/cellcentered/porousmediaccproblem.hh @@ -0,0 +1,192 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * Copyright (C) 2009 by Andreas Lauser * + * Institute for Modelling Hydraulic and Environmental Systems * + * University of Stuttgart, Germany * + * email: <givenname>.<name>@iws.uni-stuttgart.de * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \file + * \brief Base class for all problems which use the two-phase box model + */ +#ifndef DUMUX_POROUS_MEDIA_CC_ROBLEM_HH +#define DUMUX_POROUS_MEDIA_CC_PROBLEM_HH + +#include <dumux/ccmodels/common/ccproblem.hh> +#include "ccproperties.hh" + +namespace Dumux +{ +/*! + * \ingroup CCBaseProblems + * \brief Base class for all fully implicit cell centered porous media problems + */ +template<class TypeTag> +class PorousMediaCCProblem : public CCProblem<TypeTag> +{ + typedef CCProblem<TypeTag> ParentType; + + typedef typename GET_PROP_TYPE(TypeTag, Problem) Implementation; + typedef typename GET_PROP_TYPE(TypeTag, TimeManager) TimeManager; + typedef typename GET_PROP_TYPE(TypeTag, FVElementGeometry) FVElementGeometry; + typedef typename GET_PROP_TYPE(TypeTag, SpatialParams) SpatialParams; + + typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; + typedef typename GridView::template Codim<0>::Entity Element; + enum { + dim = GridView::dimension, + dimWorld = GridView::dimensionworld + }; + + typedef typename GridView::ctype CoordScalar; + typedef Dune::FieldVector<CoordScalar, dimWorld> GlobalPosition; + typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; + typedef Dune::FieldVector<Scalar, dim> Vector; + +public: + /*! + * \brief The constructor + * + * \param timeManager The time manager + * \param gridView The grid view + * \param verbose Turn verbosity on or off + */ + PorousMediaCCProblem(TimeManager &timeManager, + const GridView &gridView, + bool verbose = true) + : ParentType(timeManager, gridView), + gravity_(0) + { + newSpatialParams_ = true; + spatialParams_ = new SpatialParams(gridView); + + if (GET_PARAM_FROM_GROUP(TypeTag, bool, Problem, EnableGravity)) + gravity_[dim-1] = -9.81; + } + + ~PorousMediaCCProblem() + { + if (newSpatialParams_) + delete spatialParams_; + } + + /*! + * \name Problem parameters + */ + // \{ + + /*! + * \brief Returns the temperature \f$\mathrm{[K]}\f$ within a control volume. + * + * This is the discretization specific interface for the box + * method. By default it just calls temperature(pos). + * + * \param element The DUNE Codim<0> enitiy which intersects with + * the finite volume. + * \param fvGeom The finite volume geometry of the element. + * \param scvIdx The local index of the sub control volume inside the element + */ + Scalar boxTemperature(const Element &element, + const FVElementGeometry fvGeom, + int scvIdx) const + { return asImp_().temperatureAtPos(fvGeom.neighbors[scvIdx]->geometry().center()); } + + /*! + * \brief Returns the temperature \f$\mathrm{[K]}\f$ at a given global position. + * + * This is not specific to the discretization. By default it just + * calls temperature(). + * + * \param pos The position in global coordinates where the temperature should be specified. + */ + Scalar temperatureAtPos(const GlobalPosition &pos) const + { return asImp_().temperature(); } + + /*! + * \brief Returns the temperature \f$\mathrm{[K]}\f$ for an isothermal problem. + * + * This is not specific to the discretization. By default it just + * throws an exception so it must be overloaded by the problem if + * no energy equation is used. + */ + Scalar temperature() const + { DUNE_THROW(Dune::NotImplemented, "temperature() method not implemented by the actual problem"); }; + + /*! + * \brief Returns the acceleration due to gravity \f$\mathrm{[m/s^2]}\f$. + * + * This is the box discretization specific interface. By default + * it just calls gravityAtPos(). + */ + const Vector &boxGravity(const Element &element, + const FVElementGeometry &fvGeom, + int scvIdx) const + { return asImp_().gravityAtPos(fvGeom.neighbors[scvIdx]->geometry().center()); } + + /*! + * \brief Returns the acceleration due to gravity \f$\mathrm{[m/s^2]}\f$. + * + * This is discretization independent interface. By default it + * just calls gravity(). + */ + const Vector &gravityAtPos(const GlobalPosition &pos) const + { return asImp_().gravity(); } + + /*! + * \brief Returns the acceleration due to gravity \f$\mathrm{[m/s^2]}\f$. + * + * This method is used for problems where the gravitational + * acceleration does not depend on the spatial position. The + * default behaviour is that if the <tt>ProblemEnableGravity</tt> + * property is true, \f$\boldsymbol{g} = ( 0,\dots,\ -9.81)^T \f$ holds, + * else \f$\boldsymbol{g} = ( 0,\dots, 0)^T \f$. + */ + const Vector &gravity() const + { return gravity_; } + + /*! + * \brief Returns the spatial parameters object. + */ + SpatialParams &spatialParams() + { return *spatialParams_; } + + /*! + * \brief Returns the spatial parameters object. + */ + const SpatialParams &spatialParams() const + { return *spatialParams_; } + + // \} + +private: + //! Returns the implementation of the problem (i.e. static polymorphism) + Implementation &asImp_() + { return *static_cast<Implementation *>(this); } + //! \copydoc asImp_() + const Implementation &asImp_() const + { return *static_cast<const Implementation *>(this); } + + Vector gravity_; + + // fluids and material properties + SpatialParams* spatialParams_; + bool newSpatialParams_; +}; + +} + +#endif diff --git a/dumux/implicit/co2/Makefile.am b/dumux/implicit/co2/Makefile.am new file mode 100644 index 0000000000..52b76eb57e --- /dev/null +++ b/dumux/implicit/co2/Makefile.am @@ -0,0 +1,4 @@ +co2dir = $(includedir)/dumux/boxmodels/co2 +co2_HEADERS = *.hh + +include $(top_srcdir)/am/global-rules diff --git a/dumux/implicit/co2/co2model.hh b/dumux/implicit/co2/co2model.hh new file mode 100644 index 0000000000..fcf6360603 --- /dev/null +++ b/dumux/implicit/co2/co2model.hh @@ -0,0 +1,283 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * Copyright (C) 2012 by * + * Institute for Modelling Hydraulic and Environmental Systems * + * University of Stuttgart, Germany * + * email: <givenname>.<name>@iws.uni-stuttgart.de * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \file + * + * \brief Adaption of the BOX scheme to the two-phase two-component flow model without constraint solver. + */ +#ifndef DUMUX_CO2_MODEL_HH +#define DUMUX_CO2_MODEL_HH + +#include <dumux/boxmodels/2p2c/2p2cmodel.hh> + +namespace Dumux +{ +/*! + * \ingroup CO2Model + * \brief Adaption of the BOX scheme to the non-isothermal two-phase two-component flow model. + * See TwoPTwoCModel for reference to the equations used. + * The CO2 model is derived from the 2p2c model. In the CO2 model the phase switch criterion + * is different from the 2p2c model. + * The phase switch occurs when the equilibrium concentration + * of a component in a phase is exceeded, instead of the sum of the components in the virtual phase + * (the phase which is not present) being greater that unity as done in the 2p2c model. + * The CO2VolumeVariables do not use a constraint solver for calculating the mole fractions as is the + * case in the 2p2c model. Instead mole fractions are calculated in the FluidSystem with a given + * temperature, pressurem and salinity. + * + */ + +template<class TypeTag> +class CO2Model: public TwoPTwoCModel<TypeTag> +{ + + + typedef TwoPTwoCModel<TypeTag> ParentType; + typedef typename GET_PROP_TYPE(TypeTag, BaseModel) BaseType; + + typedef typename GET_PROP_TYPE(TypeTag, Problem) Problem; + typedef typename GET_PROP_TYPE(TypeTag, FluidSystem) FluidSystem; + typedef typename GET_PROP_TYPE(TypeTag, FVElementGeometry) FVElementGeometry; + typedef typename GET_PROP_TYPE(TypeTag, PrimaryVariables) PrimaryVariables; + typedef typename GET_PROP_TYPE(TypeTag, VolumeVariables) VolumeVariables; + typedef typename GET_PROP_TYPE(TypeTag, ElementVolumeVariables) ElementVolumeVariables; + typedef typename GET_PROP_TYPE(TypeTag, FluxVariables) FluxVariables; + typedef typename GET_PROP_TYPE(TypeTag, SolutionVector) SolutionVector; + enum { + numPhases = GET_PROP_VALUE(TypeTag, NumPhases), + numComponents = GET_PROP_VALUE(TypeTag, NumComponents) + }; + + typedef typename GET_PROP_TYPE(TypeTag, Indices) Indices; + enum { + pressureIdx = Indices::pressureIdx, + switchIdx = Indices::switchIdx, + + wPhaseIdx = Indices::wPhaseIdx, + nPhaseIdx = Indices::nPhaseIdx, + wCompIdx = Indices::wCompIdx, + nCompIdx = Indices::nCompIdx, + + wPhaseOnly = Indices::wPhaseOnly, + nPhaseOnly = Indices::nPhaseOnly, + bothPhases = Indices::bothPhases, + + pwSn = TwoPTwoCFormulation::pwSn, + pnSw = TwoPTwoCFormulation::pnSw, + formulation = GET_PROP_VALUE(TypeTag, Formulation) + }; + + typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; + typedef typename GridView::ctype CoordScalar; + typedef typename GridView::template Codim<0>::Entity Element; + typedef typename GridView::template Codim<0>::Iterator ElementIterator; + enum { + dim = GridView::dimension, + dimWorld = GridView::dimensionworld + }; + typedef typename GridView::template Codim<dim>::Entity Vertex; + typedef typename GridView::template Codim<dim>::Iterator VertexIterator; + + typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; + typedef Dune::FieldVector<Scalar, numPhases> PhasesVector; + typedef Dune::FieldVector<Scalar, dimWorld> GlobalPosition; + + public: + + + /*! + * \brief Update the static data of all vertices in the grid. + * + * \param curGlobalSol The current global solution + * \param oldGlobalSol The previous global solution + */ + void updateStaticData(SolutionVector &curGlobalSol, + const SolutionVector &oldGlobalSol) + { + bool wasSwitched = false; + + for (unsigned i = 0; i < ParentType::staticVertexDat_.size(); ++i) + ParentType::staticVertexDat_[i].visited = false; + + unsigned numDofs = this->numDofs(); + unsigned numVertices = this->problem_().gridView().size(dim); + + FVElementGeometry fvGeometry; + static VolumeVariables volVars; + ElementIterator eIt = this->gridView_().template begin<0> (); + const ElementIterator &eEndIt = this->gridView_().template end<0> (); + for (; eIt != eEndIt; ++eIt) + { + fvGeometry.update(this->gridView_(), *eIt); + for (int scvIdx = 0; scvIdx < fvGeometry.numSCV; ++scvIdx) + { + int globalIdx; + + if (numDofs != numVertices) + globalIdx = this->elementMapper().map(*eIt); + else + globalIdx = this->vertexMapper().map(*eIt, scvIdx, dim); + + if (ParentType::staticVertexDat_[globalIdx].visited) + continue; + + ParentType::staticVertexDat_[globalIdx].visited = true; + volVars.update(curGlobalSol[globalIdx], + this->problem_(), + *eIt, + fvGeometry, + scvIdx, + false); + const GlobalPosition &globalPos = eIt->geometry().corner(scvIdx); + if (primaryVarSwitch_(curGlobalSol, + volVars, + globalIdx, + globalPos)) + { + this->jacobianAssembler().markVertexRed(globalIdx); + wasSwitched = true; + } + } + } + + // make sure that if there was a variable switch in an + // other partition we will also set the switch flag + // for our partition. + if (this->gridView_().comm().size() > 1) + wasSwitched = this->gridView_().comm().max(wasSwitched); + + ParentType::setSwitched_(wasSwitched); + } + + protected: + + + /*! + * \brief Set the old phase of all verts state to the current one. + */ + bool primaryVarSwitch_(SolutionVector &globalSol, + const VolumeVariables &volVars, int globalIdx, + const GlobalPosition &globalPos) + { + typename FluidSystem::ParameterCache paramCache; + // evaluate primary variable switch + bool wouldSwitch = false; + int phasePresence = ParentType::staticVertexDat_[globalIdx].phasePresence; + int newPhasePresence = phasePresence; + + // check if a primary var switch is necessary + if (phasePresence == nPhaseOnly) + { + + Scalar xnw = volVars.fluidState().moleFraction(nPhaseIdx, wCompIdx); + Scalar xnwMax = FluidSystem::equilibriumMoleFraction(volVars.fluidState(), paramCache, nPhaseIdx); + + if(xnw > xnwMax) + wouldSwitch = true; + + if (ParentType::staticVertexDat_[globalIdx].wasSwitched) + xnwMax *= 1.02; + + //If mole fraction is higher than the equilibrium mole fraction make a phase switch + if(xnw > xnwMax) + { + // wetting phase appears + std::cout << "wetting phase appears at vertex " << globalIdx + << ", coordinates: " << globalPos << ", xnw > xnwMax: " + << xnw << " > "<< xnwMax << std::endl; + newPhasePresence = bothPhases; + if (formulation == pnSw) + globalSol[globalIdx][switchIdx] = 0.0; + else if (formulation == pwSn) + globalSol[globalIdx][switchIdx] = 1.0; + } + } + else if (phasePresence == wPhaseOnly) + { + + Scalar xwn = volVars.fluidState().moleFraction(wPhaseIdx, nCompIdx); + Scalar xwnMax = FluidSystem::equilibriumMoleFraction(volVars.fluidState(), paramCache, wPhaseIdx); + + //If mole fraction is higher than the equilibrium mole fraction make a phase switch + if(xwn > xwnMax) + wouldSwitch = true; + if (ParentType::staticVertexDat_[globalIdx].wasSwitched) + xwnMax *= 1.02; + + + if(xwn > xwnMax) + { + // non-wetting phase appears + std::cout << "non-wetting phase appears at vertex " << globalIdx + << ", coordinates: " << globalPos << ", xwn > xwnMax: " + << xwn << " > "<< xwnMax << std::endl; + + newPhasePresence = bothPhases; + if (formulation == pnSw) + globalSol[globalIdx][switchIdx] = 0.999; + else if (formulation == pwSn) + globalSol[globalIdx][switchIdx] = 0.001; + } + } + else if (phasePresence == bothPhases) + { + Scalar Smin = 0.0; + if (ParentType::staticVertexDat_[globalIdx].wasSwitched) + Smin = -0.01; + + if (volVars.saturation(nPhaseIdx) <= Smin) + { + wouldSwitch = true; + // nonwetting phase disappears + std::cout << "Nonwetting phase disappears at vertex " << globalIdx + << ", coordinates: " << globalPos << ", Sn: " + << volVars.saturation(nPhaseIdx) << std::endl; + newPhasePresence = wPhaseOnly; + + globalSol[globalIdx][switchIdx] + = volVars.fluidState().massFraction(wPhaseIdx, nCompIdx); + } + else if (volVars.saturation(wPhaseIdx) <= Smin) + { + wouldSwitch = true; + // wetting phase disappears + std::cout << "Wetting phase disappears at vertex " << globalIdx + << ", coordinates: " << globalPos << ", Sw: " + << volVars.saturation(wPhaseIdx) << std::endl; + newPhasePresence = nPhaseOnly; + + globalSol[globalIdx][switchIdx] + = volVars.fluidState().massFraction(nPhaseIdx, wCompIdx); + } + } + + ParentType::staticVertexDat_[globalIdx].phasePresence = newPhasePresence; + ParentType::staticVertexDat_[globalIdx].wasSwitched = wouldSwitch; + return phasePresence != newPhasePresence; + } + + +}; + +} + +#endif diff --git a/dumux/implicit/co2/co2volumevariables.hh b/dumux/implicit/co2/co2volumevariables.hh new file mode 100644 index 0000000000..0d0e41b3a1 --- /dev/null +++ b/dumux/implicit/co2/co2volumevariables.hh @@ -0,0 +1,404 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * Copyright (C) 2012 by * + * Institute for Modelling Hydraulic and Environmental Systems * + * University of Stuttgart, Germany * + * email: <givenname>.<name>@iws.uni-stuttgart.de * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +#ifndef DUMUX_CO2_VOLUME_VARIABLES_HH +#define DUMUX_CO2_VOLUME_VARIABLES_HH + +#include <dumux/boxmodels/2p2c/2p2cvolumevariables.hh> + +namespace Dumux +{ +/*! + * \ingroup CO2Model + * \ingroup BoxVolumeVariables + * \brief Contains the quantities which are are constant within a + * finite volume in the non-isothermal two-phase, two-component + * model. + */ +template <class TypeTag> +class CO2VolumeVariables: public TwoPTwoCVolumeVariables<TypeTag> +{ + typedef TwoPTwoCVolumeVariables<TypeTag> ParentType; + typedef BoxVolumeVariables<TypeTag> BaseClassType; + + typedef typename GET_PROP_TYPE(TypeTag, VolumeVariables) Implementation; + + typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; +// typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; + typedef typename GET_PROP_TYPE(TypeTag, Problem) Problem; + typedef typename GET_PROP_TYPE(TypeTag, FVElementGeometry) FVElementGeometry; + typedef typename GET_PROP_TYPE(TypeTag, PrimaryVariables) PrimaryVariables; + typedef typename GET_PROP_TYPE(TypeTag, FluidSystem) FluidSystem; + typedef typename GET_PROP_TYPE(TypeTag, MaterialLaw) MaterialLaw; + typedef typename GET_PROP_TYPE(TypeTag, MaterialLawParams) MaterialLawParams; + + typedef typename GET_PROP_TYPE(TypeTag, Indices) Indices; + + enum { + numPhases = GET_PROP_VALUE(TypeTag, NumPhases), + numComponents = GET_PROP_VALUE(TypeTag, NumComponents) + }; + + enum { + wCompIdx = Indices::wCompIdx, + nCompIdx = Indices::nCompIdx, + wPhaseIdx = Indices::wPhaseIdx, + nPhaseIdx = Indices::nPhaseIdx + }; + + // present phases + enum { + wPhaseOnly = Indices::wPhaseOnly, + nPhaseOnly = Indices::nPhaseOnly, + bothPhases = Indices::bothPhases + }; + + // formulations + enum { + formulation = GET_PROP_VALUE(TypeTag, Formulation), + pwSn = TwoPTwoCFormulation::pwSn, + pnSw = TwoPTwoCFormulation::pnSw + }; + + // primary variable indices + enum { + switchIdx = Indices::switchIdx, + pressureIdx = Indices::pressureIdx + }; + + typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; + typedef typename GridView::template Codim<0>::Entity Element; + enum { dim = GridView::dimension}; + + static const Scalar R; // universial nonwetting constant + +public: + //! The type of the object returned by the fluidState() method + typedef Dumux::CompositionalFluidState<Scalar, FluidSystem> FluidState; + + + /*! + * \brief Update all quantities for a given control volume. + * + * \param priVars The primary variables + * \param problem The problem + * \param element The element + * \param fvGeometry The finite-volume geometry in the box scheme + * \param scvIdx The local index of the SCV (sub-control volume) + * \param isOldSol Evaluate function with solution of current or previous time step + */ + void update(const PrimaryVariables &priVars, + const Problem &problem, + const Element &element, + const FVElementGeometry &fvGeometry, + const int scvIdx, + const bool isOldSol) + { + // Update BoxVolVars but not 2p2cvolvars + // ToDo: Is BaseClassType the right name? + BaseClassType::update(priVars, + problem, + element, + fvGeometry, + scvIdx, + isOldSol); + + unsigned numDofs = problem.model().numDofs(); + unsigned numVertices = problem.gridView().size(dim); + + int globalIdx; + if (numDofs != numVertices) // element data + globalIdx = problem.model().dofMapper().map(element); + else + globalIdx = problem.model().dofMapper().map(element, scvIdx, dim); + + int phasePresence = problem.model().phasePresence(globalIdx, isOldSol); + + Scalar temp = Implementation::temperature_(priVars, problem, element, fvGeometry, scvIdx); + ParentType::fluidState_.setTemperature(temp); + + ///////////// + // set the saturations + ///////////// + Scalar Sn; + if (phasePresence == nPhaseOnly) + Sn = 1.0; + else if (phasePresence == wPhaseOnly) { + Sn = 0.0; + } + else if (phasePresence == bothPhases) { + if (formulation == pwSn) + Sn = priVars[switchIdx]; + else if (formulation == pnSw) + Sn = 1.0 - priVars[switchIdx]; + else DUNE_THROW(Dune::InvalidStateException, "Formulation: " << formulation << " is invalid."); + } + else DUNE_THROW(Dune::InvalidStateException, "phasePresence: " << phasePresence << " is invalid."); + ParentType::fluidState_.setSaturation(wPhaseIdx, 1 - Sn); + ParentType::fluidState_.setSaturation(nPhaseIdx, Sn); + + // capillary pressure parameters + const MaterialLawParams &materialParams = + problem.spatialParams().materialLawParams(element, fvGeometry, scvIdx); + + + Scalar pC = MaterialLaw::pC(materialParams, 1 - Sn); + + if (formulation == pwSn) { + ParentType::fluidState_.setPressure(wPhaseIdx, priVars[pressureIdx]); + ParentType::fluidState_.setPressure(nPhaseIdx, priVars[pressureIdx] + pC); + } + else if (formulation == pnSw) { + ParentType::fluidState_.setPressure(nPhaseIdx, priVars[pressureIdx]); + ParentType::fluidState_.setPressure(wPhaseIdx, priVars[pressureIdx] - pC); + } + else DUNE_THROW(Dune::InvalidStateException, "Formulation: " << formulation << " is invalid."); + + ///////////// + // calculate the phase compositions + ///////////// + typename FluidSystem::ParameterCache paramCache; + + + // calculate phase composition + if (phasePresence == bothPhases) { + + //Get the equilibrium mole fractions from the FluidSystem and set them in the fluidState + //xCO2 = equilibrium mole fraction of CO2 in the liquid phase + //yH2O = equilibrium mole fraction of H2O in the gas phase + + Scalar xwCO2 = FluidSystem::equilibriumMoleFraction(ParentType::fluidState_, paramCache, wPhaseIdx); + Scalar xgH2O = FluidSystem::equilibriumMoleFraction(ParentType::fluidState_, paramCache, nPhaseIdx); + Scalar xwH2O = 1 - xwCO2; + Scalar xgCO2 = 1 - xgH2O; + + ParentType::fluidState_.setMoleFraction(wPhaseIdx, wCompIdx, xwH2O); + ParentType::fluidState_.setMoleFraction(wPhaseIdx, nCompIdx, xwCO2); + ParentType::fluidState_.setMoleFraction(nPhaseIdx, wCompIdx, xgH2O); + ParentType::fluidState_.setMoleFraction(nPhaseIdx, nCompIdx, xgCO2); + + + //Get the phase densities from the FluidSystem and set them in the fluidState + + Scalar rhoW = FluidSystem::density(ParentType::fluidState_, paramCache, wPhaseIdx); + Scalar rhoN = FluidSystem::density(ParentType::fluidState_, paramCache, nPhaseIdx); + + ParentType::fluidState_.setDensity(wPhaseIdx, rhoW); + ParentType::fluidState_.setDensity(nPhaseIdx, rhoN); + + + //Get the phase viscosities from the FluidSystem and set them in the fluidState + + Scalar muW = FluidSystem::viscosity(ParentType::fluidState_, paramCache, wPhaseIdx); + Scalar muN = FluidSystem::viscosity(ParentType::fluidState_, paramCache, nPhaseIdx); + + ParentType::fluidState_.setViscosity(wPhaseIdx, muW); + ParentType::fluidState_.setViscosity(nPhaseIdx, muN); + + } + else if (phasePresence == nPhaseOnly) { + // only the nonwetting phase is present, i.e. nonwetting phase + // composition is stored explicitly. + + // extract _mass_ fractions in the nonwetting phase + Scalar massFractionN[numComponents]; + massFractionN[wCompIdx] = priVars[switchIdx]; + massFractionN[nCompIdx] = 1 - massFractionN[wCompIdx]; + + // calculate average molar mass of the nonwetting phase + Scalar M1 = FluidSystem::molarMass(wCompIdx); + Scalar M2 = FluidSystem::molarMass(nCompIdx); + Scalar X2 = massFractionN[nCompIdx]; + Scalar avgMolarMass = M1*M2/(M2 + X2*(M1 - M2)); + + // convert mass to mole fractions and set the fluid state + ParentType::fluidState_.setMoleFraction(nPhaseIdx, wCompIdx, massFractionN[wCompIdx]*avgMolarMass/M1); + ParentType::fluidState_.setMoleFraction(nPhaseIdx, nCompIdx, massFractionN[nCompIdx]*avgMolarMass/M2); + + // TODO give values for non-existing wetting phase + Scalar xwCO2 = FluidSystem::equilibriumMoleFraction(ParentType::fluidState_, paramCache, wPhaseIdx); + Scalar xwH2O = 1 - xwCO2; +// Scalar xwCO2 = FluidSystem::equilibriumMoleFraction(ParentType::fluidState_, paramCache, wPhaseIdx, nPhaseOnly); +// Scalar xwH2O = 1 - xwCO2; + ParentType::fluidState_.setMoleFraction(wPhaseIdx, nCompIdx, xwCO2); + ParentType::fluidState_.setMoleFraction(wPhaseIdx, wCompIdx, xwH2O); + + + //Get the phase densities from the FluidSystem and set them in the fluidState + + Scalar rhoW = FluidSystem::density(ParentType::fluidState_, paramCache, wPhaseIdx); + Scalar rhoN = FluidSystem::density(ParentType::fluidState_, paramCache, nPhaseIdx); + + ParentType::fluidState_.setDensity(wPhaseIdx, rhoW); + ParentType::fluidState_.setDensity(nPhaseIdx, rhoN); + + //Get the phase viscosities from the FluidSystem and set them in the fluidState + + Scalar muW = FluidSystem::viscosity(ParentType::fluidState_, paramCache, wPhaseIdx); + Scalar muN = FluidSystem::viscosity(ParentType::fluidState_, paramCache, nPhaseIdx); + + ParentType::fluidState_.setViscosity(wPhaseIdx, muW); + ParentType::fluidState_.setViscosity(nPhaseIdx, muN); + } + else if (phasePresence == wPhaseOnly) { + // only the wetting phase is present, i.e. wetting phase + // composition is stored explicitly. + + // extract _mass_ fractions in the nonwetting phase + Scalar massFractionW[numComponents]; + massFractionW[nCompIdx] = priVars[switchIdx]; + massFractionW[wCompIdx] = 1 - massFractionW[nCompIdx]; + + // calculate average molar mass of the nonwetting phase + Scalar M1 = FluidSystem::molarMass(wCompIdx); + Scalar M2 = FluidSystem::molarMass(nCompIdx); + Scalar X2 = massFractionW[nCompIdx]; + Scalar avgMolarMass = M1*M2/(M2 + X2*(M1 - M2)); + + // convert mass to mole fractions and set the fluid state + ParentType::fluidState_.setMoleFraction(wPhaseIdx, wCompIdx, massFractionW[wCompIdx]*avgMolarMass/M1); + ParentType::fluidState_.setMoleFraction(wPhaseIdx, nCompIdx, massFractionW[nCompIdx]*avgMolarMass/M2); + + // TODO give values for non-existing nonwetting phase + Scalar xnH2O = FluidSystem::equilibriumMoleFraction(ParentType::fluidState_, paramCache, nPhaseIdx); + Scalar xnCO2 = 1 - xnH2O; //FluidSystem::equilibriumMoleFraction(ParentType::fluidState_, paramCache, nPhaseIdx); +// Scalar xnH2O = FluidSystem::equilibriumMoleFraction(ParentType::fluidState_, paramCache, nPhaseIdx, wPhaseOnly); +// Scalar xnCO2 = 1 - xnH2O; + ParentType::fluidState_.setMoleFraction(nPhaseIdx, nCompIdx, xnCO2); + ParentType::fluidState_.setMoleFraction(nPhaseIdx, wCompIdx, xnH2O); + + + Scalar rhoW = FluidSystem::density(ParentType::fluidState_, paramCache, wPhaseIdx); + Scalar rhoN = FluidSystem::density(ParentType::fluidState_, paramCache, nPhaseIdx); + + ParentType::fluidState_.setDensity(wPhaseIdx, rhoW); + ParentType::fluidState_.setDensity(nPhaseIdx, rhoN); + + //Get the phase viscosities from the FluidSystem and set them in the fluidState + + Scalar muW = FluidSystem::viscosity(ParentType::fluidState_, paramCache, wPhaseIdx); + Scalar muN = FluidSystem::viscosity(ParentType::fluidState_, paramCache, nPhaseIdx); + + ParentType::fluidState_.setViscosity(wPhaseIdx, muW); + ParentType::fluidState_.setViscosity(nPhaseIdx, muN); + } + + for (int phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) { + // compute and set the enthalpy + Scalar h = Implementation::enthalpy_(ParentType::fluidState_, paramCache, phaseIdx); + ParentType::fluidState_.setEnthalpy(phaseIdx, h); + } + + paramCache.updateAll(ParentType::fluidState_); + + for (int phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) { + // relative permeabilities + Scalar kr; + if (phaseIdx == wPhaseIdx) + kr = MaterialLaw::krw(materialParams, this->saturation(wPhaseIdx)); + else // ATTENTION: krn requires the liquid saturation + // as parameter! + kr = MaterialLaw::krn(materialParams, this->saturation(wPhaseIdx)); + ParentType::relativePermeability_[phaseIdx] = kr; + Valgrind::CheckDefined(ParentType::relativePermeability_[phaseIdx]); + + // binary diffusion coefficents + ParentType::diffCoeff_[phaseIdx] = + FluidSystem::binaryDiffusionCoefficient(ParentType::fluidState_, + paramCache, + phaseIdx, + wCompIdx, + nCompIdx); + Valgrind::CheckDefined(ParentType::diffCoeff_[phaseIdx]); + } + + // porosity + ParentType::porosity_ = problem.spatialParams().porosity(element, + fvGeometry, + scvIdx); + Valgrind::CheckDefined(ParentType::porosity_); +// if(phasePresence == bothPhases) +// { +// std::cout<<"globalIdx = "<<globalIdx<<std::endl; +// std::cout<<"scvIdx = "<<globalIdx<<std::endl; +// std::cout<<"Sn = "<<ParentType::fluidState_.saturation(nPhaseIdx)<<std::endl; +// std::cout<<"Sw = "<<ParentType::fluidState_.saturation(wPhaseIdx)<<std::endl; +// std::cout<<"mobilityN = "<<ParentType::mobility(nPhaseIdx)<<std::endl; +// std::cout<<"xgH2O = "<<ParentType::fluidState_.moleFraction(nPhaseIdx, wCompIdx)<<std::endl; +// std::cout<<"xgCO2 = "<<ParentType::fluidState_.moleFraction(nPhaseIdx, nCompIdx)<<std::endl; +// std::cout<<"xwH2O = "<<ParentType::fluidState_.moleFraction(wPhaseIdx, wCompIdx)<<std::endl; +// std::cout<<"xwCO2 = "<<ParentType::fluidState_.moleFraction(wPhaseIdx, nCompIdx)<<std::endl; +// std::cout<<"XgH2O = "<<ParentType::fluidState_.massFraction(nPhaseIdx, wCompIdx)<<std::endl; +// std::cout<<"XgCO2 = "<<ParentType::fluidState_.massFraction(nPhaseIdx, nCompIdx)<<std::endl; +// std::cout<<"XwH2O = "<<ParentType::fluidState_.massFraction(wPhaseIdx, wCompIdx)<<std::endl; +// std::cout<<"XwCO2 = "<<ParentType::fluidState_.massFraction(wPhaseIdx, nCompIdx)<<std::endl; +// } + + // energy related quantities not contained in the fluid state + asImp_().updateEnergy_(priVars, problem, element, fvGeometry, scvIdx, isOldSol); + } + + + + +protected: + + static Scalar temperature_(const PrimaryVariables &priVars, + const Problem& problem, + const Element &element, + const FVElementGeometry &fvGeometry, + int scvIdx) + { + return problem.boxTemperature(element, fvGeometry, scvIdx); + } + + template<class ParameterCache> + static Scalar enthalpy_(const FluidState& fluidState, + const ParameterCache& paramCache, + const int phaseIdx) + { + return 0; + } + + void updateEnergy_(const PrimaryVariables &sol, + const Problem &problem, + const Element &element, + const FVElementGeometry &fvGeometry, + const int vertIdx, + bool isOldSol) + { } + + + +private: + + + + Implementation &asImp_() + { return *static_cast<Implementation*>(this); } + + const Implementation &asImp_() const + { return *static_cast<const Implementation*>(this); } +}; + +} // end namepace + +#endif diff --git a/dumux/implicit/co2ni/Makefile.am b/dumux/implicit/co2ni/Makefile.am new file mode 100644 index 0000000000..e1f4e38102 --- /dev/null +++ b/dumux/implicit/co2ni/Makefile.am @@ -0,0 +1,4 @@ +co2nidir = $(includedir)/dumux/boxmodels/co2ni +co2ni_HEADERS = *.hh + +include $(top_srcdir)/am/global-rules diff --git a/dumux/implicit/co2ni/co2nimodel.hh b/dumux/implicit/co2ni/co2nimodel.hh new file mode 100644 index 0000000000..987f1b193c --- /dev/null +++ b/dumux/implicit/co2ni/co2nimodel.hh @@ -0,0 +1,56 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * Copyright (C) 2012 by * + * Institute for Modelling Hydraulic and Environmental Systems * + * University of Stuttgart, Germany * + * email: <givenname>.<name>@iws.uni-stuttgart.de * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \file + * + * \brief Adaption of the BOX scheme to the non-isothermal two-phase two-component flow model without constraint solver. + */ +#ifndef DUMUX_CO2NI_MODEL_HH +#define DUMUX_CO2NI_MODEL_HH + +#include <dumux/boxmodels/co2/co2model.hh> + +namespace Dumux { +/*! + * \ingroup CO2NIModel + * \brief Adaption of the BOX scheme to the non-isothermal two-phase two-component flow model. + * See TwoPTwoCNI model for reference to the equations. + * The CO2NI model is derived from the CO2 model. In the CO2 model the phase switch criterion + * is different from the 2p2c model. + * The phase switch occurs when the equilibrium concentration + * of a component in a phase is exceeded instead of the sum of the components in the virtual phase + * (the phase which is not present) being greater that unity as done in the 2p2c model. + * The CO2VolumeVariables do not use a constraint solver for calculating the mole fractions as is the + * case in the 2p2c model. Instead mole fractions are calculated in the FluidSystem with a given + * temperature, pressure and salinity. + * + */ +template<class TypeTag> +class CO2NIModel : public CO2Model<TypeTag> +{ +}; + +} + +#include <dumux/boxmodels/2p2cni/2p2cnipropertydefaults.hh> + +#endif diff --git a/dumux/implicit/co2ni/co2nivolumevariables.hh b/dumux/implicit/co2ni/co2nivolumevariables.hh new file mode 100644 index 0000000000..9609d0095b --- /dev/null +++ b/dumux/implicit/co2ni/co2nivolumevariables.hh @@ -0,0 +1,139 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * Copyright (C) 2012 by * + * Institute for Modelling Hydraulic and Environmental Systems * + * University of Stuttgart, Germany * + * email: <givenname>.<name>@iws.uni-stuttgart.de * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \file + * + * \brief Contains the quantities which are constant within a + * finite volume in the non-isothermal two-phase, two-component + * model. + */ +#ifndef DUMUX_CO2NI_VOLUME_VARIABLES_HH +#define DUMUX_CO2NI_VOLUME_VARIABLES_HH + +#include <dumux/boxmodels/co2/co2volumevariables.hh> + +namespace Dumux +{ + +/*! + * \ingroup CO2NIModel + * \ingroup BoxVolumeVariables + * \brief Contains the quantities which are are constant within a + * finite volume in the non-isothermal two-phase, two-component + * model. + */ +template <class TypeTag> +class CO2NIVolumeVariables : public CO2VolumeVariables<TypeTag> +{ + //! \cond 0 + typedef CO2VolumeVariables<TypeTag> ParentType; + typedef typename ParentType::FluidState FluidState; + + typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; + typedef typename GET_PROP_TYPE(TypeTag, FVElementGeometry) FVElementGeometry; + typedef typename GET_PROP_TYPE(TypeTag, Problem) Problem; + typedef typename GET_PROP_TYPE(TypeTag, PrimaryVariables) PrimaryVariables; + typedef typename GET_PROP_TYPE(TypeTag, FluidSystem) FluidSystem; + + typedef typename GET_PROP_TYPE(TypeTag, Indices) Indices; + enum { temperatureIdx = Indices::temperatureIdx }; + + typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; + typedef typename GridView::template Codim<0>::Entity Element; + //! \endcond + +public: + /*! + * \brief Returns the total internal energy of a phase in the + * sub-control volume. + * + * \param phaseIdx The phase index + */ + Scalar internalEnergy(const int phaseIdx) const + { return this->fluidState_.internalEnergy(phaseIdx); }; + + /*! + * \brief Returns the total enthalpy of a phase in the sub-control + * volume. + * + * \param phaseIdx The phase index + */ + Scalar enthalpy(const int phaseIdx) const + { return this->fluidState_.enthalpy(phaseIdx); }; + + /*! + * \brief Returns the total heat capacity \f$\mathrm{[J/(K*m^3]}\f$ of the rock matrix in + * the sub-control volume. + */ + Scalar heatCapacity() const + { return heatCapacity_; }; + +protected: + // this method gets called by the parent class. since this method + // is protected, we are friends with our parent.. + friend class CO2VolumeVariables<TypeTag>; + + static Scalar temperature_(const PrimaryVariables &priVars, + const Problem& problem, + const Element &element, + const FVElementGeometry &fvGeometry, + const int scvIdx) + { + return priVars[temperatureIdx]; + } + + template<class ParameterCache> + static Scalar enthalpy_(const FluidState& fluidState, + const ParameterCache& paramCache, + const int phaseIdx) + { + return FluidSystem::enthalpy(fluidState, paramCache, phaseIdx); + } + + /*! + * \brief Update all quantities for a given control volume. + * + * \param sol The solution primary variables + * \param problem The problem + * \param element The element + * \param fvGeometry Evaluate function with solution of current or previous time step + * \param scvIdx The local index of the SCV (sub-control volume) + * \param isOldSol Evaluate function with solution of current or previous time step + */ + void updateEnergy_(const PrimaryVariables &sol, + const Problem &problem, + const Element &element, + const FVElementGeometry &fvGeometry, + const int scvIdx, + bool isOldSol) + { + // copmute and set the heat capacity of the solid phase + heatCapacity_ = problem.spatialParams().heatCapacity(element, fvGeometry, scvIdx); + Valgrind::CheckDefined(heatCapacity_); + }; + + Scalar heatCapacity_; +}; + +} // end namepace + +#endif diff --git a/dumux/implicit/common/Makefile.am b/dumux/implicit/common/Makefile.am new file mode 100644 index 0000000000..2fd0a582d6 --- /dev/null +++ b/dumux/implicit/common/Makefile.am @@ -0,0 +1,4 @@ +commondir = $(includedir)/dumux/boxmodels/common +common_HEADERS = *.hh + +include $(top_srcdir)/am/global-rules diff --git a/dumux/implicit/common/implicitassembler.hh b/dumux/implicit/common/implicitassembler.hh new file mode 100644 index 0000000000..18e196c3e8 --- /dev/null +++ b/dumux/implicit/common/implicitassembler.hh @@ -0,0 +1,858 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \file + * + * \brief An assembler for the global Jacobian matrix for models using the box discretization. + */ +#ifndef DUMUX_BOX_ASSEMBLER_HH +#define DUMUX_BOX_ASSEMBLER_HH + +#include <dune/grid/common/gridenums.hh> + +#include <dumux/boxmodels/common/boxproperties.hh> +#include <dumux/linear/vertexborderlistfromgrid.hh> +#include <dumux/linear/foreignoverlapfrombcrsmatrix.hh> +#include <dumux/parallel/vertexhandles.hh> + +namespace Dumux { + +/*! + * \brief An assembler for the global Jacobian matrix for models using the box discretization. + */ +template<class TypeTag> +class BoxAssembler +{ + typedef typename GET_PROP_TYPE(TypeTag, Model) Model; + typedef typename GET_PROP_TYPE(TypeTag, Problem) Problem; + typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; + typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; + typedef typename GET_PROP_TYPE(TypeTag, VertexMapper) VertexMapper; + typedef typename GET_PROP_TYPE(TypeTag, ElementMapper) ElementMapper; + + typedef typename GET_PROP_TYPE(TypeTag, SolutionVector) SolutionVector; + typedef typename GET_PROP_TYPE(TypeTag, JacobianMatrix) JacobianMatrix; + typedef typename GET_PROP_TYPE(TypeTag, PrimaryVariables) PrimaryVariables; + + enum{ dim = GridView::dimension }; + typedef typename GridView::template Codim<0>::Entity Element; + typedef typename GridView::template Codim<0>::Iterator ElementIterator; + typedef typename GridView::template Codim<dim>::EntityPointer VertexPointer; + + enum { numEq = GET_PROP_VALUE(TypeTag, NumEq) }; + typedef Dune::FieldMatrix<Scalar, numEq, numEq> MatrixBlock; + typedef Dune::FieldVector<Scalar, numEq> VectorBlock; + + // copying the jacobian assembler is not a good idea + BoxAssembler(const BoxAssembler &); + +public: + /*! + * \brief The colors of elements and vertices required for partial + * Jacobian reassembly. + */ + enum EntityColor { + /*! + * Vertex/element that needs to be reassembled because some + * relative error is above the tolerance + */ + Red = 0, + + /*! + * Vertex/element that needs to be reassembled because a + * neighboring element/vertex is red + */ + Yellow = 1, + + /*! + * Yellow vertex has only non-green neighbor elements. + * + * This means that its relative error is below the tolerance, + * but its defect can be linearized without any additional + * cost. This is just an "internal" color which is not used + * ouside of the jacobian assembler. + */ + Orange = 2, + + /*! + * Vertex/element that does not need to be reassembled + */ + Green = 3 + }; + + BoxAssembler() + { + problemPtr_ = 0; + matrix_ = 0; + + // set reassemble accuracy to 0, so that if partial reassembly + // of the jacobian matrix is disabled, the reassemble accuracy + // is always smaller than the current relative tolerance + reassembleAccuracy_ = 0.0; + } + + ~BoxAssembler() + { + delete matrix_; + } + + /*! + * \brief Initialize the jacobian assembler. + * + * At this point we can assume that all objects in the problem and + * the model have been allocated. We can not assume that they are + * fully initialized, though. + * + * \param problem The problem object + */ + void init(Problem& problem) + { + problemPtr_ = &problem; + + // initialize the BCRS matrix + createMatrix_(); + + // initialize the jacobian matrix and the right hand side + // vector + *matrix_ = 0; + reuseMatrix_ = false; + + int numVerts = gridView_().size(dim); + int numElems = gridView_().size(0); + + residual_.resize(numVerts); + + // initialize the storage part of the Jacobian matrix. Since + // we only need this if Jacobian matrix recycling is enabled, + // we do not waste space if it is disabled + if (enableJacobianRecycling_()) { + storageJacobian_.resize(numVerts); + storageTerm_.resize(numVerts); + } + + if (gridView_().comm().size() > 1) + totalElems_ = gridView_().comm().sum(numElems); + else + totalElems_ = numElems; + + // initialize data needed for partial reassembly + if (enablePartialReassemble_()) { + vertexColor_.resize(numVerts); + vertexDelta_.resize(numVerts); + elementColor_.resize(numElems); + } + reassembleAll(); + } + + /*! + * \brief Assemble the global Jacobian of the residual and the residual for the current solution. + * + * The current state of affairs (esp. the previous and the current + * solutions) is represented by the model object. + */ + void assemble() + { + bool printReassembleStatistics = enablePartialReassemble_() && !reuseMatrix_; + int succeeded; + try { + assemble_(); + succeeded = 1; + if (gridView_().comm().size() > 1) + succeeded = gridView_().comm().min(succeeded); + } + catch (Dumux::NumericalProblem &e) + { + std::cout << "rank " << problem_().gridView().comm().rank() + << " caught an exception while assembling:" << e.what() + << "\n"; + succeeded = 0; + if (gridView_().comm().size() > 1) + succeeded = gridView_().comm().min(succeeded); + } + + if (!succeeded) { + DUNE_THROW(NumericalProblem, + "A process did not succeed in linearizing the system"); + } + + if (printReassembleStatistics) + { + if (gridView_().comm().size() > 1) + { + greenElems_ = gridView_().comm().sum(greenElems_); + reassembleAccuracy_ = gridView_().comm().max(nextReassembleAccuracy_); + } + else + { + reassembleAccuracy_ = nextReassembleAccuracy_; + } + + problem_().newtonController().endIterMsg() + << ", reassembled " + << totalElems_ - greenElems_ << "/" << totalElems_ + << " (" << 100*Scalar(totalElems_ - greenElems_)/totalElems_ << "%) elems @accuracy=" + << reassembleAccuracy_; + } + + // reset all vertex colors to green + for (unsigned int i = 0; i < vertexColor_.size(); ++i) { + vertexColor_[i] = Green; + } + } + + /*! + * \brief If Jacobian matrix recycling is enabled, this method + * specifies whether the next call to assemble() just + * rescales the storage term or does a full reassembly + * + * \param yesno If true, only rescale; else do full Jacobian assembly. + */ + void setMatrixReuseable(const bool yesno = true) + { + if (enableJacobianRecycling_()) + reuseMatrix_ = yesno; + } + + /*! + * \brief If partial Jacobian matrix reassembly is enabled, this + * method causes all elements to be reassembled in the next + * assemble() call. + */ + void reassembleAll() + { + // do not reuse the current linearization + reuseMatrix_ = false; + + // do not use partial reassembly for the next iteration + nextReassembleAccuracy_ = 0.0; + if (enablePartialReassemble_()) { + std::fill(vertexColor_.begin(), + vertexColor_.end(), + Red); + std::fill(elementColor_.begin(), + elementColor_.end(), + Red); + std::fill(vertexDelta_.begin(), + vertexDelta_.end(), + 0.0); + } + } + + /*! + * \brief Returns the largest relative error of a "green" vertex + * for the most recent call of the assemble() method. + * + * This only has an effect if partial Jacobian reassembly is + * enabled. If it is disabled, then this method always returns 0. + * + * This returns the _actual_ relative computed seen by + * computeColors(), not the tolerance which it was given. + */ + Scalar reassembleAccuracy() const + { return reassembleAccuracy_; } + + /*! + * \brief Update the distance where the non-linear system was + * originally insistently linearized and the point where it + * will be linerized the next time. + * + * This only has an effect if partial reassemble is enabled. + */ + void updateDiscrepancy(const SolutionVector &u, + const SolutionVector &uDelta) + { + if (!enablePartialReassemble_()) + return; + + // update the vector with the distances of the current + // evaluation point used for linearization from the original + // evaluation point + for (unsigned int i = 0; i < vertexDelta_.size(); ++i) { + PrimaryVariables currentPriVars(u[i]); + PrimaryVariables nextPriVars(currentPriVars); + nextPriVars -= uDelta[i]; + + // we need to add the distance the solution was moved for + // this vertex + Scalar dist = model_().relativeErrorVertex(i, + currentPriVars, + nextPriVars); + vertexDelta_[i] += std::abs(dist); + } + + } + + /*! + * \brief Force to reassemble a given vertex next time the + * assemble() method is called. + * + * \param globalVertIdx The global index of the vertex which ought + * to be red. + */ + void markVertexRed(const int globalVertIdx) + { + if (!enablePartialReassemble_()) + return; + + vertexColor_[globalVertIdx] = Red; + } + + /*! + * \brief Determine the colors of vertices and elements for partial + * reassembly given a relative tolerance. + * + * The following approach is used: + * + * - Set all vertices and elements to 'green' + * - Mark all vertices as 'red' which exhibit an relative error above + * the tolerance + * - Mark all elements which feature a 'red' vetex as 'red' + * - Mark all vertices which are not 'red' and are part of a + * 'red' element as 'yellow' + * - Mark all elements which are not 'red' and contain a + * 'yellow' vertex as 'yellow' + * + * \param relTol The relative error below which a vertex won't be + * reassembled. Note that this specifies the + * worst-case relative error between the last + * linearization point and the current solution and + * _not_ the delta vector of the Newton iteration! + */ + void computeColors(const Scalar relTol) + { + if (!enablePartialReassemble_()) + return; + + ElementIterator elemIt = gridView_().template begin<0>(); + ElementIterator elemEndIt = gridView_().template end<0>(); + + // mark the red vertices and update the tolerance of the + // linearization which actually will get achieved + nextReassembleAccuracy_ = 0; + for (unsigned int i = 0; i < vertexColor_.size(); ++i) { + if (vertexDelta_[i] > relTol) + // mark vertex as red if discrepancy is larger than + // the relative tolerance + vertexColor_[i] = Red; + else + nextReassembleAccuracy_ = + std::max(nextReassembleAccuracy_, vertexDelta_[i]); + } + + // Mark all red elements + for (; elemIt != elemEndIt; ++elemIt) { + // find out whether the current element features a red + // vertex + bool isRed = false; + int numVerts = elemIt->template count<dim>(); + for (int i=0; i < numVerts; ++i) { + int globalI = vertexMapper_().map(*elemIt, i, dim); + if (vertexColor_[globalI] == Red) { + isRed = true; + break; + } + } + + // if yes, the element color is also red, else it is not + // red, i.e. green for the mean time + int globalElemIdx = elementMapper_().map(*elemIt); + if (isRed) + elementColor_[globalElemIdx] = Red; + else + elementColor_[globalElemIdx] = Green; + } + + // Mark yellow vertices (as orange for the mean time) + elemIt = gridView_().template begin<0>(); + for (; elemIt != elemEndIt; ++elemIt) { + int elemIdx = this->elementMapper_().map(*elemIt); + if (elementColor_[elemIdx] != Red) + continue; // non-red elements do not tint vertices + // yellow! + + int numVerts = elemIt->template count<dim>(); + for (int i=0; i < numVerts; ++i) { + int globalI = vertexMapper_().map(*elemIt, i, dim); + // if a vertex is already red, don't recolor it to + // yellow! + if (vertexColor_[globalI] != Red) { + vertexColor_[globalI] = Orange; + } + } + } + + // at this point we communicate the yellow vertices to the + // neighboring processes because a neigbor process may not see + // the red vertex for yellow border vertices + VertexHandleMin<EntityColor, std::vector<EntityColor>, VertexMapper> + minHandle(vertexColor_, vertexMapper_()); + gridView_().communicate(minHandle, + Dune::InteriorBorder_InteriorBorder_Interface, + Dune::ForwardCommunication); + + // Mark yellow elements + elemIt = gridView_().template begin<0>(); + for (; elemIt != elemEndIt; ++elemIt) { + int elemIdx = this->elementMapper_().map(*elemIt); + if (elementColor_[elemIdx] == Red) { + continue; // element is red already! + } + + // check whether the element features a yellow + // (resp. orange at this point) vertex + bool isYellow = false; + int numVerts = elemIt->template count<dim>(); + for (int i=0; i < numVerts; ++i) { + int globalI = vertexMapper_().map(*elemIt, i, dim); + if (vertexColor_[globalI] == Orange) { + isYellow = true; + break; + } + } + + if (isYellow) + elementColor_[elemIdx] = Yellow; + } + + // Demote orange vertices to yellow ones if it has at least + // one green element as a neighbor. + elemIt = gridView_().template begin<0>(); + for (; elemIt != elemEndIt; ++elemIt) { + int elemIdx = this->elementMapper_().map(*elemIt); + if (elementColor_[elemIdx] != Green) + continue; // yellow and red elements do not make + // orange vertices yellow! + + int numVerts = elemIt->template count<dim>(); + for (int i=0; i < numVerts; ++i) { + int globalI = vertexMapper_().map(*elemIt, i, dim); + // if a vertex is orange, recolor it to yellow! + if (vertexColor_[globalI] == Orange) + vertexColor_[globalI] = Yellow; + } + } + + // demote the border orange vertices + VertexHandleMax<EntityColor, std::vector<EntityColor>, VertexMapper> + maxHandle(vertexColor_, + vertexMapper_()); + gridView_().communicate(maxHandle, + Dune::InteriorBorder_InteriorBorder_Interface, + Dune::ForwardCommunication); + + // promote the remaining orange vertices to red + for (unsigned int i=0; i < vertexColor_.size(); ++i) { + // if a vertex is green or yellow don't do anything! + if (vertexColor_[i] == Green || vertexColor_[i] == Yellow) + continue; + + // make sure the vertex is red (this is a no-op vertices + // which are already red!) + vertexColor_[i] = Red; + + // set the error of this vertex to 0 because the system + // will be consistently linearized at this vertex + vertexDelta_[i] = 0.0; + } + } + + /*! + * \brief Returns the reassemble color of a vertex + * + * \param element An element which contains the vertex + * \param vertIdx The local index of the vertex in the element. + */ + int vertexColor(const Element &element, const int vertIdx) const + { + if (!enablePartialReassemble_()) + return Red; // reassemble unconditionally! + + int globalIdx = vertexMapper_().map(element, vertIdx, dim); + return vertexColor_[globalIdx]; + } + + /*! + * \brief Returns the reassemble color of a vertex + * + * \param globalVertIdx The global index of the vertex. + */ + int vertexColor(const int globalVertIdx) const + { + if (!enablePartialReassemble_()) + return Red; // reassemble unconditionally! + return vertexColor_[globalVertIdx]; + } + + /*! + * \brief Returns the Jacobian reassemble color of an element + * + * \param element The Codim-0 DUNE entity + */ + int elementColor(const Element &element) const + { + if (!enablePartialReassemble_()) + return Red; // reassemble unconditionally! + + int globalIdx = elementMapper_().map(element); + return elementColor_[globalIdx]; + } + + /*! + * \brief Returns the Jacobian reassemble color of an element + * + * \param globalElementIdx The global index of the element. + */ + int elementColor(const int globalElementIdx) const + { + if (!enablePartialReassemble_()) + return Red; // reassemble unconditionally! + return elementColor_[globalElementIdx]; + } + + /*! + * \brief Return constant reference to global Jacobian matrix. + */ + const JacobianMatrix& matrix() const + { return *matrix_; } + JacobianMatrix& matrix() + { return *matrix_; } + + /*! + * \brief Return constant reference to global residual vector. + */ + const SolutionVector& residual() const + { return residual_; } + SolutionVector& residual() + { return residual_; } + +private: + static bool enableJacobianRecycling_() + { return GET_PARAM_FROM_GROUP(TypeTag, bool, Implicit, EnableJacobianRecycling); } + static bool enablePartialReassemble_() + { return GET_PARAM_FROM_GROUP(TypeTag, bool, Implicit, EnablePartialReassemble); } + + // Construct the BCRS matrix for the global jacobian + void createMatrix_() + { + int numVerticesGlobal = gridView_().size(dim); + + // allocate raw matrix + matrix_ = new JacobianMatrix(numVerticesGlobal, numVerticesGlobal, JacobianMatrix::random); + + // find out the global indices of the neighboring vertices of + // each vertex + typedef std::set<int> NeighborSet; + std::vector<NeighborSet> neighbors(numVerticesGlobal); + ElementIterator eIt = gridView_().template begin<0>(); + const ElementIterator eEndIt = gridView_().template end<0>(); + for (; eIt != eEndIt; ++eIt) { + const Element &elem = *eIt; + int numVerticesLocal = elem.template count<dim>(); + + // if the element is not in the interior or the process + // border, all dofs just contain main-diagonal entries + if (elem.partitionType() != Dune::InteriorEntity && + elem.partitionType() != Dune::BorderEntity) + { + for (int i = 0; i < numVerticesLocal; ++i) { + int globalI = vertexMapper_().map(*eIt, i, dim); + neighbors[globalI].insert(globalI); + } + } + else + { + // loop over all element vertices + for (int i = 0; i < numVerticesLocal - 1; ++i) { + int globalI = vertexMapper_().map(*eIt, i, dim); + for (int j = i + 1; j < numVerticesLocal; ++j) { + int globalJ = vertexMapper_().map(*eIt, j, dim); + // make sure that vertex j is in the neighbor set + // of vertex i and vice-versa + neighbors[globalI].insert(globalJ); + neighbors[globalJ].insert(globalI); + } + } + } + } + + // make vertices neighbors to themselfs + for (int i = 0; i < numVerticesGlobal; ++i) { + neighbors[i].insert(i); + } + + // allocate space for the rows of the matrix + for (int i = 0; i < numVerticesGlobal; ++i) { + matrix_->setrowsize(i, neighbors[i].size()); + } + matrix_->endrowsizes(); + + // fill the rows with indices. each vertex talks to all of its + // neighbors. (it also talks to itself since vertices are + // sometimes quite egocentric.) + for (int i = 0; i < numVerticesGlobal; ++i) { + typename NeighborSet::iterator nIt = neighbors[i].begin(); + typename NeighborSet::iterator nEndIt = neighbors[i].end(); + for (; nIt != nEndIt; ++nIt) { + matrix_->addindex(i, *nIt); + } + } + matrix_->endindices(); + } + + // reset the global linear system of equations. if partial + // reassemble is enabled, this means that the jacobian matrix must + // only be erased partially! + void resetSystem_() + { + // do not do anything if we can re-use the current linearization + if (reuseMatrix_) + return; + + // reset the right hand side. + residual_ = 0.0; + + if (!enablePartialReassemble_()) { + // If partial reassembly of the jacobian is not enabled, + // we can just reset everything! + (*matrix_) = 0; + + // reset the parts needed for Jacobian recycling + if (enableJacobianRecycling_()) { + int numVerticesGlobal = matrix_->N(); + for (int i=0; i < numVerticesGlobal; ++ i) { + storageJacobian_[i] = 0; + storageTerm_[i] = 0; + } + } + + return; + } + + // reset all entries corrosponding to a red or yellow vertex + for (unsigned int rowIdx = 0; rowIdx < matrix_->N(); ++rowIdx) { + if (vertexColor_[rowIdx] == Green) + continue; // the equations for this control volume are + // already below the treshold + + // here we have yellow or red vertices... + + // reset the parts needed for Jacobian recycling + if (enableJacobianRecycling_()) { + storageJacobian_[rowIdx] = 0; + storageTerm_[rowIdx] = 0; + } + + // set all matrix entries in the row to 0 + typedef typename JacobianMatrix::ColIterator ColIterator; + ColIterator colIt = (*matrix_)[rowIdx].begin(); + const ColIterator &colEndIt = (*matrix_)[rowIdx].end(); + for (; colIt != colEndIt; ++colIt) { + (*colIt) = 0.0; + } + } + } + + // linearize the whole system + void assemble_() + { + resetSystem_(); + + // if we can "recycle" the current linearization, we do it + // here and be done with it... + Scalar curDt = problem_().timeManager().timeStepSize(); + if (reuseMatrix_) { + int numVerticesGlobal = storageJacobian_.size(); + for (int i = 0; i < numVerticesGlobal; ++i) { + // rescale the mass term of the jacobian matrix + MatrixBlock &J_i_i = (*matrix_)[i][i]; + + J_i_i -= storageJacobian_[i]; + storageJacobian_[i] *= oldDt_/curDt; + J_i_i += storageJacobian_[i]; + + // use the flux term plus the source term as the new + // residual (since the delta in the d(storage)/dt is 0 + // for the first iteration and the residual is + // approximately 0 in the last iteration, the flux + // term plus the source term must be equal to the + // negative change of the storage term of the last + // iteration of the last time step...) + residual_[i] = storageTerm_[i]; + residual_[i] *= -1; + } + + reuseMatrix_ = false; + oldDt_ = curDt; + return; + } + + oldDt_ = curDt; + greenElems_ = 0; + + // reassemble the elements... + ElementIterator elemIt = gridView_().template begin<0>(); + ElementIterator elemEndIt = gridView_().template end<0>(); + for (; elemIt != elemEndIt; ++elemIt) { + const Element &elem = *elemIt; + if (elem.partitionType() != Dune::InteriorEntity && + elem.partitionType() != Dune::BorderEntity) + { + assembleGhostElement_(elem); + } + else + { + assembleElement_(elem); + } + } + } + + // assemble a non-ghost element + void assembleElement_(const Element &elem) + { + if (enablePartialReassemble_()) { + int globalElemIdx = model_().elementMapper().map(elem); + if (elementColor_[globalElemIdx] == Green) { + ++greenElems_; + + assembleGreenElement_(elem); + return; + } + } + + model_().localJacobian().assemble(elem); + + int numVerticesLocal = elem.template count<dim>(); + for (int i=0; i < numVerticesLocal; ++ i) { + int globI = vertexMapper_().map(elem, i, dim); + + // update the right hand side + residual_[globI] += model_().localJacobian().residual(i); + for (int j = 0; j < residual_[globI].dimension; ++j) + assert(std::isfinite(residual_[globI][j])); + if (enableJacobianRecycling_()) { + storageTerm_[globI] += + model_().localJacobian().storageTerm(i); + } + + // only update the jacobian matrix for non-green vertices + if (vertexColor(globI) != Green) { + if (enableJacobianRecycling_()) + storageJacobian_[globI] += + model_().localJacobian().storageJacobian(i); + + // update the jacobian matrix + for (int j=0; j < numVerticesLocal; ++ j) { + int globJ = vertexMapper_().map(elem, j, dim); + (*matrix_)[globI][globJ] += + model_().localJacobian().mat(i,j); + } + } + } + } + + // "assemble" a green element. green elements only get the + // residual updated, but the jacobian is left alone... + void assembleGreenElement_(const Element &elem) + { + model_().localResidual().eval(elem); + + int numVerticesLocal = elem.template count<dim>(); + for (int i=0; i < numVerticesLocal; ++ i) { + int globI = vertexMapper_().map(elem, i, dim); + + // update the right hand side + residual_[globI] += model_().localResidual().residual(i); + if (enableJacobianRecycling_()) + storageTerm_[globI] += model_().localResidual().storageTerm(i); + } + } + + // "assemble" a ghost element + void assembleGhostElement_(const Element &elem) + { + int numVerticesLocal = elem.template count<dim>(); + for (int i=0; i < numVerticesLocal; ++i) { + const VertexPointer vp = elem.template subEntity<dim>(i); + + if (vp->partitionType() == Dune::InteriorEntity || + vp->partitionType() == Dune::BorderEntity) + { + // do not change the non-ghost vertices + continue; + } + + // set main diagonal entries for the vertex + int vIdx = vertexMapper_().map(*vp); + typedef typename JacobianMatrix::block_type BlockType; + BlockType &J = (*matrix_)[vIdx][vIdx]; + for (int j = 0; j < BlockType::rows; ++j) + J[j][j] = 1.0; + + // set residual for the vertex + residual_[vIdx] = 0; + } + } + + + Problem &problem_() + { return *problemPtr_; } + const Problem &problem_() const + { return *problemPtr_; } + const Model &model_() const + { return problem_().model(); } + Model &model_() + { return problem_().model(); } + const GridView &gridView_() const + { return problem_().gridView(); } + const VertexMapper &vertexMapper_() const + { return problem_().vertexMapper(); } + const ElementMapper &elementMapper_() const + { return problem_().elementMapper(); } + + Problem *problemPtr_; + + // the jacobian matrix + JacobianMatrix *matrix_; + // the right-hand side + SolutionVector residual_; + + // attributes required for jacobian matrix recycling + bool reuseMatrix_; + // The storage part of the local Jacobian + std::vector<MatrixBlock> storageJacobian_; + std::vector<VectorBlock> storageTerm_; + // time step size of last assembly + Scalar oldDt_; + + + // attributes required for partial jacobian reassembly + std::vector<EntityColor> vertexColor_; + std::vector<EntityColor> elementColor_; + std::vector<Scalar> vertexDelta_; + + int totalElems_; + int greenElems_; + + Scalar nextReassembleAccuracy_; + Scalar reassembleAccuracy_; +}; + +} // namespace Dumux + +#endif diff --git a/dumux/implicit/common/implicitdarcyfluxvariables.hh b/dumux/implicit/common/implicitdarcyfluxvariables.hh new file mode 100644 index 0000000000..6e5d3df449 --- /dev/null +++ b/dumux/implicit/common/implicitdarcyfluxvariables.hh @@ -0,0 +1,313 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \file + * + * \brief This file contains the data which is required to calculate + * all fluxes of fluid phases over a face of a finite volume. + * + * This means pressure and temperature gradients, phase densities at + * the integration point, etc. + */ +#ifndef DUMUX_BOX_DARCY_FLUX_VARIABLES_HH +#define DUMUX_BOX_DARCY_FLUX_VARIABLES_HH + +#include "boxproperties.hh" + +#include <dumux/common/parameters.hh> +#include <dumux/common/math.hh> + +namespace Dumux +{ + +namespace Properties +{ +// forward declaration of properties +NEW_PROP_TAG(ImplicitMobilityUpwindWeight); +NEW_PROP_TAG(SpatialParams); +NEW_PROP_TAG(NumPhases); +NEW_PROP_TAG(ProblemEnableGravity); +} + +/*! + * \ingroup BoxModel + * \ingroup BoxFluxVariables + * \brief Evaluates the normal component of the Darcy velocity + * on a (sub)control volume face. + */ +template <class TypeTag> +class BoxDarcyFluxVariables +{ + typedef typename GET_PROP_TYPE(TypeTag, Problem) Problem; + typedef typename GET_PROP_TYPE(TypeTag, SpatialParams) SpatialParams; + typedef typename GET_PROP_TYPE(TypeTag, ElementVolumeVariables) ElementVolumeVariables; + typedef typename GET_PROP_TYPE(TypeTag, VolumeVariables) VolumeVariables; + + typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; + typedef typename GridView::template Codim<0>::Entity Element; + + enum { dim = GridView::dimension} ; + enum { dimWorld = GridView::dimensionworld} ; + enum { numPhases = GET_PROP_VALUE(TypeTag, NumPhases)} ; + + typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; + typedef Dune::FieldMatrix<Scalar, dimWorld, dimWorld> Tensor; + typedef Dune::FieldVector<Scalar, dimWorld> DimVector; + + typedef typename GET_PROP_TYPE(TypeTag, FVElementGeometry) FVElementGeometry; + typedef typename FVElementGeometry::SubControlVolumeFace SCVFace; + +public: + /* + * \brief The constructor + * + * \param problem The problem + * \param element The finite element + * \param fvGeometry The finite-volume geometry in the box scheme + * \param faceIdx The local index of the SCV (sub-control-volume) face + * \param elemVolVars The volume variables of the current element + * \param onBoundary A boolean variable to specify whether the flux variables + * are calculated for interior SCV faces or boundary faces, default=false + */ + BoxDarcyFluxVariables(const Problem &problem, + const Element &element, + const FVElementGeometry &fvGeometry, + const int faceIdx, + const ElementVolumeVariables &elemVolVars, + const bool onBoundary = false) + : fvGeometry_(fvGeometry), faceIdx_(faceIdx), onBoundary_(onBoundary) + { + mobilityUpwindWeight_ = GET_PARAM_FROM_GROUP(TypeTag, Scalar, Implicit, MobilityUpwindWeight); + calculateGradients_(problem, element, elemVolVars); + calculateNormalVelocity_(problem, element, elemVolVars); + } + +public: + /*! + * \brief Return the volumetric flux over a face of a given phase. + * + * This is the calculated velocity multiplied by the unit normal + * and the area of the face. + * face().normal + * has already the magnitude of the area. + * + * \param phaseIdx index of the phase + */ + Scalar volumeFlux(const unsigned int phaseIdx) const + { return volumeFlux_[phaseIdx]; } + + /*! + * \brief Return the velocity of a given phase. + * + * This is the full velocity vector on the + * face (without being multiplied with normal). + * + * \param phaseIdx index of the phase + */ + DimVector velocity(const unsigned int phaseIdx) const + { return velocity_[phaseIdx] ; } + + /*! + * \brief Return intrinsic permeability multiplied with potential + * gradient multiplied with normal. + * I.e. everything that does not need upwind decisions. + * + * \param phaseIdx index of the phase + */ + Scalar kGradPNormal(const unsigned int phaseIdx) const + { return kGradPNormal_[phaseIdx] ; } + + /*! + * \brief Return the local index of the downstream control volume + * for a given phase. + * + * \param phaseIdx index of the phase + */ + const unsigned int downstreamIdx(const unsigned phaseIdx) const + { return downstreamIdx_[phaseIdx]; } + + /*! + * \brief Return the local index of the upstream control volume + * for a given phase. + * + * \param phaseIdx index of the phase + */ + const unsigned int upstreamIdx(const unsigned phaseIdx) const + { return upstreamIdx_[phaseIdx]; } + + /*! + * \brief Return the SCV (sub-control-volume) face. This may be either + * a face within the element or a face on the element boundary, + * depending on the value of onBoundary_. + */ + const SCVFace &face() const + { + if (onBoundary_) + return fvGeometry_.boundaryFace[faceIdx_]; + else + return fvGeometry_.subContVolFace[faceIdx_]; + } + +protected: + const FVElementGeometry &fvGeometry_; //!< Information about the geometry of discretization + const unsigned int faceIdx_; //!< The index of the sub control volume face + const bool onBoundary_; //!< Specifying whether we are currently on the boundary of the simulation domain + unsigned int upstreamIdx_[numPhases] , downstreamIdx_[numPhases]; //!< local index of the upstream / downstream vertex + Scalar volumeFlux_[numPhases] ; //!< Velocity multiplied with normal (magnitude=area) + DimVector velocity_[numPhases] ; //!< The velocity as determined by Darcy's law or by the Forchheimer relation + Scalar kGradPNormal_[numPhases] ; //!< Permeability multiplied with gradient in potential, multiplied with normal (magnitude=area) + DimVector kGradP_[numPhases] ; //!< Permeability multiplied with gradient in potential + DimVector gradPotential_[numPhases] ; //!< Gradient of potential, which drives flow + Scalar mobilityUpwindWeight_; //!< Upwind weight for mobility. Set to one for full upstream weighting + + /* + * \brief Calculation of the potential gradients + * + * \param problem The problem + * \param element The finite element + * \param elemVolVars The volume variables of the current element + * are calculated for interior SCV faces or boundary faces, default=false + */ + void calculateGradients_(const Problem &problem, + const Element &element, + const ElementVolumeVariables &elemVolVars) + { + // loop over all phases + for (int phaseIdx = 0; phaseIdx < numPhases; phaseIdx++) + { + gradPotential_[phaseIdx]= 0.0 ; + for (int idx = 0; + idx < fvGeometry_.numFAP; + idx++) // loop over adjacent vertices + { + // FE gradient at vertex idx + const DimVector &feGrad = face().grad[idx]; + + // index for the element volume variables + int volVarsIdx = face().fapIndices[idx]; + + // the pressure gradient + DimVector tmp(feGrad); + tmp *= elemVolVars[volVarsIdx].fluidState().pressure(phaseIdx); + gradPotential_[phaseIdx] += tmp; + } + + // correct the pressure gradient by the gravitational acceleration + if (GET_PARAM_FROM_GROUP(TypeTag, bool, Problem, EnableGravity)) + { + // estimate the gravitational acceleration at a given SCV face + // using the arithmetic mean + DimVector g(problem.boxGravity(element, fvGeometry_, face().i)); + g += problem.boxGravity(element, fvGeometry_, face().j); + g /= 2; + + // calculate the phase density at the integration point. we + // only do this if the wetting phase is present in both cells + Scalar SI = elemVolVars[face().i].fluidState().saturation(phaseIdx); + Scalar SJ = elemVolVars[face().j].fluidState().saturation(phaseIdx); + Scalar rhoI = elemVolVars[face().i].fluidState().density(phaseIdx); + Scalar rhoJ = elemVolVars[face().j].fluidState().density(phaseIdx); + Scalar fI = std::max(0.0, std::min(SI/1e-5, 0.5)); + Scalar fJ = std::max(0.0, std::min(SJ/1e-5, 0.5)); + if (fI + fJ == 0) + // doesn't matter because no wetting phase is present in + // both cells! + fI = fJ = 0.5; + const Scalar density = (fI*rhoI + fJ*rhoJ)/(fI + fJ); + + // make gravity acceleration a force + DimVector f(g); + f *= density; + + // calculate the final potential gradient + gradPotential_[phaseIdx] -= f; + } // gravity + } // loop over all phases + } + + /* + * \brief Actual calculation of the normal Darcy velocities. + * + * \param problem The problem + * \param element The finite element + * \param elemVolVars The volume variables of the current element + */ + void calculateNormalVelocity_(const Problem &problem, + const Element &element, + const ElementVolumeVariables &elemVolVars) + { + // calculate the mean intrinsic permeability + const SpatialParams &spatialParams = problem.spatialParams(); + Tensor K; + spatialParams.meanK(K, + spatialParams.intrinsicPermeability(element, + fvGeometry_, + face().i), + spatialParams.intrinsicPermeability(element, + fvGeometry_, + face().j)); + // loop over all phases + for (int phaseIdx = 0; phaseIdx < numPhases; phaseIdx++) + { + // calculate the flux in the normal direction of the + // current sub control volume face: + // + // v = - (K_f grad phi) * n + // with K_f = rho g / mu K + // + // Mind, that the normal has the length of it's area. + // This means that we are actually calculating + // Q = - (K grad phi) dot n /|n| * A + + + K.mv(gradPotential_[phaseIdx], kGradP_[phaseIdx]); + kGradPNormal_[phaseIdx] = kGradP_[phaseIdx]*face().normal; + + // determine the upwind direction + if (kGradPNormal_[phaseIdx] < 0) + { + upstreamIdx_[phaseIdx] = face().i; + downstreamIdx_[phaseIdx] = face().j; + } + else + { + upstreamIdx_[phaseIdx] = face().j; + downstreamIdx_[phaseIdx] = face().i; + } + + // obtain the upwind volume variables + const VolumeVariables& upVolVars = elemVolVars[ upstreamIdx(phaseIdx) ]; + const VolumeVariables& downVolVars = elemVolVars[ downstreamIdx(phaseIdx) ]; + + // the minus comes from the Darcy relation which states that + // the flux is from high to low potentials. + // set the velocity + velocity_[phaseIdx] = kGradP_[phaseIdx]; + velocity_[phaseIdx] *= - ( mobilityUpwindWeight_*upVolVars.mobility(phaseIdx) + + (1.0 - mobilityUpwindWeight_)*downVolVars.mobility(phaseIdx)) ; + + // set the volume flux + volumeFlux_[phaseIdx] = velocity_[phaseIdx] * face().normal ; + }// loop all phases + } +}; + +} // end namespace + +#endif diff --git a/dumux/implicit/common/implicitelementboundarytypes.hh b/dumux/implicit/common/implicitelementboundarytypes.hh new file mode 100644 index 0000000000..fe7e02a09c --- /dev/null +++ b/dumux/implicit/common/implicitelementboundarytypes.hh @@ -0,0 +1,148 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \file + * + * \brief Boundary types gathered on an element + */ +#ifndef DUMUX_BOX_ELEMENT_BOUNDARY_TYPES_HH +#define DUMUX_BOX_ELEMENT_BOUNDARY_TYPES_HH + +#include "boxproperties.hh" + +#include <dumux/common/valgrind.hh> + +namespace Dumux +{ + +/*! + * \ingroup BoxModel + * \ingroup BoxBoundaryTypes + * + * \brief This class stores an array of BoundaryTypes objects + */ +template<class TypeTag> +class BoxElementBoundaryTypes : public std::vector<typename GET_PROP_TYPE(TypeTag, BoundaryTypes) > +{ + typedef typename GET_PROP_TYPE(TypeTag, BoundaryTypes) BoundaryTypes; + typedef std::vector<BoundaryTypes> ParentType; + + typedef typename GET_PROP_TYPE(TypeTag, Problem) Problem; + typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; + typedef typename GET_PROP_TYPE(TypeTag, FVElementGeometry) FVElementGeometry; + + enum { dim = GridView::dimension }; + typedef typename GridView::template Codim<0>::Entity Element; + typedef typename GridView::template Codim<dim>::EntityPointer VertexPointer; + +public: + /*! + * \brief Copy constructor. + * + * Copying a the boundary types of an element should be explicitly + * requested + */ + explicit BoxElementBoundaryTypes(const BoxElementBoundaryTypes &v) + : ParentType(v) + {} + + /*! + * \brief Default constructor. + */ + BoxElementBoundaryTypes() + { + hasDirichlet_ = false; + hasNeumann_ = false; + hasOutflow_ = false; + } + + /*! + * \brief Update the boundary types for all vertices of an element. + * + * \param problem The problem object which needs to be simulated + * \param element The DUNE Codim<0> entity for which the boundary + * types should be collected + */ + void update(const Problem &problem, + const Element &element) + { + int numVerts = element.template count<dim>(); + this->resize(numVerts); + + hasDirichlet_ = false; + hasNeumann_ = false; + hasOutflow_ = false; + + for (int i = 0; i < numVerts; ++i) { + (*this)[i].reset(); + + if (problem.model().onBoundary(element, i)) { + const VertexPointer vptr = element.template subEntity<dim>(i); + problem.boundaryTypes((*this)[i], *vptr); + + hasDirichlet_ = hasDirichlet_ || (*this)[i].hasDirichlet(); + hasNeumann_ = hasNeumann_ || (*this)[i].hasNeumann(); + hasOutflow_ = hasOutflow_ || (*this)[i].hasOutflow(); + } + } + } + + /*! + * \brief Update the boundary types for all vertices of an element. + * + * \param problem The problem object which needs to be simulated + * \param element The DUNE Codim<0> entity for which the boundary + * types should be collected + * \param fvGeometry The element's finite volume geometry + */ + void update(const Problem &problem, + const Element &element, + const FVElementGeometry &fvGeometry) + { update(problem, element); } + + /*! + * \brief Returns whether the element has a vertex which contains + * a Dirichlet value. + */ + bool hasDirichlet() const + { return hasDirichlet_; } + + /*! + * \brief Returns whether the element potentially features a + * Neumann boundary segment. + */ + bool hasNeumann() const + { return hasNeumann_; } + + /*! + * \brief Returns whether the element potentially features an + * outflow boundary segment. + */ + bool hasOutflow() const + { return hasOutflow_; } + +protected: + bool hasDirichlet_; + bool hasNeumann_; + bool hasOutflow_; +}; + +} // namespace Dumux + +#endif diff --git a/dumux/implicit/common/implicitelementvolumevariables.hh b/dumux/implicit/common/implicitelementvolumevariables.hh new file mode 100644 index 0000000000..c791bee09b --- /dev/null +++ b/dumux/implicit/common/implicitelementvolumevariables.hh @@ -0,0 +1,138 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \file + * + * \brief Volume variables gathered on an element + */ +#ifndef DUMUX_BOX_ELEMENT_VOLUME_VARIABLES_HH +#define DUMUX_BOX_ELEMENT_VOLUME_VARIABLES_HH + +#include "boxproperties.hh" + + +namespace Dumux +{ + +/*! + * \ingroup BoxModel + * + * \brief This class stores an array of VolumeVariables objects, one + * volume variables object for each of the element's vertices + */ +template<class TypeTag> +class BoxElementVolumeVariables : public std::vector<typename GET_PROP_TYPE(TypeTag, VolumeVariables) > +{ + typedef typename GET_PROP_TYPE(TypeTag, Problem) Problem; + typedef typename GET_PROP_TYPE(TypeTag, FVElementGeometry) FVElementGeometry; + typedef typename GET_PROP_TYPE(TypeTag, SolutionVector) SolutionVector; + typedef typename GET_PROP_TYPE(TypeTag, VertexMapper) VertexMapper; + typedef typename GET_PROP_TYPE(TypeTag, PrimaryVariables) PrimaryVariables; + + typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; + typedef typename GridView::template Codim<0>::Entity Element; + + enum { dim = GridView::dimension }; + enum { numEq = GET_PROP_VALUE(TypeTag, NumEq) }; + +public: + /*! + * \brief The constructor. + */ + BoxElementVolumeVariables() + { } + + /*! + * \brief Construct the volume variables for all of vertices of an element. + * + * \param problem The problem which needs to be simulated. + * \param element The DUNE Codim<0> entity for which the volume variables ought to be calculated + * \param fvGeometry The finite volume geometry of the element + * \param oldSol Tells whether the model's previous or current solution should be used. + */ + void update(const Problem &problem, + const Element &element, + const FVElementGeometry &fvGeometry, + const bool oldSol) + { + const SolutionVector &globalSol = + oldSol? + problem.model().prevSol(): + problem.model().curSol(); + const VertexMapper &vertexMapper = problem.vertexMapper(); + // we assert that the i-th shape function is + // associated to the i-th vert of the element. + int numVertices = element.template count<dim>(); + this->resize(numVertices); + for (int scvIdx = 0; scvIdx < numVertices; scvIdx++) { + const PrimaryVariables &priVars + = globalSol[vertexMapper.map(element, scvIdx, dim)]; + + // reset evaluation point to zero + (*this)[scvIdx].setEvalPoint(0); + + (*this)[scvIdx].update(priVars, + problem, + element, + fvGeometry, + scvIdx, + oldSol); + } + } + + /*! + * \brief Construct the volume variables for all of vertices of an + * element given a solution vector computed by PDELab. + * + * \tparam ElementSolutionVector The container type which stores the + * primary variables of the element + * using _local_ indices + * + * \param problem The problem which needs to be simulated. + * \param element The DUNE Codim<0> entity for which the volume variables ought to be calculated + * \param fvGeometry The finite volume geometry of the element + * \param elementSolVector The local solution for the element using PDELab ordering + */ + template<typename ElementSolutionVector> + void updatePDELab(const Problem &problem, + const Element &element, + const FVElementGeometry &fvGeometry, + const ElementSolutionVector& elementSolVector) + { + int numVertices = element.template count<dim>(); + this->resize(numVertices); + for (int scvIdx = 0; scvIdx < numVertices; scvIdx++) + { + PrimaryVariables priVars(0); + for (int eqnIdx = 0; eqnIdx < numEq; eqnIdx++) + priVars[eqnIdx] = elementSolVector[scvIdx + eqnIdx*numVertices]; + (*this)[scvIdx].update(priVars, + problem, + element, + fvGeometry, + scvIdx, + false); + + } + } +}; + +} // namespace Dumux + +#endif diff --git a/dumux/implicit/common/implicitforchheimerfluxvariables.hh b/dumux/implicit/common/implicitforchheimerfluxvariables.hh new file mode 100644 index 0000000000..e7f7e3bf32 --- /dev/null +++ b/dumux/implicit/common/implicitforchheimerfluxvariables.hh @@ -0,0 +1,358 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \file + * + * \brief This file contains the data which is required to calculate + * all fluxes of fluid phases over a face of a finite volume, + * according to the Forchheimer-relation between velocity and pressure. + * + */ +#ifndef DUMUX_BOX_FORCHHEIMER_FLUX_VARIABLES_HH +#define DUMUX_BOX_FORCHHEIMER_FLUX_VARIABLES_HH + +#include "boxproperties.hh" + +#include <dumux/common/parameters.hh> +#include <dumux/common/math.hh> +#include <dumux/boxmodels/common/boxdarcyfluxvariables.hh> + +namespace Dumux +{ + +namespace Properties +{ +// forward declaration of properties +NEW_PROP_TAG(MobilityUpwindWeight); +NEW_PROP_TAG(SpatialParams); +NEW_PROP_TAG(NumPhases); +NEW_PROP_TAG(ProblemEnableGravity); +} + +/*! + * \ingroup BoxModel + * \ingroup BoxFluxVariables + * \brief Evaluates the normal component of the Forchheimer velocity + * on a (sub)control volume face. + * + * The commonly used Darcy relation looses it's validity for \f$ Re < 1\f$. + * If one encounters flow velocities in porous media above this Reynolds number, + * the Forchheimer relation can be used. Like the Darcy relation, it relates + * the gradient in potential to velocity. + * However, this relation is not linear (as in the Darcy case) any more. + * + * Therefore, a Newton scheme is implemented in this class in order to calculate a + * velocity from the current set of variables. This velocity can subsequently be used + * e.g. by the localresidual. + * + * For Reynolds numbers above \f$ 500\f$ the (Standard) forchheimer relation also + * looses it's validity. + * + * The Forchheimer equation looks as follows: + * \f[ \nabla \left({p_\alpha + \rho_\alpha g z} \right)= - \frac{\mu_\alpha}{k_{r \alpha} K} \underline{v_\alpha} - \frac{c_F}{\eta_{\alpha r} \sqrt{K}} \rho_\alpha |\underline{v_\alpha}| \underline{v_\alpha} \f] + * + * For the formulation that is actually used in this class, see the documentation of the function calculating the residual. + * + * - This algorithm does not find a solution if the fluid is incompressible and the + * initial pressure distribution is uniform. + * - This algorithm assumes the relative passabilities to have the same functional + * form as the relative permeabilities. + */ +template <class TypeTag> +class BoxForchheimerFluxVariables + : public BoxDarcyFluxVariables<TypeTag> +{ + typedef typename GET_PROP_TYPE(TypeTag, Problem) Problem; + typedef typename GET_PROP_TYPE(TypeTag, SpatialParams) SpatialParams; + typedef typename GET_PROP_TYPE(TypeTag, ElementVolumeVariables) ElementVolumeVariables; + typedef typename GET_PROP_TYPE(TypeTag, VolumeVariables) VolumeVariables; + typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; + typedef typename GridView::template Codim<0>::Entity Element; + + enum { dim = GridView::dimension} ; + enum { dimWorld = GridView::dimensionworld} ; + enum { numPhases = GET_PROP_VALUE(TypeTag, NumPhases)} ; + + typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; + typedef Dune::FieldMatrix<Scalar, dimWorld, dimWorld> Tensor; + typedef Dune::FieldVector<Scalar, dimWorld> DimVector; + typedef typename GET_PROP_TYPE(TypeTag, FVElementGeometry) FVElementGeometry; + typedef typename FVElementGeometry::SubControlVolumeFace SCVFace; + +public: + /*! + * \brief The constructor + * + * \param problem The problem + * \param element The finite element + * \param fvGeometry The finite-volume geometry in the box scheme + * \param faceIdx The local index of the SCV (sub-control-volume) face + * \param elemVolVars The volume variables of the current element + * \param onBoundary A boolean variable to specify whether the flux variables + * are calculated for interior SCV faces or boundary faces, default=false + */ + BoxForchheimerFluxVariables(const Problem &problem, + const Element &element, + const FVElementGeometry &fvGeometry, + const unsigned int faceIdx, + const ElementVolumeVariables &elemVolVars, + const bool onBoundary = false) + : BoxDarcyFluxVariables<TypeTag>(problem, element, fvGeometry, faceIdx, elemVolVars, onBoundary) + { + calculateNormalVelocity_(problem, element, elemVolVars); + } + +protected: + /*! + * \brief Function for calculation of velocities. + * + * \param problem The problem + * \param element The finite element + * \param elemVolVars The volume variables of the current element + */ + void calculateNormalVelocity_(const Problem &problem, + const Element &element, + const ElementVolumeVariables &elemVolVars) + { + // calculate the mean intrinsic permeability + const SpatialParams &spatialParams = problem.spatialParams(); + Tensor K; + spatialParams.meanK(K, + spatialParams.intrinsicPermeability(element, + this->fvGeometry_, + this->face().i), + spatialParams.intrinsicPermeability(element, + this->fvGeometry_, + this->face().j)); + + // obtain the Forchheimer coefficient from the spatial parameters + const Scalar forchCoeff = spatialParams.forchCoeff(element, + this->fvGeometry_, + this->face().i); + + // Make sure the permeability matrix does not have off-diagonal entries + assert( isDiagonal_(K) ); + + Tensor sqrtK(0.0); + for (int i = 0; i < dim; ++i) + sqrtK[i][i] = std::sqrt(K[i][i]); + + // loop over all phases + for (int phaseIdx = 0; phaseIdx < numPhases; phaseIdx++) + { + // initial guess of velocity: Darcy relation + // first taken from base class, later overwritten in base class + DimVector velocity = this-> velocity(phaseIdx); + + DimVector deltaV; // the change in velocity between Newton iterations + DimVector residual(10e10); // the residual (function value that is to be minimized ) of the equation that is to be fulfilled + DimVector tmp; // temporary variable for numerical differentiation + Tensor gradF; // slope of equation that is to be solved + + // search by means of the Newton method for a root of Forchheimer equation + for (int k = 0; residual.two_norm() > 1e-12 ; ++k) { + + if (k >= 30) + DUNE_THROW(NumericalProblem, "could not determine forchheimer velocity within "<< k <<" iterations"); + + // calculate current value of Forchheimer equation + forchheimerResidual_(residual, + forchCoeff, + sqrtK, + K, + velocity, + elemVolVars, + this->gradPotential_[phaseIdx], + phaseIdx); + + // newton's method: slope (gradF) and current value (residual) of function is known, + forchheimerDerivative_(gradF, + forchCoeff, + sqrtK, + velocity, + elemVolVars, + phaseIdx); + + // solve for change in velocity ("x-Axis intercept") + gradF.solve(deltaV, residual); + velocity -= deltaV; + } + + this->velocity_[phaseIdx] = velocity ; // store the converged velocity solution + this->volumeFlux_[phaseIdx] = velocity * this->face().normal; // velocity dot normal times cross sectional area is volume flux + }// end loop over all phases + } + + /*! \brief Function for calculation of Forchheimer relation. + * + * The relative passability \f$ \eta_r\f$ is the "Forchheimer-equivalent" to the relative permeability \f$ k_r\f$. + * We use the same function as for \f$ k_r \f$ (VG, BC, linear) other authors use a simple power law e.g.: \f$\eta_{rw} = S_w^3\f$ + * + * Some rearrangements have been made to the original Forchheimer relation: + * + * \f[ \underline{v_\alpha} + c_F \sqrt{K} \frac{\rho_\alpha}{\mu_\alpha } |\underline{v_\alpha}| \underline{v_\alpha} + \frac{k_{r \alpha}}{\mu_\alpha} K \nabla \left(p_\alpha + \rho_\alpha g z \right)= 0 \f] + * + * This already includes the assumption \f$ k_r(S_w) = \eta_r(S_w)\f$: + * - \f$\eta_{rw} = S_w^x\f$ looks very similar to e.g. Van Genuchten relative permeabilities + * - Fichot, et al. (2006), Nuclear Engineering and Design, state that several authors claim that \f$ k_r(S_w), \eta_r(S_w)\f$ can be chosen equal + * - It leads to the equation not degenerating for the case of \f$S_w=1\f$, because I do not need to multiply with two different functions, and therefore there are terms not being zero. + * - If this assumption is not to be made: Some regularization needs to be introduced ensuring that not all terms become zero for \f$S_w=1\f$. + * + * As long as the correct velocity is not found yet, the above equation is not fulfilled, but + * the left hand side yields some residual. This function calculates the left hand side of the + * equation and returns the residual. + * + * \param residual The current function value for the given velocity + * \param forchCoeff The Forchheimer coefficient + * \param sqrtK A diagonal matrix whose entries are square roots of the permeability matrix entries + * \param K The permeability matrix + * \param velocity The current velocity approximation. + * \param elemVolVars The volume variables of the current element + * \param gradPotential The gradient in potential + * \param phaseIdx The index of the currently considered phase + */ + void forchheimerResidual_(DimVector & residual, + const Scalar forchCoeff, + const Tensor & sqrtK, + const Tensor & K, + const DimVector & velocity, + const ElementVolumeVariables & elemVolVars, + const DimVector & gradPotential , + const unsigned int phaseIdx) const + { + const VolumeVariables upVolVars = elemVolVars[this->upstreamIdx(phaseIdx)]; + const VolumeVariables downVolVars = elemVolVars[this->downstreamIdx(phaseIdx)]; + + // Obtaining the physical quantities + const Scalar mobility = this->mobilityUpwindWeight_ * upVolVars.mobility(phaseIdx) + + (1.-this->mobilityUpwindWeight_)* downVolVars.mobility(phaseIdx) ; + + const Scalar viscosity = this->mobilityUpwindWeight_ * upVolVars.fluidState().viscosity(phaseIdx) + + (1.-this->mobilityUpwindWeight_)* downVolVars.fluidState().viscosity(phaseIdx) ; + + const Scalar density = this->mobilityUpwindWeight_ * upVolVars.fluidState().density(phaseIdx) + + (1.-this->mobilityUpwindWeight_) * downVolVars.fluidState().density(phaseIdx) ; + + // residual = v + residual = velocity; + + // residual += k_r/mu K grad p + K.usmv(mobility, gradPotential, residual); + + // residual += c_F rho / mu abs(v) sqrt(K) v + sqrtK.usmv(forchCoeff * density / viscosity * velocity.two_norm(), velocity, residual); + } + + + /*! \brief Function for calculation of the gradient of Forchheimer relation with respect to velocity. + * + * This function already exploits that \f$ \sqrt{K}\f$ is a diagonal matrix. Therefore, we only have + * to deal with the main entries. + * The gradient of the Forchheimer relations looks as follows (mind that \f$ \sqrt{K}\f$ is a tensor): + * + * \f[ f\left(\underline{v_\alpha}\right) = + * \left( + * \begin{array}{ccc} + * 1 & 0 &0 \\ + * 0 & 1 &0 \\ + * 0 & 0 &1 \\ + * \end{array} + * \right) + * + + * c_F \frac{\rho_\alpha}{\mu_\alpha} |\underline{v}_\alpha| \sqrt{K} + * + + * c_F \frac{\rho_\alpha}{\mu_\alpha}\frac{1}{|\underline{v}_\alpha|} \sqrt{K} + * \left( + * \begin{array}{ccc} + * v_x^2 & v_xv_y & v_xv_z \\ + * v_yv_x & v_{y}^2 & v_yv_z \\ + * v_zv_x & v_zv_y &v_{z}^2 \\ + * \end{array} + * \right) + * \f] + * + * \param derivative The gradient of the forchheimer equation + * \param forchCoeff Forchheimer coefficient + * \param sqrtK A diagonal matrix whose entries are square roots of the permeability matrix entries. + * \param velocity The current velocity approximation. + * \param elemVolVars The volume variables of the current element + * \param phaseIdx The index of the currently considered phase + */ + void forchheimerDerivative_(Tensor & derivative, + const Scalar forchCoeff, + const Tensor & sqrtK, + const DimVector & velocity, + const ElementVolumeVariables & elemVolVars, + const unsigned int phaseIdx) const + { + const VolumeVariables upVolVars = elemVolVars[this->upstreamIdx(phaseIdx)]; + const VolumeVariables downVolVars = elemVolVars[this->downstreamIdx(phaseIdx)]; + + // Obtaining the physical quantities + const Scalar viscosity = this->mobilityUpwindWeight_ * upVolVars.fluidState().viscosity(phaseIdx) + + (1.-this->mobilityUpwindWeight_)* downVolVars.fluidState().viscosity(phaseIdx) ; + + const Scalar density = this->mobilityUpwindWeight_ * upVolVars.fluidState().density(phaseIdx) + + (1.-this->mobilityUpwindWeight_) * downVolVars.fluidState().density(phaseIdx) ; + + // Initialize because for low velocities, we add and do not set the entries. + derivative = 0.; + + const Scalar absV = velocity.two_norm() ; + // This part of the derivative is only calculated if the velocity is sufficiently small: do not divide by zero! + // This should not be very bad because the derivative is only intended to give an approximation of the gradient of the + // function that goes into the Newton scheme. + // This is important in the case of a one-phase region in two-phase flow. The non-existing phase has a velocity of zero (kr=0). + // f = sqrtK * vTimesV (this is a matrix) + // f *= forchCoeff density / |velocity| / viscosity (multiply all entries with scalars) + if(absV > 1e-20) + for (int i=0; i<dim; i++) + for (int k=0; k<dim; k++) + derivative[i][k]= sqrtK[i][i] * velocity[i] * velocity[k] * forchCoeff * density / absV / viscosity; + + // add on the main diagonal: + // 1 + sqrtK_i forchCoeff density |v| / viscosity + for (int i=0; i<dim; i++) + derivative[i][i] += (1.0 + ( sqrtK[i][i]*forchCoeff * density * absV / viscosity ) ) ; + } + + /*! + * \brief Check whether all off-diagonal entries of a tensor are zero. + * + * \param K the tensor that is to be checked. + * + * \return True iff all off-diagonals are zero. + * + */ + const bool isDiagonal_(const Tensor & K) const + { + for (int i = 0; i < dim; i++) { + for (int k = 0; k < dim; k++) { + if ((i != k) && (std::abs(K[i][k]) >= 1e-25)) { + return false; + } + } + } + return true; + } +}; + +} // end namespace + +#endif diff --git a/dumux/implicit/common/implicitfvelementgeometry.hh b/dumux/implicit/common/implicitfvelementgeometry.hh new file mode 100644 index 0000000000..fd0b5c89e2 --- /dev/null +++ b/dumux/implicit/common/implicitfvelementgeometry.hh @@ -0,0 +1,933 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \file + * + * \brief Represents the finite volume geometry of a single element in + * the box scheme. + */ +#ifndef DUMUX_BOX_FV_ELEMENTGEOMETRY_HH +#define DUMUX_BOX_FV_ELEMENTGEOMETRY_HH + +#include <dune/common/version.hh> +#include <dune/geometry/referenceelements.hh> +#include <dune/grid/common/intersectioniterator.hh> +#include <dune/localfunctions/lagrange/pqkfactory.hh> + +#include <dumux/common/parameters.hh> +#include <dumux/common/propertysystem.hh> +#include "boxproperties.hh" + +namespace Dumux +{ +namespace Properties +{ +NEW_PROP_TAG(GridView); +NEW_PROP_TAG(Scalar); +NEW_PROP_TAG(ImplicitUseTwoPointFlux); +} + +///////////////////// +// HACK: Functions to initialize the subcontrol volume data +// structures of BoxFVElementGeomerty. +// +// this is a work around to be able to to use specialization for +// doing this. it is required since it is not possible to +// specialize member functions of template classes because of some +// weird reason I didn't really get... + +//! \cond INTERNAL + +template <typename BoxFVElementGeometry, int dim> +class _BoxFVElemGeomHelper +{ +public: + static void fillSubContVolData(BoxFVElementGeometry &eg, int numVertices) + { + DUNE_THROW(Dune::NotImplemented, "_BoxFVElemGeomHelper::fillSubContVolData dim = " << dim); + } +}; + +template <typename BoxFVElementGeometry> +class _BoxFVElemGeomHelper<BoxFVElementGeometry, 1> +{ +public: + enum { dim = 1 }; + static void fillSubContVolData(BoxFVElementGeometry &eg, int numVertices) + { + // 1D + eg.subContVol[0].volume = 0.5*eg.elementVolume; + eg.subContVol[1].volume = 0.5*eg.elementVolume; + } +}; + +template <typename BoxFVElementGeometry> +class _BoxFVElemGeomHelper<BoxFVElementGeometry, 2> +{ +public: + enum { dim = 2 }; + + static void fillSubContVolData(BoxFVElementGeometry &eg, int numVertices) + { + switch (numVertices) { + case 3: // 2D, triangle + eg.subContVol[0].volume = eg.elementVolume/3; + eg.subContVol[1].volume = eg.elementVolume/3; + eg.subContVol[2].volume = eg.elementVolume/3; + break; + case 4: // 2D, quadrilinear + eg.subContVol[0].volume = eg.quadrilateralArea(eg.subContVol[0].global, eg.edgeCoord[2], eg.elementGlobal, eg.edgeCoord[0]); + eg.subContVol[1].volume = eg.quadrilateralArea(eg.subContVol[1].global, eg.edgeCoord[1], eg.elementGlobal, eg.edgeCoord[2]); + eg.subContVol[2].volume = eg.quadrilateralArea(eg.subContVol[2].global, eg.edgeCoord[0], eg.elementGlobal, eg.edgeCoord[3]); + eg.subContVol[3].volume = eg.quadrilateralArea(eg.subContVol[3].global, eg.edgeCoord[3], eg.elementGlobal, eg.edgeCoord[1]); + break; + default: + DUNE_THROW(Dune::NotImplemented, "_BoxFVElemGeomHelper::fillSubContVolData dim = " << dim << ", numVertices = " << numVertices); + } + } +}; + +template <typename BoxFVElementGeometry> +class _BoxFVElemGeomHelper<BoxFVElementGeometry, 3> +{ +public: + enum { dim = 3 }; + + static void fillSubContVolData(BoxFVElementGeometry &eg, int numVertices) + { + switch (numVertices) { + case 4: // 3D, tetrahedron + for (int k = 0; k < eg.numVertices; k++) + eg.subContVol[k].volume = eg.elementVolume / 4.0; + break; + case 5: // 3D, pyramid + eg.subContVol[0].volume = eg.hexahedronVolume(eg.subContVol[0].global, + eg.edgeCoord[2], + eg.faceCoord[0], + eg.edgeCoord[0], + eg.edgeCoord[4], + eg.faceCoord[3], + eg.elementGlobal, + eg.faceCoord[1]); + eg.subContVol[1].volume = eg.hexahedronVolume(eg.subContVol[1].global, + eg.edgeCoord[1], + eg.faceCoord[0], + eg.edgeCoord[2], + eg.edgeCoord[5], + eg.faceCoord[2], + eg.elementGlobal, + eg.faceCoord[3]); + eg.subContVol[2].volume = eg.hexahedronVolume(eg.subContVol[2].global, + eg.edgeCoord[0], + eg.faceCoord[0], + eg.edgeCoord[3], + eg.edgeCoord[6], + eg.faceCoord[1], + eg.elementGlobal, + eg.faceCoord[4]); + eg.subContVol[3].volume = eg.hexahedronVolume(eg.subContVol[3].global, + eg.edgeCoord[3], + eg.faceCoord[0], + eg.edgeCoord[1], + eg.edgeCoord[7], + eg.faceCoord[4], + eg.elementGlobal, + eg.faceCoord[2]); + eg.subContVol[4].volume = eg.elementVolume - + eg.subContVol[0].volume - + eg.subContVol[1].volume - + eg.subContVol[2].volume - + eg.subContVol[3].volume; + break; + case 6: // 3D, prism + eg.subContVol[0].volume = eg.hexahedronVolume(eg.subContVol[0].global, + eg.edgeCoord[3], + eg.faceCoord[3], + eg.edgeCoord[4], + eg.edgeCoord[0], + eg.faceCoord[0], + eg.elementGlobal, + eg.faceCoord[1]); + eg.subContVol[1].volume = eg.hexahedronVolume(eg.subContVol[1].global, + eg.edgeCoord[5], + eg.faceCoord[3], + eg.edgeCoord[3], + eg.edgeCoord[1], + eg.faceCoord[2], + eg.elementGlobal, + eg.faceCoord[0]); + eg.subContVol[2].volume = eg.hexahedronVolume(eg.subContVol[2].global, + eg.edgeCoord[4], + eg.faceCoord[3], + eg.edgeCoord[5], + eg.edgeCoord[2], + eg.faceCoord[1], + eg.elementGlobal, + eg.faceCoord[2]); + eg.subContVol[3].volume = eg.hexahedronVolume(eg.edgeCoord[0], + eg.faceCoord[0], + eg.elementGlobal, + eg.faceCoord[1], + eg.subContVol[3].global, + eg.edgeCoord[6], + eg.faceCoord[4], + eg.edgeCoord[7]); + eg.subContVol[4].volume = eg.hexahedronVolume(eg.edgeCoord[1], + eg.faceCoord[2], + eg.elementGlobal, + eg.faceCoord[0], + eg.subContVol[4].global, + eg.edgeCoord[8], + eg.faceCoord[4], + eg.edgeCoord[6]); + eg.subContVol[5].volume = eg.hexahedronVolume(eg.edgeCoord[2], + eg.faceCoord[1], + eg.elementGlobal, + eg.faceCoord[2], + eg.subContVol[5].global, + eg.edgeCoord[7], + eg.faceCoord[4], + eg.edgeCoord[8]); + break; + case 8: // 3D, hexahedron + eg.subContVol[0].volume = eg.hexahedronVolume(eg.subContVol[0].global, + eg.edgeCoord[6], + eg.faceCoord[4], + eg.edgeCoord[4], + eg.edgeCoord[0], + eg.faceCoord[2], + eg.elementGlobal, + eg.faceCoord[0]); + eg.subContVol[1].volume = eg.hexahedronVolume(eg.subContVol[1].global, + eg.edgeCoord[5], + eg.faceCoord[4], + eg.edgeCoord[6], + eg.edgeCoord[1], + eg.faceCoord[1], + eg.elementGlobal, + eg.faceCoord[2]); + eg.subContVol[2].volume = eg.hexahedronVolume(eg.subContVol[2].global, + eg.edgeCoord[4], + eg.faceCoord[4], + eg.edgeCoord[7], + eg.edgeCoord[2], + eg.faceCoord[0], + eg.elementGlobal, + eg.faceCoord[3]); + eg.subContVol[3].volume = eg.hexahedronVolume(eg.subContVol[3].global, + eg.edgeCoord[7], + eg.faceCoord[4], + eg.edgeCoord[5], + eg.edgeCoord[3], + eg.faceCoord[3], + eg.elementGlobal, + eg.faceCoord[1]); + eg.subContVol[4].volume = eg.hexahedronVolume(eg.edgeCoord[0], + eg.faceCoord[2], + eg.elementGlobal, + eg.faceCoord[0], + eg.subContVol[4].global, + eg.edgeCoord[10], + eg.faceCoord[5], + eg.edgeCoord[8]); + eg.subContVol[5].volume = eg.hexahedronVolume(eg.edgeCoord[1], + eg.faceCoord[1], + eg.elementGlobal, + eg.faceCoord[2], + eg.subContVol[5].global, + eg.edgeCoord[9], + eg.faceCoord[5], + eg.edgeCoord[10]); + eg.subContVol[6].volume = eg.hexahedronVolume(eg.edgeCoord[2], + eg.faceCoord[0], + eg.elementGlobal, + eg.faceCoord[3], + eg.subContVol[6].global, + eg.edgeCoord[8], + eg.faceCoord[5], + eg.edgeCoord[11]); + eg.subContVol[7].volume = eg.hexahedronVolume(eg.edgeCoord[3], + eg.faceCoord[3], + eg.elementGlobal, + eg.faceCoord[1], + eg.subContVol[7].global, + eg.edgeCoord[11], + eg.faceCoord[5], + eg.edgeCoord[9]); + break; + default: + DUNE_THROW(Dune::NotImplemented, "_BoxFVElemGeomHelper::fillSubContVolData dim = " << dim << ", numVertices = " << numVertices); + } + } +}; + +//! \endcond + +// END HACK +///////////////////// + +/*! + * \brief Represents the finite volume geometry of a single element in + * the box scheme. + * + * The box scheme is a vertex centered finite volume approach. This + * means that each vertex corrosponds to a control volume which + * intersects each of the vertex' neighboring elements. If only + * looking at a single element of the primary grid (which is what this + * class does), the element is subdivided into multiple fragments of + * control volumes called sub-control volumes. Each of the element's + * vertices corrosponds to exactly one sub-control volume in this + * scenario. + * + * For the box methods the sub-control volumes are constructed by + * connecting the element's center with each edge of the element. + */ +template<class TypeTag> +class BoxFVElementGeometry +{ + typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; + enum{dim = GridView::dimension}; + enum{dimWorld = GridView::dimensionworld}; + + typedef BoxFVElementGeometry<TypeTag> ThisType; + + /** \todo Please doc me! */ + friend class _BoxFVElemGeomHelper<ThisType, dim>; + + typedef _BoxFVElemGeomHelper<ThisType, dim> BoxFVElemGeomHelper; + + enum{maxNC = (dim < 3 ? 4 : 8)}; + enum{maxNE = (dim < 3 ? 4 : 12)}; + enum{maxNF = (dim < 3 ? 1 : 6)}; + enum{maxCOS = (dim < 3 ? 2 : 4)}; + enum{maxBF = (dim < 3 ? 8 : 24)}; + enum{maxNFAP = (dim < 3 ? 4 : 8)}; + typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; + typedef typename GridView::ctype CoordScalar; + typedef typename GridView::Traits::template Codim<0>::Entity Element; + typedef typename Element::Geometry Geometry; + typedef Dune::FieldVector<Scalar,dimWorld> Vector; + typedef Dune::FieldVector<CoordScalar,dimWorld> GlobalPosition; + typedef Dune::FieldVector<CoordScalar,dim> LocalPosition; + typedef typename GridView::IntersectionIterator IntersectionIterator; +#if DUNE_VERSION_NEWER_REV(DUNE_GRID, 2, 3, 0) + typedef typename Geometry::JacobianInverseTransposed JacobianInverseTransposed; +#else + typedef typename Geometry::Jacobian JacobianInverseTransposed; +#endif + + typedef Dune::PQkLocalFiniteElementCache<CoordScalar, Scalar, dim, 1> LocalFiniteElementCache; + typedef typename LocalFiniteElementCache::FiniteElementType LocalFiniteElement; + typedef typename LocalFiniteElement::Traits::LocalBasisType::Traits LocalBasisTraits; + typedef typename LocalBasisTraits::JacobianType ShapeJacobian; + + Scalar quadrilateralArea(const GlobalPosition& p0, const GlobalPosition& p1, const GlobalPosition& p2, const GlobalPosition& p3) + { + return 0.5*fabs((p3[0] - p1[0])*(p2[1] - p0[1]) - (p3[1] - p1[1])*(p2[0] - p0[0])); + } + + void crossProduct(Vector& c, const Vector& a, const Vector& b) + { + c[0] = a[1]*b[2] - a[2]*b[1]; + c[1] = a[2]*b[0] - a[0]*b[2]; + c[2] = a[0]*b[1] - a[1]*b[0]; + } + + Scalar pyramidVolume (const GlobalPosition& p0, const GlobalPosition& p1, const GlobalPosition& p2, const GlobalPosition& p3, const GlobalPosition& p4) + { + Vector a(p2); a -= p0; + Vector b(p3); b -= p1; + + Vector n; + crossProduct(n, a, b); + + a = p4; a -= p0; + + return 1.0/6.0*(n*a); + } + + Scalar prismVolume (const GlobalPosition& p0, const GlobalPosition& p1, const GlobalPosition& p2, const GlobalPosition& p3, const GlobalPosition& p4, const GlobalPosition& p5) + { + Vector a(p4); + for (int k = 0; k < dimWorld; ++k) + a[k] -= p0[k]; + Vector b(p1); + for (int k = 0; k < dimWorld; ++k) + b[k] -= p3[k]; + Vector m; + crossProduct(m, a, b); + + a = p1; + for (int k = 0; k < dimWorld; ++k) + a[k] -= p0[k]; + b = p2; + for (int k = 0; k < dimWorld; ++k) + b[k] -= p0[k]; + Vector n; + crossProduct(n, a, b); + n += m; + + a = p5; + for (int k = 0; k < dimWorld; ++k) + a[k] -= p0[k]; + + return fabs(1.0/6.0*(n*a)); + } + + Scalar hexahedronVolume (const GlobalPosition& p0, const GlobalPosition& p1, const GlobalPosition& p2, const GlobalPosition& p3, + const GlobalPosition& p4, const GlobalPosition& p5, const GlobalPosition& p6, const GlobalPosition& p7) + { + return + prismVolume(p0,p1,p2,p4,p5,p6) + + prismVolume(p0,p2,p3,p4,p6,p7); + } + + void normalOfQuadrilateral3D(Vector &normal, const GlobalPosition& p0, const GlobalPosition& p1, const GlobalPosition& p2, const GlobalPosition& p3) + { + Vector a(p2); + for (int k = 0; k < dimWorld; ++k) + a[k] -= p0[k]; + Vector b(p3); + for (int k = 0; k < dimWorld; ++k) + b[k] -= p1[k]; + + crossProduct(normal, a, b); + normal *= 0.5; + } + + Scalar quadrilateralArea3D(const GlobalPosition& p0, const GlobalPosition& p1, const GlobalPosition& p2, const GlobalPosition& p3) + { + Vector normal; + normalOfQuadrilateral3D(normal, p0, p1, p2, p3); + return normal.two_norm(); + } + + void getFaceIndices(int numVertices, int k, int& leftFace, int& rightFace) + { + static const int edgeToFaceTet[2][6] = { + {1, 0, 3, 2, 1, 3}, + {0, 2, 0, 1, 3, 2} + }; + static const int edgeToFacePyramid[2][8] = { + {1, 2, 3, 4, 1, 3, 4, 2}, + {0, 0, 0, 0, 3, 2, 1, 4} + }; + static const int edgeToFacePrism[2][9] = { + {1, 0, 2, 0, 1, 2, 4, 4, 4}, + {0, 2, 1, 3, 3, 3, 0, 1, 2} + }; + static const int edgeToFaceHex[2][12] = { + {0, 2, 3, 1, 4, 1, 2, 4, 0, 5, 5, 3}, + {2, 1, 0, 3, 0, 4, 4, 3, 5, 1, 2, 5} + }; + + switch (numVertices) { + case 4: + leftFace = edgeToFaceTet[0][k]; + rightFace = edgeToFaceTet[1][k]; + break; + case 5: + leftFace = edgeToFacePyramid[0][k]; + rightFace = edgeToFacePyramid[1][k]; + break; + case 6: + leftFace = edgeToFacePrism[0][k]; + rightFace = edgeToFacePrism[1][k]; + break; + case 8: + leftFace = edgeToFaceHex[0][k]; + rightFace = edgeToFaceHex[1][k]; + break; + default: + DUNE_THROW(Dune::NotImplemented, "BoxFVElementGeometry :: getFaceIndices for numVertices = " << numVertices); + break; + } + } + + void getEdgeIndices(int numVertices, int face, int vert, int& leftEdge, int& rightEdge) + { + static const int faceAndVertexToLeftEdgeTet[4][4] = { + { 0, 0, 2, -1}, + { 0, 0, -1, 3}, + { 1, -1, 1, 3}, + {-1, 2, 2, 4} + }; + static const int faceAndVertexToRightEdgeTet[4][4] = { + { 1, 2, 1, -1}, + { 3, 4, -1, 4}, + { 3, -1, 5, 5}, + {-1, 4, 5, 5} + }; + static const int faceAndVertexToLeftEdgePyramid[5][5] = { + { 0, 2, 3, 1, -1}, + { 0, -1, 0, -1, 4}, + {-1, 1, -1, 1, 5}, + { 2, 2, -1, -1, 4}, + {-1, -1, 3, 3, 7} + }; + static const int faceAndVertexToRightEdgePyramid[5][5] = { + { 2, 1, 0, 3, -1}, + { 4, -1, 6, -1, 6}, + {-1, 5, -1, 7, 7}, + { 4, 5, -1, -1, 5}, + {-1, -1, 6, 7, 6} + }; + static const int faceAndVertexToLeftEdgePrism[5][6] = { + { 3, 3, -1, 0, 1, -1}, + { 4, -1, 4, 0, -1, 2}, + {-1, 5, 5, -1, 1, 2}, + { 3, 3, 5, -1, -1, -1}, + {-1, -1, -1, 6, 6, 8} + }; + static const int faceAndVertexToRightEdgePrism[5][6] = { + { 0, 1, -1, 6, 6, -1}, + { 0, -1, 2, 7, -1, 7}, + {-1, 1, 2, -1, 8, 8}, + { 4, 5, 4, -1, -1, -1}, + {-1, -1, -1, 7, 8, 7} + }; + static const int faceAndVertexToLeftEdgeHex[6][8] = { + { 0, -1, 4, -1, 8, -1, 2, -1}, + {-1, 5, -1, 3, -1, 1, -1, 9}, + { 6, 1, -1, -1, 0, 10, -1, -1}, + {-1, -1, 2, 7, -1, -1, 11, 3}, + { 4, 6, 7, 5, -1, -1, -1, -1}, + {-1, -1, -1, -1, 10, 9, 8, 11} + }; + static const int faceAndVertexToRightEdgeHex[6][8] = { + { 4, -1, 2, -1, 0, -1, 8, -1}, + {-1, 1, -1, 5, -1, 9, -1, 3}, + { 0, 6, -1, -1, 10, 1, -1, -1}, + {-1, -1, 7, 3, -1, -1, 2, 11}, + { 6, 5, 4, 7, -1, -1, -1, -1}, + {-1, -1, -1, -1, 8, 10, 11, 9} + }; + + switch (numVertices) { + case 4: + leftEdge = faceAndVertexToLeftEdgeTet[face][vert]; + rightEdge = faceAndVertexToRightEdgeTet[face][vert]; + break; + case 5: + leftEdge = faceAndVertexToLeftEdgePyramid[face][vert]; + rightEdge = faceAndVertexToRightEdgePyramid[face][vert]; + break; + case 6: + leftEdge = faceAndVertexToLeftEdgePrism[face][vert]; + rightEdge = faceAndVertexToRightEdgePrism[face][vert]; + break; + case 8: + leftEdge = faceAndVertexToLeftEdgeHex[face][vert]; + rightEdge = faceAndVertexToRightEdgeHex[face][vert]; + break; + default: + DUNE_THROW(Dune::NotImplemented, "BoxFVElementGeometry :: getFaceIndices for numVertices = " << numVertices); + break; + } + } + +public: + int boundaryFaceIndex(const int face, const int vertInFace) const + { + return (face*maxCOS + vertInFace); + } + + struct SubControlVolume //! FV intersected with element + { + LocalPosition local; //!< local vert position + GlobalPosition global; //!< global vert position + LocalPosition localCenter; //!< local position of scv center + Scalar volume; //!< volume of scv + Dune::FieldVector<Vector, maxNC> grad; //! derivative of shape function associated with the sub control volume + Dune::FieldVector<Vector, maxNC> gradCenter; //! derivative of shape function at the center of the sub control volume + Dune::FieldVector<Scalar, maxNC> shapeValue; //! value of shape function associated with the sub control volume + bool inner; + }; + + struct SubControlVolumeFace //! interior face of a sub control volume + { + int i,j; //!< scvf seperates corner i and j of elem + LocalPosition ipLocal; //!< integration point in local coords + GlobalPosition ipGlobal; //!< integration point in global coords + Vector normal; //!< normal on face pointing to CV j or outward of the domain with length equal to |scvf| + Scalar area; //!< area of face + Dune::FieldVector<Vector, maxNC> grad; //!< derivatives of shape functions at ip + Dune::FieldVector<Scalar, maxNC> shapeValue; //!< value of shape functions at ip + Dune::FieldVector<int, maxNFAP> fapIndices; //!< indices w.r.t.neighbors of the flux approximation points + }; + + typedef SubControlVolumeFace BoundaryFace; //!< compatibility typedef + + LocalPosition elementLocal; //!< local coordinate of element center + GlobalPosition elementGlobal; //!< global coordinate of element center + Scalar elementVolume; //!< element volume + SubControlVolume subContVol[maxNC]; //!< data of the sub control volumes + SubControlVolumeFace subContVolFace[maxNE]; //!< data of the sub control volume faces + BoundaryFace boundaryFace[maxBF]; //!< data of the boundary faces + GlobalPosition edgeCoord[maxNE]; //!< global coordinates of the edge centers + GlobalPosition faceCoord[maxNF]; //!< global coordinates of the face centers + int numVertices; //!< number of verts + int numEdges; //!< number of edges + int numFaces; //!< number of faces (0 in < 3D) + int numSCV; //!< number of subcontrol volumes + int numFAP; //!< number of flux approximation points + + const LocalFiniteElementCache feCache_; + + void update(const GridView& gridView, const Element& element) + { + const Geometry& geometry = element.geometry(); + Dune::GeometryType gt = geometry.type(); + + const typename Dune::GenericReferenceElementContainer<CoordScalar,dim>::value_type& + referenceElement = Dune::GenericReferenceElements<CoordScalar,dim>::general(gt); + + const LocalFiniteElement + &localFiniteElement = feCache_.get(gt); + + elementVolume = geometry.volume(); + elementLocal = referenceElement.position(0,0); + elementGlobal = geometry.global(elementLocal); + + numVertices = referenceElement.size(dim); + numEdges = referenceElement.size(dim-1); + numFaces = (dim<3)?0:referenceElement.size(1); + numSCV = numVertices; + + bool useTwoPointFlux + = GET_PARAM_FROM_GROUP(TypeTag, bool, Implicit, UseTwoPointFlux); + if (useTwoPointFlux) + numFAP = 2; + else + numFAP = numVertices; + + // corners: + for (int vert = 0; vert < numVertices; vert++) { + subContVol[vert].local = referenceElement.position(vert, dim); + subContVol[vert].global = geometry.global(subContVol[vert].local); + subContVol[vert].inner = true; + } + + // edges: + for (int edge = 0; edge < numEdges; edge++) { + edgeCoord[edge] = geometry.global(referenceElement.position(edge, dim-1)); + } + + // faces: + for (int face = 0; face < numFaces; face++) { + faceCoord[face] = geometry.global(referenceElement.position(face, 1)); + } + + // fill sub control volume data use specialization for this + // \todo maybe it would be a good idea to migrate everything + // which is dependend of the grid's dimension to + // _BoxFVElemGeomHelper in order to benefit from more aggressive + // compiler optimizations... + BoxFVElemGeomHelper::fillSubContVolData(*this, numVertices); + + // fill sub control volume face data: + for (int k = 0; k < numEdges; k++) { // begin loop over edges / sub control volume faces + int i = referenceElement.subEntity(k, dim-1, 0, dim); + int j = referenceElement.subEntity(k, dim-1, 1, dim); + if (numEdges == 4 && (i == 2 || j == 2)) + std::swap(i, j); + subContVolFace[k].i = i; + subContVolFace[k].j = j; + + // calculate the local integration point and + // the face normal. note that since dim is a + // constant which is known at compile time + // the compiler can optimize away all if + // cases which don't apply. + LocalPosition ipLocal; + Vector diffVec; + if (dim==1) { + subContVolFace[k].ipLocal = 0.5; + subContVolFace[k].normal = 1.0; + ipLocal = subContVolFace[k].ipLocal; + } + else if (dim==2) { + ipLocal = referenceElement.position(k, dim-1) + elementLocal; + ipLocal *= 0.5; + subContVolFace[k].ipLocal = ipLocal; + diffVec = elementGlobal - edgeCoord[k]; + subContVolFace[k].normal[0] = diffVec[1]; + subContVolFace[k].normal[1] = -diffVec[0]; + + diffVec = subContVol[j].global; + for (int m = 0; m < dimWorld; ++m) + diffVec[m] -= subContVol[i].global[m]; + // make sure the normal points to the right direction + if (subContVolFace[k].normal * diffVec < 0) + subContVolFace[k].normal *= -1; + + } + else if (dim==3) { + int leftFace; + int rightFace; + getFaceIndices(numVertices, k, leftFace, rightFace); + ipLocal = referenceElement.position(k, dim-1) + elementLocal + + referenceElement.position(leftFace, 1) + + referenceElement.position(rightFace, 1); + ipLocal *= 0.25; + subContVolFace[k].ipLocal = ipLocal; + normalOfQuadrilateral3D(subContVolFace[k].normal, + edgeCoord[k], faceCoord[rightFace], + elementGlobal, faceCoord[leftFace]); + } + + if (useTwoPointFlux) + { + GlobalPosition distVec = subContVol[i].global; + distVec -= subContVol[j].global; + distVec /= distVec.two_norm2(); + + // gradients using a two-point flux approximation + for (int idx = 0; idx < 2; idx++) + { + subContVolFace[k].grad[idx] = distVec; + subContVolFace[k].shapeValue[idx] = 0.5; + } + subContVolFace[k].grad[1] *= -1.0; + + subContVolFace[k].fapIndices[0] = subContVolFace[k].i; + subContVolFace[k].fapIndices[1] = subContVolFace[k].j; + } + else + { + // get the global integration point and the Jacobian inverse + subContVolFace[k].ipGlobal = geometry.global(ipLocal); + JacobianInverseTransposed jacInvT = + geometry.jacobianInverseTransposed(ipLocal); + + // calculate the shape function gradients + //typedef Dune::FieldVector< Dune::FieldVector< CoordScalar, dim >, 1 > ShapeJacobian; + typedef Dune::FieldVector< Scalar, 1 > ShapeValue; + std::vector<ShapeJacobian> localJac; + std::vector<ShapeValue> shapeVal; + localFiniteElement.localBasis().evaluateJacobian(subContVolFace[k].ipLocal, localJac); + localFiniteElement.localBasis().evaluateFunction(subContVolFace[k].ipLocal, shapeVal); + for (int vert = 0; vert < numVertices; vert++) { + jacInvT.mv(localJac[vert][0], subContVolFace[k].grad[vert]); + subContVolFace[k].shapeValue[vert] = Scalar(shapeVal[vert]); + subContVolFace[k].fapIndices[vert] = vert; + } + } + } // end loop over edges / sub control volume faces + + // fill boundary face data: + IntersectionIterator endit = gridView.iend(element); + for (IntersectionIterator it = gridView.ibegin(element); it != endit; ++it) + if (it->boundary()) + { + int face = it->indexInInside(); + int numVerticesOfFace = referenceElement.size(face, 1, dim); + for (int vertInFace = 0; vertInFace < numVerticesOfFace; vertInFace++) + { + int vertInElement = referenceElement.subEntity(face, 1, vertInFace, dim); + int bfIdx = boundaryFaceIndex(face, vertInFace); + subContVol[vertInElement].inner = false; + switch ((short) dim) { + case 1: + boundaryFace[bfIdx].ipLocal = referenceElement.position(vertInElement, dim); + boundaryFace[bfIdx].area = 1.0; + break; + case 2: + boundaryFace[bfIdx].ipLocal = referenceElement.position(vertInElement, dim) + + referenceElement.position(face, 1); + boundaryFace[bfIdx].ipLocal *= 0.5; + boundaryFace[bfIdx].area = 0.5*it->geometry().volume(); + break; + case 3: + int leftEdge; + int rightEdge; + getEdgeIndices(numVertices, face, vertInElement, leftEdge, rightEdge); + boundaryFace[bfIdx].ipLocal = referenceElement.position(vertInElement, dim) + + referenceElement.position(face, 1) + + referenceElement.position(leftEdge, dim-1) + + referenceElement.position(rightEdge, dim-1); + boundaryFace[bfIdx].ipLocal *= 0.25; + boundaryFace[bfIdx].area = quadrilateralArea3D(subContVol[vertInElement].global, + edgeCoord[rightEdge], faceCoord[face], edgeCoord[leftEdge]); + break; + default: + DUNE_THROW(Dune::NotImplemented, "BoxFVElementGeometry for dim = " << dim); + } + boundaryFace[bfIdx].ipGlobal = geometry.global(boundaryFace[bfIdx].ipLocal); + boundaryFace[bfIdx].i = vertInElement; + boundaryFace[bfIdx].j = vertInElement; + + // ASSUME constant normal + Dune::FieldVector<CoordScalar, dim-1> localDimM1(0); + boundaryFace[bfIdx].normal = it->unitOuterNormal(localDimM1); + boundaryFace[bfIdx].normal *= boundaryFace[bfIdx].area; + + typedef Dune::FieldVector< Scalar, 1 > ShapeValue; + std::vector<ShapeJacobian> localJac; + std::vector<ShapeValue> shapeVal; + localFiniteElement.localBasis().evaluateJacobian(boundaryFace[bfIdx].ipLocal, localJac); + localFiniteElement.localBasis().evaluateFunction(boundaryFace[bfIdx].ipLocal, shapeVal); + + JacobianInverseTransposed jacInvT = + geometry.jacobianInverseTransposed(boundaryFace[bfIdx].ipLocal); + for (int vert = 0; vert < numVertices; vert++) + { + jacInvT.mv(localJac[vert][0], boundaryFace[bfIdx].grad[vert]); + boundaryFace[bfIdx].shapeValue[vert] = Scalar(shapeVal[vert]); + boundaryFace[bfIdx].fapIndices[vert] = vert; + } + } + } + + bool evalGradientsAtSCVCenter = GET_PROP_VALUE(TypeTag, EvalGradientsAtSCVCenter); + if(evalGradientsAtSCVCenter) + { + // calculate gradients at the center of the scv + for (int scvIdx = 0; scvIdx < numVertices; scvIdx++){ + if (dim == 2) + { + switch (scvIdx) + { + case 0: + if (numVertices == 4) { + subContVol[scvIdx].localCenter[0] = 0.25; + subContVol[scvIdx].localCenter[1] = 0.25; + } + else { + subContVol[scvIdx].localCenter[0] = 1.0/6.0; + subContVol[scvIdx].localCenter[1] = 1.0/6.0; + } + break; + case 1: + if (numVertices == 4) { + subContVol[scvIdx].localCenter[0] = 0.75; + subContVol[scvIdx].localCenter[1] = 0.25; + } + else { + subContVol[scvIdx].localCenter[0] = 4.0/6.0; + subContVol[scvIdx].localCenter[1] = 1.0/6.0; + } + break; + case 2: + if (numVertices == 4) { + subContVol[scvIdx].localCenter[0] = 0.25; + subContVol[scvIdx].localCenter[1] = 0.75; + } + else { + subContVol[scvIdx].localCenter[0] = 1.0/6.0; + subContVol[scvIdx].localCenter[1] = 4.0/6.0; + } + break; + case 3: + subContVol[scvIdx].localCenter[0] = 0.75; + subContVol[scvIdx].localCenter[1] = 0.75; + break; + } + } + + else if (dim == 3) + { + switch (scvIdx) + { + case 0: + if (numVertices == 8) { + subContVol[scvIdx].localCenter[0] = 0.25; + subContVol[scvIdx].localCenter[1] = 0.25; + subContVol[scvIdx].localCenter[2] = 0.25; + } + else if (numVertices == 4) { + subContVol[scvIdx].localCenter[0] = 3.0/16.0; + subContVol[scvIdx].localCenter[1] = 3.0/16.0; + subContVol[scvIdx].localCenter[2] = 3.0/16.0; + } + break; + case 1: + if (numVertices == 8) { + subContVol[scvIdx].localCenter[0] = 0.75; + subContVol[scvIdx].localCenter[1] = 0.25; + subContVol[scvIdx].localCenter[2] = 0.25; + } + else if (numVertices == 4) { + subContVol[scvIdx].localCenter[0] = 7.0/16.0; + subContVol[scvIdx].localCenter[1] = 3.0/16.0; + subContVol[scvIdx].localCenter[2] = 3.0/16.0; + } + break; + case 2: + if (numVertices == 8) { + subContVol[scvIdx].localCenter[0] = 0.25; + subContVol[scvIdx].localCenter[1] = 0.75; + subContVol[scvIdx].localCenter[2] = 0.25; + } + else if (numVertices == 4) { + subContVol[scvIdx].localCenter[0] = 3.0/16.0; + subContVol[scvIdx].localCenter[1] = 7.0/16.0; + subContVol[scvIdx].localCenter[2] = 3.0/16.0; + } + break; + case 3: + if (numVertices == 8) { + subContVol[scvIdx].localCenter[0] = 0.75; + subContVol[scvIdx].localCenter[1] = 0.75; + subContVol[scvIdx].localCenter[2] = 0.25; + } + else if (numVertices == 4) { + subContVol[scvIdx].localCenter[0] = 3.0/16.0; + subContVol[scvIdx].localCenter[1] = 3.0/16.0; + subContVol[scvIdx].localCenter[2] = 7.0/16.0; + } + break; + case 4: + subContVol[scvIdx].localCenter[0] = 0.25; + subContVol[scvIdx].localCenter[1] = 0.25; + subContVol[scvIdx].localCenter[2] = 0.75; + break; + case 5: + subContVol[scvIdx].localCenter[0] = 0.75; + subContVol[scvIdx].localCenter[1] = 0.25; + subContVol[scvIdx].localCenter[2] = 0.75; + break; + case 6: + subContVol[scvIdx].localCenter[0] = 0.25; + subContVol[scvIdx].localCenter[1] = 0.75; + subContVol[scvIdx].localCenter[2] = 0.75; + break; + case 7: + subContVol[scvIdx].localCenter[0] = 0.75; + subContVol[scvIdx].localCenter[1] = 0.75; + subContVol[scvIdx].localCenter[2] = 0.75; + break; + } + } + std::vector<ShapeJacobian> localJac; + localFiniteElement.localBasis().evaluateJacobian(subContVol[scvIdx].localCenter, localJac); + + JacobianInverseTransposed jacInvT = + geometry.jacobianInverseTransposed(subContVol[scvIdx].localCenter); + for (int vert = 0; vert < numVertices; vert++) + jacInvT.mv(localJac[vert][0], subContVol[scvIdx].gradCenter[vert]); + } + } + } +}; + +} + +#endif + diff --git a/dumux/implicit/common/implicitlocaljacobian.hh b/dumux/implicit/common/implicitlocaljacobian.hh new file mode 100644 index 0000000000..7fc5140d25 --- /dev/null +++ b/dumux/implicit/common/implicitlocaljacobian.hh @@ -0,0 +1,534 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \file + * \brief Caculates the Jacobian of the local residual for box models + */ +#ifndef DUMUX_BOX_LOCAL_JACOBIAN_HH +#define DUMUX_BOX_LOCAL_JACOBIAN_HH + +#include <dune/istl/matrix.hh> + +#include <dumux/common/math.hh> +#include "boxelementboundarytypes.hh" + +namespace Dumux +{ +/*! + * \ingroup BoxModel + * \ingroup BoxLocalJacobian + * \brief Calculates the Jacobian of the local residual for box models + * + * The default behavior is to use numeric differentiation, i.e. + * forward or backward differences (2nd order), or central + * differences (3rd order). The method used is determined by the + * "NumericDifferenceMethod" property: + * + * - if the value of this property is smaller than 0, backward + * differences are used, i.e.: + * \f[ + \frac{\partial f(x)}{\partial x} \approx \frac{f(x) - f(x - \epsilon)}{\epsilon} + * \f] + * + * - if the value of this property is 0, central + * differences are used, i.e.: + * \f[ + \frac{\partial f(x)}{\partial x} \approx \frac{f(x + \epsilon) - f(x - \epsilon)}{2 \epsilon} + * \f] + * + * - if the value of this property is larger than 0, forward + * differences are used, i.e.: + * \f[ + \frac{\partial f(x)}{\partial x} \approx \frac{f(x + \epsilon) - f(x)}{\epsilon} + * \f] + * + * Here, \f$ f \f$ is the residual function for all equations, \f$x\f$ + * is the value of a sub-control volume's primary variable at the + * evaluation point and \f$\epsilon\f$ is a small value larger than 0. + */ +template<class TypeTag> +class BoxLocalJacobian +{ +private: + typedef typename GET_PROP_TYPE(TypeTag, LocalJacobian) Implementation; + typedef typename GET_PROP_TYPE(TypeTag, LocalResidual) LocalResidual; + typedef typename GET_PROP_TYPE(TypeTag, Problem) Problem; + typedef typename GET_PROP_TYPE(TypeTag, Model) Model; + typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; + typedef typename GridView::template Codim<0>::Entity Element; + typedef typename GET_PROP_TYPE(TypeTag, JacobianAssembler) JacobianAssembler; + + enum { + numEq = GET_PROP_VALUE(TypeTag, NumEq), + dim = GridView::dimension, + + Green = JacobianAssembler::Green + }; + + typedef typename GET_PROP_TYPE(TypeTag, FVElementGeometry) FVElementGeometry; + typedef typename GET_PROP_TYPE(TypeTag, VertexMapper) VertexMapper; + typedef typename GET_PROP_TYPE(TypeTag, ElementSolutionVector) ElementSolutionVector; + typedef typename GET_PROP_TYPE(TypeTag, PrimaryVariables) PrimaryVariables; + + typedef typename GET_PROP_TYPE(TypeTag, VolumeVariables) VolumeVariables; + typedef typename GET_PROP_TYPE(TypeTag, ElementVolumeVariables) ElementVolumeVariables; + typedef typename GET_PROP_TYPE(TypeTag, ElementBoundaryTypes) ElementBoundaryTypes; + + typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; + typedef Dune::FieldMatrix<Scalar, numEq, numEq> MatrixBlock; + typedef Dune::Matrix<MatrixBlock> LocalBlockMatrix; + + // copying a local jacobian is not a good idea + BoxLocalJacobian(const BoxLocalJacobian &); + +public: + BoxLocalJacobian() + { + numericDifferenceMethod_ = GET_PARAM_FROM_GROUP(TypeTag, int, Implicit, NumericDifferenceMethod); + Valgrind::SetUndefined(problemPtr_); + } + + + /*! + * \brief Initialize the local Jacobian object. + * + * At this point we can assume that everything has been allocated, + * although some objects may not yet be completely initialized. + * + * \param prob The problem which we want to simulate. + */ + void init(Problem &prob) + { + problemPtr_ = &prob; + localResidual_.init(prob); + + // assume quadrilinears as elements with most vertices + A_.setSize(2<<dim, 2<<dim); + storageJacobian_.resize(2<<dim); + } + + /*! + * \brief Assemble an element's local Jacobian matrix of the + * defect. + * + * \param element The DUNE Codim<0> entity which we look at. + */ + void assemble(const Element &element) + { + // set the current grid element and update the element's + // finite volume geometry + elemPtr_ = &element; + fvElemGeom_.update(gridView_(), element); + reset_(); + + bcTypes_.update(problem_(), element_(), fvElemGeom_); + + // this is pretty much a HACK because the internal state of + // the problem is not supposed to be changed during the + // evaluation of the residual. (Reasons: It is a violation of + // abstraction, makes everything more prone to errors and is + // not thread save.) The real solution are context objects! + problem_().updateCouplingParams(element_()); + + // set the hints for the volume variables + model_().setHints(element, prevVolVars_, curVolVars_); + + // update the secondary variables for the element at the last + // and the current time levels + prevVolVars_.update(problem_(), + element_(), + fvElemGeom_, + true /* isOldSol? */); + + curVolVars_.update(problem_(), + element_(), + fvElemGeom_, + false /* isOldSol? */); + + // update the hints of the model + model_().updateCurHints(element, curVolVars_); + + // calculate the local residual + localResidual().eval(element_(), + fvElemGeom_, + prevVolVars_, + curVolVars_, + bcTypes_); + residual_ = localResidual().residual(); + storageTerm_ = localResidual().storageTerm(); + + model_().updatePVWeights(element_(), curVolVars_); + + // calculate the local jacobian matrix + int numVertices = fvElemGeom_.numVertices; + ElementSolutionVector partialDeriv(numVertices); + PrimaryVariables storageDeriv; + for (int j = 0; j < numVertices; j++) { + for (int pvIdx = 0; pvIdx < numEq; pvIdx++) { + asImp_().evalPartialDerivative_(partialDeriv, + storageDeriv, + j, + pvIdx); + + // update the local stiffness matrix with the current partial + // derivatives + updateLocalJacobian_(j, + pvIdx, + partialDeriv, + storageDeriv); + } + } + } + + /*! + * \brief Returns a reference to the object which calculates the + * local residual. + */ + const LocalResidual &localResidual() const + { return localResidual_; } + + /*! + * \brief Returns a reference to the object which calculates the + * local residual. + */ + LocalResidual &localResidual() + { return localResidual_; } + + /*! + * \brief Returns the Jacobian of the equations at vertex i to the + * primary variables at vertex j. + * + * \param i The local vertex (or sub-control volume) index on which + * the equations are defined + * \param j The local vertex (or sub-control volume) index which holds + * primary variables + */ + const MatrixBlock &mat(const int i, const int j) const + { return A_[i][j]; } + + /*! + * \brief Returns the Jacobian of the storage term at vertex i. + * + * \param i The local vertex (or sub-control volume) index + */ + const MatrixBlock &storageJacobian(const int i) const + { return storageJacobian_[i]; } + + /*! + * \brief Returns the residual of the equations at vertex i. + * + * \param i The local vertex (or sub-control volume) index on which + * the equations are defined + */ + const PrimaryVariables &residual(const int i) const + { return residual_[i]; } + + /*! + * \brief Returns the storage term for vertex i. + * + * \param i The local vertex (or sub-control volume) index on which + * the equations are defined + */ + const PrimaryVariables &storageTerm(const int i) const + { return storageTerm_[i]; } + + /*! + * \brief Returns the epsilon value which is added and removed + * from the current solution. + * + * \param scvIdx The local index of the element's vertex for + * which the local derivative ought to be calculated. + * \param pvIdx The index of the primary variable which gets varied + */ + Scalar numericEpsilon(const int scvIdx, + const int pvIdx) const + { + // define the base epsilon as the geometric mean of 1 and the + // resolution of the scalar type. E.g. for standard 64 bit + // floating point values, the resolution is about 10^-16 and + // the base epsilon is thus approximately 10^-8. + /* + static const Scalar baseEps + = Dumux::geometricMean<Scalar>(std::numeric_limits<Scalar>::epsilon(), 1.0); + */ + static const Scalar baseEps = 1e-10; + assert(std::numeric_limits<Scalar>::epsilon()*1e4 < baseEps); + // the epsilon value used for the numeric differentiation is + // now scaled by the absolute value of the primary variable... + Scalar priVar = this->curVolVars_[scvIdx].priVar(pvIdx); + return baseEps*(std::abs(priVar) + 1.0); + } + +protected: + Implementation &asImp_() + { return *static_cast<Implementation*>(this); } + const Implementation &asImp_() const + { return *static_cast<const Implementation*>(this); } + + /*! + * \brief Returns a reference to the problem. + */ + const Problem &problem_() const + { + Valgrind::CheckDefined(problemPtr_); + return *problemPtr_; + }; + + /*! + * \brief Returns a reference to the grid view. + */ + const GridView &gridView_() const + { return problem_().gridView(); } + + /*! + * \brief Returns a reference to the element. + */ + const Element &element_() const + { + Valgrind::CheckDefined(elemPtr_); + return *elemPtr_; + }; + + /*! + * \brief Returns a reference to the model. + */ + const Model &model_() const + { return problem_().model(); }; + + /*! + * \brief Returns a reference to the jacobian assembler. + */ + const JacobianAssembler &jacAsm_() const + { return model_().jacobianAssembler(); } + + /*! + * \brief Returns a reference to the vertex mapper. + */ + const VertexMapper &vertexMapper_() const + { return problem_().vertexMapper(); }; + + /*! + * \brief Reset the local jacobian matrix to 0 + */ + void reset_() + { + int n = element_().template count<dim>(); + for (int i = 0; i < n; ++ i) { + storageJacobian_[i] = 0.0; + for (int j = 0; j < n; ++ j) { + A_[i][j] = 0.0; + } + } + } + + /*! + * \brief Compute the partial derivatives to a primary variable at + * an degree of freedom. + * + * This method can be overwritten by the implementation if a + * better scheme than numerical differentiation is available. + * + * The default implementation of this method uses numeric + * differentiation, i.e. forward or backward differences (2nd + * order), or central differences (3rd order). The method used is + * determined by the "NumericDifferenceMethod" property: + * + * - if the value of this property is smaller than 0, backward + * differences are used, i.e.: + * \f[ + \frac{\partial f(x)}{\partial x} \approx \frac{f(x) - f(x - \epsilon)}{\epsilon} + * \f] + * + * - if the value of this property is 0, central + * differences are used, i.e.: + * \f[ + \frac{\partial f(x)}{\partial x} \approx \frac{f(x + \epsilon) - f(x - \epsilon)}{2 \epsilon} + * \f] + * + * - if the value of this property is larger than 0, forward + * differences are used, i.e.: + * \f[ + \frac{\partial f(x)}{\partial x} \approx \frac{f(x + \epsilon) - f(x)}{\epsilon} + * \f] + * + * Here, \f$ f \f$ is the residual function for all equations, \f$x\f$ + * is the value of a sub-control volume's primary variable at the + * evaluation point and \f$\epsilon\f$ is a small value larger than 0. + * + * \param partialDeriv The vector storing the partial derivatives of all + * equations + * \param storageDeriv the mass matrix contributions + * \param scvIdx The sub-control volume index of the current + * finite element for which the partial derivative + * ought to be calculated + * \param pvIdx The index of the primary variable at the scvIdx' + * sub-control volume of the current finite element + * for which the partial derivative ought to be + * calculated + */ + void evalPartialDerivative_(ElementSolutionVector &partialDeriv, + PrimaryVariables &storageDeriv, + const int scvIdx, + const int pvIdx) + { + int globalIdx = vertexMapper_().map(element_(), scvIdx, dim); + + PrimaryVariables priVars(model_().curSol()[globalIdx]); + VolumeVariables origVolVars(curVolVars_[scvIdx]); + + curVolVars_[scvIdx].setEvalPoint(&origVolVars); + Scalar eps = asImp_().numericEpsilon(scvIdx, pvIdx); + Scalar delta = 0; + + if (numericDifferenceMethod_ >= 0) { + // we are not using backward differences, i.e. we need to + // calculate f(x + \epsilon) + + // deflect primary variables + priVars[pvIdx] += eps; + delta += eps; + + // calculate the residual + curVolVars_[scvIdx].update(priVars, + problem_(), + element_(), + fvElemGeom_, + scvIdx, + false); + localResidual().eval(element_(), + fvElemGeom_, + prevVolVars_, + curVolVars_, + bcTypes_); + + // store the residual and the storage term + partialDeriv = localResidual().residual(); + storageDeriv = localResidual().storageTerm()[scvIdx]; + } + else { + // we are using backward differences, i.e. we don't need + // to calculate f(x + \epsilon) and we can recycle the + // (already calculated) residual f(x) + partialDeriv = residual_; + storageDeriv = storageTerm_[scvIdx]; + } + + + if (numericDifferenceMethod_ <= 0) { + // we are not using forward differences, i.e. we don't + // need to calculate f(x - \epsilon) + + // deflect the primary variables + priVars[pvIdx] -= delta + eps; + delta += eps; + + // calculate residual again + curVolVars_[scvIdx].update(priVars, + problem_(), + element_(), + fvElemGeom_, + scvIdx, + false); + localResidual().eval(element_(), + fvElemGeom_, + prevVolVars_, + curVolVars_, + bcTypes_); + partialDeriv -= localResidual().residual(); + storageDeriv -= localResidual().storageTerm()[scvIdx]; + } + else { + // we are using forward differences, i.e. we don't need to + // calculate f(x - \epsilon) and we can recycle the + // (already calculated) residual f(x) + partialDeriv -= residual_; + storageDeriv -= storageTerm_[scvIdx]; + } + + // divide difference in residuals by the magnitude of the + // deflections between the two function evaluation + partialDeriv /= delta; + storageDeriv /= delta; + + // restore the original state of the element's volume variables + curVolVars_[scvIdx] = origVolVars; + +#if HAVE_VALGRIND + for (unsigned i = 0; i < partialDeriv.size(); ++i) + Valgrind::CheckDefined(partialDeriv[i]); +#endif + } + + /*! + * \brief Updates the current local Jacobian matrix with the + * partial derivatives of all equations in regard to the + * primary variable 'pvIdx' at vertex 'scvIdx' . + */ + void updateLocalJacobian_(const int scvIdx, + const int pvIdx, + const ElementSolutionVector &partialDeriv, + const PrimaryVariables &storageDeriv) + { + // store the derivative of the storage term + for (int eqIdx = 0; eqIdx < numEq; eqIdx++) { + storageJacobian_[scvIdx][eqIdx][pvIdx] = storageDeriv[eqIdx]; + } + + for (int i = 0; i < fvElemGeom_.numVertices; i++) + { + // Green vertices are not to be changed! + if (jacAsm_().vertexColor(element_(), i) != Green) { + for (int eqIdx = 0; eqIdx < numEq; eqIdx++) { + // A[i][scvIdx][eqIdx][pvIdx] is the rate of change of + // the residual of equation 'eqIdx' at vertex 'i' + // depending on the primary variable 'pvIdx' at vertex + // 'scvIdx'. + this->A_[i][scvIdx][eqIdx][pvIdx] = partialDeriv[i][eqIdx]; + Valgrind::CheckDefined(this->A_[i][scvIdx][eqIdx][pvIdx]); + } + } + } + } + + const Element *elemPtr_; + FVElementGeometry fvElemGeom_; + + ElementBoundaryTypes bcTypes_; + + // The problem we would like to solve + Problem *problemPtr_; + + // secondary variables at the previous and at the current time + // levels + ElementVolumeVariables prevVolVars_; + ElementVolumeVariables curVolVars_; + + LocalResidual localResidual_; + + LocalBlockMatrix A_; + std::vector<MatrixBlock> storageJacobian_; + + ElementSolutionVector residual_; + ElementSolutionVector storageTerm_; + + int numericDifferenceMethod_; +}; +} + +#endif diff --git a/dumux/implicit/common/implicitlocalresidual.hh b/dumux/implicit/common/implicitlocalresidual.hh new file mode 100644 index 0000000000..8c02754133 --- /dev/null +++ b/dumux/implicit/common/implicitlocalresidual.hh @@ -0,0 +1,742 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \file + * \brief Calculates the residual of models based on the box scheme element-wise. + */ +#ifndef DUMUX_BOX_LOCAL_RESIDUAL_HH +#define DUMUX_BOX_LOCAL_RESIDUAL_HH + +#include <dune/istl/matrix.hh> +#include <dune/grid/common/geometry.hh> + +#include <dumux/common/valgrind.hh> + +#include "boxproperties.hh" + +namespace Dumux +{ +/*! + * \ingroup BoxModel + * \ingroup BoxLocalResidual + * \brief Element-wise calculation of the residual matrix for models + * based on the box scheme. + * + * \todo Please doc me more! + */ +template<class TypeTag> +class BoxLocalResidual +{ +private: + typedef typename GET_PROP_TYPE(TypeTag, LocalResidual) Implementation; + typedef typename GET_PROP_TYPE(TypeTag, Problem) Problem; + typedef typename GET_PROP_TYPE(TypeTag, Model) Model; + typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; + typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; + + enum { + numEq = GET_PROP_VALUE(TypeTag, NumEq), + dim = GridView::dimension + }; + + typedef typename GridView::template Codim<0>::Entity Element; + typedef typename GridView::template Codim<dim>::EntityPointer VertexPointer; + typedef typename GridView::IntersectionIterator IntersectionIterator; + + typedef typename GridView::Grid::ctype CoordScalar; + typedef typename Dune::GenericReferenceElements<CoordScalar, dim> ReferenceElements; + typedef typename Dune::GenericReferenceElement<CoordScalar, dim> ReferenceElement; + + typedef typename GET_PROP_TYPE(TypeTag, FVElementGeometry) FVElementGeometry; + typedef typename GET_PROP_TYPE(TypeTag, VertexMapper) VertexMapper; + typedef typename GET_PROP_TYPE(TypeTag, ElementSolutionVector) ElementSolutionVector; + typedef typename GET_PROP_TYPE(TypeTag, PrimaryVariables) PrimaryVariables; + typedef typename GET_PROP_TYPE(TypeTag, BoundaryTypes) BoundaryTypes; + typedef typename GET_PROP_TYPE(TypeTag, ElementBoundaryTypes) ElementBoundaryTypes; + typedef typename GET_PROP_TYPE(TypeTag, VolumeVariables) VolumeVariables; + typedef typename GET_PROP_TYPE(TypeTag, ElementVolumeVariables) ElementVolumeVariables; + + // copying the local residual class is not a good idea + BoxLocalResidual(const BoxLocalResidual &); + +public: + BoxLocalResidual() + { } + + ~BoxLocalResidual() + { } + + /*! + * \brief Initialize the local residual. + * + * This assumes that all objects of the simulation have been fully + * allocated but not necessarily initialized completely. + * + * \param problem The representation of the physical problem to be + * solved. + */ + void init(Problem &problem) + { problemPtr_ = &problem; } + + /*! + * \brief Compute the local residual, i.e. the deviation of the + * equations from zero. + * + * \param element The DUNE Codim<0> entity for which the residual + * ought to be calculated + */ + void eval(const Element &element) + { + FVElementGeometry fvGeometry; + + fvGeometry.update(gridView_(), element); + fvElemGeomPtr_ = &fvGeometry; + + ElementVolumeVariables volVarsPrev, volVarsCur; + // update the hints + model_().setHints(element, volVarsPrev, volVarsCur); + + volVarsPrev.update(problem_(), + element, + fvGeometry_(), + true /* oldSol? */); + volVarsCur.update(problem_(), + element, + fvGeometry_(), + false /* oldSol? */); + + ElementBoundaryTypes bcTypes; + bcTypes.update(problem_(), element, fvGeometry_()); + + // this is pretty much a HACK because the internal state of + // the problem is not supposed to be changed during the + // evaluation of the residual. (Reasons: It is a violation of + // abstraction, makes everything more prone to errors and is + // not thread save.) The real solution are context objects! + problem_().updateCouplingParams(element); + + asImp_().eval(element, fvGeometry_(), volVarsPrev, volVarsCur, bcTypes); + } + + /*! + * \brief Compute the storage term for the current solution. + * + * This can be used to figure out how much of each conservation + * quantity is inside the element. + * + * \param element The DUNE Codim<0> entity for which the storage + * term ought to be calculated + */ + void evalStorage(const Element &element) + { + elemPtr_ = &element; + + FVElementGeometry fvGeometry; + fvGeometry.update(gridView_(), element); + fvElemGeomPtr_ = &fvGeometry; + + ElementBoundaryTypes bcTypes; + bcTypes.update(problem_(), element, fvGeometry_()); + bcTypesPtr_ = &bcTypes; + + // no previous volume variables! + prevVolVarsPtr_ = 0; + + ElementVolumeVariables volVars; + + // update the hints + model_().setHints(element, volVars); + + // calculate volume current variables + volVars.update(problem_(), element, fvGeometry_(), false); + curVolVarsPtr_ = &volVars; + + asImp_().evalStorage_(); + } + + /*! + * \brief Compute the flux term for the current solution. + * + * \param element The DUNE Codim<0> entity for which the residual + * ought to be calculated + * \param curVolVars The volume averaged variables for all + * sub-contol volumes of the element + */ + void evalFluxes(const Element &element, + const ElementVolumeVariables &curVolVars) + { + elemPtr_ = &element; + + FVElementGeometry fvGeometry; + fvGeometry.update(gridView_(), element); + fvElemGeomPtr_ = &fvGeometry; + + ElementBoundaryTypes bcTypes; + bcTypes.update(problem_(), element, fvGeometry_()); + + residual_.resize(fvGeometry_().numVertices); + residual_ = 0; + + bcTypesPtr_ = &bcTypes; + prevVolVarsPtr_ = 0; + curVolVarsPtr_ = &curVolVars; + asImp_().evalFluxes_(); + } + + /*! + * \brief Compute the local residual, i.e. the deviation of the + * equations from zero. + * + * \param element The DUNE Codim<0> entity for which the residual + * ought to be calculated + * \param fvGeometry The finite-volume geometry of the element + * \param prevVolVars The volume averaged variables for all + * sub-control volumes of the element at the previous + * time level + * \param curVolVars The volume averaged variables for all + * sub-control volumes of the element at the current + * time level + * \param bcTypes The types of the boundary conditions for all + * vertices of the element + */ + void eval(const Element &element, + const FVElementGeometry &fvGeometry, + const ElementVolumeVariables &prevVolVars, + const ElementVolumeVariables &curVolVars, + const ElementBoundaryTypes &bcTypes) + { + Valgrind::CheckDefined(prevVolVars); + Valgrind::CheckDefined(curVolVars); + +#if !defined NDEBUG && HAVE_VALGRIND + for (int i=0; i < fvGeometry.numVertices; i++) { + prevVolVars[i].checkDefined(); + curVolVars[i].checkDefined(); + } +#endif // HAVE_VALGRIND + + elemPtr_ = &element; + fvElemGeomPtr_ = &fvGeometry; + bcTypesPtr_ = &bcTypes; + prevVolVarsPtr_ = &prevVolVars; + curVolVarsPtr_ = &curVolVars; + + // resize the vectors for all terms + int numVerts = fvGeometry_().numVertices; + residual_.resize(numVerts); + storageTerm_.resize(numVerts); + + residual_ = 0.0; + storageTerm_ = 0.0; + + asImp_().evalFluxes_(); + +#if !defined NDEBUG && HAVE_VALGRIND + for (int i=0; i < fvGeometry_().numVertices; i++) + Valgrind::CheckDefined(residual_[i]); +#endif // HAVE_VALGRIND + + asImp_().evalVolumeTerms_(); + +#if !defined NDEBUG && HAVE_VALGRIND + for (int i=0; i < fvGeometry_().numVertices; i++) { + Valgrind::CheckDefined(residual_[i]); + } +#endif // HAVE_VALGRIND + + // evaluate the boundary conditions + asImp_().evalBoundary_(); + +#if !defined NDEBUG && HAVE_VALGRIND + for (int i=0; i < fvGeometry_().numVertices; i++) + Valgrind::CheckDefined(residual_[i]); +#endif // HAVE_VALGRIND + } + + /*! + * \brief Returns the local residual for all sub-control + * volumes of the element. + */ + const ElementSolutionVector &residual() const + { return residual_; } + + /*! + * \brief Returns the local residual for a given sub-control + * volume of the element. + * + * \param scvIdx The local index of the sub-control volume + * (i.e. the element's local vertex index) + */ + const PrimaryVariables &residual(const int scvIdx) const + { return residual_[scvIdx]; } + + /*! + * \brief Returns the storage term for all sub-control volumes of the + * element. + */ + const ElementSolutionVector &storageTerm() const + { return storageTerm_; } + + /*! + * \brief Returns the storage term for a given sub-control volumes + * of the element. + */ + const PrimaryVariables &storageTerm(const int scvIdx) const + { return storageTerm_[scvIdx]; } + +protected: + Implementation &asImp_() + { + assert(static_cast<Implementation*>(this) != 0); + return *static_cast<Implementation*>(this); + } + + const Implementation &asImp_() const + { + assert(static_cast<const Implementation*>(this) != 0); + return *static_cast<const Implementation*>(this); + } + + /*! + * \brief Evaluate the boundary conditions + * of the current element. + */ + void evalBoundary_() + { + if (bcTypes_().hasNeumann() || bcTypes_().hasOutflow()) + asImp_().evalBoundaryFluxes_(); +#if !defined NDEBUG && HAVE_VALGRIND + for (int i=0; i < fvGeometry_().numVertices; i++) + Valgrind::CheckDefined(residual_[i]); +#endif // HAVE_VALGRIND + + if (bcTypes_().hasDirichlet()) + asImp_().evalDirichlet_(); + } + + /*! + * \brief Set the values of the Dirichlet boundary control volumes + * of the current element. + */ + void evalDirichlet_() + { + PrimaryVariables dirichletValues(0); + for (int scvIdx = 0; scvIdx < fvGeometry_().numVertices; ++scvIdx) { + const BoundaryTypes &bcTypes = bcTypes_(scvIdx); + + if (bcTypes.hasDirichlet()) { + // ask the problem for the dirichlet values + const VertexPointer vPtr = element_().template subEntity<dim>(scvIdx); + Valgrind::SetUndefined(dirichletValues); + asImp_().problem_().dirichlet(dirichletValues, *vPtr); + + // set the dirichlet conditions + for (int eqIdx = 0; eqIdx < numEq; ++eqIdx) { + if (bcTypes.isDirichlet(eqIdx)) { + int pvIdx = bcTypes.eqToDirichletIndex(eqIdx); + assert(0 <= pvIdx && pvIdx < numEq); + Valgrind::CheckDefined(dirichletValues[pvIdx]); + + residual_[scvIdx][eqIdx] = + curPriVar_(scvIdx, pvIdx) - dirichletValues[pvIdx]; + + storageTerm_[scvIdx][eqIdx] = 0.0; + } + } + } + } + } + + /*! + * \brief Add all Neumann and outflow boundary conditions to the local + * residual. + */ + void evalBoundaryFluxes_() + { + Dune::GeometryType geoType = element_().geometry().type(); + const ReferenceElement &refElement = ReferenceElements::general(geoType); + + IntersectionIterator isIt = gridView_().ibegin(element_()); + const IntersectionIterator &endIt = gridView_().iend(element_()); + for (; isIt != endIt; ++isIt) + { + // handle only faces on the boundary + if (isIt->boundary()) { + // Assemble the boundary for all vertices of the current + // face + int faceIdx = isIt->indexInInside(); + int numFaceVerts = refElement.size(faceIdx, 1, dim); + for (int faceVertIdx = 0; + faceVertIdx < numFaceVerts; + ++faceVertIdx) + { + int scvIdx = refElement.subEntity(faceIdx, + 1, + faceVertIdx, + dim); + + int boundaryFaceIdx = + fvGeometry_().boundaryFaceIndex(faceIdx, faceVertIdx); + + // add the residual of all vertices of the boundary + // segment + asImp_().evalNeumannSegment_(isIt, + scvIdx, + boundaryFaceIdx); + // evaluate the outflow conditions at the boundary face + // ATTENTION: This is so far a beta version that is only for the 2p2c and 2p2cni model + // available and not thoroughly tested. + asImp_().evalOutflowSegment_(isIt, + scvIdx, + boundaryFaceIdx); + } + } + } + } + + /*! + * \brief Add Neumann boundary conditions for a single sub-control + * volume face to the local residual. + */ + void evalNeumannSegment_(const IntersectionIterator &isIt, + const int scvIdx, + const int boundaryFaceIdx) + { + // temporary vector to store the neumann boundary fluxes + PrimaryVariables neumannFlux(0.0); + const BoundaryTypes &bcTypes = bcTypes_(scvIdx); + + // deal with neumann boundaries + if (bcTypes.hasNeumann()) { + Valgrind::SetUndefined(neumannFlux); + problem_().boxSDNeumann(neumannFlux, + element_(), + fvGeometry_(), + *isIt, + scvIdx, + boundaryFaceIdx, + curVolVars_()); + neumannFlux *= + fvGeometry_().boundaryFace[boundaryFaceIdx].area + * curVolVars_(scvIdx).extrusionFactor(); + Valgrind::CheckDefined(neumannFlux); + + // set the neumann conditions + for (int eqIdx = 0; eqIdx < numEq; ++eqIdx) { + if (!bcTypes.isNeumann(eqIdx)) + continue; + residual_[scvIdx][eqIdx] += neumannFlux[eqIdx]; + } + } + } + + /*! + * \brief Add outflow boundary conditions for a single sub-control + * volume face to the local residual. + * + * \param isIt The intersection iterator of current element + * \param scvIdx The index of the considered face of the sub-control volume + * \param boundaryFaceIdx The index of the considered boundary face of the sub control volume + */ + void evalOutflowSegment_(const IntersectionIterator &isIt, + const int scvIdx, + const int boundaryFaceIdx) + { + const BoundaryTypes &bcTypes = this->bcTypes_(scvIdx); + // deal with outflow boundaries + if (bcTypes.hasOutflow()) + { + //calculate outflow fluxes + PrimaryVariables values(0.0); + asImp_().computeFlux(values, boundaryFaceIdx, true); + Valgrind::CheckDefined(values); + + for (int equationIdx = 0; equationIdx < numEq; ++equationIdx) + { + if (!bcTypes.isOutflow(equationIdx) ) + continue; + // deduce outflow + this->residual_[scvIdx][equationIdx] += values[equationIdx]; + } + } + } + + /*! + * \brief Add the flux terms to the local residual of all + * sub-control volumes of the current element. + */ + void evalFluxes_() + { + // calculate the mass flux over the faces and subtract + // it from the local rates + for (int scvfIdx = 0; scvfIdx < fvGeometry_().numEdges; scvfIdx++) + { + int i = fvGeometry_().subContVolFace[scvfIdx].i; + int j = fvGeometry_().subContVolFace[scvfIdx].j; + + PrimaryVariables flux; + + Valgrind::SetUndefined(flux); + asImp_().computeFlux(flux, scvfIdx); + Valgrind::CheckDefined(flux); + + Scalar extrusionFactor = + (curVolVars_(i).extrusionFactor() + + curVolVars_(j).extrusionFactor()) + / 2; + flux *= extrusionFactor; + + // The balance equation for a finite volume is: + // + // dStorage/dt = Flux + Source + // + // where the 'Flux' and the 'Source' terms represent the + // mass per second which _ENTER_ the finite + // volume. Re-arranging this, we get + // + // dStorage/dt - Source - Flux = 0 + // + // Since the flux calculated by computeFlux() goes _OUT_ + // of sub-control volume i and _INTO_ sub-control volume + // j, we need to add the flux to finite volume i and + // subtract it from finite volume j + residual_[i] += flux; + residual_[j] -= flux; + } + } + + /*! + * \brief Set the local residual to the storage terms of all + * sub-control volumes of the current element. + */ + void evalStorage_() + { + storageTerm_.resize(fvGeometry_().numVertices); + storageTerm_ = 0; + + // calculate the amount of conservation each quantity inside + // all sub control volumes + for (int scvIdx = 0; scvIdx < fvGeometry_().numVertices; scvIdx++) { + Valgrind::SetUndefined(storageTerm_[scvIdx]); + asImp_().computeStorage(storageTerm_[scvIdx], scvIdx, /*isOldSol=*/false); + storageTerm_[scvIdx] *= + fvGeometry_().subContVol[scvIdx].volume + * curVolVars_(scvIdx).extrusionFactor(); + Valgrind::CheckDefined(storageTerm_[scvIdx]); + } + } + + /*! + * \brief Add the change in the storage terms and the source term + * to the local residual of all sub-control volumes of the + * current element. + */ + void evalVolumeTerms_() + { + // evaluate the volume terms (storage + source terms) + for (int scvIdx = 0; scvIdx < fvGeometry_().numVertices; scvIdx++) + { + Scalar extrusionFactor = + curVolVars_(scvIdx).extrusionFactor(); + + PrimaryVariables values(0.0); + + // mass balance within the element. this is the + // \f$\frac{m}{\partial t}\f$ term if using implicit + // euler as time discretization. + // + // TODO (?): we might need a more explicit way for + // doing the time discretization... + Valgrind::SetUndefined(storageTerm_[scvIdx]); + Valgrind::SetUndefined(values); + asImp_().computeStorage(storageTerm_[scvIdx], scvIdx, false); + asImp_().computeStorage(values, scvIdx, true); + Valgrind::CheckDefined(storageTerm_[scvIdx]); + Valgrind::CheckDefined(values); + + storageTerm_[scvIdx] -= values; + storageTerm_[scvIdx] *= + fvGeometry_().subContVol[scvIdx].volume + / problem_().timeManager().timeStepSize() + * extrusionFactor; + residual_[scvIdx] += storageTerm_[scvIdx]; + + // subtract the source term from the local rate + Valgrind::SetUndefined(values); + asImp_().computeSource(values, scvIdx); + Valgrind::CheckDefined(values); + values *= fvGeometry_().subContVol[scvIdx].volume * extrusionFactor; + residual_[scvIdx] -= values; + + // make sure that only defined quantities were used + // to calculate the residual. + Valgrind::CheckDefined(residual_[scvIdx]); + } + } + + /*! + * \brief Returns a reference to the problem. + */ + const Problem &problem_() const + { return *problemPtr_; }; + + /*! + * \brief Returns a reference to the model. + */ + const Model &model_() const + { return problem_().model(); }; + + /*! + * \brief Returns a reference to the vertex mapper. + */ + const VertexMapper &vertexMapper_() const + { return problem_().vertexMapper(); }; + + /*! + * \brief Returns a reference to the grid view. + */ + const GridView &gridView_() const + { return problem_().gridView(); } + + /*! + * \brief Returns a reference to the current element. + */ + const Element &element_() const + { + Valgrind::CheckDefined(elemPtr_); + return *elemPtr_; + } + + /*! + * \brief Returns a reference to the current element's finite + * volume geometry. + */ + const FVElementGeometry &fvGeometry_() const + { + Valgrind::CheckDefined(fvElemGeomPtr_); + return *fvElemGeomPtr_; + } + + /*! + * \brief Returns a reference to the primary variables of + * the last time step of the i'th + * sub-control volume of the current element. + */ + const PrimaryVariables &prevPriVars_(const int i) const + { + return prevVolVars_(i).priVars(); + } + + /*! + * \brief Returns a reference to the primary variables of the i'th + * sub-control volume of the current element. + */ + const PrimaryVariables &curPriVars_(const int i) const + { + return curVolVars_(i).priVars(); + } + + /*! + * \brief Returns the j'th primary of the i'th sub-control volume + * of the current element. + */ + Scalar curPriVar_(const int i, const int j) const + { + return curVolVars_(i).priVar(j); + } + + /*! + * \brief Returns a reference to the current volume variables of + * all sub-control volumes of the current element. + */ + const ElementVolumeVariables &curVolVars_() const + { + Valgrind::CheckDefined(curVolVarsPtr_); + return *curVolVarsPtr_; + } + + /*! + * \brief Returns a reference to the volume variables of the i-th + * sub-control volume of the current element. + */ + const VolumeVariables &curVolVars_(const int i) const + { + return curVolVars_()[i]; + } + + /*! + * \brief Returns a reference to the previous time step's volume + * variables of all sub-control volumes of the current + * element. + */ + const ElementVolumeVariables &prevVolVars_() const + { + Valgrind::CheckDefined(prevVolVarsPtr_); + return *prevVolVarsPtr_; + } + + /*! + * \brief Returns a reference to the previous time step's volume + * variables of the i-th sub-control volume of the current + * element. + */ + const VolumeVariables &prevVolVars_(const int i) const + { + return prevVolVars_()[i]; + } + + /*! + * \brief Returns a reference to the boundary types of all + * sub-control volumes of the current element. + */ + const ElementBoundaryTypes &bcTypes_() const + { + Valgrind::CheckDefined(bcTypesPtr_); + return *bcTypesPtr_; + } + + /*! + * \brief Returns a reference to the boundary types of the i-th + * sub-control volume of the current element. + */ + const BoundaryTypes &bcTypes_(const int i) const + { + return bcTypes_()[i]; + } + +protected: + ElementSolutionVector storageTerm_; + ElementSolutionVector residual_; + + // The problem we would like to solve + Problem *problemPtr_; + + const Element *elemPtr_; + const FVElementGeometry *fvElemGeomPtr_; + + // current and previous secondary variables for the element + const ElementVolumeVariables *prevVolVarsPtr_; + const ElementVolumeVariables *curVolVarsPtr_; + + const ElementBoundaryTypes *bcTypesPtr_; +}; + +} + +#endif diff --git a/dumux/implicit/common/implicitmodel.hh b/dumux/implicit/common/implicitmodel.hh new file mode 100644 index 0000000000..567d706dd7 --- /dev/null +++ b/dumux/implicit/common/implicitmodel.hh @@ -0,0 +1,927 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \file + * + * \brief Base class for models using box discretization + */ +#ifndef DUMUX_BOX_MODEL_HH +#define DUMUX_BOX_MODEL_HH + +#include "boxproperties.hh" +#include "boxpropertydefaults.hh" + +#include "boxelementvolumevariables.hh" +#include "boxlocaljacobian.hh" +#include "boxlocalresidual.hh" + +#include <dumux/parallel/vertexhandles.hh> + +#include <dune/grid/common/geometry.hh> + +namespace Dumux +{ + +/*! + * \ingroup BoxModel + * + * \brief The base class for the vertex centered finite volume + * discretization scheme. + */ +template<class TypeTag> +class BoxModel +{ + typedef typename GET_PROP_TYPE(TypeTag, Model) Implementation; + typedef typename GET_PROP_TYPE(TypeTag, Problem) Problem; + typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; + typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; + typedef typename GET_PROP_TYPE(TypeTag, ElementMapper) ElementMapper; + typedef typename GET_PROP_TYPE(TypeTag, VertexMapper) VertexMapper; + typedef typename GET_PROP_TYPE(TypeTag, DofMapper) DofMapper; + typedef typename GET_PROP_TYPE(TypeTag, SolutionVector) SolutionVector; + typedef typename GET_PROP_TYPE(TypeTag, PrimaryVariables) PrimaryVariables; + typedef typename GET_PROP_TYPE(TypeTag, JacobianAssembler) JacobianAssembler; + typedef typename GET_PROP_TYPE(TypeTag, ElementVolumeVariables) ElementVolumeVariables; + typedef typename GET_PROP_TYPE(TypeTag, VolumeVariables) VolumeVariables; + + enum { + numEq = GET_PROP_VALUE(TypeTag, NumEq), + dim = GridView::dimension + }; + + typedef typename GET_PROP_TYPE(TypeTag, FVElementGeometry) FVElementGeometry; + typedef typename GET_PROP_TYPE(TypeTag, LocalJacobian) LocalJacobian; + typedef typename GET_PROP_TYPE(TypeTag, LocalResidual) LocalResidual; + typedef typename GET_PROP_TYPE(TypeTag, NewtonMethod) NewtonMethod; + typedef typename GET_PROP_TYPE(TypeTag, NewtonController) NewtonController; + + typedef typename GridView::ctype CoordScalar; + typedef typename GridView::template Codim<0>::Entity Element; + typedef typename GridView::template Codim<0>::Iterator ElementIterator; + typedef typename GridView::template Codim<dim>::Entity Vertex; + typedef typename GridView::template Codim<dim>::Iterator VertexIterator; + typedef typename GridView::IntersectionIterator IntersectionIterator; + + typedef typename Dune::GenericReferenceElements<CoordScalar, dim> ReferenceElements; + typedef typename Dune::GenericReferenceElement<CoordScalar, dim> ReferenceElement; + + // copying a model is not a good idea + BoxModel(const BoxModel &); + +public: + /*! + * \brief The constructor. + */ + BoxModel() + { + enableHints_ = GET_PARAM_FROM_GROUP(TypeTag, bool, Implicit, EnableHints); + } + + ~BoxModel() + { delete jacAsm_; } + + /*! + * \brief Apply the initial conditions to the model. + * + * \param problem The object representing the problem which needs to + * be simulated. + */ + void init(Problem &problem) + { + problemPtr_ = &problem; + + updateBoundaryIndices_(); + + int nDofs = asImp_().numDofs(); + uCur_.resize(nDofs); + uPrev_.resize(nDofs); + boxVolume_.resize(nDofs); + + localJacobian_.init(problem_()); + jacAsm_ = new JacobianAssembler(); + jacAsm_->init(problem_()); + + asImp_().applyInitialSolution_(); + + // resize the hint vectors + if (enableHints_) { + int nVerts = gridView_().size(dim); + curHints_.resize(nVerts); + prevHints_.resize(nVerts); + hintsUsable_.resize(nVerts); + std::fill(hintsUsable_.begin(), + hintsUsable_.end(), + false); + } + + // also set the solution of the "previous" time step to the + // initial solution. + uPrev_ = uCur_; + } + + void setHints(const Element &element, + ElementVolumeVariables &prevVolVars, + ElementVolumeVariables &curVolVars) const + { + if (!enableHints_) + return; + + int n = element.template count<dim>(); + prevVolVars.resize(n); + curVolVars.resize(n); + for (int i = 0; i < n; ++i) { + int globalIdx = vertexMapper().map(element, i, dim); + + if (!hintsUsable_[globalIdx]) { + curVolVars[i].setHint(NULL); + prevVolVars[i].setHint(NULL); + } + else { + curVolVars[i].setHint(&curHints_[globalIdx]); + prevVolVars[i].setHint(&prevHints_[globalIdx]); + } + } + } + + void setHints(const Element &element, + ElementVolumeVariables &curVolVars) const + { + if (!enableHints_) + return; + + int n = element.template count<dim>(); + curVolVars.resize(n); + for (int i = 0; i < n; ++i) { + int globalIdx = vertexMapper().map(element, i, dim); + + if (!hintsUsable_[globalIdx]) + curVolVars[i].setHint(NULL); + else + curVolVars[i].setHint(&curHints_[globalIdx]); + } + } + + void updatePrevHints() + { + if (!enableHints_) + return; + + prevHints_ = curHints_; + } + + void updateCurHints(const Element &element, + const ElementVolumeVariables &elemVolVars) const + { + if (!enableHints_) + return; + + for (unsigned int i = 0; i < elemVolVars.size(); ++i) { + int globalIdx = vertexMapper().map(element, i, dim); + curHints_[globalIdx] = elemVolVars[i]; + if (!hintsUsable_[globalIdx]) + prevHints_[globalIdx] = elemVolVars[i]; + hintsUsable_[globalIdx] = true; + } + } + + + /*! + * \brief Compute the global residual for an arbitrary solution + * vector. + * + * \param residual Stores the result + * \param u The solution for which the residual ought to be calculated + */ + Scalar globalResidual(SolutionVector &residual, + const SolutionVector &u) + { + SolutionVector tmp(curSol()); + curSol() = u; + Scalar res = globalResidual(residual); + curSol() = tmp; + return res; + } + + /*! + * \brief Compute the global residual for the current solution + * vector. + * + * \param residual Stores the result + */ + Scalar globalResidual(SolutionVector &residual) + { + residual = 0; + + ElementIterator elemIt = gridView_().template begin<0>(); + const ElementIterator elemEndIt = gridView_().template end<0>(); + for (; elemIt != elemEndIt; ++elemIt) { + localResidual().eval(*elemIt); + + for (int i = 0; i < elemIt->template count<dim>(); ++i) { + int globalI = vertexMapper().map(*elemIt, i, dim); + residual[globalI] += localResidual().residual(i); + } + } + + // calculate the square norm of the residual + Scalar result2 = residual.two_norm2(); + if (gridView_().comm().size() > 1) + result2 = gridView_().comm().sum(result2); + + // add up the residuals on the process borders + if (gridView_().comm().size() > 1) { + VertexHandleSum<PrimaryVariables, SolutionVector, VertexMapper> + sumHandle(residual, vertexMapper()); + gridView_().communicate(sumHandle, + Dune::InteriorBorder_InteriorBorder_Interface, + Dune::ForwardCommunication); + } + + return std::sqrt(result2); + } + + /*! + * \brief Compute the integral over the domain of the storage + * terms of all conservation quantities. + * + * \param storage Stores the result + */ + void globalStorage(PrimaryVariables &storage) + { + storage = 0; + + ElementIterator elemIt = gridView_().template begin<0>(); + const ElementIterator elemEndIt = gridView_().template end<0>(); + for (; elemIt != elemEndIt; ++elemIt) { + localResidual().evalStorage(*elemIt); + + for (int i = 0; i < elemIt->template count<dim>(); ++i) + storage += localResidual().storageTerm()[i]; + } + + if (gridView_().comm().size() > 1) + storage = gridView_().comm().sum(storage); + } + + /*! + * \brief Returns the volume \f$\mathrm{[m^3]}\f$ of a given control volume. + * + * \param globalIdx The global index of the control volume's + * associated vertex + */ + Scalar boxVolume(const int globalIdx) const + { return boxVolume_[globalIdx][0]; } + + /*! + * \brief Reference to the current solution as a block vector. + */ + const SolutionVector &curSol() const + { return uCur_; } + + /*! + * \brief Reference to the current solution as a block vector. + */ + SolutionVector &curSol() + { return uCur_; } + + /*! + * \brief Reference to the previous solution as a block vector. + */ + const SolutionVector &prevSol() const + { return uPrev_; } + + /*! + * \brief Reference to the previous solution as a block vector. + */ + SolutionVector &prevSol() + { return uPrev_; } + + /*! + * \brief Returns the operator assembler for the global jacobian of + * the problem. + */ + JacobianAssembler &jacobianAssembler() + { return *jacAsm_; } + + /*! + * \copydoc jacobianAssembler() + */ + const JacobianAssembler &jacobianAssembler() const + { return *jacAsm_; } + + /*! + * \brief Returns the local jacobian which calculates the local + * stiffness matrix for an arbitrary element. + * + * The local stiffness matrices of the element are used by + * the jacobian assembler to produce a global linerization of the + * problem. + */ + LocalJacobian &localJacobian() + { return localJacobian_; } + /*! + * \copydoc localJacobian() + */ + const LocalJacobian &localJacobian() const + { return localJacobian_; } + + /*! + * \brief Returns the local residual function. + */ + LocalResidual &localResidual() + { return localJacobian().localResidual(); } + /*! + * \copydoc localResidual() + */ + const LocalResidual &localResidual() const + { return localJacobian().localResidual(); } + + /*! + * \brief Returns the relative error between two vectors of + * primary variables. + * + * \param vertexIdx The global index of the control volume's + * associated vertex + * \param priVars1 The first vector of primary variables + * \param priVars2 The second vector of primary variables + * + * \todo The vertexIdx argument is pretty hacky. it is required by + * models with pseudo primary variables (i.e. the primary + * variable switching models). the clean solution would be + * to access the pseudo primary variables from the primary + * variables. + */ + Scalar relativeErrorVertex(const int vertexIdx, + const PrimaryVariables &priVars1, + const PrimaryVariables &priVars2) + { + Scalar result = 0.0; + for (int j = 0; j < numEq; ++j) { + Scalar eqErr = std::abs(priVars1[j] - priVars2[j]); + eqErr /= std::max<Scalar>(1.0, std::abs(priVars1[j] + priVars2[j])/2); + + result = std::max(result, eqErr); + } + return result; + } + + /*! + * \brief Try to progress the model to the next timestep. + * + * \param solver The non-linear solver + * \param controller The controller which specifies the behaviour + * of the non-linear solver + */ + bool update(NewtonMethod &solver, + NewtonController &controller) + { +#if HAVE_VALGRIND + for (size_t i = 0; i < curSol().size(); ++i) + Valgrind::CheckDefined(curSol()[i]); +#endif // HAVE_VALGRIND + + asImp_().updateBegin(); + + bool converged = solver.execute(controller); + if (converged) { + asImp_().updateSuccessful(); + } + else + asImp_().updateFailed(); + +#if HAVE_VALGRIND + for (size_t i = 0; i < curSol().size(); ++i) { + Valgrind::CheckDefined(curSol()[i]); + } +#endif // HAVE_VALGRIND + + return converged; + } + + + /*! + * \brief Called by the update() method before it tries to + * apply the newton method. This is primary a hook + * which the actual model can overload. + */ + void updateBegin() + { } + + + /*! + * \brief Called by the update() method if it was + * successful. This is primary a hook which the actual + * model can overload. + */ + void updateSuccessful() + { } + + /*! + * \brief Called by the update() method if it was + * unsuccessful. This is primary a hook which the actual + * model can overload. + */ + void updateFailed() + { + // Reset the current solution to the one of the + // previous time step so that we can start the next + // update at a physically meaningful solution. + uCur_ = uPrev_; + curHints_ = prevHints_; + + jacAsm_->reassembleAll(); + } + + /*! + * \brief Called by the problem if a time integration was + * successful, post processing of the solution is done and + * the result has been written to disk. + * + * This should prepare the model for the next time integration. + */ + void advanceTimeLevel() + { + // make the current solution the previous one. + uPrev_ = uCur_; + prevHints_ = curHints_; + + updatePrevHints(); + } + + /*! + * \brief Serializes the current state of the model. + * + * \tparam Restarter The type of the serializer class + * + * \param res The serializer object + */ + template <class Restarter> + void serialize(Restarter &res) + { res.template serializeEntities<dim>(asImp_(), this->gridView_()); } + + /*! + * \brief Deserializes the state of the model. + * + * \tparam Restarter The type of the serializer class + * + * \param res The serializer object + */ + template <class Restarter> + void deserialize(Restarter &res) + { + res.template deserializeEntities<dim>(asImp_(), this->gridView_()); + prevSol() = curSol(); + } + + /*! + * \brief Write the current solution for a vertex to a restart + * file. + * + * \param outstream The stream into which the vertex data should + * be serialized to + * \param vertex The DUNE Codim<dim> entity which's data should be + * serialized + */ + void serializeEntity(std::ostream &outstream, + const Vertex &vertex) + { + int vertIdx = dofMapper().map(vertex); + + // write phase state + if (!outstream.good()) { + DUNE_THROW(Dune::IOError, + "Could not serialize vertex " + << vertIdx); + } + + for (int eqIdx = 0; eqIdx < numEq; ++eqIdx) { + outstream << curSol()[vertIdx][eqIdx] << " "; + } + } + + /*! + * \brief Reads the current solution variables for a vertex from a + * restart file. + * + * \param instream The stream from which the vertex data should + * be deserialized from + * \param vertex The DUNE Codim<dim> entity which's data should be + * deserialized + */ + void deserializeEntity(std::istream &instream, + const Vertex &vertex) + { + int vertIdx = dofMapper().map(vertex); + for (int eqIdx = 0; eqIdx < numEq; ++eqIdx) { + if (!instream.good()) + DUNE_THROW(Dune::IOError, + "Could not deserialize vertex " + << vertIdx); + instream >> curSol()[vertIdx][eqIdx]; + } + } + + /*! + * \brief Returns the number of global degrees of freedoms (DOFs) + */ + size_t numDofs() const + { return gridView_().size(dim); } + + /*! + * \brief Mapper for the entities where degrees of freedoms are + * defined to indices. + * + * This usually means a mapper for vertices. + */ + const DofMapper &dofMapper() const + { return problem_().vertexMapper(); } + + /*! + * \brief Mapper for vertices to indices. + */ + const VertexMapper &vertexMapper() const + { return problem_().vertexMapper(); } + + /*! + * \brief Mapper for elements to indices. + */ + const ElementMapper &elementMapper() const + { return problem_().elementMapper(); } + + /*! + * \brief Resets the Jacobian matrix assembler, so that the + * boundary types can be altered. + */ + void resetJacobianAssembler () + { + delete jacAsm_; + jacAsm_ = new JacobianAssembler; + jacAsm_->init(problem_()); + } + + /*! + * \brief Update the weights of all primary variables within an + * element given the complete set of volume variables + * + * \param element The DUNE codim 0 entity + * \param volVars All volume variables for the element + */ + void updatePVWeights(const Element &element, + const ElementVolumeVariables &volVars) const + { } + + /*! + * \brief Add the vector fields for analysing the convergence of + * the newton method to the a VTK multi writer. + * + * \tparam MultiWriter The type of the VTK multi writer + * + * \param writer The VTK multi writer object on which the fields should be added. + * \param u The solution function + * \param deltaU The delta of the solution function before and after the Newton update + */ + template <class MultiWriter> + void addConvergenceVtkFields(MultiWriter &writer, + const SolutionVector &u, + const SolutionVector &deltaU) + { + typedef Dune::BlockVector<Dune::FieldVector<double, 1> > ScalarField; + + SolutionVector residual(u); + asImp_().globalResidual(residual, u); + + // create the required scalar fields + unsigned numVertices = this->gridView_().size(dim); + + // global defect of the two auxiliary equations + ScalarField* def[numEq]; + ScalarField* delta[numEq]; + ScalarField* x[numEq]; + for (int eqIdx = 0; eqIdx < numEq; ++eqIdx) { + x[eqIdx] = writer.allocateManagedBuffer(numVertices); + delta[eqIdx] = writer.allocateManagedBuffer(numVertices); + def[eqIdx] = writer.allocateManagedBuffer(numVertices); + } + + VertexIterator vIt = this->gridView_().template begin<dim>(); + VertexIterator vEndIt = this->gridView_().template end<dim>(); + for (; vIt != vEndIt; ++ vIt) + { + int globalIdx = vertexMapper().map(*vIt); + for (int eqIdx = 0; eqIdx < numEq; ++eqIdx) { + (*x[eqIdx])[globalIdx] = u[globalIdx][eqIdx]; + (*delta[eqIdx])[globalIdx] = - deltaU[globalIdx][eqIdx]; + (*def[eqIdx])[globalIdx] = residual[globalIdx][eqIdx]; + } + } + + for (int eqIdx = 0; eqIdx < numEq; ++eqIdx) { + std::ostringstream oss; + oss.str(""); oss << "x_" << eqIdx; + writer.attachVertexData(*x[eqIdx], oss.str()); + oss.str(""); oss << "delta_" << eqIdx; + writer.attachVertexData(*delta[eqIdx], oss.str()); + oss.str(""); oss << "defect_" << eqIdx; + writer.attachVertexData(*def[eqIdx], oss.str()); + } + + asImp_().addOutputVtkFields(u, writer); + } + + /*! + * \brief Add the quantities of a time step which ought to be written to disk. + * + * This should be overwritten by the actual model if any secondary + * variables should be written out. Read: This should _always_ be + * overwritten by well behaved models! + * + * \tparam MultiWriter The type of the VTK multi writer + * + * \param sol The global vector of primary variable values. + * \param writer The VTK multi writer where the fields should be added. + */ + template <class MultiWriter> + void addOutputVtkFields(const SolutionVector &sol, + MultiWriter &writer) + { + typedef Dune::BlockVector<Dune::FieldVector<Scalar, 1> > ScalarField; + + // create the required scalar fields + unsigned numVertices = this->gridView_().size(dim); + + // global defect of the two auxiliary equations + ScalarField* x[numEq]; + for (int eqIdx = 0; eqIdx < numEq; ++eqIdx) { + x[eqIdx] = writer.allocateManagedBuffer(numVertices); + } + + VertexIterator vIt = this->gridView_().template begin<dim>(); + VertexIterator vEndIt = this->gridView_().template end<dim>(); + for (; vIt != vEndIt; ++ vIt) + { + int globalIdx = vertexMapper().map(*vIt); + for (int eqIdx = 0; eqIdx < numEq; ++eqIdx) { + (*x[eqIdx])[globalIdx] = sol[globalIdx][eqIdx]; + } + } + + for (int eqIdx = 0; eqIdx < numEq; ++eqIdx) { + std::ostringstream oss; + oss << "primaryVar_" << eqIdx; + writer.attachVertexData(*x[eqIdx], oss.str()); + } + } + + /*! + * \brief Reference to the grid view of the spatial domain. + */ + const GridView &gridView() const + { return problem_().gridView(); } + + /*! + * \brief Returns true if the vertex with 'globalVertIdx' is + * located on the grid's boundary. + * + * \param globalVertIdx The global index of the control volume's + * associated vertex + */ + bool onBoundary(const int globalVertIdx) const + { return boundaryIndices_[globalVertIdx]; } + + /*! + * \brief Returns true if a vertex is located on the grid's + * boundary. + * + * \param element A DUNE Codim<0> entity which contains the control + * volume's associated vertex. + * \param vIdx The local vertex index inside element + */ + bool onBoundary(const Element &element, const int vIdx) const + { return onBoundary(vertexMapper().map(element, vIdx, dim)); } + + /*! + * \brief Fill the fluid state according to the primary variables. + * + * Taking the information from the primary variables, + * the fluid state is filled with every information that is + * necessary to evaluate the model's local residual. + * + * \param priVars The primary variables of the model. + * \param problem The problem at hand. + * \param element The current element. + * \param fvGeometry The finite volume element geometry. + * \param scvIdx The index of the subcontrol volume. + * \param fluidState The fluid state to fill. + */ + template <class FluidState> + static void completeFluidState(const PrimaryVariables& priVars, + const Problem& problem, + const Element& element, + const FVElementGeometry& fvGeometry, + const int scvIdx, + FluidState& fluidState) + { + VolumeVariables::completeFluidState(priVars, problem, element, + fvGeometry, scvIdx, fluidState); + } +protected: + /*! + * \brief A reference to the problem on which the model is applied. + */ + Problem &problem_() + { return *problemPtr_; } + /*! + * \copydoc problem_() + */ + const Problem &problem_() const + { return *problemPtr_; } + + /*! + * \brief Reference to the grid view of the spatial domain. + */ + const GridView &gridView_() const + { return problem_().gridView(); } + + /*! + * \brief Reference to the local residal object + */ + LocalResidual &localResidual_() + { return localJacobian_.localResidual(); } + + /*! + * \brief Applies the initial solution for all vertices of the grid. + */ + void applyInitialSolution_() + { + // first set the whole domain to zero + uCur_ = Scalar(0.0); + boxVolume_ = Scalar(0.0); + + FVElementGeometry fvGeometry; + + // iterate through leaf grid and evaluate initial + // condition at the center of each sub control volume + // + // TODO: the initial condition needs to be unique for + // each vertex. we should think about the API... + ElementIterator eIt = gridView_().template begin<0>(); + const ElementIterator &eEndIt = gridView_().template end<0>(); + for (; eIt != eEndIt; ++eIt) { + // deal with the current element + fvGeometry.update(gridView_(), *eIt); + + // loop over all element vertices, i.e. sub control volumes + for (int scvIdx = 0; scvIdx < fvGeometry.numVertices; scvIdx++) + { + // map the local vertex index to the global one + int globalIdx = vertexMapper().map(*eIt, + scvIdx, + dim); + + // let the problem do the dirty work of nailing down + // the initial solution. + PrimaryVariables initPriVars; + Valgrind::SetUndefined(initPriVars); + problem_().initial(initPriVars, + *eIt, + fvGeometry, + scvIdx); + Valgrind::CheckDefined(initPriVars); + + // add up the initial values of all sub-control + // volumes. If the initial values disagree for + // different sub control volumes, the initial value + // will be the arithmetic mean. + initPriVars *= fvGeometry.subContVol[scvIdx].volume; + boxVolume_[globalIdx] += fvGeometry.subContVol[scvIdx].volume; + uCur_[globalIdx] += initPriVars; + Valgrind::CheckDefined(uCur_[globalIdx]); + } + } + + // add up the primary variables and the volumes of the boxes + // which cross process borders + if (gridView_().comm().size() > 1) { + VertexHandleSum<Dune::FieldVector<Scalar, 1>, + Dune::BlockVector<Dune::FieldVector<Scalar, 1> >, + VertexMapper> sumVolumeHandle(boxVolume_, vertexMapper()); + gridView_().communicate(sumVolumeHandle, + Dune::InteriorBorder_InteriorBorder_Interface, + Dune::ForwardCommunication); + + VertexHandleSum<PrimaryVariables, SolutionVector, VertexMapper> + sumPVHandle(uCur_, vertexMapper()); + gridView_().communicate(sumPVHandle, + Dune::InteriorBorder_InteriorBorder_Interface, + Dune::ForwardCommunication); + } + + // divide all primary variables by the volume of their boxes + int n = gridView_().size(dim); + for (int i = 0; i < n; ++i) { + uCur_[i] /= boxVolume(i); + } + } + + /*! + * \brief Find all indices of boundary vertices. + * + * For this we need to loop over all intersections (which is slow + * in general). If the DUNE grid interface would provide a + * onBoundary() method for entities this could be done in a much + * nicer way (actually this would not be necessary) + */ + void updateBoundaryIndices_() + { + boundaryIndices_.resize(numDofs()); + std::fill(boundaryIndices_.begin(), boundaryIndices_.end(), false); + + ElementIterator eIt = gridView_().template begin<0>(); + ElementIterator eEndIt = gridView_().template end<0>(); + for (; eIt != eEndIt; ++eIt) { + Dune::GeometryType geoType = eIt->geometry().type(); + const ReferenceElement &refElement = ReferenceElements::general(geoType); + + IntersectionIterator isIt = gridView_().ibegin(*eIt); + IntersectionIterator isEndIt = gridView_().iend(*eIt); + for (; isIt != isEndIt; ++isIt) { + if (isIt->boundary()) { + // add all vertices on the intersection to the set of + // boundary vertices + int faceIdx = isIt->indexInInside(); + int numFaceVerts = refElement.size(faceIdx, 1, dim); + for (int faceVertIdx = 0; + faceVertIdx < numFaceVerts; + ++faceVertIdx) + { + int elemVertIdx = refElement.subEntity(faceIdx, + 1, + faceVertIdx, + dim); + int globalVertIdx = vertexMapper().map(*eIt, elemVertIdx, dim); + boundaryIndices_[globalVertIdx] = true; + } + } + } + } + } + + // the hint cache for the previous and the current volume + // variables + mutable std::vector<bool> hintsUsable_; + mutable std::vector<VolumeVariables> curHints_; + mutable std::vector<VolumeVariables> prevHints_; + + // the problem we want to solve. defines the constitutive + // relations, matxerial laws, etc. + Problem *problemPtr_; + + // calculates the local jacobian matrix for a given element + LocalJacobian localJacobian_; + // Linearizes the problem at the current time step using the + // local jacobian + JacobianAssembler *jacAsm_; + + // the set of all indices of vertices on the boundary + std::vector<bool> boundaryIndices_; + + // cur is the current iterative solution, prev the converged + // solution of the previous time step + SolutionVector uCur_; + SolutionVector uPrev_; + + Dune::BlockVector<Dune::FieldVector<Scalar, 1> > boxVolume_; + +private: + /*! + * \brief Returns whether messages should be printed + */ + bool verbose_() const + { return gridView_().comm().rank() == 0; } + + Implementation &asImp_() + { return *static_cast<Implementation*>(this); } + const Implementation &asImp_() const + { return *static_cast<const Implementation*>(this); } + + bool enableHints_; +}; +} + +#endif diff --git a/dumux/implicit/common/implicitproblem.hh b/dumux/implicit/common/implicitproblem.hh new file mode 100644 index 0000000000..c455443807 --- /dev/null +++ b/dumux/implicit/common/implicitproblem.hh @@ -0,0 +1,835 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \file + * \brief Base class for all problems which use the box scheme + */ +#ifndef DUMUX_BOX_PROBLEM_HH +#define DUMUX_BOX_PROBLEM_HH + +#include "boxproperties.hh" + +#include <dumux/io/vtkmultiwriter.hh> +#include <dumux/io/restart.hh> + +namespace Dumux +{ +/*! + * \ingroup BoxModel + * \ingroup BoxBaseProblems + * \brief Base class for all problems which use the box scheme. + * + * \note All quantities are specified assuming a threedimensional + * world. Problems discretized using 2D grids are assumed to be + * extruded by \f$1 m\f$ and 1D grids are assumed to have a + * cross section of \f$1m \times 1m\f$. + */ +template<class TypeTag> +class BoxProblem +{ +private: + typedef typename GET_PROP_TYPE(TypeTag, Problem) Implementation; + typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; + + typedef Dumux::VtkMultiWriter<GridView> VtkMultiWriter; + + typedef typename GET_PROP_TYPE(TypeTag, NewtonMethod) NewtonMethod; + typedef typename GET_PROP_TYPE(TypeTag, NewtonController) NewtonController; + + typedef typename GET_PROP_TYPE(TypeTag, Model) Model; + typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; + typedef typename GET_PROP_TYPE(TypeTag, TimeManager) TimeManager; + + typedef typename GET_PROP_TYPE(TypeTag, VertexMapper) VertexMapper; + typedef typename GET_PROP_TYPE(TypeTag, ElementMapper) ElementMapper; + + typedef typename GET_PROP_TYPE(TypeTag, PrimaryVariables) PrimaryVariables; + typedef typename GET_PROP_TYPE(TypeTag, ElementVolumeVariables) ElementVolumeVariables; + typedef typename GET_PROP_TYPE(TypeTag, FVElementGeometry) FVElementGeometry; + typedef typename GET_PROP_TYPE(TypeTag, BoundaryTypes) BoundaryTypes; + + enum { + dim = GridView::dimension, + dimWorld = GridView::dimensionworld + }; + + typedef typename GridView::template Codim<0>::Entity Element; + typedef typename GridView::template Codim<dim>::Entity Vertex; + typedef typename GridView::template Codim<dim>::Iterator VertexIterator; + typedef typename GridView::Intersection Intersection; + + typedef typename GridView::Grid::ctype CoordScalar; + typedef Dune::FieldVector<CoordScalar, dimWorld> GlobalPosition; + + // copying a problem is not a good idea + BoxProblem(const BoxProblem &); + +public: + /*! + * \brief Constructor + * + * \param timeManager The TimeManager which is used by the simulation + * \param gridView The simulation's idea about physical space + */ + BoxProblem(TimeManager &timeManager, const GridView &gridView) + : gridView_(gridView) + , bboxMin_(std::numeric_limits<double>::max()) + , bboxMax_(-std::numeric_limits<double>::max()) + , elementMapper_(gridView) + , vertexMapper_(gridView) + , timeManager_(&timeManager) + , newtonMethod_(asImp_()) + , newtonCtl_(asImp_()) + { + // calculate the bounding box of the local partition of the grid view + VertexIterator vIt = gridView.template begin<dim>(); + const VertexIterator vEndIt = gridView.template end<dim>(); + for (; vIt!=vEndIt; ++vIt) { + for (int i=0; i<dim; i++) { + bboxMin_[i] = std::min(bboxMin_[i], vIt->geometry().corner(0)[i]); + bboxMax_[i] = std::max(bboxMax_[i], vIt->geometry().corner(0)[i]); + } + } + + // communicate to get the bounding box of the whole domain + if (gridView.comm().size() > 1) + for (int i = 0; i < dim; ++i) { + bboxMin_[i] = gridView.comm().min(bboxMin_[i]); + bboxMax_[i] = gridView.comm().max(bboxMax_[i]); + } + + // set a default name for the problem + simName_ = "sim"; + + resultWriter_ = NULL; + } + + ~BoxProblem() + { + delete resultWriter_; + }; + + + /*! + * \brief Called by the Dumux::TimeManager in order to + * initialize the problem. + * + * If you overload this method don't forget to call + * ParentType::init() + */ + void init() + { + // set the initial condition of the model + model().init(asImp_()); + } + + /*! + * \brief Specifies which kind of boundary condition should be + * used for which equation on a given boundary segment. + * + * \param values The boundary types for the conservation equations + * \param vertex The vertex for which the boundary type is set + */ + void boundaryTypes(BoundaryTypes &values, + const Vertex &vertex) const + { + // forward it to the method which only takes the global coordinate + asImp_().boundaryTypesAtPos(values, vertex.geometry().center()); + } + + /*! + * \brief Specifies which kind of boundary condition should be + * used for which equation on a given boundary segment. + * + * \param values The boundary types for the conservation equations + * \param pos The position of the finite volume in global coordinates + */ + void boundaryTypesAtPos(BoundaryTypes &values, + const GlobalPosition &pos) const + { + // Throw an exception (there is no reasonable default value + // for Dirichlet conditions) + DUNE_THROW(Dune::InvalidStateException, + "The problem does not provide " + "a boundaryTypes() method."); + } + + + /*! + * \brief Evaluate the boundary conditions for a dirichlet + * control volume. + * + * \param values The dirichlet values for the primary variables + * \param vertex The vertex representing the "half volume on the boundary" + * + * For this method, the \a values parameter stores primary variables. + */ + void dirichlet(PrimaryVariables &values, + const Vertex &vertex) const + { + // forward it to the method which only takes the global coordinate + asImp_().dirichletAtPos(values, vertex.geometry().center()); + } + + /*! + * \brief Evaluate the boundary conditions for a dirichlet + * control volume. + * + * \param values The dirichlet values for the primary variables + * \param pos The position of the center of the finite volume + * for which the dirichlet condition ought to be + * set in global coordinates + * + * For this method, the \a values parameter stores primary variables. + */ + void dirichletAtPos(PrimaryVariables &values, + const GlobalPosition &pos) const + { + // Throw an exception (there is no reasonable default value + // for Dirichlet conditions) + DUNE_THROW(Dune::InvalidStateException, + "The problem specifies that some boundary " + "segments are dirichlet, but does not provide " + "a dirichlet() method."); + } + + /*! + * \brief Evaluate the boundary conditions for a neumann + * boundary segment. + * + * This is the method for the case where the Neumann condition is + * potentially solution dependent and requires some box method + * specific things. + * + * \param values The neumann values for the conservation equations [kg / (m^2 *s )] + * \param element The finite element + * \param fvGeometry The finite-volume geometry in the box scheme + * \param is The intersection between element and boundary + * \param scvIdx The local vertex index + * \param boundaryFaceIdx The index of the boundary face + * \param elemVolVars All volume variables for the element + * + * For this method, the \a values parameter stores the mass flux + * in normal direction of each phase. Negative values mean influx. + */ + void boxSDNeumann(PrimaryVariables &values, + const Element &element, + const FVElementGeometry &fvGeometry, + const Intersection &is, + const int scvIdx, + const int boundaryFaceIdx, + const ElementVolumeVariables &elemVolVars) const + { + // forward it to the interface without the volume variables + asImp_().neumann(values, + element, + fvGeometry, + is, + scvIdx, + boundaryFaceIdx); + } + + /*! + * \brief Evaluate the boundary conditions for a neumann + * boundary segment. + * + * \param values The neumann values for the conservation equations [kg / (m^2 *s )] + * \param element The finite element + * \param fvGeometry The finite-volume geometry in the box scheme + * \param is The intersection between element and boundary + * \param scvIdx The local vertex index + * \param boundaryFaceIdx The index of the boundary face + * + * For this method, the \a values parameter stores the mass flux + * in normal direction of each phase. Negative values mean influx. + */ + void neumann(PrimaryVariables &values, + const Element &element, + const FVElementGeometry &fvGeometry, + const Intersection &is, + const int scvIdx, + const int boundaryFaceIdx) const + { + // forward it to the interface with only the global position + asImp_().neumannAtPos(values, fvGeometry.boundaryFace[boundaryFaceIdx].ipGlobal); + } + + /*! + * \brief Evaluate the boundary conditions for a neumann + * boundary segment. + * + * \param values The neumann values for the conservation equations [kg / (m^2 *s )] + * \param pos The position of the boundary face's integration point in global coordinates + * + * For this method, the \a values parameter stores the mass flux + * in normal direction of each phase. Negative values mean influx. + */ + void neumannAtPos(PrimaryVariables &values, + const GlobalPosition &pos) const + { + // Throw an exception (there is no reasonable default value + // for Neumann conditions) + DUNE_THROW(Dune::InvalidStateException, + "The problem specifies that some boundary " + "segments are neumann, but does not provide " + "a neumannAtPos() method."); + } + + /*! + * \brief Evaluate the source term for all phases within a given + * sub-control-volume. + * + * This is the method for the case where the source term is + * potentially solution dependent and requires some box method + * specific things. + * + * \param values The source and sink values for the conservation equations + * \param element The finite element + * \param fvGeometry The finite-volume geometry in the box scheme + * \param scvIdx The local vertex index + * \param elemVolVars All volume variables for the element + * + * For this method, the \a values parameter stores the rate mass + * generated or annihilate per volume unit. Positive values mean + * that mass is created, negative ones mean that it vanishes. + */ + void boxSDSource(PrimaryVariables &values, + const Element &element, + const FVElementGeometry &fvGeometry, + const int scvIdx, + const ElementVolumeVariables &elemVolVars) const + { + // forward to solution independent, box specific interface + asImp_().source(values, element, fvGeometry, scvIdx); + } + + /*! + * \brief Evaluate the source term for all phases within a given + * sub-control-volume. + * + * \param values The source and sink values for the conservation equations + * \param element The finite element + * \param fvGeometry The finite-volume geometry in the box scheme + * \param scvIdx The local vertex index + * + * For this method, the \a values parameter stores the rate mass + * generated or annihilate per volume unit. Positive values mean + * that mass is created, negative ones mean that it vanishes. + */ + void source(PrimaryVariables &values, + const Element &element, + const FVElementGeometry &fvGeometry, + const int scvIdx) const + { + // forward to generic interface + asImp_().sourceAtPos(values, fvGeometry.subContVol[scvIdx].global); + } + + /*! + * \brief Evaluate the source term for all phases within a given + * sub-control-volume. + * + * \param values The source and sink values for the conservation equations + * \param pos The position of the center of the finite volume + * for which the source term ought to be + * specified in global coordinates + * + * For this method, the \a values parameter stores the rate mass + * generated or annihilate per volume unit. Positive values mean + * that mass is created, negative ones mean that it vanishes. + */ + void sourceAtPos(PrimaryVariables &values, + const GlobalPosition &pos) const + { + DUNE_THROW(Dune::InvalidStateException, + "The problem does not provide " + "a sourceAtPos() method."); + } + + /*! + * \brief Evaluate the initial value for a control volume. + * + * \param values The initial values for the primary variables + * \param element The finite element + * \param fvGeometry The finite-volume geometry in the box scheme + * \param scvIdx The local vertex index + * + * For this method, the \a values parameter stores primary + * variables. + */ + void initial(PrimaryVariables &values, + const Element &element, + const FVElementGeometry &fvGeometry, + const int scvIdx) const + { + // forward to generic interface + asImp_().initialAtPos(values, fvGeometry.subContVol[scvIdx].global); + } + + /*! + * \brief Evaluate the initial value for a control volume. + * + * \param values The dirichlet values for the primary variables + * \param pos The position of the center of the finite volume + * for which the initial values ought to be + * set (in global coordinates) + * + * For this method, the \a values parameter stores primary variables. + */ + void initialAtPos(PrimaryVariables &values, + const GlobalPosition &pos) const + { + // Throw an exception (there is no reasonable default value + // for Dirichlet conditions) + DUNE_THROW(Dune::InvalidStateException, + "The problem does not provide " + "a initialAtPos() method."); + } + + /*! + * \brief Return how much the domain is extruded at a given sub-control volume. + * + * This means the factor by which a lower-dimensional (1D or 2D) + * entity needs to be expanded to get a full dimensional cell. The + * default is 1.0 which means that 1D problems are actually + * thought as pipes with a cross section of 1 m^2 and 2D problems + * are assumed to extend 1 m to the back. + */ + Scalar boxExtrusionFactor(const Element &element, + const FVElementGeometry &fvGeometry, + const int scvIdx) const + { + // forward to generic interface + return asImp_().extrusionFactorAtPos(fvGeometry.subContVol[scvIdx].global); + } + + /*! + * \brief Return how much the domain is extruded at a given position. + * + * This means the factor by which a lower-dimensional (1D or 2D) + * entity needs to be expanded to get a full dimensional cell. The + * default is 1.0 which means that 1D problems are actually + * thought as pipes with a cross section of 1 m^2 and 2D problems + * are assumed to extend 1 m to the back. + */ + Scalar extrusionFactorAtPos(const GlobalPosition &pos) const + { return 1.0; } + + /*! + * \brief If model coupling is used, this updates the parameters + * required to calculate the coupling fluxes between the + * sub-models. + * + * By default it does nothing + * + * \param element The DUNE Codim<0> entity for which the coupling + * parameters should be computed. + */ + void updateCouplingParams(const Element &element) const + {} + + /*! + * \name Simulation steering + */ + // \{ + + /*! + * \brief Called by the time manager before the time integration. + */ + void preTimeStep() + {} + + /*! + * \brief Called by Dumux::TimeManager in order to do a time + * integration on the model. + */ + void timeIntegration() + { + const int maxFails = 10; + for (int i = 0; i < maxFails; ++i) { + if (model_.update(newtonMethod_, newtonCtl_)) + return; + + Scalar dt = timeManager().timeStepSize(); + Scalar nextDt = dt / 2; + timeManager().setTimeStepSize(nextDt); + + // update failed + std::cout << "Newton solver did not converge with dt="<<dt<<" seconds. Retrying with time step of " + << nextDt << " seconds\n"; + } + + DUNE_THROW(Dune::MathError, + "Newton solver didn't converge after " + << maxFails + << " time-step divisions. dt=" + << timeManager().timeStepSize()); + } + + /*! + * \brief Returns the newton method object + */ + NewtonMethod &newtonMethod() + { return newtonMethod_; } + + /*! + * \copydoc newtonMethod() + */ + const NewtonMethod &newtonMethod() const + { return newtonMethod_; } + + /*! + * \brief Returns the newton contoller object + */ + NewtonController &newtonController() + { return newtonCtl_; } + + /*! + * \copydoc newtonController() + */ + const NewtonController &newtonController() const + { return newtonCtl_; } + + /*! + * \brief Called by Dumux::TimeManager whenever a solution for a + * time step has been computed and the simulation time has + * been updated. + * + * \param dt The current time-step size + */ + Scalar nextTimeStepSize(const Scalar dt) + { + return std::min(GET_PARAM_FROM_GROUP(TypeTag, Scalar, TimeManager, MaxTimeStepSize), + newtonCtl_.suggestTimeStepSize(dt)); + }; + + /*! + * \brief Returns true if a restart file should be written to + * disk. + * + * The default behavior is to write one restart file every 5 time + * steps. This file is intended to be overwritten by the + * implementation. + */ + bool shouldWriteRestartFile() const + { + return timeManager().timeStepIndex() > 0 && + (timeManager().timeStepIndex() % 10 == 0); + } + + /*! + * \brief Returns true if the current solution should be written to + * disk (i.e. as a VTK file) + * + * The default behavior is to write out every the solution for + * very time step. This file is intended to be overwritten by the + * implementation. + */ + bool shouldWriteOutput() const + { return true; } + + /*! + * \brief Called by the time manager after the time integration to + * do some post processing on the solution. + */ + void postTimeStep() + { } + + /*! + * \brief Called by the time manager after everything which can be + * done about the current time step is finished and the + * model should be prepared to do the next time integration. + */ + void advanceTimeLevel() + { + model_.advanceTimeLevel(); + } + + /*! + * \brief Called when the end of an simulation episode is reached. + * + * Typically a new episode should be started in this method. + */ + void episodeEnd() + { + std::cerr << "The end of an episode is reached, but the problem " + << "does not override the episodeEnd() method. " + << "Doing nothing!\n"; + }; + // \} + + /*! + * \brief The problem name. + * + * This is used as a prefix for files generated by the simulation. + * It could be either overwritten by the problem files, or simply + * declared over the setName() function in the application file. + */ + const char *name() const + { + return simName_.c_str(); + } + + /*! + * \brief Set the problem name. + * + * This static method sets the simulation name, which should be + * called before the application problem is declared! If not, the + * default name "sim" will be used. + * + * \param newName The problem's name + */ + void setName(const char *newName) + { + simName_ = newName; + } + + + /*! + * \brief Returns the number of the current VTK file. + */ + int currentVTKFileNumber() + { + createResultWriter_(); + return resultWriter_->curWriterNum(); + } + + /*! + * \brief The GridView which used by the problem. + */ + const GridView &gridView() const + { return gridView_; } + + /*! + * \brief The coordinate of the corner of the GridView's bounding + * box with the smallest values. + */ + const GlobalPosition &bboxMin() const + { return bboxMin_; } + + /*! + * \brief The coordinate of the corner of the GridView's bounding + * box with the largest values. + */ + const GlobalPosition &bboxMax() const + { return bboxMax_; } + + /*! + * \brief Returns the mapper for vertices to indices. + */ + const VertexMapper &vertexMapper() const + { return vertexMapper_; } + + /*! + * \brief Returns the mapper for elements to indices. + */ + const ElementMapper &elementMapper() const + { return elementMapper_; } + + /*! + * \brief Returns TimeManager object used by the simulation + */ + TimeManager &timeManager() + { return *timeManager_; } + + /*! + * \copydoc timeManager() + */ + const TimeManager &timeManager() const + { return *timeManager_; } + + /*! + * \brief Returns numerical model used for the problem. + */ + Model &model() + { return model_; } + + /*! + * \copydoc model() + */ + const Model &model() const + { return model_; } + // \} + + /*! + * \name Restart mechanism + */ + // \{ + + /*! + * \brief This method writes the complete state of the simulation + * to the harddisk. + * + * The file will start with the prefix returned by the name() + * method, has the current time of the simulation clock in it's + * name and uses the extension <tt>.drs</tt>. (Dumux ReStart + * file.) See Dumux::Restart for details. + */ + void serialize() + { + typedef Dumux::Restart Restarter; + Restarter res; + res.serializeBegin(asImp_()); + if (gridView().comm().rank() == 0) + std::cout << "Serialize to file '" << res.fileName() << "'\n"; + + timeManager().serialize(res); + asImp_().serialize(res); + res.serializeEnd(); + } + + /*! + * \brief This method writes the complete state of the problem + * to the harddisk. + * + * The file will start with the prefix returned by the name() + * method, has the current time of the simulation clock in it's + * name and uses the extension <tt>.drs</tt>. (Dumux ReStart + * file.) See Dumux::Restart for details. + * + * \tparam Restarter The serializer type + * + * \param res The serializer object + */ + template <class Restarter> + void serialize(Restarter &res) + { + createResultWriter_(); + resultWriter_->serialize(res); + model().serialize(res); + } + + /*! + * \brief Load a previously saved state of the whole simulation + * from disk. + * + * \param tRestart The simulation time on which the program was + * written to disk. + */ + void restart(const Scalar tRestart) + { + typedef Dumux::Restart Restarter; + + Restarter res; + + res.deserializeBegin(asImp_(), tRestart); + if (gridView().comm().rank() == 0) + std::cout << "Deserialize from file '" << res.fileName() << "'\n"; + timeManager().deserialize(res); + asImp_().deserialize(res); + res.deserializeEnd(); + } + + /*! + * \brief This method restores the complete state of the problem + * from disk. + * + * It is the inverse of the serialize() method. + * + * \tparam Restarter The deserializer type + * + * \param res The deserializer object + */ + template <class Restarter> + void deserialize(Restarter &res) + { + createResultWriter_(); + resultWriter_->deserialize(res); + model().deserialize(res); + } + + // \} + + /*! + * \brief Adds additional VTK output data to the VTKWriter. Function is called by writeOutput(). + */ + void addOutputVtkFields() + {} + + /*! + * \brief Write the relevant secondary variables of the current + * solution into an VTK output file. + */ + void writeOutput(const bool verbose = true) + { + // write the current result to disk + if (asImp_().shouldWriteOutput()) { + if (verbose && gridView().comm().rank() == 0) + std::cout << "Writing result file for \"" << asImp_().name() << "\"\n"; + + // calculate the time _after_ the time was updated + Scalar t = timeManager().time() + timeManager().timeStepSize(); + createResultWriter_(); + resultWriter_->beginWrite(t); + model().addOutputVtkFields(model().curSol(), *resultWriter_); + asImp_().addOutputVtkFields(); + resultWriter_->endWrite(); + } + } + +protected: + //! Returns the implementation of the problem (i.e. static polymorphism) + Implementation &asImp_() + { return *static_cast<Implementation *>(this); } + + //! \copydoc asImp_() + const Implementation &asImp_() const + { return *static_cast<const Implementation *>(this); } + + //! Returns the applied VTK-writer for the output + VtkMultiWriter& resultWriter() + { + createResultWriter_(); + return *resultWriter_; + } + //! \copydoc Dumux::IMPETProblem::resultWriter() + VtkMultiWriter& resultWriter() const + { + createResultWriter_(); + return *resultWriter_; + } + + +private: + // makes sure that the result writer exists + void createResultWriter_() + { if (!resultWriter_) resultWriter_ = new VtkMultiWriter(gridView_, asImp_().name()); }; + + std::string simName_; + const GridView gridView_; + + GlobalPosition bboxMin_; + GlobalPosition bboxMax_; + + ElementMapper elementMapper_; + VertexMapper vertexMapper_; + + TimeManager *timeManager_; + + Model model_; + + NewtonMethod newtonMethod_; + NewtonController newtonCtl_; + + VtkMultiWriter *resultWriter_; +}; + +} + +#endif diff --git a/dumux/implicit/common/implicitproperties.hh b/dumux/implicit/common/implicitproperties.hh new file mode 100644 index 0000000000..9fd7fffa05 --- /dev/null +++ b/dumux/implicit/common/implicitproperties.hh @@ -0,0 +1,142 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +#ifndef DUMUX_BOX_PROPERTIES_HH +#define DUMUX_BOX_PROPERTIES_HH + +#include <dumux/common/propertysystem.hh> + +#include <dumux/common/basicproperties.hh> +#include <dumux/linear/linearsolverproperties.hh> +#include <dumux/nonlinear/newtonmethod.hh> + +/*! + * \ingroup Properties + * \ingroup BoxProperties + * \ingroup BoxModel + * \file + * \brief Specify the shape functions, operator assemblers, etc + * used for the BoxModel. + */ +namespace Dumux +{ + +namespace Properties +{ +/*! + * \ingroup BoxModel + */ +// \{ + +////////////////////////////////////////////////////////////////// +// Type tags +////////////////////////////////////////////////////////////////// + +//! The type tag for models based on the box-scheme +NEW_TYPE_TAG(BoxModel, INHERITS_FROM(NewtonMethod, LinearSolverTypeTag, ImplicitModel)); + +////////////////////////////////////////////////////////////////// +// Property tags +////////////////////////////////////////////////////////////////// + +NEW_PROP_TAG(Grid); //!< The type of the DUNE grid +NEW_PROP_TAG(GridView); //!< The type of the grid view + +NEW_PROP_TAG(FVElementGeometry); //! The type of the finite-volume geometry in the box scheme +NEW_PROP_TAG(EvalGradientsAtSCVCenter); //! Evaluate shape function gradients additionally at the sub-control volume center + +NEW_PROP_TAG(Problem); //!< The type of the problem +NEW_PROP_TAG(BaseModel); //!< The type of the base class of the model +NEW_PROP_TAG(Model); //!< The type of the model +NEW_PROP_TAG(NumEq); //!< Number of equations in the system of PDEs +NEW_PROP_TAG(BaseLocalResidual); //!< The type of the base class of the local residual +NEW_PROP_TAG(LocalResidual); //!< The type of the local residual function +NEW_PROP_TAG(LocalJacobian); //!< The type of the local jacobian operator + +NEW_PROP_TAG(JacobianAssembler); //!< Assembles the global jacobian matrix +NEW_PROP_TAG(JacobianMatrix); //!< Type of the global jacobian matrix +NEW_PROP_TAG(BoundaryTypes); //!< Stores the boundary types of a single degree of freedom +NEW_PROP_TAG(ElementBoundaryTypes); //!< Stores the boundary types on an element + +NEW_PROP_TAG(PrimaryVariables); //!< A vector of primary variables within a sub-control volume +NEW_PROP_TAG(SolutionVector); //!< Vector containing all primary variable vector of the grid +NEW_PROP_TAG(ElementSolutionVector); //!< A vector of primary variables within a sub-control volume + +NEW_PROP_TAG(VolumeVariables); //!< The secondary variables within a sub-control volume +NEW_PROP_TAG(ElementVolumeVariables); //!< The secondary variables of all sub-control volumes in an element +NEW_PROP_TAG(FluxVariables); //!< Data required to calculate a flux over a face +NEW_PROP_TAG(BoundaryVariables); //!< Data required to calculate fluxes over boundary faces (outflow) + +// high level simulation control +NEW_PROP_TAG(TimeManager); //!< Manages the simulation time +NEW_PROP_TAG(NewtonMethod); //!< The type of the newton method +NEW_PROP_TAG(NewtonController); //!< The type of the newton controller + +//! Specify whether the jacobian matrix of the last iteration of a +//! time step should be re-used as the jacobian of the first iteration +//! of the next time step. +NEW_PROP_TAG(ImplicitEnableJacobianRecycling); + +//! Specify whether the jacobian matrix should be only reassembled for +//! elements where at least one vertex is above the specified +//! tolerance +NEW_PROP_TAG(ImplicitEnablePartialReassemble); +/*! + * \brief Specify the maximum size of a time integration [s]. + * + * The default is to not limit the step size. + */ +NEW_PROP_TAG(TimeManagerMaxTimeStepSize); + +/*! + * \brief Specify which kind of method should be used to numerically + * calculate the partial derivatives of the residual. + * + * -1 means backward differences, 0 means central differences, 1 means + * forward differences. By default we use central differences. + */ +NEW_PROP_TAG(ImplicitNumericDifferenceMethod); + +/*! + * \brief Specify whether to use the already calculated solutions as + * starting values of the volume variables. + * + * This only makes sense if the calculation of the volume variables is + * very expensive (e.g. for non-linear fugacity functions where the + * solver converges faster). + */ +NEW_PROP_TAG(ImplicitEnableHints); + +//! indicates whether two-point flux should be used +NEW_PROP_TAG(ImplicitUseTwoPointFlux); + +// mappers from local to global indices + +//! maper for vertices +NEW_PROP_TAG(VertexMapper); +//! maper for elements +NEW_PROP_TAG(ElementMapper); +//! maper for degrees of freedom +NEW_PROP_TAG(DofMapper); + +} +} + +// \} + +#endif diff --git a/dumux/implicit/common/implicitpropertydefaults.hh b/dumux/implicit/common/implicitpropertydefaults.hh new file mode 100644 index 0000000000..50ec31e2c7 --- /dev/null +++ b/dumux/implicit/common/implicitpropertydefaults.hh @@ -0,0 +1,207 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \ingroup Properties + * \ingroup BoxProperties + * \ingroup BoxModel + * \file + * + * \brief Default properties for box models + */ +#ifndef DUMUX_BOX_PROPERTY_DEFAULTS_HH +#define DUMUX_BOX_PROPERTY_DEFAULTS_HH + +#include <dumux/nonlinear/newtonmethod.hh> +#include <dumux/nonlinear/newtoncontroller.hh> + +#include "boxassembler.hh" +#include "boxmodel.hh" +#include "boxfvelementgeometry.hh" +#include "boxelementboundarytypes.hh" +#include "boxlocaljacobian.hh" +#include "boxlocalresidual.hh" +#include "boxelementvolumevariables.hh" +#include "boxvolumevariables.hh" + +#include <dumux/common/boundarytypes.hh> +#include <dumux/common/timemanager.hh> + +#include "boxproperties.hh" + +#include <limits> + +namespace Dumux { + +// forward declaration +template<class TypeTag> +class BoxModel; + +namespace Properties { +////////////////////////////////////////////////////////////////// +// Some defaults for very fundamental properties +////////////////////////////////////////////////////////////////// + +//! Set the default type for the time manager +SET_TYPE_PROP(BoxModel, TimeManager, Dumux::TimeManager<TypeTag>); + +////////////////////////////////////////////////////////////////// +// Properties +////////////////////////////////////////////////////////////////// + +//! Use the leaf grid view if not defined otherwise +SET_TYPE_PROP(BoxModel, + GridView, + typename GET_PROP_TYPE(TypeTag, Grid)::LeafGridView); + +//! Set the default for the FVElementGeometry +SET_TYPE_PROP(BoxModel, FVElementGeometry, Dumux::BoxFVElementGeometry<TypeTag>); + +//! Disable evaluation of shape function gradients at the sub-control volume center by default +// The shape function gradients at the sub-control volume center are currently only +// needed for the stokes and the linear elastic models +SET_BOOL_PROP(BoxModel, EvalGradientsAtSCVCenter, false); + +//! Set the default for the ElementBoundaryTypes +SET_TYPE_PROP(BoxModel, ElementBoundaryTypes, Dumux::BoxElementBoundaryTypes<TypeTag>); + +//! use the plain newton method for the box scheme by default +SET_TYPE_PROP(BoxModel, NewtonMethod, Dumux::NewtonMethod<TypeTag>); + +//! use the plain newton controller for the box scheme by default +SET_TYPE_PROP(BoxModel, NewtonController, Dumux::NewtonController<TypeTag>); + +//! Mapper for the grid view's vertices. +SET_TYPE_PROP(BoxModel, + VertexMapper, + Dune::MultipleCodimMultipleGeomTypeMapper<typename GET_PROP_TYPE(TypeTag, GridView), + Dune::MCMGVertexLayout>); + +//! Mapper for the grid view's elements. +SET_TYPE_PROP(BoxModel, + ElementMapper, + Dune::MultipleCodimMultipleGeomTypeMapper<typename GET_PROP_TYPE(TypeTag, GridView), + Dune::MCMGElementLayout>); + +//! Mapper for the degrees of freedoms. +SET_TYPE_PROP(BoxModel, DofMapper, typename GET_PROP_TYPE(TypeTag, VertexMapper)); + +//! Set the BaseLocalResidual to BoxLocalResidual +SET_TYPE_PROP(BoxModel, BaseLocalResidual, Dumux::BoxLocalResidual<TypeTag>); + +//! Set the BaseModel to BoxModel +SET_TYPE_PROP(BoxModel, BaseModel, Dumux::BoxModel<TypeTag>); + +//! The local jacobian operator for the box scheme +SET_TYPE_PROP(BoxModel, LocalJacobian, Dumux::BoxLocalJacobian<TypeTag>); + +/*! + * \brief The type of a solution for the whole grid at a fixed time. + */ +SET_TYPE_PROP(BoxModel, + SolutionVector, + Dune::BlockVector<typename GET_PROP_TYPE(TypeTag, PrimaryVariables)>); + +/*! + * \brief The type of a solution for a whole element. + */ +SET_TYPE_PROP(BoxModel, + ElementSolutionVector, + Dune::BlockVector<typename GET_PROP_TYPE(TypeTag, PrimaryVariables)>); + +/*! + * \brief A vector of primary variables. + */ +SET_TYPE_PROP(BoxModel, + PrimaryVariables, + Dune::FieldVector<typename GET_PROP_TYPE(TypeTag, Scalar), + GET_PROP_VALUE(TypeTag, NumEq)>); + +/*! + * \brief The volume variable class. + * + * This should almost certainly be overloaded by the model... + */ +SET_TYPE_PROP(BoxModel, VolumeVariables, Dumux::BoxVolumeVariables<TypeTag>); + +/*! + * \brief An array of secondary variable containers. + */ +SET_TYPE_PROP(BoxModel, ElementVolumeVariables, Dumux::BoxElementVolumeVariables<TypeTag>); + +/*! + * \brief Boundary types at a single degree of freedom. + */ +SET_TYPE_PROP(BoxModel, + BoundaryTypes, + Dumux::BoundaryTypes<GET_PROP_VALUE(TypeTag, NumEq)>); + +/*! + * \brief Assembler for the global jacobian matrix. + */ +SET_TYPE_PROP(BoxModel, JacobianAssembler, Dumux::BoxAssembler<TypeTag>); + +//! use an unlimited time step size by default +SET_SCALAR_PROP(BoxModel, TimeManagerMaxTimeStepSize, 1e100); + +//! use forward differences to calculate the jacobian by default +SET_INT_PROP(BoxModel, ImplicitNumericDifferenceMethod, +1); + +//! do not use hints by default +SET_BOOL_PROP(BoxModel, ImplicitEnableHints, false); + +// disable jacobian matrix recycling by default +SET_BOOL_PROP(BoxModel, ImplicitEnableJacobianRecycling, false); + +// disable partial reassembling by default +SET_BOOL_PROP(BoxModel, ImplicitEnablePartialReassemble, false); + +// disable two-point-flux by default +SET_BOOL_PROP(BoxModel, ImplicitUseTwoPointFlux, false); + +//! Set the type of a global jacobian matrix from the solution types +SET_PROP(BoxModel, JacobianMatrix) +{ +private: + typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; + enum { numEq = GET_PROP_VALUE(TypeTag, NumEq) }; + typedef typename Dune::FieldMatrix<Scalar, numEq, numEq> MatrixBlock; +public: + typedef typename Dune::BCRSMatrix<MatrixBlock> type; +}; + +// use the stabilized BiCG solver preconditioned by the ILU-0 by default +SET_TYPE_PROP(BoxModel, LinearSolver, Dumux::BoxBiCGStabILU0Solver<TypeTag> ); + +// if the deflection of the newton method is large, we do not +// need to solve the linear approximation accurately. Assuming +// that the initial value for the delta vector u is quite +// close to the final value, a reduction of 6 orders of +// magnitude in the defect should be sufficient... +SET_SCALAR_PROP(BoxModel, LinearSolverResidualReduction, 1e-6); + +//! set the default number of maximum iterations for the linear solver +SET_INT_PROP(BoxModel, LinearSolverMaxIterations, 250); + +//! set number of equations of the mathematical model as default +SET_INT_PROP(BoxModel, LinearSolverBlockSize, GET_PROP_VALUE(TypeTag, NumEq)); + +} // namespace Properties +} // namespace Dumux + +#endif diff --git a/dumux/implicit/common/implicitvolumevariables.hh b/dumux/implicit/common/implicitvolumevariables.hh new file mode 100644 index 0000000000..0c0bb6d8f6 --- /dev/null +++ b/dumux/implicit/common/implicitvolumevariables.hh @@ -0,0 +1,190 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \file + * + * \brief Base class for the model specific class which provides + * access to all volume averaged quantities. + */ +#ifndef DUMUX_BOX_VOLUME_VARIABLES_HH +#define DUMUX_BOX_VOLUME_VARIABLES_HH + +#include "boxproperties.hh" + +#include <dumux/common/valgrind.hh> + +namespace Dumux +{ + +/*! + * \ingroup BoxModel + * \ingroup BoxVolumeVariables + * \brief Base class for the model specific class which provides + * access to all volume averaged quantities. + */ +template <class TypeTag> +class BoxVolumeVariables +{ + typedef typename GET_PROP_TYPE(TypeTag, VolumeVariables) Implementation; + + typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; + typedef typename GET_PROP_TYPE(TypeTag, Problem) Problem; + typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; + typedef typename GridView::template Codim<0>::Entity Element; + typedef typename GET_PROP_TYPE(TypeTag, FVElementGeometry) FVElementGeometry; + + typedef typename GET_PROP_TYPE(TypeTag, PrimaryVariables) PrimaryVariables; + +public: + // default constructor + BoxVolumeVariables() + { evalPoint_ = 0; }; + + // copy constructor + BoxVolumeVariables(const BoxVolumeVariables &v) + { + evalPoint_ = 0; + priVars_ = v.priVars_; + extrusionFactor_ = v.extrusionFactor_; + }; + + /*! + * \brief Assignment operator + */ + BoxVolumeVariables &operator=(const BoxVolumeVariables &v) + { + evalPoint_ = 0; + priVars_ = v.priVars_; + extrusionFactor_ = v.extrusionFactor_; + + return *this; + }; + + /*! + * \brief Sets the evaluation point used by the local jacobian. + * + * The evaluation point is only used by semi-smooth models. + */ + void setEvalPoint(const Implementation *ep) + { + evalPoint_ = ep; + Valgrind::CheckDefined(evalPoint_); + } + + /*! + * \brief Returns the evaluation point used by the local jacobian. + * + * The evaluation point is only used by semi-smooth models. + */ + const Implementation &evalPoint() const + { return (evalPoint_ == 0)?asImp_():*evalPoint_; } + + /*! + * \brief Set the volume variables which should be used as initial + * conditions for complex calculations. + */ + void setHint(const Implementation *hint) + {}; + + /*! + * \brief Update all quantities for a given control volume + * + * \param priVars A vector containing the primary variables for the control volume + * \param problem The object specifying the problem which ought to + * be simulated + * \param element An element which contains part of the control volume + * \param fvGeometry The finite volume geometry for the element + * \param scvIdx Local index of the sub control volume which is inside the element + * \param isOldSol Specifies whether this is the previous solution or the current one + * + * \todo Eliminate the 'isOldSol' parameter. This implies that the + * 'pseudo-primary variables' must be somehow be stored + * inside the PrimaryVariables. (e.g. we need to know the + * phase state in the 2p2c model) + */ + void update(const PrimaryVariables &priVars, + const Problem &problem, + const Element &element, + const FVElementGeometry &fvGeometry, + const int scvIdx, + const bool isOldSol) + { + Valgrind::CheckDefined(priVars); + priVars_ = priVars; + extrusionFactor_ = problem.boxExtrusionFactor(element, fvGeometry, scvIdx); + } + + /*! + * \brief Return the vector of primary variables + */ + const PrimaryVariables &priVars() const + { return priVars_; } + + /*! + * \brief Return a component of primary variable vector + * + * \param pvIdx The index of the primary variable of interest + */ + Scalar priVar(const int pvIdx) const + { + return priVars_[pvIdx]; + } + + /*! + * \brief Return how much the sub-control volume is extruded. + * + * This means the factor by which a lower-dimensional (1D or 2D) + * entity needs to be expanded to get a full dimensional cell. The + * default is 1.0 which means that 1D problems are actually + * thought as pipes with a cross section of 1 m^2 and 2D problems + * are assumed to extend 1 m to the back. + */ + Scalar extrusionFactor() const + { return extrusionFactor_; } + + /*! + * \brief If running in valgrind this makes sure that all + * quantities in the volume variables are defined. + */ + void checkDefined() const + { +#if !defined NDEBUG && HAVE_VALGRIND + Valgrind::CheckDefined(priVars_); + Valgrind::CheckDefined(evalPoint_); + if (evalPoint_ && evalPoint_ != this) + evalPoint_->checkDefined(); +#endif + }; + +protected: + const Implementation &asImp_() const + { return *static_cast<const Implementation*>(this); } + Implementation &asImp_() + { return *static_cast<Implementation*>(this); } + + // the evaluation point of the local jacobian + const Implementation *evalPoint_; + + PrimaryVariables priVars_; + Scalar extrusionFactor_; +}; + +} // end namepace + +#endif diff --git a/dumux/implicit/common/porousmediaimplicitproblem.hh b/dumux/implicit/common/porousmediaimplicitproblem.hh new file mode 100644 index 0000000000..151f953703 --- /dev/null +++ b/dumux/implicit/common/porousmediaimplicitproblem.hh @@ -0,0 +1,197 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * 2012 by Bernd Flemisch * + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \file + * \brief Base class for all problems which use the two-phase box model + */ +#ifndef DUMUX_POROUS_MEDIA_BOX_PROBLEM_HH +#define DUMUX_POROUS_MEDIA_BOX_PROBLEM_HH + +#include "boxproperties.hh" + +#include <dumux/boxmodels/common/boxproblem.hh> + +namespace Dumux +{ +namespace Properties +{ +NEW_PROP_TAG(SpatialParams); //!< The type of the spatial parameters object +NEW_PROP_TAG(ProblemEnableGravity); //!< Returns whether gravity is considered in the problem +} + +/*! + * \ingroup BoxBaseProblems + * \brief Base class for all porous media box problems + */ +template<class TypeTag> +class PorousMediaBoxProblem : public BoxProblem<TypeTag> +{ + typedef BoxProblem<TypeTag> ParentType; + + typedef typename GET_PROP_TYPE(TypeTag, Problem) Implementation; + typedef typename GET_PROP_TYPE(TypeTag, TimeManager) TimeManager; + typedef typename GET_PROP_TYPE(TypeTag, FVElementGeometry) FVElementGeometry; + typedef typename GET_PROP_TYPE(TypeTag, SpatialParams) SpatialParams; + + typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; + typedef typename GridView::template Codim<0>::Entity Element; + enum { + dim = GridView::dimension, + dimWorld = GridView::dimensionworld + }; + + typedef typename GridView::ctype CoordScalar; + typedef Dune::FieldVector<CoordScalar, dimWorld> GlobalPosition; + typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; + typedef Dune::FieldVector<Scalar, dim> DimVector; + +public: + /*! + * \brief The constructor + * + * \param timeManager The time manager + * \param gridView The grid view + * \param verbose Turn verbosity on or off + */ + PorousMediaBoxProblem(TimeManager &timeManager, + const GridView &gridView, + const bool verbose = true) + : ParentType(timeManager, gridView), + gravity_(0) + { + newSpatialParams_ = true; + spatialParams_ = new SpatialParams(gridView); + + if (GET_PARAM_FROM_GROUP(TypeTag, bool, Problem, EnableGravity)) + gravity_[dim-1] = -9.81; + } + + ~PorousMediaBoxProblem() + { + if (newSpatialParams_) + delete spatialParams_; + } + + /*! + * \name Problem parameters + */ + // \{ + + /*! + * \brief Returns the temperature \f$\mathrm{[K]}\f$ within a control volume. + * + * This is the discretization specific interface for the box + * method. By default it just calls temperature(pos). + * + * \param element The DUNE Codim<0> enitiy which intersects with + * the finite volume. + * \param fvGeometry The finite volume geometry of the element. + * \param scvIdx The local index of the sub control volume inside the element + */ + Scalar boxTemperature(const Element &element, + const FVElementGeometry fvGeometry, + const int scvIdx) const + { return asImp_().temperatureAtPos(fvGeometry.subContVol[scvIdx].global); } + + /*! + * \brief Returns the temperature \f$\mathrm{[K]}\f$ at a given global position. + * + * This is not specific to the discretization. By default it just + * calls temperature(). + * + * \param pos The position in global coordinates where the temperature should be specified. + */ + Scalar temperatureAtPos(const GlobalPosition &pos) const + { return asImp_().temperature(); } + + /*! + * \brief Returns the temperature \f$\mathrm{[K]}\f$ for an isothermal problem. + * + * This is not specific to the discretization. By default it just + * throws an exception so it must be overloaded by the problem if + * no energy equation is used. + */ + Scalar temperature() const + { DUNE_THROW(Dune::NotImplemented, "temperature() method not implemented by the actual problem"); }; + + /*! + * \brief Returns the acceleration due to gravity \f$\mathrm{[m/s^2]}\f$. + * + * This is the box discretization specific interface. By default + * it just calls gravityAtPos(). + */ + const DimVector &boxGravity(const Element &element, + const FVElementGeometry &fvGeometry, + const int scvIdx) const + { return asImp_().gravityAtPos(fvGeometry.subContVol[scvIdx].global); } + + /*! + * \brief Returns the acceleration due to gravity \f$\mathrm{[m/s^2]}\f$. + * + * This is discretization independent interface. By default it + * just calls gravity(). + */ + const DimVector &gravityAtPos(const GlobalPosition &pos) const + { return asImp_().gravity(); } + + /*! + * \brief Returns the acceleration due to gravity \f$\mathrm{[m/s^2]}\f$. + * + * This method is used for problems where the gravitational + * acceleration does not depend on the spatial position. The + * default behaviour is that if the <tt>EnableGravity</tt> + * property is true, \f$\boldsymbol{g} = ( 0,\dots,\ -9.81)^T \f$ holds, + * else \f$\boldsymbol{g} = ( 0,\dots, 0)^T \f$. + */ + const DimVector &gravity() const + { return gravity_; } + + /*! + * \brief Returns the spatial parameters object. + */ + SpatialParams &spatialParams() + { return *spatialParams_; } + + /*! + * \brief Returns the spatial parameters object. + */ + const SpatialParams &spatialParams() const + { return *spatialParams_; } + + // \} + +protected: + //! Returns the implementation of the problem (i.e. static polymorphism) + Implementation &asImp_() + { return *static_cast<Implementation *>(this); } + //! \copydoc asImp_() + const Implementation &asImp_() const + { return *static_cast<const Implementation *>(this); } + + DimVector gravity_; + + // fluids and material properties + SpatialParams* spatialParams_; + bool newSpatialParams_; +}; + +} + +#endif diff --git a/dumux/implicit/mpnc/Makefile.am b/dumux/implicit/mpnc/Makefile.am new file mode 100644 index 0000000000..30f151f003 --- /dev/null +++ b/dumux/implicit/mpnc/Makefile.am @@ -0,0 +1,6 @@ +SUBDIRS = diffusion energy mass + +mpncdir = $(includedir)/dumux/boxmodels/mpnc +mpnc_HEADERS = *.hh + +include $(top_srcdir)/am/global-rules diff --git a/dumux/implicit/mpnc/diffusion/Makefile.am b/dumux/implicit/mpnc/diffusion/Makefile.am new file mode 100644 index 0000000000..53a44407d5 --- /dev/null +++ b/dumux/implicit/mpnc/diffusion/Makefile.am @@ -0,0 +1,4 @@ +diffusiondir = $(includedir)/dumux/boxmodels/mpnc/diffusion +diffusion_HEADERS = *.hh + +include $(top_srcdir)/am/global-rules diff --git a/dumux/implicit/mpnc/diffusion/diffusion.hh b/dumux/implicit/mpnc/diffusion/diffusion.hh new file mode 100644 index 0000000000..1df1d41a50 --- /dev/null +++ b/dumux/implicit/mpnc/diffusion/diffusion.hh @@ -0,0 +1,150 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \file + * + * \brief This file contains parts to calculate the diffusive flux in + * the fully coupled two-phase N-component model + */ +#ifndef DUMUX_MPNC_DIFFUSION_HH +#define DUMUX_MPNC_DIFFUSION_HH + +#include <dune/common/fmatrix.hh> +#include <dune/common/fvector.hh> + +#include <dumux/boxmodels/mpnc/mpncproperties.hh> + +namespace Dumux { + +template <class TypeTag, bool enableDiffusion> +class MPNCDiffusion +{ + typedef typename GET_PROP_TYPE(TypeTag, FluidSystem) FluidSystem; + typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; + + enum { numComponents = GET_PROP_VALUE(TypeTag, NumComponents)}; + enum { nPhaseIdx = FluidSystem::nPhaseIdx }; + enum { wPhaseIdx = FluidSystem::wPhaseIdx }; + + typedef typename GET_PROP_TYPE(TypeTag, FluxVariables) FluxVariables; + typedef Dune::FieldMatrix<Scalar, numComponents, numComponents> ComponentMatrix; + typedef Dune::FieldVector<Scalar, numComponents> ComponentVector; + +public: + static void flux(ComponentVector &fluxes, + const unsigned int phaseIdx, + const FluxVariables &fluxVars, + const Scalar molarDensity) + { + if (phaseIdx == nPhaseIdx) + gasFlux_(fluxes, fluxVars, molarDensity); + else if (phaseIdx == wPhaseIdx){ + #if MACROSCALE_DIFFUSION_ONLY_GAS + return ; // in the case that only the diffusion in the gas phase is considered, the liquidFlux should not be called + #endif + liquidFlux_(fluxes, fluxVars, molarDensity); + } + else + DUNE_THROW(Dune::InvalidStateException, + "Invalid phase index: " << phaseIdx); + } + +protected: + static void liquidFlux_(ComponentVector &fluxes, + const FluxVariables &fluxVars, + const Scalar molarDensity) + { + for (int compIdx = 0; compIdx < numComponents; ++compIdx) { + // TODO: tensorial diffusion coefficients + const Scalar xGrad = fluxVars.moleFractionGrad(wPhaseIdx, compIdx)*fluxVars.face().normal; + fluxes[compIdx] = + - xGrad * + molarDensity * + fluxVars.face().normal.two_norm() * // because we want a mole flux and not an area specific flux + fluxVars.porousDiffCoeffL(compIdx) ; + } + } + + static void gasFlux_(ComponentVector &fluxes, + const FluxVariables &fluxVars, + const Scalar molarDensity) + { + // Stefan-Maxwell equation + // + // See: R. Reid, et al.: "The Properties of Liquids and + // Gases", 4th edition, 1987, McGraw-Hill, p 596 + + // TODO: tensorial diffusion coefficients + ComponentMatrix M(0); + + for (int compIIdx = 0; compIIdx < numComponents - 1; ++compIIdx) { + for (int compJIdx = 0; compJIdx < numComponents; ++compJIdx) { + Scalar Dij = fluxVars.porousDiffCoeffG(compIIdx, compJIdx); + if (Dij) { + M[compIIdx][compJIdx] += fluxVars.moleFraction(nPhaseIdx, compIIdx) / Dij; + M[compIIdx][compIIdx] -= fluxVars.moleFraction(nPhaseIdx, compJIdx) / Dij; + } + } + } + + for (int compIIdx = 0; compIIdx < numComponents; ++compIIdx) { + M[numComponents - 1][compIIdx] = 1.0; + } + + ComponentVector rightHandSide ; // see source cited above + for (int compIIdx = 0; compIIdx < numComponents - 1; ++compIIdx) { + rightHandSide[compIIdx] = molarDensity*(fluxVars.moleFractionGrad(nPhaseIdx, compIIdx)*fluxVars.face().normal); + } + rightHandSide[numComponents - 1] = 0.0; + + M.solve(fluxes, rightHandSide); + } + + // return whether a concentration can be assumed to be a trace + // component in the context of diffusion + static Scalar isTraceComp_(Scalar x) + { return x < 0.5/numComponents; } +}; + +/*! + * \brief Specialization of the diffusion module for the case where + * diffusion is disabled. + * + * This class just does nothing. + */ +template <class TypeTag> +class MPNCDiffusion<TypeTag, false> +{ + enum { numComponents = GET_PROP_VALUE(TypeTag, NumComponents) }; + + typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; + typedef typename GET_PROP_TYPE(TypeTag, FluxVariables) FluxVariables; + typedef Dune::FieldVector<Scalar, numComponents> ComponentVector; + +public: + static void flux(ComponentVector &fluxes, + const unsigned int phaseIdx, + const FluxVariables &fluxVars, + const Scalar molarDensity) + { fluxes = 0; } +}; + +} + +#endif // DUMUX_MPNC_DIFFUSION_HH diff --git a/dumux/implicit/mpnc/diffusion/fluxvariables.hh b/dumux/implicit/mpnc/diffusion/fluxvariables.hh new file mode 100644 index 0000000000..7ed8570e08 --- /dev/null +++ b/dumux/implicit/mpnc/diffusion/fluxvariables.hh @@ -0,0 +1,221 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \file + * + * \brief This file contains the diffusion module for the flux data of + * the fully coupled two-phase N-component model + */ +#ifndef DUMUX_MPNC_DIFFUSION_FLUX_VARIABLES_HH +#define DUMUX_MPNC_DIFFUSION_FLUX_VARIABLES_HH + +#include <dune/common/fvector.hh> + +#include "../mpncproperties.hh" + +namespace Dumux { + +template<class TypeTag, bool enableDiffusion> +class MPNCFluxVariablesDiffusion +{ + typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; + typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; + typedef typename GET_PROP_TYPE(TypeTag, Problem) Problem; + typedef typename GET_PROP_TYPE(TypeTag, FluidSystem) FluidSystem; + typedef typename GET_PROP_TYPE(TypeTag, ElementVolumeVariables) ElementVolumeVariables; + typedef typename GridView::template Codim<0>::Entity Element; + typedef typename GET_PROP_TYPE(TypeTag, FVElementGeometry) FVElementGeometry; + typedef typename FVElementGeometry::SubControlVolumeFace SCVFace; + + enum{dim = GridView::dimension}; + enum{numPhases = GET_PROP_VALUE(TypeTag, NumPhases)}; + enum{numComponents = GET_PROP_VALUE(TypeTag, NumComponents)}; + enum{wPhaseIdx = FluidSystem::wPhaseIdx}; + enum{nPhaseIdx = FluidSystem::nPhaseIdx}; + + typedef Dune::FieldVector<Scalar, dim> DimVector; + +public: + MPNCFluxVariablesDiffusion() + {} + + void update(const Problem & problem, + const Element & element, + const FVElementGeometry & fvGeometry, + const SCVFace & face, + const ElementVolumeVariables & elemVolVars) + { + const unsigned int i = face.i; + const unsigned int j = face.j; + + + for (int phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx){ + for (int compIdx = 0; compIdx < numComponents; ++compIdx){ + moleFraction_[phaseIdx][compIdx] = 0. ; + moleFractionGrad_[phaseIdx][compIdx] = 0. ; + } + } + + + DimVector tmp ; + for (int idx = 0; + idx < fvGeometry.numFAP; + idx++) // loop over adjacent vertices + { + // FE gradient at vertex idx + const DimVector & feGrad = face.grad[idx]; + + // index for the element volume variables + int volVarsIdx = face.fapIndices[idx]; + + for (int phaseIdx = 0; phaseIdx < numPhases; phaseIdx++){ + for (int compIdx = 0; compIdx < numComponents; ++compIdx){ + + // calculate mole fractions at the integration points of the face + moleFraction_[phaseIdx][compIdx] += elemVolVars[volVarsIdx].fluidState().moleFraction(phaseIdx, compIdx)* + face.shapeValue[idx]; + + // calculate mole fraction gradients + tmp = feGrad; + tmp *= elemVolVars[volVarsIdx].fluidState().moleFraction(phaseIdx, compIdx); + moleFractionGrad_[phaseIdx][compIdx] += tmp; + } + } + } + + // initialize the diffusion coefficients to zero + for (int i = 0; i < numComponents; ++i) { + porousDiffCoeffL_[i] = 0.0; + for (int j = 0; j < numComponents; ++j) + porousDiffCoeffG_[i][j] = 0.0; + } + + // calculate the diffusion coefficients at the integration + // point in the porous medium + for (int phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) + { + // make sure to only calculate diffusion coefficents + // for phases which exist in both finite volumes + if (elemVolVars[i].fluidState().saturation(phaseIdx) <= 1e-4 || + elemVolVars[j].fluidState().saturation(phaseIdx) <= 1e-4) + { + continue; + } + + // reduction factor for the diffusion coefficients in the + // porous medium in nodes i and j. this is the tortuosity + // times porosity times phase saturation at the nodes i + // and j + // + // TODO (?): move this calculation to the soil (possibly + // that's a bad idea, though) + Scalar red_i = + elemVolVars[i].fluidState().saturation(phaseIdx)/elemVolVars[i].porosity() * + pow(elemVolVars[i].porosity() * elemVolVars[i].fluidState().saturation(phaseIdx), 7.0/3); + Scalar red_j = + elemVolVars[j].fluidState().saturation(phaseIdx)/elemVolVars[j].porosity() * + pow(elemVolVars[j].porosity() * elemVolVars[j].fluidState().saturation(phaseIdx), 7.0/3); + + if (phaseIdx == wPhaseIdx) { + // Liquid phase diffusion coefficients in the porous medium + for (int i = 0; i < numComponents; ++i) { + // -> arithmetic mean + porousDiffCoeffL_[i] + = 1./2*(red_i * elemVolVars[i].diffCoeff(wPhaseIdx, 0, i) + + red_j * elemVolVars[j].diffCoeff(wPhaseIdx, 0, i)); + } + } + else { + // Gas phase diffusion coefficients in the porous medium + for (int i = 0; i < numComponents; ++i) { + for (int j = 0; j < numComponents; ++j) { + // -> arithmetic mean + porousDiffCoeffG_[i][j] + = 1./2*(red_i * elemVolVars[i].diffCoeff(nPhaseIdx, i, j) + + red_j * elemVolVars[j].diffCoeff(nPhaseIdx, i, j)); + } + } + } + } + } + + Scalar porousDiffCoeffL(const unsigned int compIdx) const + { + // TODO: tensorial diffusion coefficients + return porousDiffCoeffL_[compIdx]; + } + + Scalar porousDiffCoeffG(const unsigned int compIIdx, + const unsigned int compJIdx) const + { + // TODO: tensorial diffusion coefficients + return porousDiffCoeffG_[compIIdx][compJIdx]; + } + + Scalar moleFraction(const unsigned int phaseIdx, + const unsigned int compIdx) const + { return moleFraction_[phaseIdx][compIdx]; } + + const DimVector &moleFractionGrad(const unsigned int phaseIdx, + const unsigned int compIdx) const + { return moleFractionGrad_[phaseIdx][compIdx];} + +protected: + // the diffusion coefficients for the porous medium for the + // liquid phase + Scalar porousDiffCoeffL_[numComponents]; + + // the diffusion coefficients for the porous medium for the + // gas phase + Scalar porousDiffCoeffG_[numComponents][numComponents]; + + // the concentration gradients of all components in all phases + DimVector moleFractionGrad_[numPhases][numComponents]; + + // the mole fractions of each component at the integration point + Scalar moleFraction_[numPhases][numComponents]; +}; + + +template<class TypeTag> +class MPNCFluxVariablesDiffusion<TypeTag, false> +{ + typedef typename GET_PROP_TYPE(TypeTag, Problem) Problem; + typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; + typedef typename GridView::template Codim<0>::Entity Element; + typedef typename GET_PROP_TYPE(TypeTag, ElementVolumeVariables) ElementVolumeVariables; + typedef typename GET_PROP_TYPE(TypeTag, FVElementGeometry) FVElementGeometry; + typedef typename FVElementGeometry::SubControlVolumeFace SCVFace; + +public: + MPNCFluxVariablesDiffusion() + {} + + void update(const Problem & problem, + const Element & element, + const FVElementGeometry & fvGeometry, + const SCVFace & face, + const ElementVolumeVariables & elemVolVars) + { + } +}; + +} + +#endif // DUMUX_MPNC_DIFFUSION_FLUX_VARIABLES_HH diff --git a/dumux/implicit/mpnc/diffusion/volumevariables.hh b/dumux/implicit/mpnc/diffusion/volumevariables.hh new file mode 100644 index 0000000000..6c7e523467 --- /dev/null +++ b/dumux/implicit/mpnc/diffusion/volumevariables.hh @@ -0,0 +1,164 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \file + * + * \brief This file contains the diffusion module for the vertex data + * of the fully coupled two-phase N-component model + */ +#ifndef DUMUX_MPNC_DIFFUSION_VOLUME_VARIABLES_HH +#define DUMUX_MPNC_DIFFUSION_VOLUME_VARIABLES_HH + +#include <dumux/common/valgrind.hh> +#include <dumux/boxmodels/mpnc/mpncproperties.hh> + +namespace Dumux { + +template<class TypeTag, bool enableDiffusion> +class MPNCVolumeVariablesDiffusion +{ + typedef typename GET_PROP_TYPE(TypeTag, Problem) Problem; + typedef typename GET_PROP_TYPE(TypeTag, VolumeVariables) VolumeVariables; + typedef typename GET_PROP_TYPE(TypeTag, FluidSystem) FluidSystem; + typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; + typedef typename GET_PROP_TYPE(TypeTag, FluidState) FluidState; + typedef typename FluidSystem::ParameterCache ParameterCache; + + enum { numComponents = GET_PROP_VALUE(TypeTag, NumComponents) }; + enum { wPhaseIdx = FluidSystem::wPhaseIdx }; + enum { nPhaseIdx = FluidSystem::nPhaseIdx }; + +public: + + MPNCVolumeVariablesDiffusion() + {} + + void update(FluidState &fluidState, + ParameterCache ¶mCache, + const VolumeVariables &volVars, + const Problem &problem) + { + Valgrind::SetUndefined(*this); + + // diffusion coefficents in liquid + diffCoeffL_[0] = 0.0; + for (int compIdx = 1; compIdx < numComponents; ++compIdx) { + diffCoeffL_[compIdx] = + FluidSystem::binaryDiffusionCoefficient(fluidState, + paramCache, + wPhaseIdx, + 0, + compIdx); + } + Valgrind::CheckDefined(diffCoeffL_); + + // diffusion coefficents in gas + for (int compIIdx = 0; compIIdx < numComponents; ++compIIdx) { + diffCoeffG_[compIIdx][compIIdx] = 0; + for (int compJIdx = compIIdx + 1; compJIdx < numComponents; ++compJIdx) { + diffCoeffG_[compIIdx][compJIdx] = + FluidSystem::binaryDiffusionCoefficient(fluidState, + paramCache, + nPhaseIdx, + compIIdx, + compJIdx); + + // fill the symmetric part of the diffusion coefficent + // matrix + diffCoeffG_[compJIdx][compIIdx] = diffCoeffG_[compIIdx][compJIdx]; + } + } + Valgrind::CheckDefined(diffCoeffG_); + } + + + Scalar diffCoeff(const unsigned int phaseIdx, + const unsigned int compIIdx, + const unsigned int compJIdx) const + { + if (phaseIdx == nPhaseIdx) + // TODO: tensorial diffusion coefficients + return diffCoeffG_[compIIdx][compJIdx]; + + const unsigned int i = std::min(compIIdx, compJIdx); + const unsigned int j = std::max(compIIdx, compJIdx); + if (i != 0) + return 0; + return diffCoeffL_[j]; + } + + /*! + * \brief If running under valgrind this produces an error message + * if some of the object's attributes is undefined. + */ + void checkDefined() const + { + Valgrind::CheckDefined(diffCoeffL_); + Valgrind::CheckDefined(diffCoeffG_); + } + + +protected: + // the diffusion coefficients for the porous medium for the + // liquid phase + Scalar diffCoeffL_[numComponents]; + + // the diffusion coefficients for the porous medium for the + // gas phase + Scalar diffCoeffG_[numComponents][numComponents]; +}; + +// dummy class for the case where diffusion is disabled +template<class TypeTag> +class MPNCVolumeVariablesDiffusion<TypeTag, false> +{ + typedef typename GET_PROP_TYPE(TypeTag, Problem) Problem; + typedef typename GET_PROP_TYPE(TypeTag, VolumeVariables) VolumeVariables; + typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; + typedef typename GET_PROP_TYPE(TypeTag, FluidSystem) FluidSystem; + typedef typename GET_PROP_TYPE(TypeTag, FluidState) FluidState; + typedef typename FluidSystem::ParameterCache ParameterCache; + +public: + MPNCVolumeVariablesDiffusion() + {} + + void update(FluidState &fluidState, + ParameterCache ¶mCache, + const VolumeVariables &volVars, + const Problem &problem) + { } + + Scalar diffCoeffL(const unsigned int compIdx) const + { return 0; } + + Scalar diffCoeffG(const unsigned int compIIdx, const unsigned int compJIdx) const + { return 0; } + + /*! + * \brief If running under valgrind this produces an error message + * if some of the object's attributes is undefined. + */ + void checkDefined() const + { } +}; + +} + +#endif // DUMUX_MPNC_DIFFUSION_VOLUME_VARIABLES_HH diff --git a/dumux/implicit/mpnc/energy/Makefile.am b/dumux/implicit/mpnc/energy/Makefile.am new file mode 100644 index 0000000000..0f5e064a86 --- /dev/null +++ b/dumux/implicit/mpnc/energy/Makefile.am @@ -0,0 +1,4 @@ +energydir = $(includedir)/dumux/boxmodels/mpnc/energy +energy_HEADERS = *.hh + +include $(top_srcdir)/am/global-rules diff --git a/dumux/implicit/mpnc/energy/mpncfluxvariablesenergy.hh b/dumux/implicit/mpnc/energy/mpncfluxvariablesenergy.hh new file mode 100644 index 0000000000..a345453e15 --- /dev/null +++ b/dumux/implicit/mpnc/energy/mpncfluxvariablesenergy.hh @@ -0,0 +1,205 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \file + * + * \brief Contains the quantities to calculate the energy flux in the + * MpNc box model. + */ +#ifndef DUMUX_MPNC_ENERGY_FLUX_VARIABLES_HH +#define DUMUX_MPNC_ENERGY_FLUX_VARIABLES_HH + +#include <dune/common/fmatrix.hh> +#include <dune/common/fvector.hh> + +#include <dumux/boxmodels/mpnc/mpncproperties.hh> +#include <dumux/common/spline.hh> + +namespace Dumux +{ + +template <class TypeTag, bool enableEnergy/*=false*/, bool kineticEnergyTransfer/*=false*/> +class MPNCFluxVariablesEnergy +{ + static_assert(!(kineticEnergyTransfer && !enableEnergy), + "No kinetic energy transfer may only be enabled " + "if energy is enabled in general."); + static_assert(!kineticEnergyTransfer, + "No kinetic energy transfer module included, " + "but kinetic energy transfer enabled."); + + typedef typename GET_PROP_TYPE(TypeTag, Problem) Problem; + typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; + typedef typename GridView::template Codim<0>::Entity Element; + typedef typename GET_PROP_TYPE(TypeTag, FVElementGeometry) FVElementGeometry; + typedef typename GET_PROP_TYPE(TypeTag, VolumeVariables) VolumeVariables; + typedef typename GET_PROP_TYPE(TypeTag, ElementVolumeVariables) ElementVolumeVariables; + typedef typename GET_PROP_TYPE(TypeTag, FluxVariables) FluxVariables; + typedef typename FVElementGeometry::SubControlVolumeFace SCVFace; + +public: + MPNCFluxVariablesEnergy() + { + } + + void update(const Problem & problem, + const Element & element, + const FVElementGeometry & fvGeometry, + const SCVFace & face, + const FluxVariables & fluxVars, + const ElementVolumeVariables & elemVolVars) + {} +}; + +template <class TypeTag> +class MPNCFluxVariablesEnergy<TypeTag, /*enableEnergy=*/true, /*kineticEnergyTransfer=*/false> +{ + typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; + typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; + typedef typename GET_PROP_TYPE(TypeTag, Problem) Problem; + typedef typename GET_PROP_TYPE(TypeTag, VolumeVariables) VolumeVariables; + typedef typename GET_PROP_TYPE(TypeTag, ElementVolumeVariables) ElementVolumeVariables; + typedef typename GET_PROP_TYPE(TypeTag, FluxVariables) FluxVariables; + typedef typename GET_PROP_TYPE(TypeTag, FluidSystem) FluidSystem; + typedef typename GET_PROP_TYPE(TypeTag, FluidState) FluidState; + typedef typename FluidSystem::ParameterCache ParameterCache; + typedef typename GridView::ctype CoordScalar; + typedef typename GridView::template Codim<0>::Entity Element; + + enum{dim = GridView::dimension}; + enum{dimWorld = GridView::dimensionworld}; + enum{nPhaseIdx = FluidSystem::nPhaseIdx}; + enum{wPhaseIdx = FluidSystem::wPhaseIdx}; + enum{numPhases = GET_PROP_VALUE(TypeTag, NumPhases)}; + + typedef Dune::FieldVector<CoordScalar, dimWorld> DimVector; + typedef typename GET_PROP_TYPE(TypeTag, FVElementGeometry) FVElementGeometry; + typedef typename FVElementGeometry::SubControlVolumeFace SCVFace; + +public: + MPNCFluxVariablesEnergy() + {} + + void update(const Problem & problem, + const Element & element, + const FVElementGeometry & fvGeometry, + const SCVFace & face, + const FluxVariables & fluxVars, + const ElementVolumeVariables & elemVolVars) + { + // calculate temperature gradient using finite element + // gradients + DimVector tmp(0.0); + DimVector temperatureGradient(0.); + for (int idx = 0; idx < fvGeometry.numFAP; idx++) + { + tmp = face.grad[idx]; + + // index for the element volume variables + int volVarsIdx = face.fapIndices[idx]; + + tmp *= elemVolVars[volVarsIdx].fluidState().temperature(/*phaseIdx=*/0); + temperatureGradient += tmp; + } + + // project the heat flux vector on the face's normal vector + temperatureGradientNormal_ = temperatureGradient * face.normal; + + + lambdaPm_ = lumpedLambdaPm(problem, + element, + fvGeometry, + face, + elemVolVars) ; + + } + + Scalar lumpedLambdaPm(const Problem &problem, + const Element &element, + const FVElementGeometry & fvGeometry, + const SCVFace & face, + const ElementVolumeVariables & elemVolVars) + { + // arithmetic mean of the liquid saturation and the porosity + const unsigned int i = face.i; + const unsigned int j = face.j; + + const FluidState &fsI = elemVolVars[i].fluidState(); + const FluidState &fsJ = elemVolVars[j].fluidState(); + const Scalar Swi = fsI.saturation(wPhaseIdx); + const Scalar Swj = fsJ.saturation(wPhaseIdx); + + typename FluidSystem::ParameterCache paramCacheI, paramCacheJ; + paramCacheI.updateAll(fsI); + paramCacheJ.updateAll(fsJ); + + const Scalar Sw = std::max<Scalar>(0.0, 0.5*(Swi + Swj)); + + // const Scalar lambdaDry = 0.583; // W / (K m) // works, orig + // const Scalar lambdaWet = 1.13; // W / (K m) // works, orig + + const Scalar lambdaSoilI = problem.spatialParams().soilThermalConductivity(element, fvGeometry, i); + const Scalar lambdaSoilJ = problem.spatialParams().soilThermalConductivity(element, fvGeometry, i); + const Scalar lambdaDry = 0.5 * (lambdaSoilI + FluidSystem::thermalConductivity(fsI, paramCacheI, nPhaseIdx)); // W / (K m) + const Scalar lambdaWet = 0.5 * (lambdaSoilJ + FluidSystem::thermalConductivity(fsJ, paramCacheJ, wPhaseIdx)) ; // W / (K m) + + // the heat conductivity of the matrix. in general this is a + // tensorial value, but we assume isotropic heat conductivity. + // This is the Sommerton approach with lambdaDry = + // lambdaSn100%. Taken from: H. Class: "Theorie und + // numerische Modellierung nichtisothermer Mehrphasenprozesse + // in NAPL-kontaminierten poroesen Medien", PhD Thesis, University of + // Stuttgart, Institute of Hydraulic Engineering, p. 57 + + Scalar result; + if (Sw < 0.1) { + // regularization + Dumux::Spline<Scalar> sp(0, 0.1, // x1, x2 + 0, sqrt(0.1), // y1, y2 + 5*0.5/sqrt(0.1), 0.5/sqrt(0.1)); // m1, m2 + result = lambdaDry + sp.eval(Sw)*(lambdaWet - lambdaDry); + } + else + result = lambdaDry + std::sqrt(Sw)*(lambdaWet - lambdaDry); + + return result; + } + + /*! + * \brief The lumped / average conductivity of solid plus phases \f$[W/mK]\f$. + */ + Scalar lambdaPm() const + { return lambdaPm_; } + + /*! + * \brief The normal of the gradient of temperature . + */ + Scalar temperatureGradientNormal() const + { + return temperatureGradientNormal_; + } + +private: + Scalar lambdaPm_; + Scalar temperatureGradientNormal_; +}; + +} // end namepace + +#endif diff --git a/dumux/implicit/mpnc/energy/mpncindicesenergy.hh b/dumux/implicit/mpnc/energy/mpncindicesenergy.hh new file mode 100644 index 0000000000..05a8b33ce2 --- /dev/null +++ b/dumux/implicit/mpnc/energy/mpncindicesenergy.hh @@ -0,0 +1,75 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \brief The indices for the non-isothermal part of the compositional + * multi-phase model. + */ +#ifndef DUMUX_MPNC_INDICES_ENERGY_HH +#define DUMUX_MPNC_INDICES_ENERGY_HH + +namespace Dumux +{ +/*! + * \brief The indices for the energy equation. + * + * This is a dummy class for the isothermal case. + */ +template <int PVOffset, bool enableEnergy/*=false*/, bool kineticEnergyTransfer/*=false*/> +struct MPNCEnergyIndices +{ + static_assert(!(kineticEnergyTransfer && !enableEnergy), + "No kinetic energy transfer may only be enabled " + "if energy is enabled in general."); + static_assert(!kineticEnergyTransfer, + "No kinetic energy transfer module included, " + "but kinetic energy transfer enabled."); +public: + /*! + * \brief This module does not define any primary variables in the + * isothermal case. + */ + static const unsigned int NumPrimaryVars = 0; +}; + +/*! + * \brief The indices required for the energy equation. + */ +template <int PVOffset> +struct MPNCEnergyIndices<PVOffset, /*isNonIsothermal=*/true, /*kineticEnergyTransfer*/false> +{ +public: + /*! + * \brief This module defines one new primary variable. + */ + static const unsigned int NumPrimaryVars = 1; + + /*! + * \brief Index for the temperature in a vector of primary + * variables. + */ + static const unsigned int temperatureIdx = PVOffset + 0; + /*! + * \brief Equation index of the energy equation. + */ + static const unsigned int energyEqIdx = PVOffset + 0; +}; + +} + +#endif diff --git a/dumux/implicit/mpnc/energy/mpnclocalresidualenergy.hh b/dumux/implicit/mpnc/energy/mpnclocalresidualenergy.hh new file mode 100644 index 0000000000..67c3b8b9ee --- /dev/null +++ b/dumux/implicit/mpnc/energy/mpnclocalresidualenergy.hh @@ -0,0 +1,233 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \file + * + * \brief This file contains the parts of the local residual to + * calculate the heat flux in the fully coupled two-phase + * N-component model + */ +#ifndef DUMUX_MPNC_LOCAL_RESIDUAL_ENERGY_HH +#define DUMUX_MPNC_LOCAL_RESIDUAL_ENERGY_HH + +#include <dumux/boxmodels/mpnc/mpncproperties.hh> + +namespace Dumux { + +/*! + * \brief Specialization of the energy module for the isothermal case. + * + * This class just does nothing. + */ +template <class TypeTag, bool enableEnergy/*=false*/, bool kineticEnergyTransfer /*=false*/> +class MPNCLocalResidualEnergy +{ + static_assert(!(kineticEnergyTransfer && !enableEnergy), + "No kinetic energy transfer may only be enabled " + "if energy is enabled in general."); + static_assert(!kineticEnergyTransfer, + "No kinetic energy transfer module included, " + "but kinetic energy transfer enabled."); + + typedef typename GET_PROP_TYPE(TypeTag, PrimaryVariables) PrimaryVariables; + typedef typename GET_PROP_TYPE(TypeTag, VolumeVariables) VolumeVariables; + typedef typename GET_PROP_TYPE(TypeTag, FluxVariables) FluxVariables; + typedef typename GET_PROP_TYPE(TypeTag, ElementVolumeVariables) ElementVolumeVariables; + typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; + + enum { numPhases = GET_PROP_VALUE(TypeTag, NumPhases) }; + enum { numComponents = GET_PROP_VALUE(TypeTag, NumComponents) }; + + typedef typename Dune::FieldVector<Scalar, numComponents> ComponentVector; + +public: + static void computeStorage(PrimaryVariables &storage, + const VolumeVariables &volVars) + { + // do nothing, we're isothermal! + } + + + static void addPhaseStorage(PrimaryVariables &storage, + const VolumeVariables &volVars, + const unsigned int phaseIdx) + { + // do nothing, we're isothermal! + } + + static void phaseEnthalpyFlux(PrimaryVariables &enthalpyFlux, + const unsigned int phaseIdx, + const PrimaryVariables &compMolFlux, + const ElementVolumeVariables &volVars, + const FluxVariables &fluxVars) + { + // do nothing, we're isothermal! + } + + static void heatConduction(PrimaryVariables &heatConduction, + const ElementVolumeVariables &volVars, + const FluxVariables &fluxVars) + { + // do nothing, we're isothermal! + } + + + static void computeFlux(PrimaryVariables & flux, + const FluxVariables & fluxVars, + const ElementVolumeVariables & volVars, + const ComponentVector molarPhaseComponentValuesMassTransport[numPhases]) + { + // do nothing, we're isothermal! + } + + static void computeSource(PrimaryVariables &source, + const VolumeVariables &volVars, + const ComponentVector componentIntoPhaseMassTransfer[numPhases]) + { + // do nothing, we're isothermal! + } +}; + + +template <class TypeTag> +class MPNCLocalResidualEnergy<TypeTag, /*enableEnergy=*/true, /*kineticenergyTransfer=*/false> +{ + typedef typename GET_PROP_TYPE(TypeTag, FluidSystem) FluidSystem; + typedef typename GET_PROP_TYPE(TypeTag, FluidState) FluidState; + typedef typename FluidSystem::ParameterCache ParameterCache; + typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; + typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; + typedef typename GET_PROP_TYPE(TypeTag, PrimaryVariables) PrimaryVariables; + typedef typename GET_PROP_TYPE(TypeTag, FluxVariables) FluxVariables; + typedef typename GET_PROP_TYPE(TypeTag, VolumeVariables) VolumeVariables; + typedef typename GET_PROP_TYPE(TypeTag, ElementVolumeVariables) ElementVolumeVariables; + typedef typename GET_PROP_TYPE(TypeTag, Indices) Indices; + + enum { dim = GridView::dimension }; + enum { numPhases = GET_PROP_VALUE(TypeTag, NumPhases) }; + enum { numComponents = GET_PROP_VALUE(TypeTag, NumComponents) }; + enum { conti0EqIdx = Indices::conti0EqIdx }; + enum { energyEqIdx = Indices::energyEqIdx }; + + typedef typename Dune::FieldVector<Scalar, numComponents> ComponentVector; + +public: + static void computeStorage(PrimaryVariables &storage, + const VolumeVariables &volVars) + { + storage[energyEqIdx] = 0; + + // energy of the fluids + for (int phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) { + addPhaseStorage(storage, volVars, phaseIdx); + } + + // heat stored in the rock matrix + storage[energyEqIdx] += + volVars.fluidState().temperature(/*phaseIdx=*/0) + * volVars.soilDensity() + * (1.0 - volVars.porosity()) + * volVars.heatCapacity(); + } + + static void addPhaseStorage(PrimaryVariables &storage, + const VolumeVariables &volVars, + const unsigned int phaseIdx) + { + const FluidState &fs = volVars.fluidState(); + + // energy of the fluid + storage[energyEqIdx] += + fs.density(phaseIdx) + * fs.internalEnergy(phaseIdx) + * fs.saturation(phaseIdx) + * volVars.porosity(); + } + + static void computeFlux(PrimaryVariables & flux, + const FluxVariables & fluxVars, + const ElementVolumeVariables & elemVolVars, + const ComponentVector molarPhaseComponentValuesMassTransport[numPhases]) + { + flux[energyEqIdx] = 0.0; + + // fluid phases transport enthalpy individually + for(int phaseIdx=0; phaseIdx<numPhases; ++phaseIdx) + computePhaseEnthalpyFlux(flux, + fluxVars, + elemVolVars, + phaseIdx, + molarPhaseComponentValuesMassTransport[phaseIdx]); + + //conduction is treated lumped in this model + computeHeatConduction(flux, + fluxVars, + elemVolVars); + } + + static void computePhaseEnthalpyFlux(PrimaryVariables & flux, + const FluxVariables & fluxVars, + const ElementVolumeVariables & elemVolVars, + const unsigned int phaseIdx, + const ComponentVector & molarComponentValuesMassTransport) + { + Scalar massFlux = 0; + + // calculate the mass flux in the phase i.e. make mass flux out of mole flux and add up the fluxes of a phase + for (int compIdx = 0; compIdx < numComponents; ++compIdx) + massFlux += molarComponentValuesMassTransport[compIdx] + * FluidSystem::molarMass(compIdx); + + unsigned int upIdx = fluxVars.face().i; + if (massFlux < 0) upIdx = fluxVars.face().j; + + // use the phase enthalpy of the upstream vertex to calculate + // the enthalpy transport + const VolumeVariables &up = elemVolVars[upIdx]; + flux[energyEqIdx] += up.fluidState().enthalpy(phaseIdx) * massFlux; + } + + static void computeHeatConduction(PrimaryVariables & flux, + const FluxVariables & fluxVars, + const ElementVolumeVariables & elemVolVars) + { + //lumped heat conduction of the rock matrix and the fluid phases + Scalar lumpedConductivity = fluxVars.fluxVarsEnergy().lambdaPm() ; + Scalar temperatureGradientNormal = fluxVars.fluxVarsEnergy().temperatureGradientNormal() ; + Scalar lumpedHeatConduction = - lumpedConductivity * temperatureGradientNormal ; + flux[energyEqIdx] += lumpedHeatConduction ; + } + + + + static void computeSource(PrimaryVariables &source, + const VolumeVariables &volVars, + const ComponentVector componentIntoPhaseMassTransfer[numPhases]) + { + source[energyEqIdx] = 0.0; + } +}; + + + + + +} + +#endif // DUMUX_MPNC_ENERGY_HH diff --git a/dumux/implicit/mpnc/energy/mpncvolumevariablesenergy.hh b/dumux/implicit/mpnc/energy/mpncvolumevariablesenergy.hh new file mode 100644 index 0000000000..185884fdc3 --- /dev/null +++ b/dumux/implicit/mpnc/energy/mpncvolumevariablesenergy.hh @@ -0,0 +1,207 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \file + * + * \brief Contains the energy part of volume variables of the M-phase, + * N-component model. + */ +#ifndef DUMUX_MPNC_ENERGY_VOLUME_VARIABLES_HH +#define DUMUX_MPNC_ENERGY_VOLUME_VARIABLES_HH + +#include <dumux/boxmodels/mpnc/mpncproperties.hh> +#include <dumux/material/fluidstates/compositionalfluidstate.hh> + +namespace Dumux +{ +/*! + * \brief Contains the energy related quantities which are constant within a + * finite volume in the two-phase, N-component model. + * + * This is the dummy class for the isothermal case. Note that we're + * only isothermal in the sense that the temperature at a location and + * a time is specified outside of the model! + */ +template <class TypeTag, bool enableEnergy/*=false*/, bool kineticEnergyTransfer /*=don't care*/> +class MPNCVolumeVariablesEnergy +{ + static_assert(!(kineticEnergyTransfer && !enableEnergy), + "No kinetic energy transfer may only be enabled " + "if energy is enabled in general."); + static_assert(!kineticEnergyTransfer, + "No kinetic energy transfer module included, " + "but kinetic energy transfer enabled."); + + typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; + typedef typename GET_PROP_TYPE(TypeTag, Problem) Problem; + typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; + typedef typename GridView::template Codim<0>::Entity Element; + typedef typename GET_PROP_TYPE(TypeTag, PrimaryVariables) PrimaryVariables; + typedef typename GET_PROP_TYPE(TypeTag, FVElementGeometry) FVElementGeometry; + + enum { numPhases = GET_PROP_VALUE(TypeTag, NumPhases) }; + + typedef typename GET_PROP_TYPE(TypeTag, FluidSystem) FluidSystem; + typedef typename GET_PROP_TYPE(TypeTag, FluidState) FluidState; + typedef typename FluidSystem::ParameterCache ParameterCache; + +public: + /*! + * \brief Update the temperature of the sub-control volume. + */ + void updateTemperatures(FluidState &fs, + ParameterCache ¶mCache, + const PrimaryVariables &priVars, + const Element &element, + const FVElementGeometry &fvGeometry, + const unsigned int scvIdx, + const Problem &problem) const + { + Scalar T = problem.boxTemperature(element, fvGeometry, scvIdx); + fs.setTemperature(T); + } + + + /*! + * \brief Update the enthalpy and the internal energy for a given + * control volume. + * + * Since we are isothermal, we don't need to do anything! + */ + void update(FluidState &fs, + ParameterCache ¶mCache, + const Element &element, + const FVElementGeometry &fvGeometry, + const unsigned int scvIdx, + const Problem &problem) + { + } + + /*! + * \brief If running under valgrind this produces an error message + * if some of the object's attributes is undefined. + */ + void checkDefined() const + { + } +}; + +/*! + * \brief Contains the energy related quantities which are constant within a + * finite volume in the two-phase, N-component model. + */ +template <class TypeTag> +class MPNCVolumeVariablesEnergy<TypeTag, /*enableEnergy=*/true, /*kineticEnergyTransfer=*/false> +{ + typedef typename GET_PROP_TYPE(TypeTag, Problem) Problem; + typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; + typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; + typedef typename GridView::template Codim<0>::Entity Element; + typedef typename GET_PROP_TYPE(TypeTag, PrimaryVariables) PrimaryVariables; + typedef typename GET_PROP_TYPE(TypeTag, FVElementGeometry) FVElementGeometry; + typedef typename GET_PROP_TYPE(TypeTag, Indices) Indices; + + enum { numPhases = GET_PROP_VALUE(TypeTag, NumPhases) }; + enum { numComponents = GET_PROP_VALUE(TypeTag, NumComponents) }; + enum { temperatureIdx = Indices::temperatureIdx }; + enum { numEnergyEqs = Indices::NumPrimaryEnergyVars}; + enum { temperature0Idx = Indices::temperatureIdx }; + + typedef typename GET_PROP_TYPE(TypeTag, FluidSystem) FluidSystem; + typedef typename GET_PROP_TYPE(TypeTag, FluidState) FluidState; + typedef typename FluidSystem::ParameterCache ParameterCache; + +public: + /*! + * \brief Update the temperature of the sub-control volume. + */ + void updateTemperatures(FluidState &fs, + ParameterCache ¶mCache, + const PrimaryVariables &sol, + const Element &element, + const FVElementGeometry &fvGeometry, + const unsigned int scvIdx, + const Problem &problem) const + { + // retrieve temperature from solution vector + Scalar T = sol[temperatureIdx]; + fs.setTemperature(T); + } + + /*! + * \brief Update the enthalpy and the internal energy for a given + * control volume. + */ + void update(FluidState &fs, + ParameterCache ¶mCache, + const Element &element, + const FVElementGeometry &fvGeometry, + const unsigned int scvIdx, + const Problem &problem) + { + Valgrind::SetUndefined(*this); + + // heat capacities of the fluids plus the porous medium + heatCapacity_ = + problem.spatialParams().heatCapacity(element, fvGeometry, scvIdx); + Valgrind::CheckDefined(heatCapacity_); + + soilDensity_ = + problem.spatialParams().soilDensity(element, fvGeometry, scvIdx); + Valgrind::CheckDefined(soilDensity_); + + // set the enthalpies + for (int phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) { + Scalar h = FluidSystem::enthalpy(fs, paramCache, phaseIdx); + fs.setEnthalpy(phaseIdx, h); + } + } + + /*! + * \brief Returns the total heat capacity [J/(K m^3)] of the rock matrix in + * the sub-control volume. + */ + Scalar heatCapacity() const + { return heatCapacity_; }; + + /*! + * \brief Returns the total density of the given soil [kg / m^3] in + * the sub-control volume. + */ + Scalar soilDensity() const + { return soilDensity_; }; + + /*! + * \brief If running under valgrind this produces an error message + * if some of the object's attributes is undefined. + */ + void checkDefined() const + { + Valgrind::CheckDefined(heatCapacity_); + Valgrind::CheckDefined(soilDensity_); + }; + +protected: + Scalar heatCapacity_; + Scalar soilDensity_; +}; + +} // end namepace + +#endif diff --git a/dumux/implicit/mpnc/energy/mpncvtkwriterenergy.hh b/dumux/implicit/mpnc/energy/mpncvtkwriterenergy.hh new file mode 100644 index 0000000000..558d881b8d --- /dev/null +++ b/dumux/implicit/mpnc/energy/mpncvtkwriterenergy.hh @@ -0,0 +1,218 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \file + * + * \brief VTK writer module for the energy related quantities of the + * MpNc model. + */ +#ifndef DUMUX_MPNC_VTK_WRITER_ENERGY_HH +#define DUMUX_MPNC_VTK_WRITER_ENERGY_HH + +#include "../mpncvtkwritermodule.hh" + +namespace Dumux +{ +/*! + * \ingroup MPNCModel + * + * \brief VTK writer module for the energy related quantities of the + * MpNc model. + * + * This is the specialization for the case without energy. + */ +template<class TypeTag, + bool enableEnergy /* = false */, + bool enableKineticEnergy /* = false */> +class MPNCVtkWriterEnergy : public MPNCVtkWriterModule<TypeTag> +{ + static_assert(enableKineticEnergy == false, + "If you enable kinetic energy transfer between fluids, you" + "also have to enable the energy in general!"); + + typedef MPNCVtkWriterModule<TypeTag> ParentType; + + typedef typename GET_PROP_TYPE(TypeTag, Problem) Problem; + typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; + typedef typename GET_PROP_TYPE(TypeTag, FVElementGeometry) FVElementGeometry; + typedef typename GET_PROP_TYPE(TypeTag, ElementVolumeVariables) ElementVolumeVariables; + typedef typename GET_PROP_TYPE(TypeTag, ElementBoundaryTypes) ElementBoundaryTypes; + typedef typename GET_PROP_TYPE(TypeTag, VolumeVariables) VolumeVariables; + typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; + typedef typename GridView::template Codim<0>::Entity Element; + + enum { dim = GridView::dimension }; + + typedef typename ParentType::ScalarVector ScalarVector; + typedef typename ParentType::PhaseVector PhaseVector; + +public: + MPNCVtkWriterEnergy(const Problem &problem) + : ParentType(problem) + { + temperatureOutput_ = GET_PARAM_FROM_GROUP(TypeTag, bool, Vtk, AddTemperatures); + } + + /*! + * \brief Allocate memory for the scalar fields we would like to + * write to the VTK file. + */ + template <class MultiWriter> + void allocBuffers(MultiWriter &writer) + { + if (temperatureOutput_) this->resizeScalarBuffer_(temperature_); + } + + /*! + * \brief Modify the internal buffers according to the volume + * variables seen on an element + */ + void processElement(const Element &elem, + const FVElementGeometry &fvGeometry, + const ElementVolumeVariables &elemVolVars, + const ElementBoundaryTypes &elemBcTypes) + { + int numLocalVertices = elem.geometry().corners(); + for (int localVertexIdx = 0; localVertexIdx < numLocalVertices; ++localVertexIdx) { + const unsigned int globalIdx = this->problem_.vertexMapper().map(elem, localVertexIdx, dim); + const VolumeVariables &volVars = elemVolVars[localVertexIdx]; + + if (temperatureOutput_) + temperature_[globalIdx] = volVars.fluidState().temperature(/*phaseIdx=*/0); + } + } + + /*! + * \brief Add all buffers to the VTK output writer. + */ + template <class MultiWriter> + void commitBuffers(MultiWriter &writer) + { + if (temperatureOutput_) + this->commitScalarBuffer_(writer, "T", temperature_); + } + +private: + bool temperatureOutput_; + + ScalarVector temperature_; +}; + +/*! + * \ingroup MPNCModel + * + * \brief VTK writer module for the energy related quantities of the + * MpNc model. + * + * This is the specialization for the case with an energy equation but + * local thermal equilibrium. (i.e. no kinetic energy transfer) + */ +template<class TypeTag> +class MPNCVtkWriterEnergy<TypeTag, /* enableEnergy = */ true, /* enableKineticEnergy = */ false > + : public MPNCVtkWriterModule<TypeTag> +{ + typedef MPNCVtkWriterModule<TypeTag> ParentType; + typedef typename GET_PROP_TYPE(TypeTag, Problem) Problem; + typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; + typedef typename GET_PROP_TYPE(TypeTag, FVElementGeometry) FVElementGeometry; + typedef typename GET_PROP_TYPE(TypeTag, ElementVolumeVariables) ElementVolumeVariables; + typedef typename GET_PROP_TYPE(TypeTag, ElementBoundaryTypes) ElementBoundaryTypes; + typedef typename GET_PROP_TYPE(TypeTag, VolumeVariables) VolumeVariables; + typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; + typedef typename GridView::template Codim<0>::Entity Element; + + enum { dim = GridView::dimension }; + enum { numPhases = GET_PROP_VALUE(TypeTag, NumPhases) }; + + typedef typename ParentType::ScalarVector ScalarVector; + typedef typename ParentType::PhaseVector PhaseVector; + + +public: + MPNCVtkWriterEnergy(const Problem &problem) + : ParentType(problem) + { + temperatureOutput_ = GET_PARAM_FROM_GROUP(TypeTag, bool, Vtk, AddTemperatures); + enthalpyOutput_ = GET_PARAM_FROM_GROUP(TypeTag, bool, Vtk, AddEnthalpies); + internalEnergyOutput_ = GET_PARAM_FROM_GROUP(TypeTag, bool, Vtk, AddInternalEnergies); + } + + /*! + * \brief Allocate memory for the scalar fields we would like to + * write to the VTK file. + */ + template <class MultiWriter> + void allocBuffers(MultiWriter &writer) + { + if (temperatureOutput_) this->resizeScalarBuffer_(temperature_); + if (enthalpyOutput_) this->resizePhaseBuffer_(enthalpy_); + if (internalEnergyOutput_) this->resizePhaseBuffer_(internalEnergy_); + } + + /*! + * \brief Modify the internal buffers according to the volume + * variables seen on an element + */ + void processElement(const Element &elem, + const FVElementGeometry &fvGeometry, + const ElementVolumeVariables &elemVolVars, + const ElementBoundaryTypes &elemBcTypes) + { + const unsigned int numLocalVertices = elem.geometry().corners(); + for (int localVertexIdx = 0; localVertexIdx < numLocalVertices; ++localVertexIdx) { + int gobalIdx = this->problem_.vertexMapper().map(elem, localVertexIdx, dim); + const VolumeVariables &volVars = elemVolVars[localVertexIdx]; + + if (temperatureOutput_) temperature_[gobalIdx] = volVars.fluidState().temperature(/*phaseIdx=*/0); + for (int phaseIdx = 0; phaseIdx < numPhases; ++ phaseIdx) { + if (enthalpyOutput_) + enthalpy_[phaseIdx][gobalIdx] = volVars.fluidState().temperature(phaseIdx); + if (internalEnergyOutput_) + internalEnergy_[phaseIdx][gobalIdx] = volVars.fluidState().internalEnergy(phaseIdx); + } + } + } + + /*! + * \brief Add all buffers to the VTK output writer. + */ + template <class MultiWriter> + void commitBuffers(MultiWriter &writer) + { + if (temperatureOutput_) + this->commitScalarBuffer_(writer, "T", temperature_); + if (enthalpyOutput_) + this->commitPhaseBuffer_(writer, "h_%s", enthalpy_); + if (internalEnergyOutput_) + this->commitPhaseBuffer_(writer, "u_%s", internalEnergy_); + } + +private: + bool temperatureOutput_; + bool enthalpyOutput_; + bool internalEnergyOutput_; + + ScalarVector temperature_; + PhaseVector enthalpy_; + PhaseVector internalEnergy_; +}; + +} + +#endif diff --git a/dumux/implicit/mpnc/mass/Makefile.am b/dumux/implicit/mpnc/mass/Makefile.am new file mode 100644 index 0000000000..01a0fc6234 --- /dev/null +++ b/dumux/implicit/mpnc/mass/Makefile.am @@ -0,0 +1,4 @@ +massdir = $(includedir)/dumux/boxmodels/mpnc/mass +mass_HEADERS = *.hh + +include $(top_srcdir)/am/global-rules diff --git a/dumux/implicit/mpnc/mass/mpncindicesmass.hh b/dumux/implicit/mpnc/mass/mpncindicesmass.hh new file mode 100644 index 0000000000..06c037fc68 --- /dev/null +++ b/dumux/implicit/mpnc/mass/mpncindicesmass.hh @@ -0,0 +1,84 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \brief The indices for the mass flow part of the compositional + * multi-phase model. + */ +#ifndef DUMUX_MPNC_MASS_INDICES_HH +#define DUMUX_MPNC_MASS_INDICES_HH + +#include <dumux/boxmodels/mpnc/mpncproperties.hh> + +namespace Dumux +{ +/*! + * \brief The indices required for conservation of mass. + * + * This is the specialization for the case without kinetic mass + * transfer (i.e. assuming chemical equilibrium) + */ +template <int PVOffset, + class TypeTag, + bool enableKinetic /*=false*/> +class MPNCMassIndices +{ + static_assert(!enableKinetic, + "No kinetic mass transfer module included, " + "but kinetic mass transfer enabled."); + + enum { numComponents = GET_PROP_VALUE(TypeTag, NumComponents) }; + +public: + /*! + * \brief This module defines one new primary variable. + */ + static const unsigned int NumPrimaryVars = numComponents; + + /*! + * \brief Index for the fugacity of the first component in the + * first phase in a vector of primary variables. + * + * The next numComponents indices represent the remaining + * fugacities: + * + * fug0Idx + 0 = fugacity of component 0 + * fug0Idx + 1 = fugacity of component 1 + * ... + * fug0Idx + N - 1 = fugacity of component N + */ + static const unsigned int fug0Idx = PVOffset + 0; + + /*! + * \brief Equation index of the mass conservation equation for the + * first component. + * + * The next numComponents indices represent the equations of all + * components in all phases: + * + * conti00EqIdx + 0 = continuity of component 0 + * conti00EqIdx + 1 = continuity of component 1 + * ... + * conti00EqIdx + N - 1 = continuity of component N + */ + static const unsigned int conti0EqIdx = PVOffset + 0; +}; + +} + +#endif diff --git a/dumux/implicit/mpnc/mass/mpnclocalresidualmass.hh b/dumux/implicit/mpnc/mass/mpnclocalresidualmass.hh new file mode 100644 index 0000000000..77adf69355 --- /dev/null +++ b/dumux/implicit/mpnc/mass/mpnclocalresidualmass.hh @@ -0,0 +1,369 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +#ifndef DUMUX_MPNC_LOCAL_RESIDUAL_MASS_HH +#define DUMUX_MPNC_LOCAL_RESIDUAL_MASS_HH + +#include <dune/common/fvector.hh> + +#include <dumux/boxmodels/mpnc/mpncproperties.hh> +#include <dumux/material/constraintsolvers/compositionfromfugacities.hh> +#include <dumux/common/math.hh> +#include <dumux/common/spline.hh> + +#include "../diffusion/diffusion.hh" +#include "../energy/mpnclocalresidualenergy.hh" + +namespace Dumux +{ +/*! + * \brief The mass conservation part of the Mp-Nc model. + * + * This is the class represents methods which are shared amongst all + * mass conservation modules. + */ +template<class TypeTag> +class MPNCLocalResidualMassCommon +{ +protected: + typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; + typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; + typedef typename GET_PROP_TYPE(TypeTag, VolumeVariables) VolumeVariables; + typedef typename GET_PROP_TYPE(TypeTag, FluxVariables) FluxVariables; + + enum { dim = GridView::dimension }; + enum { numPhases = GET_PROP_VALUE(TypeTag, NumPhases) }; + enum { numComponents = GET_PROP_VALUE(TypeTag, NumComponents) }; + enum { enableDiffusion = GET_PROP_VALUE(TypeTag, EnableDiffusion) }; + enum { enableEnergy = GET_PROP_VALUE(TypeTag, EnableEnergy) }; + enum { enableKineticEnergy = GET_PROP_VALUE(TypeTag, EnableKineticEnergy) }; + + typedef typename Dune::FieldVector<Scalar, dim> DimVector; + typedef typename Dune::FieldVector<Scalar, numComponents> ComponentVector; + typedef MPNCDiffusion<TypeTag, enableDiffusion> Diffusion; + typedef MPNCLocalResidualEnergy<TypeTag, enableEnergy, enableKineticEnergy> EnergyResid; + +public: + /*! + * \brief Evaluate the amount moles within a sub-control volume in + * a phase. + * + * The result should be averaged over the volume. + */ + static void computePhaseStorage(ComponentVector &storage, + const VolumeVariables &volVars, + const unsigned int phaseIdx) + { + // compute storage term of all components within all phases + storage = 0; + for (int compIdx = 0; compIdx < numComponents; ++ compIdx) { + storage[compIdx] += + volVars.fluidState().saturation(phaseIdx)* + volVars.fluidState().molarity(phaseIdx, compIdx); + } + + storage *= volVars.porosity(); + } + + /*! + * \brief Evaluates the advective flux of all conservation + * quantities over a face of a subcontrol volume via a + * fluid phase. + */ + static void computeAdvectivePhaseFlux(ComponentVector &flux, + const FluxVariables &fluxVars, + const unsigned int phaseIdx) + { + + const Scalar volumeFlux = fluxVars.volumeFlux(phaseIdx) ; + #ifndef NDEBUG + if (!std::isfinite(volumeFlux)) + DUNE_THROW(NumericalProblem, "Calculated non-finite normal flux"); + #endif + + // retrieve the upwind weight for the mass conservation equations. Use the value + // specified via the property system as default, and overwrite + // it by the run-time parameter from the Dune::ParameterTree + const Scalar massUpwindWeight = GET_PARAM_FROM_GROUP(TypeTag, Scalar, Implicit, MassUpwindWeight); + + static bool enableSmoothUpwinding_ = GET_PARAM_FROM_GROUP(TypeTag, bool, Implicit, EnableSmoothUpwinding); + + // data attached to upstream and the downstream vertices + // of the current phase + unsigned int upIdx = fluxVars.upstreamIdx(phaseIdx); + unsigned int dnIdx = fluxVars.downstreamIdx(phaseIdx); + + const VolumeVariables &up = fluxVars.volVars(upIdx); + const VolumeVariables &dn = fluxVars.volVars(dnIdx); + + //////// + // advective fluxes of all components in the phase + //////// + for (unsigned int compIdx = 0; compIdx < numComponents; ++compIdx) { + // add advective flux of current component in current + // phase. we use full upwinding. + if (enableSmoothUpwinding_) { + const Scalar kGradPNormal = fluxVars.kGradPNormal(phaseIdx); + const Scalar mobUp = up.mobility(phaseIdx); + const Scalar conUp = up.fluidState().molarity(phaseIdx, compIdx); + + const Scalar mobDn = dn.mobility(phaseIdx); + const Scalar conDn = dn.fluidState().molarity(phaseIdx, compIdx); + + const Scalar mobConUp = mobUp*conUp; + const Scalar mobConDn = mobDn*conDn; + const Scalar meanMobCon = Dumux::harmonicMean(mobConUp, mobConDn); + + const Scalar x = std::abs(kGradPNormal); + const Scalar sign = (kGradPNormal > 0)?-1:1; + + // the direction from upstream to downstream + //GlobalPosition delta = this->curElement_().geometry().corner(upIdx); + //delta -= this->curElement_().geometry().corner(dnIdx); + + // approximate the mean viscosity at the face + const Scalar meanVisc = (up.fluidState().viscosity(phaseIdx) + + dn.fluidState().viscosity(phaseIdx))/2; + + // put the mean viscosity and permeanbility in + // relation to the viscosity of water at + // approximatly 20 degrees Celsius. + const Scalar pGradRef = 10; // [Pa/m] + const Scalar muRef = 1e-3; // [Ns/m^2] + const Scalar Kref = 1e-12; // [m^2] = approx 1 Darcy + + const Scalar faceArea = fluxVars.face().normal.two_norm(); + const Scalar eps = pGradRef * Kref * faceArea * meanVisc/muRef; // * (1e3/18e-3)/meanC; + + Scalar compFlux; + if (x >= eps) { + // we only do tricks if x is below the epsilon + // value + compFlux = x*mobConUp; + } + else { + const Scalar xPos[] = { 0, eps }; + const Scalar yPos[] = { 0, eps*mobConUp }; + const Spline<Scalar> sp2(xPos, yPos, meanMobCon, mobConUp); + compFlux = sp2.eval(x); + } + #ifndef NDEBUG + if (!std::isfinite(compFlux)) + DUNE_THROW(NumericalProblem, "Calculated non-finite normal flux in smooth upwinding"); + #endif + + flux[compIdx] = sign*compFlux; + } + else + {// not use smooth upwinding + flux[compIdx] = + volumeFlux * + (( massUpwindWeight)*up.fluidState().molarity(phaseIdx, compIdx) + + + ( 1. - massUpwindWeight)*dn.fluidState().molarity(phaseIdx, compIdx) ); + + + } + } + } + + + /*! + * \brief Evaluates the advective flux of all conservation + * quantities over a face of a subcontrol volume via a + * fluid phase. + */ + static void computeDiffusivePhaseFlux(ComponentVector &flux, + const FluxVariables &fluxVars, + const unsigned int phaseIdx) + { + if (!enableDiffusion) { + flux = 0.0; + return; + } + + + const VolumeVariables &volVarsI = fluxVars.volVars(fluxVars.face().i); + const VolumeVariables &volVarsJ = fluxVars.volVars(fluxVars.face().j); + if (volVarsI.fluidState().saturation(phaseIdx) < 1e-4 || + volVarsJ.fluidState().saturation(phaseIdx) < 1e-4) + { + return; // phase is not present in one of the finite volumes + } + + // approximate the total concentration of the phase at the + // integration point by the arithmetic mean of the + // concentration of the sub-control volumes + Scalar molarDensityAtIP; + molarDensityAtIP = volVarsI.fluidState().molarDensity(phaseIdx); + molarDensityAtIP += volVarsJ.fluidState().molarDensity(phaseIdx); + molarDensityAtIP /= 2; + + Diffusion::flux(flux, phaseIdx, fluxVars, molarDensityAtIP); + } +}; + +/*! + * \brief The mass conservation part of the Mp-Nc model. + * + * This is the specialization for the case where kinetic mass transfer + * is _not_ considered. + */ +template<class TypeTag, bool enableKinetic /*=false*/> +class MPNCLocalResidualMass +{ + static_assert(!enableKinetic, + "No kinetic mass transfer module included, " + "but kinetic mass transfer enabled."); + + typedef MPNCLocalResidualMassCommon<TypeTag> MassCommon; + typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; + typedef typename GET_PROP_TYPE(TypeTag, Indices) Indices; + typedef typename GET_PROP_TYPE(TypeTag, PrimaryVariables) PrimaryVariables; + typedef typename GET_PROP_TYPE(TypeTag, VolumeVariables) VolumeVariables; + typedef typename GET_PROP_TYPE(TypeTag, ElementVolumeVariables) ElementVolumeVariables; + typedef typename GET_PROP_TYPE(TypeTag, FluxVariables) FluxVariables; + + enum { numPhases = GET_PROP_VALUE(TypeTag, NumPhases) }; + enum { numComponents = GET_PROP_VALUE(TypeTag, NumComponents) }; + enum { conti0EqIdx = Indices::conti0EqIdx }; + enum { enableEnergy = GET_PROP_VALUE(TypeTag, EnableEnergy) }; + enum { enableKineticEnergy = GET_PROP_VALUE(TypeTag, EnableKineticEnergy) }; + + typedef typename Dune::FieldVector<Scalar, numComponents> ComponentVector; + typedef MPNCLocalResidualEnergy<TypeTag, enableEnergy, enableKineticEnergy> EnergyResid; + +public: + /*! + * \brief Calculate the storage for all mass balance equations + */ + static void computeStorage(PrimaryVariables &storage, + const VolumeVariables &volVars) + { + for (int compIdx = 0; compIdx < numComponents; ++compIdx) + storage[conti0EqIdx + compIdx] = 0.0; + + for (int phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) { + addPhaseStorage(storage, volVars, phaseIdx); + } + } + + /*! + * \brief Calculate the storage for all mass balance equations + * within a single fluid phase + */ + static void addPhaseStorage(PrimaryVariables &storage, + const VolumeVariables &volVars, + const unsigned int phaseIdx) + { + // calculate the component-wise mass storage + ComponentVector phaseComponentValues; + MassCommon::computePhaseStorage(phaseComponentValues, + volVars, + phaseIdx); + + // copy to the primary variables + for (int compIdx = 0; compIdx < numComponents; ++compIdx) + storage[conti0EqIdx + compIdx] += phaseComponentValues[compIdx]; + } + + /*! + * \brief Calculate the storage for all mass balance equations + */ + static void computeFlux(PrimaryVariables &flux, + const FluxVariables &fluxVars, + const ElementVolumeVariables & elemVolVars) + { + for (int compIdx = 0; compIdx < numComponents; ++compIdx) + flux[conti0EqIdx + compIdx] = 0.0; + + ComponentVector phaseComponentValuesAdvection(0.); + ComponentVector phaseComponentValuesDiffusion(0.); + ComponentVector phaseComponentValuesMassTransport[numPhases]; // what goes into the energy module + + for (int phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) { + MassCommon::computeAdvectivePhaseFlux(phaseComponentValuesAdvection, fluxVars, phaseIdx); + Valgrind::CheckDefined(phaseComponentValuesAdvection); + for (int compIdx = 0; compIdx < numComponents; ++compIdx) + flux[conti0EqIdx + compIdx] += + phaseComponentValuesAdvection[compIdx]; + + MassCommon::computeDiffusivePhaseFlux(phaseComponentValuesDiffusion, fluxVars, phaseIdx); + Valgrind::CheckDefined(phaseComponentValuesDiffusion); + for (int compIdx = 0; compIdx < numComponents; ++compIdx) + flux[conti0EqIdx + compIdx] += + phaseComponentValuesDiffusion[compIdx]; + + // Right now I think that adding the two contributions individually into the flux is best for debugging and understanding. + // The Energy module needs both contributions. + phaseComponentValuesMassTransport[phaseIdx] = phaseComponentValuesDiffusion + phaseComponentValuesAdvection ; + Valgrind::CheckDefined(flux); + } + + // \todo + // + // The computeflux() of the Energy module needs a + // component-wise flux (for the diffusive enthalpy transport) + // It makes some sense calling energy from here, because energy + // is carried by mass. However, it is not really a clean + // solution. + + // energy transport in fluid phases + EnergyResid::computeFlux(flux, + fluxVars, + elemVolVars, + phaseComponentValuesMassTransport); + Valgrind::CheckDefined(flux); + } + + + + /*! + * \brief Calculate the source terms for all mass balance + * equations + */ + static void computeSource(PrimaryVariables &source, + const VolumeVariables &volVars) + { + static_assert(not enableKineticEnergy, // enableKinetic is disabled, in this specialization + "In the case of kinetic energy transfer the advective energy transport between the phases has to be considered. " + "It is hard (technically) to say how much mass got transfered in the case of chemical equilibrium. " + "Therefore, kineticEnergy and no kinetic mass does not fit (yet)."); + + // mass transfer is not considered in this mass module + for (int compIdx = 0; compIdx < numComponents; ++compIdx) + source[conti0EqIdx + compIdx] = 0.0; + + + PrimaryVariables tmpPriVars(0); + // Similar to the compute Flux, the energy residual needs to be called from the + // mass residual. + ComponentVector dummy[numPhases]; + for (int iDummy =0; iDummy <numPhases; ++iDummy) + dummy[iDummy] = 0.; + EnergyResid::computeSource(tmpPriVars, + volVars, + dummy); + source += tmpPriVars; + Valgrind::CheckDefined(source); + } +}; + +} // end namepace + +#endif diff --git a/dumux/implicit/mpnc/mass/mpncvolumevariablesmass.hh b/dumux/implicit/mpnc/mass/mpncvolumevariablesmass.hh new file mode 100644 index 0000000000..625b89ae58 --- /dev/null +++ b/dumux/implicit/mpnc/mass/mpncvolumevariablesmass.hh @@ -0,0 +1,131 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \file + * + * \brief Contains the mass conservation part of the volume variables + */ +#ifndef DUMUX_MPNC_VOLUME_VARIABLES_MASS_HH +#define DUMUX_MPNC_VOLUME_VARIABLES_MASS_HH + +#include <dumux/boxmodels/mpnc/mpncproperties.hh> + +#include <dumux/material/fluidstates/compositionalfluidstate.hh> + +namespace Dumux +{ +/*! + * \brief The compositional part of the volume variables if chemical + * equilibrium _is_ assumed + */ +template <class TypeTag, bool enableKinetic /* = false */> +class MPNCVolumeVariablesMass +{ + static_assert(!enableKinetic, + "No kinetic mass transfer module included, " + "but kinetic mass transfer enabled."); + + typedef typename GET_PROP_TYPE(TypeTag, PrimaryVariables) PrimaryVariables; + typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; + typedef typename GET_PROP_TYPE(TypeTag, FluidSystem) FluidSystem; + typedef typename GET_PROP_TYPE(TypeTag, FluidState) FluidState; + typedef typename FluidSystem::ParameterCache ParameterCache; + typedef typename GET_PROP_TYPE(TypeTag, Indices) Indices; + typedef typename GET_PROP_TYPE(TypeTag, Problem) Problem; + typedef typename GET_PROP_TYPE(TypeTag, VolumeVariables) VolumeVariables; + typedef typename GET_PROP_TYPE(TypeTag, FVElementGeometry) FVElementGeometry; + typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; + typedef typename GET_PROP_TYPE(TypeTag, ConstraintSolver) ConstraintSolver; + typedef typename GridView::template Codim<0>::Entity Element; + + enum { numPhases = GET_PROP_VALUE(TypeTag, NumPhases) }; + enum { numComponents = GET_PROP_VALUE(TypeTag, NumComponents) }; + enum { fug0Idx = Indices::fug0Idx }; + + typedef Dune::FieldVector<Scalar, numComponents> ComponentVector; + +public: + /*! + * \brief Update composition of all phases in the mutable + * parameters from the primary variables. + */ + void update(FluidState &fs, + ParameterCache ¶mCache, + const PrimaryVariables &priVars, + const VolumeVariables *hint, + const Problem &problem, + const Element &element, + const FVElementGeometry &fvGeometry, + const unsigned int scvIdx) + { + ComponentVector fug; + // retrieve component fugacities + for (int compIdx = 0; compIdx < numComponents; ++compIdx) + fug[compIdx] = priVars[fug0Idx + compIdx]; + + // calculate phase compositions + for (int phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) { + // initial guess + for (int compIdx = 0; compIdx < numComponents; ++compIdx) { + Scalar x_ij = 1.0/numComponents; + if (hint) + // use the hint for the initial mole fraction! + x_ij = hint->fluidState().moleFraction(phaseIdx, compIdx); + + // set initial guess of the component's mole fraction + fs.setMoleFraction(phaseIdx, + compIdx, + x_ij); + + } + + // calculate the phase composition from the component + // fugacities + if (!hint) + // if no hint was given, we ask the constraint solver + // to make an initial guess + ConstraintSolver::guessInitial(fs, paramCache, phaseIdx, fug); + ConstraintSolver::solve(fs, paramCache, phaseIdx, fug); + + /* + std::cout << "final composition: " << FluidSystem::phaseName(phaseIdx) << "=" + << fs.moleFrac(phaseIdx, 0) << " " + << fs.moleFrac(phaseIdx, 1) << " " + << fs.moleFrac(phaseIdx, 2) << " " + << fs.moleFrac(phaseIdx, 3) << " " + << fs.moleFrac(phaseIdx, 4) << " " + << fs.moleFrac(phaseIdx, 5) << " " + << fs.moleFrac(phaseIdx, 6) << "\n"; + */ + + } + } + + /*! + * \brief If running in valgrind this makes sure that all + * quantities in the volume variables are defined. + */ + void checkDefined() const + { + } +}; + +} // end namepace + +#endif diff --git a/dumux/implicit/mpnc/mass/mpncvtkwritermass.hh b/dumux/implicit/mpnc/mass/mpncvtkwritermass.hh new file mode 100644 index 0000000000..23583d2482 --- /dev/null +++ b/dumux/implicit/mpnc/mass/mpncvtkwritermass.hh @@ -0,0 +1,118 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \file + * + * \brief VTK writer module for the mass related quantities of the + * MpNc model. + */ +#ifndef DUMUX_MPNC_VTK_WRITER_MASS_HH +#define DUMUX_MPNC_VTK_WRITER_MASS_HH + +#include "../mpncvtkwritermodule.hh" + +namespace Dumux +{ +/*! + * \ingroup MPNCModel + * + * \brief VTK writer module for the mass related quantities of the + * MpNc model. + * + * This is the specialization for the case _without_ kinetic mass + * transfer between phases. + */ +template<class TypeTag, bool enableKinetic /* = false */> +class MPNCVtkWriterMass : public MPNCVtkWriterModule<TypeTag> +{ + static_assert(!enableKinetic, + "No kinetic mass transfer module included, " + "but kinetic mass transfer enabled."); + + typedef MPNCVtkWriterModule<TypeTag> ParentType; + typedef typename GET_PROP_TYPE(TypeTag, Problem) Problem; + typedef typename GET_PROP_TYPE(TypeTag, FVElementGeometry) FVElementGeometry; + typedef typename GET_PROP_TYPE(TypeTag, ElementVolumeVariables) ElementVolumeVariables; + typedef typename GET_PROP_TYPE(TypeTag, ElementBoundaryTypes) ElementBoundaryTypes; + typedef typename GET_PROP_TYPE(TypeTag, VolumeVariables) VolumeVariables; + typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; + typedef typename GridView::template Codim<0>::Entity Element; + + enum { dim = GridView::dimension }; + enum { numComponents = GET_PROP_VALUE(TypeTag, NumComponents) }; + + typedef typename ParentType::ComponentVector ComponentVector; + bool fugacityOutput_; + +public: + MPNCVtkWriterMass(const Problem &problem) + : ParentType(problem) + { + fugacityOutput_ = GET_PARAM_FROM_GROUP(TypeTag, bool, Vtk, AddFugacities); + } + + /*! + * \brief Allocate memory for the scalar fields we would like to + * write to the VTK file. + */ + template <class MultiWriter> + void allocBuffers(MultiWriter &writer) + { + if (fugacityOutput_) this->resizeComponentBuffer_(fugacity_); + } + + /*! + * \brief Modify the internal buffers according to the volume + * variables seen on an element + */ + void processElement(const Element &elem, + const FVElementGeometry &fvElemGeom, + const ElementVolumeVariables &elemVolVars, + const ElementBoundaryTypes &elemBcTypes) + { + int numLocalVertices = elem.geometry().corners(); + for (int localVertexIdx = 0; localVertexIdx < numLocalVertices; ++localVertexIdx) { + int globalIdx = this->problem_.vertexMapper().map(elem, localVertexIdx, dim); + const VolumeVariables &volVars = elemVolVars[localVertexIdx]; + + if (fugacityOutput_) { + for (int compIdx = 0; compIdx < numComponents; ++compIdx) { + fugacity_[compIdx][globalIdx] = volVars.fluidState().fugacity(compIdx); + } + } + } + } + + /*! + * \brief Add all buffers to the VTK output writer. + */ + template <class MultiWriter> + void commitBuffers(MultiWriter &writer) + { + if (fugacityOutput_) + this->commitComponentBuffer_(writer, "f_%s", fugacity_); + } + +private: + ComponentVector fugacity_; +}; + +} + +#endif diff --git a/dumux/implicit/mpnc/mpncfluxvariables.hh b/dumux/implicit/mpnc/mpncfluxvariables.hh new file mode 100644 index 0000000000..2ef1640338 --- /dev/null +++ b/dumux/implicit/mpnc/mpncfluxvariables.hh @@ -0,0 +1,174 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \file + * + * \brief This file contains the data which is required to calculate + * all fluxes of components over a face of a finite volume. + * + * This means pressure, concentration and temperature gradients, phase + * densities at the integration point, etc. + */ +#ifndef DUMUX_MPNC_FLUX_VARIABLES_HH +#define DUMUX_MPNC_FLUX_VARIABLES_HH + +#include <dumux/common/spline.hh> + +#include "diffusion/fluxvariables.hh" +#include "energy/mpncfluxvariablesenergy.hh" +#include <dumux/boxmodels/common/boxdarcyfluxvariables.hh> +#include <dumux/boxmodels/common/boxforchheimerfluxvariables.hh> + +namespace Dumux +{ + +/*! + * \ingroup MPNCModel + * \ingroup BoxFluxVariables + * \brief This template class contains the data which is required to + * calculate all fluxes of components over a face of a finite + * volume for the two-phase, three-component model. + * + * This means pressure and concentration gradients, phase densities at + * the intergration point, etc. + */ +template <class TypeTag> +class MPNCFluxVariables + : public GET_PROP_TYPE(TypeTag, BaseFluxVariables) +{ + typedef typename GET_PROP_TYPE(TypeTag, Problem) Problem; + typedef typename GET_PROP_TYPE(TypeTag, SpatialParams) SpatialParams; + typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; + typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; + typedef typename GridView::template Codim<0>::Entity Element; + typedef typename GET_PROP_TYPE(TypeTag, ElementVolumeVariables) ElementVolumeVariables; + typedef typename GET_PROP_TYPE(TypeTag, VolumeVariables) VolumeVariables; + + typedef typename GET_PROP_TYPE(TypeTag, BaseFluxVariables) BaseFluxVariables; + + enum {dim= GridView::dimension}; + enum {numPhases = GET_PROP_VALUE(TypeTag, NumPhases)}; + enum {enableDiffusion = GET_PROP_VALUE(TypeTag, EnableDiffusion)}; + enum {enableEnergy = GET_PROP_VALUE(TypeTag, EnableEnergy)}; + enum {enableKinetic = GET_PROP_VALUE(TypeTag, EnableKinetic)}; + enum {enableKineticEnergy = GET_PROP_VALUE(TypeTag, EnableKineticEnergy)}; + + typedef Dune::FieldVector<Scalar, dim> DimVector; + typedef Dune::FieldMatrix<Scalar, dim, dim> DimMatrix; + typedef typename GET_PROP_TYPE(TypeTag, FVElementGeometry) FVElementGeometry; + typedef typename FVElementGeometry::SubControlVolumeFace SCVFace; + typedef MPNCFluxVariablesDiffusion<TypeTag, enableDiffusion> FluxVariablesDiffusion; + typedef MPNCFluxVariablesEnergy<TypeTag, enableEnergy, enableKineticEnergy> FluxVariablesEnergy; + +public: + /* + * \brief The constructor + * + * \param problem The problem + * \param element The finite element + * \param fvGeometry The finite-volume geometry in the box scheme + * \param faceIdx The local index of the SCV (sub-control-volume) face + * \param elemVolVars The volume variables of the current element + * \param onBoundary A boolean variable to specify whether the flux variables + * are calculated for interior SCV faces or boundary faces, default=false + */ + MPNCFluxVariables(const Problem &problem, + const Element &element, + const FVElementGeometry &fvGeometry, + const unsigned int faceIdx, + const ElementVolumeVariables &elemVolVars, + const bool onBoundary = false) + : BaseFluxVariables(problem, element, fvGeometry, faceIdx, elemVolVars, onBoundary), + fvGeometry_(fvGeometry), faceIdx_(faceIdx), elemVolVars_(elemVolVars), onBoundary_(onBoundary) + { + // velocities can be obtained from the Parent class. + + // update the flux data of the energy module (i.e. isothermal + // or non-isothermal) + fluxVarsEnergy_.update(problem, element, fvGeometry, this->face(), *this, elemVolVars); + + // update the flux data of the diffusion module (i.e. with or + // without diffusion) + fluxVarsDiffusion_.update(problem, element, fvGeometry, this->face(), elemVolVars); + + extrusionFactor_ = + (elemVolVars[this->face().i].extrusionFactor() + + elemVolVars[this->face().j].extrusionFactor()) / 2; + } + + /*! + * \brief Returns a reference to the volume + * variables of the i-th sub-control volume of the current + * element. + */ + const VolumeVariables &volVars(const unsigned int idx) const + { return elemVolVars_[idx]; } + + /*! + * \brief Returns th extrusion factor for the sub-control volume face + */ + Scalar extrusionFactor() const + { return extrusionFactor_; } + + //////////////////////////////////////////////// + // forward calls to the diffusion module + Scalar porousDiffCoeffL(const unsigned int compIdx) const + { return fluxVarsDiffusion_.porousDiffCoeffL(compIdx); } + + Scalar porousDiffCoeffG(const unsigned int compIIdx, + const unsigned int compJIdx) const + { return fluxVarsDiffusion_.porousDiffCoeffG(compIIdx, compJIdx); } + + const Scalar moleFraction(const unsigned int phaseIdx, + const unsigned int compIdx) const + { return fluxVarsDiffusion_.moleFraction(phaseIdx, compIdx); } + + const DimVector &moleFractionGrad(const unsigned int phaseIdx, + const unsigned int compIdx) const + { return fluxVarsDiffusion_.moleFractionGrad(phaseIdx, compIdx); } + + // end of forward calls to the diffusion module + //////////////////////////////////////////////// + + //////////////////////////////////////////////// + // forward calls to the temperature module + const DimVector &temperatureGrad() const + { return fluxVarsEnergy_.temperatureGrad(); } + + const FluxVariablesEnergy &fluxVarsEnergy() const + { return fluxVarsEnergy_; } + // end of forward calls to the temperature module + //////////////////////////////////////////////// + +private: + const FVElementGeometry &fvGeometry_; + const unsigned int faceIdx_; + const ElementVolumeVariables &elemVolVars_; + const bool onBoundary_; + + // The extrusion factor for the sub-control volume face + Scalar extrusionFactor_; + + FluxVariablesDiffusion fluxVarsDiffusion_; + FluxVariablesEnergy fluxVarsEnergy_; +}; + +} // end namepace + +#endif diff --git a/dumux/implicit/mpnc/mpncindices.hh b/dumux/implicit/mpnc/mpncindices.hh new file mode 100644 index 0000000000..eb1cc5b073 --- /dev/null +++ b/dumux/implicit/mpnc/mpncindices.hh @@ -0,0 +1,103 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +#ifndef DUMUX_MPNC_INDICES_HH +#define DUMUX_MPNC_INDICES_HH + +#include "mpncproperties.hh" + +#include "mass/mpncindicesmass.hh" +#include "energy/mpncindicesenergy.hh" + +namespace Dumux +{ +/*! + * \ingroup MPNCModel + * \ingroup BoxIndices + * \brief The primary variable and equation indices for the MpNc + * model. + */ +template <class TypeTag, int BasePVOffset = 0> +struct MPNCIndices : + public MPNCMassIndices<BasePVOffset, + TypeTag, + GET_PROP_VALUE(TypeTag, EnableKinetic) >, + public MPNCEnergyIndices<BasePVOffset + + MPNCMassIndices<0, TypeTag, GET_PROP_VALUE(TypeTag, EnableKinetic) >::NumPrimaryVars, + GET_PROP_VALUE(TypeTag, EnableEnergy), + GET_PROP_VALUE(TypeTag, EnableKineticEnergy)> +{ +private: + typedef typename GET_PROP_TYPE(TypeTag, FluidSystem) FluidSystem; + enum { enableEnergy = GET_PROP_VALUE(TypeTag, EnableEnergy) }; + enum { enableKinetic = GET_PROP_VALUE(TypeTag, EnableKinetic) }; //mass transfer + enum { enableKineticEnergy = GET_PROP_VALUE(TypeTag, EnableKineticEnergy) }; // energy transfer + enum { numPhases = FluidSystem::numPhases }; + + typedef MPNCMassIndices<BasePVOffset, TypeTag, enableKinetic> MassIndices; + typedef MPNCEnergyIndices<BasePVOffset + MassIndices::NumPrimaryVars, enableEnergy, enableKineticEnergy> EnergyIndices; + +public: + /*! + * \brief The number of primary variables / equations. + */ + // temperature + Mass Balance + constraints for switch stuff + static const unsigned int NumPrimaryVars = + MassIndices::NumPrimaryVars + + EnergyIndices::NumPrimaryVars + + numPhases; + + /*! + * \brief The number of primary variables / equations of the energy module. + */ + static const unsigned int NumPrimaryEnergyVars = + EnergyIndices::NumPrimaryVars ; + + /*! + * \brief Index of the saturation of the first phase in a vector + * of primary variables. + * + * The following (numPhases - 1) primary variables represent the + * saturations for the phases [1, ..., numPhases - 1] + */ + static const unsigned int S0Idx = + MassIndices::NumPrimaryVars + + EnergyIndices::NumPrimaryVars; + + /*! + * \brief Index of the first phase' pressure in a vector of + * primary variables. + */ + static const unsigned int p0Idx = + MassIndices::NumPrimaryVars + + EnergyIndices::NumPrimaryVars + + numPhases - 1; + + /*! + * \brief Index of the first phase NCP equation. + * + * The index for the remaining phases are consecutive. + */ + static const unsigned int phase0NcpIdx = + MassIndices::NumPrimaryVars + + EnergyIndices::NumPrimaryVars; +}; + +} + +#endif diff --git a/dumux/implicit/mpnc/mpnclocalresidual.hh b/dumux/implicit/mpnc/mpnclocalresidual.hh new file mode 100644 index 0000000000..5513b44529 --- /dev/null +++ b/dumux/implicit/mpnc/mpnclocalresidual.hh @@ -0,0 +1,247 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +#ifndef DUMUX_MPNC_LOCAL_RESIDUAL_HH +#define DUMUX_MPNC_LOCAL_RESIDUAL_HH + +#include "mpncfluxvariables.hh" +#include "diffusion/diffusion.hh" +#include "energy/mpnclocalresidualenergy.hh" +#include "mass/mpnclocalresidualmass.hh" + +#include <dumux/boxmodels/common/boxmodel.hh> +#include <dumux/common/math.hh> + +namespace Dumux +{ +/*! + * \ingroup MPNCModel + * \ingroup BoxLocalResidual + * \brief two-phase, N-component specific details needed to + * approximately calculate the local defect in the BOX scheme. + * + * This class is used to fill the gaps in BoxLocalResidual for the + * two-phase, N-component twophase flow. + */ +template<class TypeTag> +class MPNCLocalResidual : public GET_PROP_TYPE(TypeTag, BaseLocalResidual) +{ + typedef typename GET_PROP_TYPE(TypeTag, Indices) Indices; + +protected: + typedef typename GET_PROP_TYPE(TypeTag, LocalResidual) Implementation; + typedef typename GET_PROP_TYPE(TypeTag, BaseLocalResidual) ParentType; + typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; + + enum {numPhases = GET_PROP_VALUE(TypeTag, NumPhases)}; + enum {enableEnergy = GET_PROP_VALUE(TypeTag, EnableEnergy)}; + enum {enableKineticEnergy = GET_PROP_VALUE(TypeTag, EnableKineticEnergy)}; + enum {enableKinetic = GET_PROP_VALUE(TypeTag, EnableKinetic)}; + enum {phase0NcpIdx = Indices::phase0NcpIdx}; + + typedef typename GridView::template Codim<0>::Entity Element; + typedef typename GET_PROP_TYPE(TypeTag, FVElementGeometry) FVElementGeometry; + typedef typename GET_PROP_TYPE(TypeTag, PrimaryVariables) PrimaryVariables; + typedef typename GET_PROP_TYPE(TypeTag, VolumeVariables) VolumeVariables; + typedef typename GET_PROP_TYPE(TypeTag, FluxVariables) FluxVariables; + typedef typename GET_PROP_TYPE(TypeTag, ElementVolumeVariables) ElementVolumeVariables; + typedef typename GET_PROP_TYPE(TypeTag, ElementBoundaryTypes) ElementBoundaryTypes; + + typedef MPNCLocalResidualEnergy<TypeTag, enableEnergy, enableKineticEnergy> EnergyResid; + typedef MPNCLocalResidualMass<TypeTag, enableKinetic> MassResid; + +public: + /*! + * \brief Evaluate the amount all conservation quantites + * (e.g. phase mass) within a sub-control volume. + * + * The result should be averaged over the volume (e.g. phase mass + * inside a sub control volume divided by the volume) + */ + void computeStorage(PrimaryVariables &storage, + const unsigned int scvIdx, + const bool usePrevSol) const + { + // if flag usePrevSol is set, the solution from the previous + // time step is used, otherwise the current solution is + // used. The secondary variables are used accordingly. This + // is required to compute the derivative of the storage term + // using the implicit euler method. + const ElementVolumeVariables &elemVolVars = usePrevSol ? this->prevVolVars_() : this->curVolVars_(); + const VolumeVariables &volVars = elemVolVars[scvIdx]; + + storage =0; + + // compute mass and energy storage terms + MassResid::computeStorage(storage, volVars); + Valgrind::CheckDefined(storage); + EnergyResid::computeStorage(storage, volVars); + Valgrind::CheckDefined(storage); + } + + /*! + * \brief Evaluate the amount all conservation quantities + * (e.g. phase mass) within all sub-control volumes of an + * element. + */ + void addPhaseStorage(PrimaryVariables &phaseStorage, + const Element &element, + const unsigned int phaseIdx) const + { + // create a finite volume element geometry + FVElementGeometry fvGeometry; + fvGeometry.update(this->gridView_(), element); + + // calculate volume variables + ElementVolumeVariables elemVolVars; + this->model_().setHints(element, elemVolVars); + elemVolVars.update(this->problem_(), + element, + fvGeometry, + /*useOldSolution=*/false); + + // calculate the phase storage for all sub-control volumes + for (int scvIdx=0; + scvIdx < fvGeometry.numSCV; + scvIdx++) + { + PrimaryVariables tmpPriVars(0.0); + + // compute mass and energy storage terms in terms of + // averaged quantities + MassResid::addPhaseStorage(tmpPriVars, + elemVolVars[scvIdx], + phaseIdx); + EnergyResid::addPhaseStorage(tmpPriVars, + elemVolVars[scvIdx], + phaseIdx); + + // multiply with volume of sub-control volume + tmpPriVars *= fvGeometry.subContVol[scvIdx].volume; + + // Add the storage of the current SCV to the total storage + phaseStorage += tmpPriVars; + } + } + + /*! + * \brief Calculate the source term of the equation + */ + void computeSource(PrimaryVariables &source, + const unsigned int scvIdx) + { + Valgrind::SetUndefined(source); + this->problem_().boxSDSource(source, + this->element_(), + this->fvGeometry_(), + scvIdx, + this->curVolVars_() ); + const VolumeVariables &volVars = this->curVolVars_(scvIdx); + + PrimaryVariables tmp(0); + MassResid::computeSource(tmp, volVars); + source += tmp; + Valgrind::CheckDefined(source); + + /* + * EnergyResid also called in the MassResid + * 1) Makes some sense because energy is also carried by mass + * 2) The mass transfer between the phases is needed. + */ +// tmp = 0.; +// EnergyResid::computeSource(tmp, volVars); +// source += tmp; +// Valgrind::CheckDefined(source); + }; + + + /*! + * \brief Evaluates the total flux of all conservation quantities + * over a face of a subcontrol volume. + * + * \param flux The flux over the SCV (sub-control-volume) face for each component + * \param faceIdx The index of the SCV face + * \param onBoundary A boolean variable to specify whether the flux variables + * are calculated for interior SCV faces or boundary faces, default=false + */ + void computeFlux(PrimaryVariables &flux, + const unsigned int faceIdx, const bool onBoundary=false) const + { + FluxVariables fluxVars(this->problem_(), + this->element_(), + this->fvGeometry_(), + faceIdx, + this->curVolVars_(), + onBoundary); + + flux = 0.0; + MassResid::computeFlux(flux, fluxVars, this->curVolVars_() ); + Valgrind::CheckDefined(flux); +/* + * EnergyResid also called in the MassResid + * 1) Makes some sense because energy is also carried by mass + * 2) The component-wise mass flux in each phase is needed. + */ + } + + /*! + * \brief Compute the local residual, i.e. the deviation of the + * equations from zero. + */ + void eval(const Element &element) + { ParentType::eval(element); } + + /*! + * \brief Evaluate the local residual. + */ + void eval(const Element &element, + const FVElementGeometry &fvGeometry, + const ElementVolumeVariables &prevVolVars, + const ElementVolumeVariables &curVolVars, + const ElementBoundaryTypes &bcType) + { + ParentType::eval(element, + fvGeometry, + prevVolVars, + curVolVars, + bcType); + + for (int i = 0; i < this->fvGeometry_().numSCV; ++i) { + // add the two auxiliary equations, make sure that the + // dirichlet boundary condition is conserved + for (int phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) + { + if (!bcType[i].isDirichlet(phase0NcpIdx + phaseIdx)) + { + this->residual_[i][phase0NcpIdx + phaseIdx] = + this->curVolVars_(i).phaseNcp(phaseIdx); + } + } + } + } + +protected: + Implementation &asImp_() + { return *static_cast<Implementation *>(this); } + const Implementation &asImp_() const + { return *static_cast<const Implementation *>(this); } +}; + +} // end namepace + +#endif diff --git a/dumux/implicit/mpnc/mpncmodel.hh b/dumux/implicit/mpnc/mpncmodel.hh new file mode 100644 index 0000000000..8d783cca51 --- /dev/null +++ b/dumux/implicit/mpnc/mpncmodel.hh @@ -0,0 +1,188 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +#ifndef DUMUX_MPNC_MODEL_HH +#define DUMUX_MPNC_MODEL_HH + +#include "mpncproperties.hh" +#include "mpncvtkwriter.hh" + +#include <dumux/boxmodels/common/boxmodel.hh> + +namespace Dumux +{ +/*! + * \ingroup MPNCModel + * \brief A fully implicit model for M-phase, N-component flow using + * vertex centered finite volumes. + * + * This model implements a \f$M\f$-phase flow of a fluid mixture + * composed of \f$N\f$ chemical species. The phases are denoted by + * lower index \f$\alpha \in \{ 1, \dots, M \}\f$. All fluid phases + * are mixtures of \f$N \geq M - 1\f$ chemical species which are + * denoted by the upper index \f$\kappa \in \{ 1, \dots, N \} \f$. + * + * The momentum approximation can be selected via "BaseFluxVariables": + * Darcy (BoxDarcyFluxVariables) and Forchheimer (BoxForchheimerFluxVariables) + * relations are available for all Box models. + * + * By inserting this into the equations for the conservation of the + * mass of each component, one gets one mass-continuity equation for + * each component \f$\kappa\f$ + * \f[ + \sum_{\kappa} \left( + \phi \frac{\partial \left(\varrho_\alpha x_\alpha^\kappa S_\alpha\right)}{\partial t} + + + \mathrm{div}\; + \left\{ + v_\alpha + \frac{\varrho_\alpha}{\overline M_\alpha} x_\alpha^\kappa + \right\} + \right) + = q^\kappa + \f] + * with \f$\overline M_\alpha\f$ being the average molar mass of the + * phase \f$\alpha\f$: \f[ \overline M_\alpha = \sum_\kappa M^\kappa + * \; x_\alpha^\kappa \f] + * + * For the missing \f$M\f$ model assumptions, the model assumes that + * if a fluid phase is not present, the sum of the mole fractions of + * this fluid phase is smaller than \f$1\f$, i.e. + * \f[ + * \forall \alpha: S_\alpha = 0 \implies \sum_\kappa x_\alpha^\kappa \leq 1 + * \f] + * + * Also, if a fluid phase may be present at a given spatial location + * its saturation must be positive: + * \f[ \forall \alpha: \sum_\kappa x_\alpha^\kappa = 1 \implies S_\alpha \geq 0 \f] + * + * Since at any given spatial location, a phase is always either + * present or not present, one of the strict equalities on the + * right hand side is always true, i.e. + * \f[ \forall \alpha: S_\alpha \left( \sum_\kappa x_\alpha^\kappa - 1 \right) = 0 \f] + * always holds. + * + * These three equations constitute a non-linear complementarity + * problem, which can be solved using so-called non-linear + * complementarity functions \f$\Phi(a, b)\f$ which have the property + * \f[\Phi(a,b) = 0 \iff a \geq0 \land b \geq0 \land a \cdot b = 0 \f] + * + * Several non-linear complementarity functions have been suggested, + * e.g. the Fischer-Burmeister function + * \f[ \Phi(a,b) = a + b - \sqrt{a^2 + b^2} \;. \f] + * This model uses + * \f[ \Phi(a,b) = \min \{a, b \}\;, \f] + * because of its piecewise linearity. + * + * These equations are then discretized using a fully-implicit vertex + * centered finite volume scheme (often known as 'box'-scheme) for + * spatial discretization and the implicit Euler method as temporal + * discretization. + * + * The model assumes local thermodynamic equilibrium and uses the + * following primary variables: + * - The component fugacities \f$f^1, \dots, f^{N}\f$ + * - The pressure of the first phase \f$p_1\f$ + * - The saturations of the first \f$M-1\f$ phases \f$S_1, \dots, S_{M-1}\f$ + * - Temperature \f$T\f$ if the energy equation is enabled + */ +template<class TypeTag> +class MPNCModel : public GET_PROP_TYPE(TypeTag, BaseModel) +{ + typedef typename GET_PROP_TYPE(TypeTag, BaseModel) ParentType; + typedef typename GET_PROP_TYPE(TypeTag, Problem) Problem; + typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; + typedef typename GridView::template Codim<0>::Entity Element; + typedef typename GridView::template Codim<0>::Iterator ElementIterator; + typedef typename GET_PROP_TYPE(TypeTag, PrimaryVariables) PrimaryVariables; + typedef typename GET_PROP_TYPE(TypeTag, SolutionVector) SolutionVector; + typedef Dumux::MPNCVtkWriter<TypeTag> MPNCVtkWriter; + + enum {enableEnergy = GET_PROP_VALUE(TypeTag, EnableEnergy)}; + enum {enableDiffusion = GET_PROP_VALUE(TypeTag, EnableDiffusion)}; + enum {enableKinetic = GET_PROP_VALUE(TypeTag, EnableKinetic)}; + enum {enableKineticEnergy = GET_PROP_VALUE(TypeTag, EnableKineticEnergy)}; + enum {enableSmoothUpwinding = GET_PROP_VALUE(TypeTag, ImplicitEnableSmoothUpwinding)}; + enum {enablePartialReassemble = GET_PROP_VALUE(TypeTag, ImplicitEnablePartialReassemble)}; + enum {enableJacobianRecycling = GET_PROP_VALUE(TypeTag, ImplicitEnableJacobianRecycling)}; + enum {numDiffMethod = GET_PROP_VALUE(TypeTag, ImplicitNumericDifferenceMethod)}; + enum {numPhases = GET_PROP_VALUE(TypeTag, NumPhases)}; + enum {numComponents = GET_PROP_VALUE(TypeTag, NumComponents)}; + enum {numEq = GET_PROP_VALUE(TypeTag, NumEq)}; + +public: + ~MPNCModel() + { delete vtkWriter_; }; + + void init(Problem &problem) + { + ParentType::init(problem); + vtkWriter_ = new MPNCVtkWriter(problem); + + if (this->gridView_().comm().rank() == 0) + std::cout + << "Initializing M-phase N-component model: \n" + << " phases: " << numPhases << "\n" + << " components: " << numComponents << "\n" + << " equations: " << numEq << "\n" + << " kinetic mass transfer: " << enableKinetic<< "\n" + << " kinetic energy transfer: " << enableKineticEnergy<< "\n" + << " diffusion: " << enableDiffusion << "\n" + << " energy equation: " << enableEnergy << "\n" + << " smooth upwinding: " << enableSmoothUpwinding << "\n" + << " partial jacobian reassembly: " << enablePartialReassemble << "\n" + << " numeric differentiation method: " << numDiffMethod << " (-1: backward, 0: central, +1 forward)\n" + << " jacobian recycling: " << enableJacobianRecycling << "\n"; + } + + /*! + * \brief Compute the total storage inside one phase of all + * conservation quantities. + */ + void globalPhaseStorage(PrimaryVariables &phaseStorage, const unsigned int phaseIdx) + { + phaseStorage = 0; + + ElementIterator elemIt = this->gridView_().template begin<0>(); + const ElementIterator elemEndIt = this->gridView_().template end<0>(); + for (; elemIt != elemEndIt; ++elemIt) { + this->localResidual().addPhaseStorage(phaseStorage, *elemIt, phaseIdx); + } + + if (this->gridView_().comm().size() > 1) + phaseStorage = this->gridView_().comm().sum(phaseStorage); + } + + /*! + * \brief Add the result of the current timestep to the VTK output. + */ + template <class MultiWriter> + void addOutputVtkFields(const SolutionVector &sol, + MultiWriter &writer) + { + vtkWriter_->addCurrentSolution(writer); + } + + MPNCVtkWriter *vtkWriter_; +}; + +} + +#include "mpncpropertydefaults.hh" + +#endif diff --git a/dumux/implicit/mpnc/mpncnewtoncontroller.hh b/dumux/implicit/mpnc/mpncnewtoncontroller.hh new file mode 100644 index 0000000000..005def2ce2 --- /dev/null +++ b/dumux/implicit/mpnc/mpncnewtoncontroller.hh @@ -0,0 +1,266 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \file + * \brief A MpNc specific controller for the newton solver. + * + * This controller 'knows' what a 'physically meaningful' solution is + * which allows the newton method to abort quicker if the solution is + * way out of bounds. + */ +#ifndef DUMUX_MPNC_NEWTON_CONTROLLER_HH +#define DUMUX_MPNC_NEWTON_CONTROLLER_HH + +#include "mpncproperties.hh" + +#include <dumux/nonlinear/newtoncontroller.hh> +#include <algorithm> + +namespace Dumux { + +template <class TypeTag, bool enableKinetic /* = false */> +class MpNcNewtonChop +{ + typedef typename GET_PROP_TYPE(TypeTag, Indices) Indices; + typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; + typedef typename GET_PROP_TYPE(TypeTag, SolutionVector) SolutionVector; + + enum { numPhases = GET_PROP_VALUE(TypeTag, NumPhases) }; + enum { numComponents = GET_PROP_VALUE(TypeTag, NumComponents) }; + enum { fug0Idx = Indices::fug0Idx }; + enum { S0Idx = Indices::S0Idx }; + enum { p0Idx = Indices::p0Idx }; + +public: + static void chop(SolutionVector &uCurrentIter, + const SolutionVector &uLastIter) + { + for (unsigned int i = 0; i < uLastIter.size(); ++i) { + for (unsigned int phaseIdx = 0; phaseIdx < numPhases - 1; ++phaseIdx) + saturationChop_(uCurrentIter[i][S0Idx + phaseIdx], + uLastIter[i][S0Idx + phaseIdx]); + pressureChop_(uCurrentIter[i][p0Idx], uLastIter[i][p0Idx]); + for (unsigned int comp = 0; comp < numComponents; ++comp) { + pressureChop_(uCurrentIter[i][fug0Idx + comp], uLastIter[i][fug0Idx + comp]); + } + + } + }; + +private: + static void clampValue_(Scalar &val, + const Scalar minVal, + const Scalar maxVal) + { + val = std::max(minVal, std::min(val, maxVal)); + }; + + static void pressureChop_(Scalar &val, + const Scalar oldVal) + { + const Scalar maxDelta = std::max(oldVal/4.0, 10e3); + clampValue_(val, oldVal - maxDelta, oldVal + maxDelta); + val = std::max(0.0, val); // don't allow negative pressures + } + + static void saturationChop_(Scalar &val, + const Scalar oldVal) + { + const Scalar maxDelta = 0.25; + clampValue_(val, oldVal - maxDelta, oldVal + maxDelta); + clampValue_(val, -0.001, 1.001); + } + +}; + +template <class TypeTag> +class MpNcNewtonChop<TypeTag, /*enableKinetic=*/true> +{ + typedef typename GET_PROP_TYPE(TypeTag, Indices) Indices; + typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; + typedef typename GET_PROP_TYPE(TypeTag, SolutionVector) SolutionVector; + + enum { numPhases = GET_PROP_VALUE(TypeTag, NumPhases) }; + enum { numComponents = GET_PROP_VALUE(TypeTag, NumComponents) }; + enum { moleFrac00Idx = Indices::moleFrac00Idx }; + enum { S0Idx = Indices::S0Idx }; + enum { p0Idx = Indices::p0Idx }; + +public: + static void chop(SolutionVector &uCurrentIter, + const SolutionVector &uLastIter) + { + for (int i = 0; i < uLastIter.size(); ++i) { + for (int phaseIdx = 0; phaseIdx < numPhases - 1; ++phaseIdx) + saturationChop_(uCurrentIter[i][S0Idx + phaseIdx], + uLastIter[i][S0Idx + phaseIdx]); + pressureChop_(uCurrentIter[i][p0Idx], uLastIter[i][p0Idx]); + for (int phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) { + for (int compIdx = 0; compIdx < numComponents; ++compIdx) { + moleFracChop_(uCurrentIter[i][moleFrac00Idx + phaseIdx*numComponents + compIdx], + uLastIter[i][moleFrac00Idx + phaseIdx*numComponents + compIdx]); + } + } + + } + } + +private: + static void clampValue_(Scalar &val, + const Scalar minVal, + const Scalar maxVal) + { + val = std::max(minVal, std::min(val, maxVal)); + }; + + static void pressureChop_(Scalar &val, + const Scalar oldVal) + { + const Scalar maxDelta = std::max(oldVal/4.0, 10e3); + clampValue_(val, oldVal - maxDelta, oldVal + maxDelta); + val = std::max(0.0, val); // don't allow negative pressures + } + + static void saturationChop_(Scalar &val, + const Scalar oldVal) + { + const Scalar maxDelta = 0.25; + clampValue_(val, oldVal - maxDelta, oldVal + maxDelta); + clampValue_(val, -0.001, 1.001); + } + + static void moleFracChop_(Scalar &val, + const Scalar oldVal) + { + // no component mole fraction can change by more than 20% per iteration + const Scalar maxDelta = 0.20; + clampValue_(val, oldVal - maxDelta, oldVal + maxDelta); + clampValue_(val, -0.001, 1.001); + } + +}; + +/*! + * \ingroup Newton + * \brief A MpNc specific controller for the newton solver. + * + * This controller 'knows' what a 'physically meaningful' solution is + * which allows the newton method to abort quicker if the solution is + * way out of bounds. + */ +template <class TypeTag> +class MPNCNewtonController : public NewtonController<TypeTag> +{ + typedef NewtonController<TypeTag> ParentType; + typedef typename GET_PROP_TYPE(TypeTag, SolutionVector) SolutionVector; + typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; + typedef typename GET_PROP_TYPE(TypeTag, Indices) Indices; + typedef typename GET_PROP_TYPE(TypeTag, Problem) Problem; + + enum {numPhases = GET_PROP_VALUE(TypeTag, NumPhases)}; + enum { numComponents = GET_PROP_VALUE(TypeTag, NumComponents)}; + enum {enableKinetic = GET_PROP_VALUE(TypeTag, EnableKinetic)}; + enum {p0Idx = Indices::p0Idx}; + enum {S0Idx = Indices::S0Idx}; + + typedef MpNcNewtonChop<TypeTag, enableKinetic> NewtonChop; + +public: + MPNCNewtonController(const Problem &problem) + : ParentType(problem) + { + enableChop_ = GET_PARAM_FROM_GROUP(TypeTag, bool, Newton, EnableChop); + Dune::FMatrixPrecision<>::set_singular_limit(1e-35); + }; + + void newtonUpdate(SolutionVector &uCurrentIter, + const SolutionVector &uLastIter, + const SolutionVector &deltaU) + { + if (this->enableRelativeCriterion_ || this->enablePartialReassemble_) + this->newtonUpdateRelError(uLastIter, deltaU); + + // compute the vertex and element colors for partial + // reassembly + if (this->enablePartialReassemble_) { + const Scalar minReasmTol = 1e-2*this->tolerance_; + const Scalar maxReasmTol = 1e1*this->tolerance_; + Scalar reassembleTol = std::max(minReasmTol, std::min(maxReasmTol, this->error_/1e4)); + + this->model_().jacobianAssembler().updateDiscrepancy(uLastIter, deltaU); + this->model_().jacobianAssembler().computeColors(reassembleTol); + } + + this->writeConvergence_(uLastIter, deltaU); + + if (GET_PARAM_FROM_GROUP(TypeTag, bool, Newton, UseLineSearch)) { + lineSearchUpdate_(uCurrentIter, uLastIter, deltaU); + } + else { + for (unsigned int i = 0; i < uLastIter.size(); ++i) { + uCurrentIter[i] = uLastIter[i]; + uCurrentIter[i] -= deltaU[i]; + } + + if (this->numSteps_ < 2 && enableChop_) { + // put crash barriers along the update path at the + // beginning... + NewtonChop::chop(uCurrentIter, uLastIter); + } + + if (this->enableAbsoluteCriterion_) + { + SolutionVector tmp(uLastIter); + this->absoluteError_ = this->method().model().globalResidual(tmp, uCurrentIter); + this->absoluteError_ /= this->initialAbsoluteError_; + } + } + } + +private: + void lineSearchUpdate_(SolutionVector &uCurrentIter, + const SolutionVector &uLastIter, + const SolutionVector &deltaU) + { + Scalar lambda = 1.0; + Scalar globDef; + + SolutionVector tmp(uLastIter); + Scalar oldGlobDef = this->model_().globalResidual(tmp, uLastIter); + while (true) { + uCurrentIter = deltaU; + uCurrentIter *= -lambda; + uCurrentIter += uLastIter; + + globDef = this->model_().globalResidual(tmp, uCurrentIter); + if (globDef < oldGlobDef || lambda <= 1.0/64) { + this->endIterMsg() << ", defect " << oldGlobDef << "->" << globDef << "@lambda=1/" << 1/lambda; + return; + } + + // try with a smaller update + lambda /= 2; + } + } + + bool enableChop_; +}; +} + +#endif diff --git a/dumux/implicit/mpnc/mpncproperties.hh b/dumux/implicit/mpnc/mpncproperties.hh new file mode 100644 index 0000000000..a8833bb7cd --- /dev/null +++ b/dumux/implicit/mpnc/mpncproperties.hh @@ -0,0 +1,133 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +#ifndef DUMUX_MPNC_PROPERTIES_HH +#define DUMUX_MPNC_PROPERTIES_HH + +#include <dumux/boxmodels/common/boxproperties.hh> + +/*! + * \ingroup Properties + * \ingroup BoxProperties + * \ingroup BoxMpNcModel + * \file + * \brief Defines the properties required for the Mp-Nc box model. + */ +namespace Dumux +{ +namespace Properties +{ + +////////////////////////////////////////////////////////////////// +// Type tags +////////////////////////////////////////////////////////////////// + +/*! + * \brief Define the type tag for the compositional twophase box model. + */ +NEW_TYPE_TAG(BoxMPNC, INHERITS_FROM(BoxModel)); + +////////////////////////////////////////////////////////////////// +// Property tags +////////////////////////////////////////////////////////////////// +NEW_PROP_TAG(NumPhases); //!< Number of fluid phases in the system +NEW_PROP_TAG(NumComponents); //!< Number of fluid components in the system +NEW_PROP_TAG(Indices); //!< Enumerations for the model + +NEW_PROP_TAG(BaseFluxVariables); //!< The type of velocity calculation that is to be used + +NEW_PROP_TAG(MPNCVtkCommonModule); //!< Vtk writer module for writing the common quantities into the VTK output file +NEW_PROP_TAG(MPNCVtkMassModule); //!< Vtk writer module for writing the mass related quantities into the VTK output file +NEW_PROP_TAG(MPNCVtkEnergyModule); //!< Vtk writer module for writing the energy related quantities into the VTK output file +NEW_PROP_TAG(MPNCVtkCustomModule); //!< Vtk writer module for writing the user-specified quantities into the VTK output file + +NEW_PROP_TAG(VelocityAveragingInModel);//!< Should the averaging of velocities be done in the model? + +//! specify which quantities are written to the vtk output files +NEW_PROP_TAG(VtkAddPorosity); +NEW_PROP_TAG(VtkAddBoundaryTypes); +NEW_PROP_TAG(VtkAddSaturations); +NEW_PROP_TAG(VtkAddPressures); +NEW_PROP_TAG(VtkAddVarPressures); +NEW_PROP_TAG(VtkAddVelocities); +NEW_PROP_TAG(VtkAddDensities); +NEW_PROP_TAG(VtkAddMobilities); +NEW_PROP_TAG(VtkAddAverageMolarMass); +NEW_PROP_TAG(VtkAddMassFractions); +NEW_PROP_TAG(VtkAddMoleFractions); +NEW_PROP_TAG(VtkAddMolarities); +NEW_PROP_TAG(VtkAddFugacities); +NEW_PROP_TAG(VtkAddFugacityCoefficients); +NEW_PROP_TAG(VtkAddTemperatures); +NEW_PROP_TAG(VtkAddEnthalpies); +NEW_PROP_TAG(VtkAddInternalEnergies); + +NEW_PROP_TAG(VtkAddxEquil); + +NEW_PROP_TAG(VtkAddReynolds); +NEW_PROP_TAG(VtkAddPrandtl); +NEW_PROP_TAG(VtkAddNusselt); +NEW_PROP_TAG(VtkAddInterfacialArea); + +NEW_PROP_TAG(SpatialParams); //!< The type of the spatial parameters + +NEW_PROP_TAG(MaterialLaw); //!< The material law which ought to be used (extracted from the soil) +NEW_PROP_TAG(MaterialLawParams); //!< The context material law (extracted from the soil) + +//! The compositional twophase system of fluids which is considered +NEW_PROP_TAG(FluidSystem); + +//! The thermodynamic constraint solver which calculates the +//! composition of any phase given all component fugacities. +NEW_PROP_TAG(CompositionFromFugacitiesSolver); +NEW_PROP_TAG(ConstraintSolver); + +//! Enable the energy equation? +NEW_PROP_TAG(EnableEnergy); + +//! Enable diffusive fluxes? +NEW_PROP_TAG(EnableDiffusion); + +//! Enable kinetic resolution of mass transfer processes? +NEW_PROP_TAG(EnableKinetic); + +//! Enable kinetic resolution of energy transfer processes? +NEW_PROP_TAG(EnableKineticEnergy); + +//! Enable gravity? +NEW_PROP_TAG(ProblemEnableGravity); + +//! Use the smooth upwinding method? +NEW_PROP_TAG(ImplicitEnableSmoothUpwinding); + +NEW_PROP_TAG(ImplicitMassUpwindWeight); //!< The value of the weight of the upwind direction in the mass conservation equations + +NEW_PROP_TAG(ImplicitMobilityUpwindWeight); //!< Weight for the upwind mobility in the velocity calculation + +//! Chop the Newton update at the beginning of the non-linear solver? +NEW_PROP_TAG(NewtonEnableChop); + +//! Which type of fluidstate should be used? +NEW_PROP_TAG(FluidState); + +//! Property for the forchheimer coefficient +NEW_PROP_TAG(SpatialParamsForchCoeff); +} +} + +#endif diff --git a/dumux/implicit/mpnc/mpncpropertydefaults.hh b/dumux/implicit/mpnc/mpncpropertydefaults.hh new file mode 100644 index 0000000000..0ead27ef49 --- /dev/null +++ b/dumux/implicit/mpnc/mpncpropertydefaults.hh @@ -0,0 +1,279 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +#ifndef DUMUX_MPNC_PROPERTY_DEFAULTS_HH +#define DUMUX_MPNC_PROPERTY_DEFAULTS_HH + +#include "mpncindices.hh" + +#include "mpncmodel.hh" +#include "mpncindices.hh" +#include "mpnclocalresidual.hh" +#include "mpncfluxvariables.hh" +#include "mpncvolumevariables.hh" +#include "mpncproperties.hh" +#include "mpncnewtoncontroller.hh" +#include "mpncvtkwritermodule.hh" +#include "mpncvtkwritercommon.hh" +#include "mass/mpncvtkwritermass.hh" +#include "energy/mpncvtkwriterenergy.hh" + +#include <dumux/material/constraintsolvers/compositionfromfugacities.hh> +#include <dumux/material/spatialparams/boxspatialparams.hh> + + +/*! + * \ingroup Properties + * \ingroup BoxProperties + * \ingroup BoxMpNcModel + * \file + * \brief Default properties for the Mp-Nc box model. + */ +namespace Dumux +{ +namespace Properties +{ +////////////////////////////////////////////////////////////////// +// default property values +////////////////////////////////////////////////////////////////// + +/*! + * \brief Set the property for the number of components. + * + * We just forward the number from the fluid system. + */ +SET_PROP(BoxMPNC, NumComponents) +{ +private: + typedef typename GET_PROP_TYPE(TypeTag, FluidSystem) FluidSystem; + +public: + static const unsigned int value = FluidSystem::numComponents; +}; + +/*! + * \brief Set the property for the number of fluid phases. + * + * We just forward the number from the fluid system and use an static + * assert to make sure it is 2. + */ +SET_PROP(BoxMPNC, NumPhases) +{ +private: + typedef typename GET_PROP_TYPE(TypeTag, FluidSystem) FluidSystem; + +public: + static const unsigned int value = FluidSystem::numPhases; +}; + +/*! + * \brief Set the property for the number of equations and primary variables. + */ +SET_PROP(BoxMPNC, NumEq) +{ +private: + typedef typename GET_PROP_TYPE(TypeTag, Indices) Indices; + +public: + static const unsigned int value = Indices::NumPrimaryVars; +}; + +/*! + * \brief Set the property for the material parameters by extracting + * it from the material law. + */ +SET_PROP(BoxMPNC, MaterialLawParams) +{ +private: + typedef typename GET_PROP_TYPE(TypeTag, MaterialLaw) MaterialLaw; + +public: + typedef typename MaterialLaw::Params type; +}; + +/*! + * \brief Set the thermodynamic constraint solver which calculates the + * composition of any phase given all component fugacities. + * + * \deprecated version. Use "ConstraintSolver" instead of "CompositionFromFugacitiesSolver" + */ +SET_PROP(BoxMPNC, CompositionFromFugacitiesSolver) // DEPRECATED version. Use "ConstraintSolver" instead of "CompositionFromFugacitiesSolver" +{ +private: + typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; + typedef typename GET_PROP_TYPE(TypeTag, FluidSystem) FluidSystem; + +public: + typedef Dumux::CompositionFromFugacities<Scalar, FluidSystem> type; // DEPRECATED version. Use "ConstraintSolver" instead of "CompositionFromFugacitiesSolver" +}; + +/*! + * \brief Set the thermodynamic constraint solver which calculates the + * composition of any phase given all component fugacities. + */ +SET_PROP(BoxMPNC, ConstraintSolver) +{ +private: + typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; + typedef typename GET_PROP_TYPE(TypeTag, FluidSystem) FluidSystem; + +public: + typedef typename GET_PROP_TYPE(TypeTag, CompositionFromFugacitiesSolver) type; +}; + + +//! Use the MpNc local jacobian operator for the MpNc model +SET_TYPE_PROP(BoxMPNC, + LocalResidual, + MPNCLocalResidual<TypeTag>); + +//! Use the MpNc specific newton controller for the MpNc model +SET_PROP(BoxMPNC, NewtonController) +{public: + typedef MPNCNewtonController<TypeTag> type; +}; + +//! the Model property +SET_TYPE_PROP(BoxMPNC, Model, MPNCModel<TypeTag>); + +//! use an isothermal model by default +SET_BOOL_PROP(BoxMPNC, EnableEnergy, false); + +//! disable diffusion by default +SET_BOOL_PROP(BoxMPNC, EnableDiffusion, false); + +//! disable kinetic mass transfer by default +SET_BOOL_PROP(BoxMPNC, EnableKinetic, false); + +//! disable kinetic energy transfer by default +SET_BOOL_PROP(BoxMPNC, EnableKineticEnergy, false); + +//! enable smooth upwinding by default +SET_BOOL_PROP(BoxMPNC, ImplicitEnableSmoothUpwinding, false); + +//! the VolumeVariables property +SET_TYPE_PROP(BoxMPNC, VolumeVariables, MPNCVolumeVariables<TypeTag>); + +//! the FluxVariables property +SET_TYPE_PROP(BoxMPNC, FluxVariables, MPNCFluxVariables<TypeTag>); + +//! the Base of the Fluxvariables property (Darcy / Forchheimer) +SET_TYPE_PROP(BoxMPNC, BaseFluxVariables, BoxDarcyFluxVariables<TypeTag>); + +//! truncate the newton update for the first few Newton iterations of a time step +SET_BOOL_PROP(BoxMPNC, NewtonEnableChop, true); + +//! The indices required by the mpnc model +SET_TYPE_PROP(BoxMPNC, Indices, MPNCIndices<TypeTag, 0>); + +//! the upwind weight for the mass conservation equations. +SET_SCALAR_PROP(BoxMPNC, ImplicitMassUpwindWeight, 1.0); + +//! weight for the upwind mobility in the velocity calculation +SET_SCALAR_PROP(BoxMPNC, ImplicitMobilityUpwindWeight, 1.0); + +//! The spatial parameters to be employed. +//! Use BoxSpatialParams by default. +SET_TYPE_PROP(BoxMPNC, SpatialParams, BoxSpatialParams<TypeTag>); + +//! The VTK writer module for common quantities +SET_PROP(BoxMPNC, MPNCVtkCommonModule) +{ + typedef MPNCVtkWriterCommon<TypeTag> type; +}; + +//! The VTK writer module for quantities which are specific for each +//! mass module +SET_PROP(BoxMPNC, MPNCVtkMassModule) +{ +private: enum { enableKinetic = GET_PROP_VALUE(TypeTag, EnableKinetic) }; +public: typedef MPNCVtkWriterMass<TypeTag, enableKinetic> type; +}; + +//! The VTK writer module for quantities which are specific for each +//! energy module +SET_PROP(BoxMPNC, MPNCVtkEnergyModule) +{ +private: + enum { enableEnergy = GET_PROP_VALUE(TypeTag, EnableEnergy) }; + enum { enableKineticEnergy = GET_PROP_VALUE(TypeTag, EnableKineticEnergy) }; +public: + typedef MPNCVtkWriterEnergy<TypeTag, enableEnergy, enableKineticEnergy> type; +}; + +//! The VTK writer for user specified data (does nothing by default) +SET_PROP(BoxMPNC, MPNCVtkCustomModule) +{ typedef MPNCVtkWriterModule<TypeTag> type; }; + +/*! + * \brief The fluid state which is used by the volume variables to + * store the thermodynamic state. This should be chosen + * appropriately for the model ((non-)isothermal, equilibrium, ...). + * This can be done in the problem. + */ +SET_PROP(BoxMPNC, FluidState){ + private: + typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; + typedef typename GET_PROP_TYPE(TypeTag, FluidSystem) FluidSystem; + public: + typedef Dumux::CompositionalFluidState<Scalar, FluidSystem> type; +}; + +//! default value for the forchheimer coefficient +// Source: Ward, J.C. 1964 Turbulent flow in porous media. ASCE J. Hydraul. Div 90. +// Actually the Forchheimer coefficient is also a function of the dimensions of the +// porous medium. Taking it as a constant is only a first approximation +// (Nield, Bejan, Convection in porous media, 2006, p. 10) +SET_SCALAR_PROP(BoxModel, SpatialParamsForchCoeff, 0.55); + + +//!< Should the averaging of velocities be done in the Model? (By default in the output) +SET_BOOL_PROP(BoxMPNC, VelocityAveragingInModel, false); + +//! Specify what to add to the VTK output by default +SET_BOOL_PROP(BoxMPNC, VtkAddPorosity, true); +SET_BOOL_PROP(BoxMPNC, VtkAddBoundaryTypes, false); +SET_BOOL_PROP(BoxMPNC, VtkAddSaturations, true); +SET_BOOL_PROP(BoxMPNC, VtkAddPressures, true); +SET_BOOL_PROP(BoxMPNC, VtkAddVarPressures, false); +SET_BOOL_PROP(BoxMPNC, VtkAddVelocities, false); +SET_BOOL_PROP(BoxMPNC, VtkAddDensities, true); +SET_BOOL_PROP(BoxMPNC, VtkAddMobilities, true); +SET_BOOL_PROP(BoxMPNC, VtkAddAverageMolarMass, false); +SET_BOOL_PROP(BoxMPNC, VtkAddMassFractions, false); +SET_BOOL_PROP(BoxMPNC, VtkAddMoleFractions, true); +SET_BOOL_PROP(BoxMPNC, VtkAddMolarities, false); +SET_BOOL_PROP(BoxMPNC, VtkAddFugacities, false); +SET_BOOL_PROP(BoxMPNC, VtkAddFugacityCoefficients, false); +SET_BOOL_PROP(BoxMPNC, VtkAddTemperatures, false); +SET_BOOL_PROP(BoxMPNC, VtkAddEnthalpies, true); +SET_BOOL_PROP(BoxMPNC, VtkAddInternalEnergies, false); +SET_BOOL_PROP(BoxMPNC, VtkAddReynolds, false); +SET_BOOL_PROP(BoxMPNC, VtkAddPrandtl, false); +SET_BOOL_PROP(BoxMPNC, VtkAddNusselt, false); +SET_BOOL_PROP(BoxMPNC, VtkAddInterfacialArea, false); +SET_BOOL_PROP(BoxMPNC, VtkAddxEquil, false); + +// enable gravity by default +SET_BOOL_PROP(BoxMPNC, ProblemEnableGravity, true); + +} + +} + +#endif diff --git a/dumux/implicit/mpnc/mpncvolumevariables.hh b/dumux/implicit/mpnc/mpncvolumevariables.hh new file mode 100644 index 0000000000..409218898f --- /dev/null +++ b/dumux/implicit/mpnc/mpncvolumevariables.hh @@ -0,0 +1,340 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \file + * + * \brief Contains the secondary variables (Quantities which are + * constant within a finite volume) of the M-phase, N-component + * model. + */ +#ifndef DUMUX_MPNC_VOLUME_VARIABLES_HH +#define DUMUX_MPNC_VOLUME_VARIABLES_HH + +#include "diffusion/volumevariables.hh" +#include "energy/mpncvolumevariablesenergy.hh" +#include "mass/mpncvolumevariablesmass.hh" +#include "mpncvolumevariablesia.hh" + +#include <dumux/boxmodels/common/boxvolumevariables.hh> +#include <dumux/material/constraintsolvers/ncpflash.hh> + +namespace Dumux +{ +/*! + * \ingroup MPNCModel + * \ingroup BoxVolumeVariables + * \brief Contains the quantities which are are constant within a + * finite volume in the M-phase, N-component model. + */ +template <class TypeTag> +class MPNCVolumeVariables + : public BoxVolumeVariables<TypeTag> + , public MPNCVolumeVariablesIA<TypeTag, GET_PROP_VALUE(TypeTag, EnableKinetic), GET_PROP_VALUE(TypeTag, EnableKineticEnergy)> + , public MPNCVolumeVariablesMass<TypeTag, GET_PROP_VALUE(TypeTag, EnableKinetic)> + , public MPNCVolumeVariablesDiffusion<TypeTag, GET_PROP_VALUE(TypeTag, EnableDiffusion) || GET_PROP_VALUE(TypeTag, EnableKinetic)> + , public MPNCVolumeVariablesEnergy<TypeTag, GET_PROP_VALUE(TypeTag, EnableEnergy), GET_PROP_VALUE(TypeTag, EnableKineticEnergy)> +{ + typedef BoxVolumeVariables<TypeTag> ParentType; + typedef typename GET_PROP_TYPE(TypeTag, VolumeVariables) Implementation; + typedef typename GET_PROP_TYPE(TypeTag, Problem) Problem; + typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; + typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; + typedef typename GET_PROP_TYPE(TypeTag, FVElementGeometry) FVElementGeometry; + typedef typename GET_PROP_TYPE(TypeTag, FluidSystem) FluidSystem; + typedef typename GET_PROP_TYPE(TypeTag, FluidState) FluidState; + typedef typename FluidSystem::ParameterCache ParameterCache; + typedef typename GET_PROP_TYPE(TypeTag, MaterialLaw) MaterialLaw; + typedef typename GET_PROP_TYPE(TypeTag, MaterialLawParams) MaterialLawParams; + typedef typename GET_PROP_TYPE(TypeTag, PrimaryVariables) PrimaryVariables; + typedef typename GET_PROP_TYPE(TypeTag, Indices) Indices; + + enum {numPhases = GET_PROP_VALUE(TypeTag, NumPhases)}; + enum {numComponents = GET_PROP_VALUE(TypeTag, NumComponents)}; + enum {enableEnergy = GET_PROP_VALUE(TypeTag, EnableEnergy)}; + enum {enableKinetic = GET_PROP_VALUE(TypeTag, EnableKinetic)}; + enum {enableKineticEnergy = GET_PROP_VALUE(TypeTag, EnableKineticEnergy)}; + enum {enableDiffusion = GET_PROP_VALUE(TypeTag, EnableDiffusion) || enableKinetic}; + enum {S0Idx = Indices::S0Idx}; + enum {p0Idx = Indices::p0Idx}; + + typedef typename GridView::template Codim<0>::Entity Element; + typedef MPNCVolumeVariablesMass<TypeTag, enableKinetic> MassVolumeVariables; + typedef MPNCVolumeVariablesEnergy<TypeTag, enableEnergy, enableKineticEnergy> EnergyVolumeVariables; + typedef MPNCVolumeVariablesIA<TypeTag, enableKinetic, enableKineticEnergy> IAVolumeVariables; + typedef MPNCVolumeVariablesDiffusion<TypeTag, enableDiffusion> DiffusionVolumeVariables; + +public: + MPNCVolumeVariables() + { hint_ = NULL; }; + + /*! + * \brief Set the volume variables which should be used as initial + * conditions for complex calculations. + */ + void setHint(const Implementation *hint) + { + hint_ = hint; + } + + /*! + * \copydoc BoxVolumeVariables::update + */ + void update(const PrimaryVariables &priVars, + const Problem &problem, + const Element &element, + const FVElementGeometry &fvGeometry, + const unsigned int scvIdx, + const bool isOldSol) + { + Valgrind::CheckDefined(priVars); + ParentType::update(priVars, + problem, + element, + fvGeometry, + scvIdx, + isOldSol); + ParentType::checkDefined(); + + ParameterCache paramCache; + + ///////////// + // set the phase saturations + ///////////// + Scalar sumSat = 0; + for (int phaseIdx = 0; phaseIdx < numPhases - 1; ++phaseIdx) { + sumSat += priVars[S0Idx + phaseIdx]; + fluidState_.setSaturation(phaseIdx, priVars[S0Idx + phaseIdx]); + } + Valgrind::CheckDefined(sumSat); + fluidState_.setSaturation(numPhases - 1, 1.0 - sumSat); + + ///////////// + // set the fluid phase temperatures + ///////////// + EnergyVolumeVariables::updateTemperatures(fluidState_, + paramCache, + priVars, + element, + fvGeometry, + scvIdx, + problem); + + ///////////// + // set the phase pressures + ///////////// + + // capillary pressure parameters + const MaterialLawParams &materialParams = + problem.spatialParams().materialLawParams(element, fvGeometry, scvIdx); + // capillary pressures + Scalar capPress[numPhases]; + MaterialLaw::capillaryPressures(capPress, materialParams, fluidState_); + // add to the pressure of the first fluid phase + Scalar p0 = priVars[p0Idx]; + for (int phaseIdx = 0; phaseIdx < numPhases; ++ phaseIdx) + fluidState_.setPressure(phaseIdx, p0 - capPress[0] + capPress[phaseIdx]); + + ///////////// + // set the fluid compositions + ///////////// + MassVolumeVariables::update(fluidState_, + paramCache, + priVars, + hint_, + problem, + element, + fvGeometry, + scvIdx); + MassVolumeVariables::checkDefined(); + + ///////////// + // Porosity + ///////////// + + // porosity + porosity_ = problem.spatialParams().porosity(element, + fvGeometry, + scvIdx); + Valgrind::CheckDefined(porosity_); + + ///////////// + // Phase mobilities + ///////////// + + // relative permeabilities + MaterialLaw::relativePermeabilities(relativePermeability_, + materialParams, + fluidState_); + + // dynamic viscosities + for (int phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) { + // viscosities + Scalar mu = FluidSystem::viscosity(fluidState_, paramCache, phaseIdx); + fluidState_.setViscosity(phaseIdx, mu); + } + + ///////////// + // diffusion + ///////////// + + // update the diffusion part of the volume data + DiffusionVolumeVariables::update(fluidState_, paramCache, *this, problem); + DiffusionVolumeVariables::checkDefined(); + + ///////////// + // energy + ///////////// + + // update the remaining parts of the energy module + EnergyVolumeVariables::update(fluidState_, + paramCache, + element, + fvGeometry, + scvIdx, + problem); + EnergyVolumeVariables::checkDefined(); + + // make sure the quantities in the fluid state are well-defined + fluidState_.checkDefined(); + + // specific interfacial area, + // well also all the dimensionless numbers :-) + // well, also the mass transfer rate + IAVolumeVariables::update(*this, + fluidState_, + paramCache, + priVars, + problem, + element, + fvGeometry, + scvIdx); + IAVolumeVariables::checkDefined(); + checkDefined(); + } + + /*! + * \brief Return the fluid configuration at the given primary + * variables + */ + const FluidState &fluidState() const + { return fluidState_; } + + /*! + * \brief Returns the effective mobility of a given phase within + * the control volume. + */ + Scalar mobility(const unsigned int phaseIdx) const + { return relativePermeability(phaseIdx)/fluidState_.viscosity(phaseIdx); } + + /*! + * \brief Returns the viscosity of a given phase within + * the control volume. + */ + Scalar viscosity(const unsigned int phaseIdx) const + { return fluidState_.viscosity(phaseIdx); } + + /*! + * \brief Returns the relative permeability of a given phase within + * the control volume. + */ + Scalar relativePermeability(const unsigned int phaseIdx) const + { return relativePermeability_[phaseIdx]; } + + /*! + * \brief Returns the average porosity within the control volume. + */ + Scalar porosity() const + { return porosity_; } + + /*! + * \brief Returns true iff the fluid state is in the active set + * for a phase, + */ + bool isPhaseActive(const unsigned int phaseIdx) const + { + return + phasePresentIneq(fluidState(), phaseIdx) - + phaseNotPresentIneq(fluidState(), phaseIdx) + >= 0; + } + + /*! + * \brief Returns the value of the NCP-function for a phase. + */ + Scalar phaseNcp(const unsigned int phaseIdx) const + { + Scalar aEval = phaseNotPresentIneq(this->evalPoint().fluidState(), phaseIdx); + Scalar bEval = phasePresentIneq(this->evalPoint().fluidState(), phaseIdx); + if (aEval > bEval) + return phasePresentIneq(fluidState(), phaseIdx); + return phaseNotPresentIneq(fluidState(), phaseIdx); + }; + + /*! + * \brief Returns the value of the inequality where a phase is + * present. + */ + Scalar phasePresentIneq(const FluidState &fluidState, + const unsigned int phaseIdx) const + { return fluidState.saturation(phaseIdx); } + + /*! + * \brief Returns the value of the inequality where a phase is not + * present. + */ + Scalar phaseNotPresentIneq(const FluidState &fluidState, + const unsigned int phaseIdx) const + { + // difference of sum of mole fractions in the phase from 100% + Scalar a = 1; + for (int compIdx = 0; compIdx < numComponents; ++compIdx) + a -= fluidState.moleFraction(phaseIdx, compIdx); + return a; + } + + /*! + * \brief If running in valgrind this makes sure that all + * quantities in the volume variables are defined. + */ + void checkDefined() const + { +#if !defined NDEBUG && HAVE_VALGRIND + ParentType::checkDefined(); + + Valgrind::CheckDefined(porosity_); + Valgrind::CheckDefined(hint_); + Valgrind::CheckDefined(relativePermeability_); + + fluidState_.checkDefined(); +#endif + } + +protected: + Scalar porosity_; //!< Effective porosity within the control volume + Scalar relativePermeability_[numPhases]; //!< Effective relative permeability within the control volume + + const Implementation *hint_; + + //! Mass fractions of each component within each phase + FluidState fluidState_; +}; + +} // end namepace + +#endif diff --git a/dumux/implicit/mpnc/mpncvolumevariablesia.hh b/dumux/implicit/mpnc/mpncvolumevariablesia.hh new file mode 100644 index 0000000000..7e62cb7ed9 --- /dev/null +++ b/dumux/implicit/mpnc/mpncvolumevariablesia.hh @@ -0,0 +1,85 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \file + * + * \brief This class contains the volume variables required for the + * modules which require the specific interfacial area between + * fluid phases. + */ +#ifndef DUMUX_MPNC_VOLUME_VARIABLES_IA_HH +#define DUMUX_MPNC_VOLUME_VARIABLES_IA_HH + +#include <dumux/boxmodels/mpnc/mpncproperties.hh> + +namespace Dumux +{ + +/*! + * \brief This class contains the volume variables required for the + * modules which require the specific interfacial area between + * fluid phases. + * + * This is the specialization for the cases which do _not_ require + * specific interfacial area. + */ +template <class TypeTag, bool enableKinetic /* = false */, bool enableKineticEnergy /* = false */> +class MPNCVolumeVariablesIA +{ + static_assert(not enableKinetic and not enableKineticEnergy, + "The kinetic energy modules need specific interfacial area " + "but no suitable specialization of the IA volume variables module " + "has been included."); + + typedef typename GET_PROP_TYPE(TypeTag, PrimaryVariables) PrimaryVariables; + typedef typename GET_PROP_TYPE(TypeTag, FVElementGeometry) FVElementGeometry; + typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; + typedef typename GET_PROP_TYPE(TypeTag, Problem) Problem; + typedef typename GET_PROP_TYPE(TypeTag, VolumeVariables) VolumeVariables; + typedef typename GET_PROP_TYPE(TypeTag, FluidSystem) FluidSystem; + typedef typename GET_PROP_TYPE(TypeTag, FluidState) FluidState; + typedef typename FluidSystem::ParameterCache ParameterCache; + typedef typename GridView::template Codim<0>::Entity Element; + +public: + /*! + * \brief Updates the volume specific interfacial area [m^2 / m^3] between the phases. + */ + void update(const VolumeVariables & volVars, + const FluidState &fluidState, + const ParameterCache ¶mCache, + const PrimaryVariables &priVars, + const Problem &problem, + const Element & element, + const FVElementGeometry & fvGeometry, + const unsigned int scvIdx) + { + } + + /*! + * \brief If running in valgrind this makes sure that all + * quantities in the volume variables are defined. + */ + void checkDefined() const + { } +}; + +} // namespace Dumux + +#endif diff --git a/dumux/implicit/mpnc/mpncvtkwriter.hh b/dumux/implicit/mpnc/mpncvtkwriter.hh new file mode 100644 index 0000000000..afc912214e --- /dev/null +++ b/dumux/implicit/mpnc/mpncvtkwriter.hh @@ -0,0 +1,126 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +#ifndef DUMUX_MPNC_VTK_WRITER_HH +#define DUMUX_MPNC_VTK_WRITER_HH + +#include "mpncproperties.hh" + +#include <dumux/io/vtkmultiwriter.hh> + +namespace Dumux +{ +/*! + * \ingroup MPNCModel + * \brief Writes the VTK output files for a + * solution of the Mp-Nc model. + */ +template<class TypeTag> +class MPNCVtkWriter +{ + typedef typename GET_PROP_TYPE(TypeTag, MPNCVtkCommonModule) MPNCVtkCommonModule; + typedef typename GET_PROP_TYPE(TypeTag, MPNCVtkMassModule) MPNCVtkMassModule; + typedef typename GET_PROP_TYPE(TypeTag, MPNCVtkEnergyModule) MPNCVtkEnergyModule; + typedef typename GET_PROP_TYPE(TypeTag, MPNCVtkCustomModule) MPNCVtkCustomModule; + typedef typename GET_PROP_TYPE(TypeTag, ElementVolumeVariables) ElementVolumeVariables; + typedef typename GET_PROP_TYPE(TypeTag, FVElementGeometry) FVElementGeometry; + typedef typename GET_PROP_TYPE(TypeTag, ElementBoundaryTypes) ElementBoundaryTypes; + typedef typename GET_PROP_TYPE(TypeTag, Problem) Problem; + typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; + typedef typename GridView::template Codim<0>::Entity Element; + typedef typename GridView::template Codim<0>::Iterator ElementIterator; + +public: + MPNCVtkWriter(const Problem &problem) + : problem_(problem) + , commonWriter_(problem) + , massWriter_(problem) + , energyWriter_(problem) + , customWriter_(problem) + { + } + + /*! + * \brief Add the current solution to the VtkMultiWriter. + */ + template <class MultiWriter> + void addCurrentSolution(MultiWriter &writer) + { + // tell sub-writers to allocate their buffers + commonWriter_.allocBuffers(writer); + massWriter_.allocBuffers(writer); + energyWriter_.allocBuffers(writer); + customWriter_.allocBuffers(writer); + + // iterate over grid + FVElementGeometry fvGeometry; + ElementVolumeVariables elemVolVars; + ElementBoundaryTypes elemBcTypes; + + ElementIterator elemIt = problem_.gridView().template begin<0>(); + ElementIterator elemEndIt = problem_.gridView().template end<0>(); + for (; elemIt != elemEndIt; ++elemIt) + { + fvGeometry.update(problem_.gridView(), *elemIt); + elemBcTypes.update(problem_, *elemIt); + this->problem_.model().setHints(*elemIt, elemVolVars); + elemVolVars.update(problem_, + *elemIt, + fvGeometry, + false); + this->problem_.model().updateCurHints(*elemIt, elemVolVars); + + // tell the sub-writers to do what ever they need to with + // their internal buffers when a given element is seen. + commonWriter_.processElement(*elemIt, + fvGeometry, + elemVolVars, + elemBcTypes); + massWriter_.processElement(*elemIt, + fvGeometry, + elemVolVars, + elemBcTypes); + energyWriter_.processElement(*elemIt, + fvGeometry, + elemVolVars, + elemBcTypes); + customWriter_.processElement(*elemIt, + fvGeometry, + elemVolVars, + elemBcTypes); + } + + // write everything to the output file + commonWriter_.commitBuffers(writer); + massWriter_.commitBuffers(writer); + energyWriter_.commitBuffers(writer); + customWriter_.commitBuffers(writer); + } + +private: + const Problem &problem_; + + MPNCVtkCommonModule commonWriter_; + MPNCVtkMassModule massWriter_; + MPNCVtkEnergyModule energyWriter_; + MPNCVtkCustomModule customWriter_; +}; + +} + +#endif diff --git a/dumux/implicit/mpnc/mpncvtkwritercommon.hh b/dumux/implicit/mpnc/mpncvtkwritercommon.hh new file mode 100644 index 0000000000..c92536bf6d --- /dev/null +++ b/dumux/implicit/mpnc/mpncvtkwritercommon.hh @@ -0,0 +1,275 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \file + * + * \brief VTK writer module for the common quantities of the MpNc + * model. + */ +#ifndef DUMUX_MPNC_VTK_WRITER_COMMON_HH +#define DUMUX_MPNC_VTK_WRITER_COMMON_HH + +#include "mpncvtkwritermodule.hh" + +namespace Dumux +{ +/*! + * \ingroup MPNCModel + * + * \brief VTK writer module for the common quantities of the MpNc + * model. + */ +template<class TypeTag> +class MPNCVtkWriterCommon : public MPNCVtkWriterModule<TypeTag> +{ + typedef MPNCVtkWriterModule<TypeTag> ParentType; + typedef typename GET_PROP_TYPE(TypeTag, Problem) Problem; + typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; + typedef typename GET_PROP_TYPE(TypeTag, FVElementGeometry) FVElementGeometry; + typedef typename GET_PROP_TYPE(TypeTag, ElementVolumeVariables) ElementVolumeVariables; + typedef typename GET_PROP_TYPE(TypeTag, ElementBoundaryTypes) ElementBoundaryTypes; + typedef typename GET_PROP_TYPE(TypeTag, VolumeVariables) VolumeVariables; + typedef typename GET_PROP_TYPE(TypeTag, FluxVariables) FluxVariables; + typedef typename GET_PROP_TYPE(TypeTag, FluidSystem) FluidSystem; + typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; + typedef typename GridView::template Codim<0>::Entity Element; + typedef typename ParentType::ScalarVector ScalarVector; + typedef typename ParentType::PhaseVector PhaseVector; + typedef typename ParentType::ComponentVector ComponentVector; + typedef typename ParentType::PhaseComponentMatrix PhaseComponentMatrix; + + enum { dim = GridView::dimension }; + enum { numPhases = GET_PROP_VALUE(TypeTag, NumPhases) }; + enum { numComponents = GET_PROP_VALUE(TypeTag, NumComponents) }; + enum { numEq = GET_PROP_VALUE(TypeTag, NumEq) }; + + typedef Dune::FieldVector<Scalar, dim> DimVector; + typedef Dune::BlockVector<DimVector> DimField; + typedef Dune::array<DimField, numPhases> PhaseDimField; + +public: + MPNCVtkWriterCommon(const Problem &problem) + : ParentType(problem) + { + porosityOutput_ = GET_PARAM_FROM_GROUP(TypeTag, bool, Vtk, AddPorosity); + boundaryTypesOutput_ = GET_PARAM_FROM_GROUP(TypeTag, bool, Vtk, AddBoundaryTypes); + saturationOutput_ = GET_PARAM_FROM_GROUP(TypeTag, bool, Vtk, AddSaturations); + pressureOutput_ = GET_PARAM_FROM_GROUP(TypeTag, bool, Vtk, AddPressures); + velocityOutput_ = GET_PARAM_FROM_GROUP(TypeTag, bool, Vtk, AddVelocities); + densityOutput_ = GET_PARAM_FROM_GROUP(TypeTag, bool, Vtk, AddDensities); + mobilityOutput_ = GET_PARAM_FROM_GROUP(TypeTag, bool, Vtk, AddMobilities); + averageMolarMassOutput_ = GET_PARAM_FROM_GROUP(TypeTag, bool, Vtk, AddAverageMolarMass); + massFracOutput_ = GET_PARAM_FROM_GROUP(TypeTag, bool, Vtk, AddMassFractions); + moleFracOutput_ = GET_PARAM_FROM_GROUP(TypeTag, bool, Vtk, AddMoleFractions); + molarityOutput_ = GET_PARAM_FROM_GROUP(TypeTag, bool, Vtk, AddMolarities); + } + + /*! + * \brief Allocate memory for the scalar fields we would like to + * write to the VTK file. + */ + template <class MultiWriter> + void allocBuffers(MultiWriter &writer) + { + if (porosityOutput_) + this->resizeScalarBuffer_(porosity_); + if (boundaryTypesOutput_) + this->resizeScalarBuffer_(boundaryTypes_); + + if (velocityOutput_) { + Scalar nVerts = this->problem_.gridView().size(dim); + for (int phaseIdx = 0; phaseIdx < numPhases; ++ phaseIdx) { + velocity_[phaseIdx].resize(nVerts); + velocity_[phaseIdx] = 0; + } + this->resizeScalarBuffer_(boxSurface_); + } + + if (saturationOutput_) this->resizePhaseBuffer_(saturation_); + if (pressureOutput_) this->resizePhaseBuffer_(pressure_); + if (densityOutput_) this->resizePhaseBuffer_(density_); + if (mobilityOutput_) this->resizePhaseBuffer_(mobility_); + if (averageMolarMassOutput_) this->resizePhaseBuffer_(averageMolarMass_); + if (moleFracOutput_) this->resizePhaseComponentBuffer_(moleFrac_); + if (massFracOutput_) this->resizePhaseComponentBuffer_(massFrac_); + if (molarityOutput_) this->resizePhaseComponentBuffer_(molarity_); + } + + /*! + * \brief Modify the internal buffers according to the volume + * variables seen on an element + */ + void processElement(const Element &elem, + const FVElementGeometry &fvGeometry, + const ElementVolumeVariables &elemVolVars, + const ElementBoundaryTypes &elemBcTypes) + { + int numLocalVertices = elem.geometry().corners(); + for (int localVertexIdx = 0; localVertexIdx < numLocalVertices; ++localVertexIdx) { + int globalIdx = this->problem_.vertexMapper().map(elem, localVertexIdx, dim); + const VolumeVariables &volVars = elemVolVars[localVertexIdx]; + + if (porosityOutput_) porosity_[globalIdx] = volVars.porosity(); + + // calculate a single value for the boundary type: use one + // bit for each equation and set it to 1 if the equation + // is used for a dirichlet condition + int tmp = 0; + for (int eqIdx = 0; eqIdx < numEq; ++eqIdx) { + if (elemBcTypes[localVertexIdx].isDirichlet(eqIdx)) + tmp += (1 << eqIdx); + } + if (boundaryTypesOutput_) boundaryTypes_[globalIdx] = tmp; + + for (int phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) { + if (saturationOutput_) saturation_[phaseIdx][globalIdx] = volVars.fluidState().saturation(phaseIdx); + if (pressureOutput_) pressure_[phaseIdx][globalIdx] = volVars.fluidState().pressure(phaseIdx); + if (densityOutput_) density_[phaseIdx][globalIdx] = volVars.fluidState().density(phaseIdx); + if (mobilityOutput_) mobility_[phaseIdx][globalIdx] = volVars.mobility(phaseIdx); + if (averageMolarMassOutput_) averageMolarMass_[phaseIdx][globalIdx] = volVars.fluidState().averageMolarMass(phaseIdx); + for (int compIdx = 0; compIdx < numComponents; ++compIdx) { + if (moleFracOutput_) moleFrac_[phaseIdx][compIdx][globalIdx] = volVars.fluidState().moleFraction(phaseIdx, compIdx); + if (massFracOutput_) massFrac_[phaseIdx][compIdx][globalIdx] = volVars.fluidState().massFraction(phaseIdx, compIdx); + if (molarityOutput_) molarity_[phaseIdx][compIdx][globalIdx] = volVars.fluidState().molarity(phaseIdx, compIdx); + } + } + } + + // calculate velocities if requested by the problem + if (velocityOutput_) { + for (int faceIdx = 0; faceIdx < fvGeometry.numEdges; ++ faceIdx) { + int i = fvGeometry.subContVolFace[faceIdx].i; + int I = this->problem_.vertexMapper().map(elem, i, dim); + + int j = fvGeometry.subContVolFace[faceIdx].j; + int J = this->problem_.vertexMapper().map(elem, j, dim); + + FluxVariables fluxVars(this->problem_, + elem, + fvGeometry, + faceIdx, + elemVolVars); + + Scalar scvfArea = fvGeometry.subContVolFace[faceIdx].normal.two_norm(); + scvfArea *= fluxVars.extrusionFactor(); + + boxSurface_[I] += scvfArea; + boxSurface_[J] += scvfArea; + + for (int phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) { + Dune::FieldVector<Scalar, dim> velocity; + velocity = fluxVars.velocity(phaseIdx); + velocity *= scvfArea; + velocity_[phaseIdx][I] += velocity; + velocity_[phaseIdx][J] += velocity; + + } // end for all phases + } // end for all faces + } // end if velocityOutput_ + } + + /*! + * \brief Add all buffers to the VTK output writer. + */ + template <class MultiWriter> + void commitBuffers(MultiWriter &writer) + { + if (saturationOutput_) + this->commitPhaseBuffer_(writer, "S_%s", saturation_); + + if (pressureOutput_) + this->commitPhaseBuffer_(writer, "p_%s", pressure_); + + if (densityOutput_) + this->commitPhaseBuffer_(writer, "rho_%s", density_); + + if (averageMolarMassOutput_) + this->commitPhaseBuffer_(writer, "M_%s", averageMolarMass_); + + if (mobilityOutput_) + this->commitPhaseBuffer_(writer, "lambda_%s", mobility_); + + if (porosityOutput_) + this->commitScalarBuffer_(writer, "porosity", porosity_); + + if (boundaryTypesOutput_) + this->commitScalarBuffer_(writer, "boundary types", boundaryTypes_); + + if (moleFracOutput_) + this->commitPhaseComponentBuffer_(writer, "x_%s^%s", moleFrac_); + + if (massFracOutput_) + this->commitPhaseComponentBuffer_(writer, "X_%s^%s", massFrac_); + + if(molarityOutput_) + this->commitPhaseComponentBuffer_(writer, "c_%s^%s", molarity_); + + if (velocityOutput_) { + int nVerts = this->problem_.gridView().size(dim); + for (int phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) { + // first, divide the velocity field by the + // respective finite volume's surface area + for (int i = 0; i < nVerts; ++i) + velocity_[phaseIdx][i] /= boxSurface_[i]; + // commit the phase velocity + std::ostringstream oss; + oss << "velocity_" << FluidSystem::phaseName(phaseIdx); + writer.attachVertexData(velocity_[phaseIdx], + oss.str(), + dim); + } + } + } + +private: + bool porosityOutput_; + bool boundaryTypesOutput_; + bool saturationOutput_; + bool pressureOutput_; + bool velocityOutput_; + bool densityOutput_; + bool mobilityOutput_; + bool massFracOutput_; + bool moleFracOutput_; + bool molarityOutput_; + bool averageMolarMassOutput_; + + PhaseVector saturation_; + PhaseVector pressure_; + PhaseVector density_; + PhaseVector mobility_; + PhaseVector averageMolarMass_; + + PhaseDimField velocity_; + ScalarVector boxSurface_; + + ScalarVector porosity_; + ScalarVector temperature_; + ScalarVector boundaryTypes_; + + PhaseComponentMatrix moleFrac_; + PhaseComponentMatrix massFrac_; + PhaseComponentMatrix molarity_; + + ComponentVector fugacity_; +}; + +} + +#endif diff --git a/dumux/implicit/mpnc/mpncvtkwritermodule.hh b/dumux/implicit/mpnc/mpncvtkwritermodule.hh new file mode 100644 index 0000000000..061903c030 --- /dev/null +++ b/dumux/implicit/mpnc/mpncvtkwritermodule.hh @@ -0,0 +1,256 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +#ifndef DUMUX_MPNC_VTK_BASE_WRITER_HH +#define DUMUX_MPNC_VTK_BASE_WRITER_HH + +#include <cstdio> + +#include <dune/common/array.hh> +#include <dune/istl/bvector.hh> + +#include <dumux/io/vtkmultiwriter.hh> +#include "mpncproperties.hh" + +namespace Dumux +{ +/*! + * \ingroup MPNCModel + * + * \brief A VTK writer module which adheres to the required API but + * does nothing. + * + * This class also provides some convenience methods for buffer + * management. + */ +template<class TypeTag> +class MPNCVtkWriterModule +{ + typedef typename GET_PROP_TYPE(TypeTag, Problem) Problem; + typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; + typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; + typedef typename GET_PROP_TYPE(TypeTag, FVElementGeometry) FVElementGeometry; + typedef typename GET_PROP_TYPE(TypeTag, ElementVolumeVariables) ElementVolumeVariables; + typedef typename GET_PROP_TYPE(TypeTag, ElementBoundaryTypes) ElementBoundaryTypes; + typedef typename GET_PROP_TYPE(TypeTag, FluidSystem) FluidSystem; + typedef typename GridView::template Codim<0>::Entity Element; + + enum { numPhases = GET_PROP_VALUE(TypeTag, NumPhases) }; + enum { numComponents = GET_PROP_VALUE(TypeTag, NumComponents) }; + enum { dim = GridView::dimension }; + +public: + typedef std::vector<Dune::FieldVector<Scalar, 1> > ScalarVector; + typedef Dune::array<ScalarVector, numPhases> PhaseVector; + typedef Dune::array<ScalarVector, numComponents> ComponentVector; + typedef Dune::array<ComponentVector, numPhases> PhaseComponentMatrix; + + MPNCVtkWriterModule(const Problem &problem) + : problem_(problem) + { + } + + /*! + * \brief Allocate memory for the scalar fields we would like to + * write to the VTK file. + */ + template <class MultiWriter> + void allocBuffers(MultiWriter &writer) + { + } + + /*! + * \brief Modify the internal buffers according to the volume + * variables seen on an element + */ + void processElement(const Element &elem, + const FVElementGeometry &fvGeometry, + const ElementVolumeVariables &elemCurVolVars, + const ElementBoundaryTypes &elemBcTypes) + { + } + + /*! + * \brief Add all buffers to the VTK output writer. + */ + template <class MultiWriter> + void commitBuffers(MultiWriter &writer) + { + } + +protected: + /*! + * \brief Allocate the space for a buffer storing a scalar quantity + */ + void resizeScalarBuffer_(ScalarVector &buffer, + bool vertexCentered = true) + { + Scalar n; // numVertices for vertexCentereed, numVolumes for volume centered + if (vertexCentered) + n = problem_.gridView().size(dim); + else + n = problem_.gridView().size(0); + + buffer.resize(n); + std::fill(buffer.begin(), buffer.end(), 0.0); + } + + /*! + * \brief Allocate the space for a buffer storing a phase-specific + * quantity + */ + void resizePhaseBuffer_(PhaseVector &buffer, + bool vertexCentered = true) + { + Scalar n; // numVertices for vertexCentereed, numVolumes for volume centered + if (vertexCentered) + n = problem_.gridView().size(dim); + else + n = problem_.gridView().size(0); + + for (int phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) { + buffer[phaseIdx].resize(n); + std::fill(buffer[phaseIdx].begin(), buffer[phaseIdx].end(), 0.0); + } + } + + /*! + * \brief Allocate the space for a buffer storing a component + * specific quantity + */ + void resizeComponentBuffer_(ComponentVector &buffer, + bool vertexCentered = true) + { + Scalar n;// numVertices for vertexCentereed, numVolumes for volume centered + if (vertexCentered) + n = problem_.gridView().size(dim); + else + n = problem_.gridView().size(0); + + for (int compIdx = 0; compIdx < numComponents; ++compIdx) { + buffer[compIdx].resize(n); + std::fill(buffer[compIdx].begin(), buffer[compIdx].end(), 0.0); + } + } + + /*! + * \brief Allocate the space for a buffer storing a phase and + * component specific buffer + */ + void resizePhaseComponentBuffer_(PhaseComponentMatrix &buffer, + bool vertexCentered = true) + { + Scalar n;// numVertices for vertexCentereed, numVolumes for volume centered + if (vertexCentered) + n = problem_.gridView().size(dim); + else + n = problem_.gridView().size(0); + + for (int phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) { + for (int compIdx = 0; compIdx < numComponents; ++compIdx) { + buffer[phaseIdx][compIdx].resize(n); + std::fill(buffer[phaseIdx][compIdx].begin(), buffer[phaseIdx][compIdx].end(), 0.0); + } + } + } + + /*! + * \brief Add a phase-specific buffer to the VTK result file. + */ + template <class MultiWriter> + void commitScalarBuffer_(MultiWriter &writer, + const char *name, + ScalarVector &buffer, + bool vertexCentered = true) + { + if (vertexCentered) + writer.attachVertexData(buffer, name, 1); + else + writer.attachCellData(buffer, name, 1); + } + + /*! + * \brief Add a phase-specific buffer to the VTK result file. + */ + template <class MultiWriter> + void commitPhaseBuffer_(MultiWriter &writer, + const char *pattern, + PhaseVector &buffer, + bool vertexCentered = true) + { + char name[512]; + for (int phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) { + snprintf(name, 512, pattern, FluidSystem::phaseName(phaseIdx)); + + if (vertexCentered) + writer.attachVertexData(buffer[phaseIdx], name, 1); + else + writer.attachCellData(buffer[phaseIdx], name, 1); + } + } + + /*! + * \brief Add a component-specific buffer to the VTK result file. + */ + template <class MultiWriter> + void commitComponentBuffer_(MultiWriter &writer, + const char *pattern, + ComponentVector &buffer, + bool vertexCentered = true) + { + char name[512]; + for (int compIdx = 0; compIdx < numComponents; ++compIdx) { + snprintf(name, 512, pattern, FluidSystem::componentName(compIdx)); + + if (vertexCentered) + writer.attachVertexData(buffer[compIdx], name, 1); + else + writer.attachCellData(buffer[compIdx], name, 1); + } + } + + /*! + * \brief Add a phase and component specific quantities to the output. + */ + template <class MultiWriter> + void commitPhaseComponentBuffer_(MultiWriter &writer, + const char *pattern, + PhaseComponentMatrix &buffer, + bool vertexCentered = true) + { + char name[512]; + for (int phaseIdx= 0; phaseIdx < numPhases; ++phaseIdx) { + for (int compIdx = 0; compIdx < numComponents; ++compIdx) { + snprintf(name, 512, pattern, + FluidSystem::phaseName(phaseIdx), + FluidSystem::componentName(compIdx)); + + if (vertexCentered) + writer.attachVertexData(buffer[phaseIdx][compIdx], name, 1); + else + writer.attachCellData(buffer[phaseIdx][compIdx], name, 1); + } + } + } + + const Problem &problem_; +}; + +} + +#endif diff --git a/dumux/implicit/richards/Makefile.am b/dumux/implicit/richards/Makefile.am new file mode 100644 index 0000000000..26a58204fb --- /dev/null +++ b/dumux/implicit/richards/Makefile.am @@ -0,0 +1,4 @@ +richardsdir = $(includedir)/dumux/boxmodels/richards +richards_HEADERS = *.hh + +include $(top_srcdir)/am/global-rules diff --git a/dumux/implicit/richards/richardsindices.hh b/dumux/implicit/richards/richardsindices.hh new file mode 100644 index 0000000000..47e341a02a --- /dev/null +++ b/dumux/implicit/richards/richardsindices.hh @@ -0,0 +1,67 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \file + * + * \brief Index names for the Richards model. + */ +#ifndef DUMUX_RICHARDS_INDICES_HH +#define DUMUX_RICHARDS_INDICES_HH + +#include "richardsproperties.hh" + +namespace Dumux +{ +// \{ + +/*! + * \ingroup RichardsModel + * \ingroup BoxIndices + * \brief Index names for the Richards model. + */ + +template <class TypeTag> +struct RichardsIndices +{ + typedef typename GET_PROP_TYPE(TypeTag, FluidSystem) FluidSystem; + + ////////// + // primary variable indices + ////////// + + //! Primary variable index for the wetting phase pressure + static const int pwIdx = 0; + + ////////// + // equation indices + ////////// + //! Equation index for the mass conservation of the wetting phase + static const int contiEqIdx = 0; + + ////////// + // phase indices + ////////// + static const int wPhaseIdx = FluidSystem::wPhaseIdx; //!< Index of the wetting phase; + static const int nPhaseIdx = FluidSystem::nPhaseIdx; //!< Index of the non-wetting phase; +}; +// \} + +} // end namepace + +#endif diff --git a/dumux/implicit/richards/richardslocalresidual.hh b/dumux/implicit/richards/richardslocalresidual.hh new file mode 100644 index 0000000000..b1064f54c0 --- /dev/null +++ b/dumux/implicit/richards/richardslocalresidual.hh @@ -0,0 +1,158 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \file + * + * \brief Element-wise calculation of the residual for the Richards box model. + */ +#ifndef DUMUX_RICHARDS_LOCAL_RESIDUAL_HH +#define DUMUX_RICHARDS_LOCAL_RESIDUAL_HH + +#include <dumux/boxmodels/common/boxlocalresidual.hh> + +#include "richardsvolumevariables.hh" + +namespace Dumux +{ +/*! + * \ingroup RichardsModel + * \ingroup BoxLocalResidual + * \brief Element-wise calculation of the residual for the Richards box model. + */ +template<class TypeTag> +class RichardsLocalResidual : public GET_PROP_TYPE(TypeTag, BaseLocalResidual) +{ + typedef typename GET_PROP_TYPE(TypeTag, PrimaryVariables) PrimaryVariables; + typedef typename GET_PROP_TYPE(TypeTag, VolumeVariables) VolumeVariables; + typedef typename GET_PROP_TYPE(TypeTag, FluxVariables) FluxVariables; + + typedef typename GET_PROP_TYPE(TypeTag, Indices) Indices; + enum { + contiEqIdx = Indices::contiEqIdx, + wPhaseIdx = Indices::wPhaseIdx + }; + + typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; + enum { dim = GridView::dimension}; + + typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; + typedef Dune::FieldVector<Scalar, dim> DimVector; + +public: + /*! + * \brief Constructor. Sets the upwind weight. + */ + RichardsLocalResidual() + { + // retrieve the upwind weight for the mass conservation equations. Use the value + // specified via the property system as default, and overwrite + // it by the run-time parameter from the Dune::ParameterTree + massUpwindWeight_ = GET_PARAM_FROM_GROUP(TypeTag, Scalar, Implicit, MassUpwindWeight); + }; + + /*! + * \brief Evaluate the rate of change of all conservation + * quantites (e.g. phase mass) within a sub control + * volume of a finite volume element for the Richards + * model. + * + * This function should not include the source and sink terms. + * + * \param storage Stores the average mass per unit volume for each phase \f$\mathrm{[kg/m^3]}\f$ + * \param scvIdx The sub control volume index of the current element + * \param usePrevSol Calculate the storage term of the previous solution + * instead of the model's current solution. + */ + void computeStorage(PrimaryVariables &storage, const int scvIdx, bool usePrevSol) const + { + // if flag usePrevSol is set, the solution from the previous + // time step is used, otherwise the current solution is + // used. The secondary variables are used accordingly. This + // is required to compute the derivative of the storage term + // using the implicit euler method. + const VolumeVariables &volVars = + usePrevSol ? + this->prevVolVars_(scvIdx) : + this->curVolVars_(scvIdx); + + // partial time derivative of the wetting phase mass + storage[contiEqIdx] = + volVars.density(wPhaseIdx) + * volVars.saturation(wPhaseIdx) + * volVars.porosity(); + } + + + /*! + * \brief Evaluates the mass flux over a face of a subcontrol + * volume. + * + * \param flux Stores the total mass fluxes over a sub-control volume face + * of the current element \f$\mathrm{[kg/s]}\f$ + * \param faceIdx The sub control volume face index inside the current + * element + * \param onBoundary A boolean variable to specify whether the flux variables + * are calculated for interior SCV faces or boundary faces, default=false + */ + void computeFlux(PrimaryVariables &flux, const int faceIdx, bool onBoundary=false) const + { + FluxVariables fluxVars(this->problem_(), + this->element_(), + this->fvGeometry_(), + faceIdx, + this->curVolVars_(), + onBoundary); + + // data attached to upstream and the downstream vertices + // of the current phase + const VolumeVariables &up = this->curVolVars_(fluxVars.upstreamIdx(wPhaseIdx)); + const VolumeVariables &dn = this->curVolVars_(fluxVars.downstreamIdx(wPhaseIdx)); + + flux[contiEqIdx] = + fluxVars.volumeFlux(wPhaseIdx) + * + (( massUpwindWeight_)*up.density(wPhaseIdx) + + + (1 - massUpwindWeight_)*dn.density(wPhaseIdx)); + } + + /*! + * \brief Calculate the source term of the equation + * + * \param source Stores the average source term of all phases inside a + * sub-control volume of the current element \f$\mathrm{[kg/(m^3 * s)]}\f$ + * \param scvIdx The sub control volume index inside the current + * element + */ + void computeSource(PrimaryVariables &source, const int scvIdx) + { + this->problem_().boxSDSource(source, + this->element_(), + this->fvGeometry_(), + scvIdx, + this->curVolVars_()); + } + +private: + Scalar massUpwindWeight_; +}; + +} + +#endif diff --git a/dumux/implicit/richards/richardsmodel.hh b/dumux/implicit/richards/richardsmodel.hh new file mode 100644 index 0000000000..ff71be4f6f --- /dev/null +++ b/dumux/implicit/richards/richardsmodel.hh @@ -0,0 +1,208 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ + +/*! +* \file +* +* \brief Adaption of the box scheme to the Richards model. +*/ +#ifndef DUMUX_RICHARDS_MODEL_HH +#define DUMUX_RICHARDS_MODEL_HH + +#include <dumux/boxmodels/common/boxmodel.hh> + +#include "richardslocalresidual.hh" +#include "richardsproblem.hh" + +namespace Dumux +{ +/*! + * \ingroup RichardsModel + * + * \brief This model which implements a variant of the Richards' + * equation for quasi-twophase flow. + * + * In the unsaturated zone, Richards' equation + \f[ + \frac{\partial\;\phi S_w \rho_w}{\partial t} + - + \text{div} \left( + \rho_w \frac{k_{rw}}{\mu_w} \; \mathbf{K} \; + \text{\textbf{grad}}\left( + p_w - g\rho_w + \right) + \right) + = + q_w, + \f] + * is frequently used to + * approximate the water distribution above the groundwater level. + * + * It can be derived from the two-phase equations, i.e. + \f[ + \frac{\partial\;\phi S_\alpha \rho_\alpha}{\partial t} + - + \text{div} \left( + \rho_\alpha \frac{k_{r\alpha}}{\mu_\alpha}\; \mathbf{K} \; + \text{\textbf{grad}}\left( + p_\alpha - g\rho_\alpha + \right) + \right) + = + q_\alpha, + \f] + * where \f$\alpha \in \{w, n\}\f$ is the fluid phase, + * \f$\rho_\alpha\f$ is the fluid density, \f$S_\alpha\f$ is the fluid + * saturation, \f$\phi\f$ is the porosity of the soil, + * \f$k_{r\alpha}\f$ is the relative permeability for the fluid, + * \f$\mu_\alpha\f$ is the fluid's dynamic viscosity, \f$\mathbf{K}\f$ is the + * intrinsic permeability, \f$p_\alpha\f$ is the fluid pressure and + * \f$g\f$ is the potential of the gravity field. + * + * In contrast to the full two-phase model, the Richards model assumes + * gas as the non-wetting fluid and that it exhibits a much lower + * viscosity than the (liquid) wetting phase. (For example at + * atmospheric pressure and at room temperature, the viscosity of air + * is only about \f$1\%\f$ of the viscosity of liquid water.) As a + * consequence, the \f$\frac{k_{r\alpha}}{\mu_\alpha}\f$ term + * typically is much larger for the gas phase than for the wetting + * phase. For this reason, the Richards model assumes that + * \f$\frac{k_{rn}}{\mu_n}\f$ is infinitly large. This implies that + * the pressure of the gas phase is equivalent to the static pressure + * distribution and that therefore, mass conservation only needs to be + * considered for the wetting phase. + * + * The model thus choses the absolute pressure of the wetting phase + * \f$p_w\f$ as its only primary variable. The wetting phase + * saturation is calculated using the inverse of the capillary + * pressure, i.e. + \f[ + S_w = p_c^{-1}(p_n - p_w) + \f] + * holds, where \f$p_n\f$ is a given reference pressure. Nota bene, + * that the last step is assumes that the capillary + * pressure-saturation curve can be uniquely inverted, so it is not + * possible to set the capillary pressure to zero when using the + * Richards model! + */ +template<class TypeTag > +class RichardsModel : public GET_PROP_TYPE(TypeTag, BaseModel) +{ + typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; + typedef typename GET_PROP_TYPE(TypeTag, FVElementGeometry) FVElementGeometry; + typedef typename GET_PROP_TYPE(TypeTag, VolumeVariables) VolumeVariables; + typedef typename GET_PROP_TYPE(TypeTag, SolutionVector) SolutionVector; + + typedef typename GET_PROP_TYPE(TypeTag, Indices) Indices; + enum { + nPhaseIdx = Indices::nPhaseIdx, + wPhaseIdx = Indices::wPhaseIdx + }; + + typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; + typedef typename GridView::template Codim<0>::Iterator ElementIterator; + enum { dim = GridView::dimension }; + +public: + /*! + * \brief All relevant primary and secondary of a given + * solution to an ouput writer. + * + * \param sol The current solution which ought to be written to disk + * \param writer The Dumux::VtkMultiWriter which is be used to write the data + */ + template <class MultiWriter> + void addOutputVtkFields(const SolutionVector &sol, MultiWriter &writer) + { + typedef Dune::BlockVector<Dune::FieldVector<double, 1> > ScalarField; + + // create the required scalar fields + unsigned numVertices = this->problem_().gridView().size(dim); + ScalarField *pW = writer.allocateManagedBuffer(numVertices); + ScalarField *pN = writer.allocateManagedBuffer(numVertices); + ScalarField *pC = writer.allocateManagedBuffer(numVertices); + ScalarField *Sw = writer.allocateManagedBuffer(numVertices); + ScalarField *Sn = writer.allocateManagedBuffer(numVertices); + ScalarField *rhoW = writer.allocateManagedBuffer(numVertices); + ScalarField *rhoN = writer.allocateManagedBuffer(numVertices); + ScalarField *mobW = writer.allocateManagedBuffer(numVertices); + ScalarField *mobN = writer.allocateManagedBuffer(numVertices); + ScalarField *poro = writer.allocateManagedBuffer(numVertices); + ScalarField *Te = writer.allocateManagedBuffer(numVertices); + + unsigned numElements = this->gridView_().size(0); + ScalarField *rank = + writer.allocateManagedBuffer (numElements); + + FVElementGeometry fvElemGeom; + VolumeVariables volVars; + + ElementIterator elemIt = this->gridView_().template begin<0>(); + ElementIterator elemEndIt = this->gridView_().template end<0>(); + for (; elemIt != elemEndIt; ++elemIt) + { + int idx = this->problem_().model().elementMapper().map(*elemIt); + (*rank)[idx] = this->gridView_().comm().rank(); + + fvElemGeom.update(this->gridView_(), *elemIt); + + int numVerts = elemIt->template count<dim> (); + for (int i = 0; i < numVerts; ++i) + { + int globalIdx = this->vertexMapper().map(*elemIt, i, dim); + volVars.update(sol[globalIdx], + this->problem_(), + *elemIt, + fvElemGeom, + i, + false); + + (*pW)[globalIdx] = volVars.pressure(wPhaseIdx); + (*pN)[globalIdx] = volVars.pressure(nPhaseIdx); + (*pC)[globalIdx] = volVars.capillaryPressure(); + (*Sw)[globalIdx] = volVars.saturation(wPhaseIdx); + (*Sn)[globalIdx] = volVars.saturation(nPhaseIdx); + (*rhoW)[globalIdx] = volVars.density(wPhaseIdx); + (*rhoN)[globalIdx] = volVars.density(nPhaseIdx); + (*mobW)[globalIdx] = volVars.mobility(wPhaseIdx); + (*mobN)[globalIdx] = volVars.mobility(nPhaseIdx); + (*poro)[globalIdx] = volVars.porosity(); + (*Te)[globalIdx] = volVars.temperature(); + } + } + + writer.attachVertexData(*Sn, "Sn"); + writer.attachVertexData(*Sw, "Sw"); + writer.attachVertexData(*pN, "pn"); + writer.attachVertexData(*pW, "pw"); + writer.attachVertexData(*pC, "pc"); + writer.attachVertexData(*rhoW, "rhoW"); + writer.attachVertexData(*rhoN, "rhoN"); + writer.attachVertexData(*mobW, "mobW"); + writer.attachVertexData(*mobN, "mobN"); + writer.attachVertexData(*poro, "porosity"); + writer.attachVertexData(*Te, "temperature"); + writer.attachCellData(*rank, "process rank"); + } +}; +} + +#include "richardspropertydefaults.hh" + +#endif diff --git a/dumux/implicit/richards/richardsnewtoncontroller.hh b/dumux/implicit/richards/richardsnewtoncontroller.hh new file mode 100644 index 0000000000..635f6a6288 --- /dev/null +++ b/dumux/implicit/richards/richardsnewtoncontroller.hh @@ -0,0 +1,125 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \file + * \brief A newton solver specific to the Richards problem. + */ +#ifndef DUMUX_RICHARDS_NEWTON_CONTROLLER_HH +#define DUMUX_RICHARDS_NEWTON_CONTROLLER_HH + +#include "richardsproperties.hh" + +#include <dumux/nonlinear/newtoncontroller.hh> + +namespace Dumux { +/*! + * \ingroup Newton + * \brief A Richards model specific controller for the newton solver. + * + * This controller 'knows' what a 'physically meaningful' solution is + * and can thus do update smarter than the plain Newton controller. + */ +template <class TypeTag> +class RichardsNewtonController : public NewtonController<TypeTag> +{ + typedef NewtonController<TypeTag> ParentType; + + typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; + typedef typename GET_PROP_TYPE(TypeTag, SolutionVector) SolutionVector; + typedef typename GET_PROP_TYPE(TypeTag, SpatialParams) SpatialParams; + typedef typename GET_PROP_TYPE(TypeTag, FVElementGeometry) FVElementGeometry; + typedef typename GET_PROP_TYPE(TypeTag, MaterialLaw) MaterialLaw; + typedef typename GET_PROP_TYPE(TypeTag, MaterialLawParams) MaterialLawParams; + typedef typename GET_PROP_TYPE(TypeTag, Problem) Problem; + + typedef typename GET_PROP_TYPE(TypeTag, Indices) Indices; + enum { pwIdx = Indices::pwIdx }; + + typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; + typedef typename GridView::template Codim<0>::Iterator ElementIterator; + enum { dim = GridView::dimension }; +public: + /*! + * \brief Constructor + */ + RichardsNewtonController(const Problem &problem) + : ParentType(problem) + {} + + /*! + * \brief Update the current solution of the newton method + * + * This is basically the step + * \f[ u^{k+1} = u^k - \Delta u^k \f] + * + * \param uCurrentIter The solution after the current Newton iteration \f$ u^{k+1} \f$ + * \param uLastIter The solution after the last Newton iteration \f$ u^k \f$ + * \param deltaU The vector of differences between the last + * iterative solution and the next one \f$ \Delta u^k \f$ + */ + void newtonUpdate(SolutionVector &uCurrentIter, + const SolutionVector &uLastIter, + const SolutionVector &deltaU) + { + ParentType::newtonUpdate(uCurrentIter, uLastIter, deltaU); + + if (!GET_PARAM_FROM_GROUP(TypeTag, bool, Newton, UseLineSearch)) + { + // do not clamp anything after 5 iterations + if (this->numSteps_ > 4) + return; + + // clamp saturation change to at most 20% per iteration + FVElementGeometry fvGeometry; + const GridView &gridView = this->problem_().gridView(); + ElementIterator elemIt = gridView.template begin<0>(); + const ElementIterator elemEndIt = gridView.template end<0>(); + for (; elemIt != elemEndIt; ++elemIt) { + fvGeometry.update(gridView, *elemIt); + for (int i = 0; i < fvGeometry.numSCV; ++i) { + int globI = this->problem_().vertexMapper().map(*elemIt, i, dim); + + // calculate the old wetting phase saturation + const SpatialParams &spatialParams = this->problem_().spatialParams(); + const MaterialLawParams &mp = spatialParams.materialLawParams(*elemIt, fvGeometry, i); + Scalar pcMin = MaterialLaw::pC(mp, 1.0); + Scalar pW = uLastIter[globI][pwIdx]; + Scalar pN = std::max(this->problem_().referencePressure(*elemIt, fvGeometry, i), + pW + pcMin); + Scalar pcOld = pN - pW; + Scalar SwOld = std::max<Scalar>(0.0, MaterialLaw::Sw(mp, pcOld)); + + // convert into minimum and maximum wetting phase + // pressures + Scalar pwMin = pN - MaterialLaw::pC(mp, SwOld - 0.2); + Scalar pwMax = pN - MaterialLaw::pC(mp, SwOld + 0.2); + + // clamp the result + pW = uCurrentIter[globI][pwIdx]; + pW = std::max(pwMin, std::min(pW, pwMax)); + uCurrentIter[globI][pwIdx] = pW; + + } + } + } + } +}; +} + +#endif diff --git a/dumux/implicit/richards/richardsproblem.hh b/dumux/implicit/richards/richardsproblem.hh new file mode 100644 index 0000000000..99fcf05746 --- /dev/null +++ b/dumux/implicit/richards/richardsproblem.hh @@ -0,0 +1,92 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \file + * \brief Base class for all problems which use the box scheme + */ +#ifndef DUMUX_RICHARDS_PROBLEM_HH +#define DUMUX_RICHARDS_PROBLEM_HH + +#include <dumux/boxmodels/common/porousmediaboxproblem.hh> + +#include "richardsproperties.hh" + +namespace Dumux +{ +/*! + * \ingroup RichardsModel + * \ingroup BoxBaseProblems + * \brief Base class for all problems which use the two-phase box model + * + * For a description of the Richards model, see Dumux::RichardsModel + */ +template<class TypeTag> +class RichardsBoxProblem : public PorousMediaBoxProblem<TypeTag> +{ + typedef PorousMediaBoxProblem<TypeTag> ParentType; + + typedef typename GET_PROP_TYPE(TypeTag, TimeManager) TimeManager; + typedef typename GET_PROP_TYPE(TypeTag, FVElementGeometry) FVElementGeometry; + typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; + typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; + typedef typename GridView::template Codim<0>::Entity Element; + +public: + /*! + * \brief The constructor. + * + * The overloaded class must allocate all data structures + * required, but _must not_ do any calls to the model, the + * jacobian assembler, etc inside the constructor. + * + * If the problem requires information from these, the + * BoxProblem::init() method be overloaded. + * + * \param timeManager The TimeManager which keeps track of time + * \param gridView The GridView used by the problem. + */ + RichardsBoxProblem(TimeManager &timeManager, const GridView &gridView) + : ParentType(timeManager, gridView) + {} + + /*! + * \name Problem parameters + */ + // \{ + /*! + * \brief Returns the reference pressure \f$\mathrm{[Pa]}\f$ of the non-wetting + * phase within a control volume. + * + * This method MUST be overwritten by the actual problem. + * + * \param element The DUNE Codim<0> enitiy which intersects with + * the finite volume. + * \param fvGeometry The finite volume geometry of the element. + * \param scvIdx The local index of the sub control volume inside the element + */ + Scalar referencePressure(const Element &element, + const FVElementGeometry fvGeometry, + const int scvIdx) const + { DUNE_THROW(Dune::NotImplemented, "referencePressure() method not implemented by the actual problem"); }; + // \} +}; + +} + +#endif diff --git a/dumux/implicit/richards/richardsproperties.hh b/dumux/implicit/richards/richardsproperties.hh new file mode 100644 index 0000000000..fb1f3bd65b --- /dev/null +++ b/dumux/implicit/richards/richardsproperties.hh @@ -0,0 +1,70 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \ingroup Properties + * \ingroup BoxProperties + * \ingroup RichardsModel + * \file + * + * \brief Contains the property declarations for the Richards box + * model. + */ +#ifndef DUMUX_RICHARDS_PROPERTIES_HH +#define DUMUX_RICHARDS_PROPERTIES_HH + +#include <dumux/boxmodels/common/boxproperties.hh> + +namespace Dumux +{ +// \{ +/////////////////////////////////////////////////////////////////////////// +// properties for the isothermal richards model +/////////////////////////////////////////////////////////////////////////// +namespace Properties { + +////////////////////////////////////////////////////////////////// +// Type tags +////////////////////////////////////////////////////////////////// + +//! The type tag for problems discretized using the Richards model +NEW_TYPE_TAG(BoxRichards, INHERITS_FROM(BoxModel)); + +////////////////////////////////////////////////////////////////// +// Property tags +////////////////////////////////////////////////////////////////// + +NEW_PROP_TAG(NumPhases); //!< Number of fluid phases in the system +NEW_PROP_TAG(Indices); //!< Enumerations used by the model +NEW_PROP_TAG(SpatialParams); //!< The type of the spatial parameters +NEW_PROP_TAG(MaterialLaw); //!< The material law which ought to be used (by default extracted from the spatial parameters) +NEW_PROP_TAG(MaterialLawParams); //!< The type of the parameter object for the material law (by default extracted from the spatial parameters) +NEW_PROP_TAG(FluidSystem); //!< The fluid system to be used for the Richards model +NEW_PROP_TAG(WettingPhase); //!< Fluid which represents the wetting phase +NEW_PROP_TAG(NonwettingPhase); //!< Fluid which represents the non-wetting phase +NEW_PROP_TAG(ProblemEnableGravity); //!< Returns whether gravity is considered in the problem +NEW_PROP_TAG(ImplicitMassUpwindWeight); //!< The value of the weight of the upwind direction in the mass conservation equations +NEW_PROP_TAG(ImplicitMobilityUpwindWeight); //!< The value of the weight for the upwind mobility in the velocity calculation +NEW_PROP_TAG(SpatialParamsForchCoeff); //!< Property for the forchheimer coefficient + +// \} +} + +} // end namepace + +#endif diff --git a/dumux/implicit/richards/richardspropertydefaults.hh b/dumux/implicit/richards/richardspropertydefaults.hh new file mode 100644 index 0000000000..e7b6df30d4 --- /dev/null +++ b/dumux/implicit/richards/richardspropertydefaults.hh @@ -0,0 +1,168 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \ingroup Properties + * \ingroup BoxProperties + * \ingroup RichardsModel + * \file + * + * \brief Contains the default definitions for the properties required + * by the Richards box model. + */ +#ifndef DUMUX_RICHARDS_PROPERTY_DEFAULTS_HH +#define DUMUX_RICHARDS_PROPERTY_DEFAULTS_HH + +#include "richardsmodel.hh" +#include "richardsproblem.hh" +#include "richardsindices.hh" +#include <dumux/boxmodels/common/boxdarcyfluxvariables.hh> +#include "richardsvolumevariables.hh" +#include "richardsproperties.hh" +#include "richardsnewtoncontroller.hh" + +#include <dumux/material/components/nullcomponent.hh> +#include <dumux/material/fluidsystems/2pimmisciblefluidsystem.hh> +#include <dumux/material/spatialparams/boxspatialparams.hh> + +namespace Dumux +{ +// \{ + +namespace Properties { +////////////////////////////////////////////////////////////////// +// Properties values +////////////////////////////////////////////////////////////////// +//! Number of equations required by the model +SET_INT_PROP(BoxRichards, NumEq, 1); +//! Number of fluid phases considered +SET_INT_PROP(BoxRichards, NumPhases, 2); + +//! The local residual operator +SET_TYPE_PROP(BoxRichards, + LocalResidual, + RichardsLocalResidual<TypeTag>); + +//! The global model used +SET_TYPE_PROP(BoxRichards, Model, RichardsModel<TypeTag>); + +//! The class for the volume averaged quantities +SET_TYPE_PROP(BoxRichards, VolumeVariables, RichardsVolumeVariables<TypeTag>); + +//! The class for the quantities required for the flux calculation +SET_TYPE_PROP(BoxRichards, FluxVariables, BoxDarcyFluxVariables<TypeTag>); + +//! The class of the newton controller +SET_TYPE_PROP(BoxRichards, NewtonController, RichardsNewtonController<TypeTag>); + +//! The upwind weight for the mass conservation equations +SET_SCALAR_PROP(BoxRichards, ImplicitMassUpwindWeight, 1.0); + +//! weight for the upwind mobility in the velocity calculation +SET_SCALAR_PROP(BoxRichards, ImplicitMobilityUpwindWeight, 1.0); + +//! The class with all index definitions for the model +SET_TYPE_PROP(BoxRichards, Indices, RichardsIndices<TypeTag>); + +//! The spatial parameters to be employed. +//! Use BoxSpatialParams by default. +SET_TYPE_PROP(BoxRichards, SpatialParams, BoxSpatialParams<TypeTag>); + +/*! + * \brief Set type of the parameter objects for the material law + * + * By default this is just retrieved from the material law. + */ +SET_PROP(BoxRichards, MaterialLawParams) +{ +private: + typedef typename GET_PROP_TYPE(TypeTag, MaterialLaw) MaterialLaw; + +public: + typedef typename MaterialLaw::Params type; +}; + +/*! + * \brief The wetting phase used. + * + * By default we use the null-phase, i.e. this has to be defined by + * the problem for the program to work. Please be aware that you + * should be careful to use the Richards model in conjunction with + * liquid non-wetting phases. This is only meaningful if the viscosity + * of the liquid phase is _much_ lower than the viscosity of the + * wetting phase. + */ +SET_PROP(BoxRichards, WettingPhase) +{ private: + typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; +public: + typedef Dumux::LiquidPhase<Scalar, Dumux::NullComponent<Scalar> > type; +}; + +/*! + * \brief The wetting phase used. + * + * By default we use the null-phase, i.e. this has to be defined by + * the problem for the program to work. This doed not need to be + * specified by the problem for the Richards model to work because the + * Richards model does not conserve the non-wetting phase. + */ +SET_PROP(BoxRichards, NonwettingPhase) +{ private: + typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; +public: + typedef Dumux::GasPhase<Scalar, Dumux::NullComponent<Scalar> > type; +}; + +/*! + *\brief The fluid system used by the model. + * + * By default this uses the immiscible twophase fluid system. The + * actual fluids used are specified using in the problem definition by + * the WettingPhase and NonwettingPhase properties. Be aware that + * using different fluid systems in conjunction with the Richards + * model only makes very limited sense. + */ +SET_PROP(BoxRichards, FluidSystem) +{ private: + typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; + typedef typename GET_PROP_TYPE(TypeTag, WettingPhase) WettingPhase; + typedef typename GET_PROP_TYPE(TypeTag, NonwettingPhase) NonwettingPhase; + +public: + typedef Dumux::FluidSystems::TwoPImmiscible<Scalar, + WettingPhase, + NonwettingPhase> type; +}; + +// enable gravity by default +SET_BOOL_PROP(BoxRichards, ProblemEnableGravity, true); + +//! default value for the forchheimer coefficient +// Source: Ward, J.C. 1964 Turbulent flow in porous media. ASCE J. Hydraul. Div 90. +// Actually the Forchheimer coefficient is also a function of the dimensions of the +// porous medium. Taking it as a constant is only a first approximation +// (Nield, Bejan, Convection in porous media, 2006, p. 10) +SET_SCALAR_PROP(BoxModel, SpatialParamsForchCoeff, 0.55); + +// \} +} + +} // end namepace + +#endif diff --git a/dumux/implicit/richards/richardsvolumevariables.hh b/dumux/implicit/richards/richardsvolumevariables.hh new file mode 100644 index 0000000000..d32e107dad --- /dev/null +++ b/dumux/implicit/richards/richardsvolumevariables.hh @@ -0,0 +1,280 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \file + * + * \brief Volume averaged quantities required by the Richards model. + */ +#ifndef DUMUX_RICHARDS_VOLUME_VARIABLES_HH +#define DUMUX_RICHARDS_VOLUME_VARIABLES_HH + +#include "richardsproperties.hh" + +#include <dumux/material/fluidstates/immisciblefluidstate.hh> +#include <dumux/boxmodels/common/boxvolumevariables.hh> + +namespace Dumux +{ + +/*! + * \ingroup RichardsModel + * \ingroup BoxVolumeVariables + * \brief Volume averaged quantities required by the Richards model. + * + * This contains the quantities which are are constant within a finite + * volume in the Richards model + */ +template <class TypeTag> +class RichardsVolumeVariables : public BoxVolumeVariables<TypeTag> +{ + typedef BoxVolumeVariables<TypeTag> ParentType; + + typedef typename GET_PROP_TYPE(TypeTag, VolumeVariables) Implementation; + typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; + typedef typename GET_PROP_TYPE(TypeTag, Problem) Problem; + typedef typename GET_PROP_TYPE(TypeTag, FluidSystem) FluidSystem; + typedef typename GET_PROP_TYPE(TypeTag, MaterialLaw) MaterialLaw; + typedef typename GET_PROP_TYPE(TypeTag, MaterialLawParams) MaterialLawParams; + typedef typename GET_PROP_TYPE(TypeTag, FVElementGeometry) FVElementGeometry; + typedef typename GET_PROP_TYPE(TypeTag, PrimaryVariables) PrimaryVariables; + + typedef typename GET_PROP_TYPE(TypeTag, Indices) Indices; + enum { + pwIdx = Indices::pwIdx, + wPhaseIdx = Indices::wPhaseIdx, + nPhaseIdx = Indices::nPhaseIdx + }; + + typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; + typedef typename GridView::template Codim<0>::Entity Element; + +public: + //! The type returned by the fluidState() method + typedef Dumux::ImmiscibleFluidState<Scalar, FluidSystem> FluidState; + + /*! + * \copydoc BoxVolumeVariables::update + */ + void update(const PrimaryVariables &priVars, + const Problem &problem, + const Element &element, + const FVElementGeometry &fvGeometry, + const int scvIdx, + const bool isOldSol) + { + assert(!FluidSystem::isLiquid(nPhaseIdx)); + + ParentType::update(priVars, + problem, + element, + fvGeometry, + scvIdx, + isOldSol); + + completeFluidState(priVars, problem, element, fvGeometry, scvIdx, fluidState_); + + ////////// + // specify the other parameters + ////////// + const MaterialLawParams &matParams = + problem.spatialParams().materialLawParams(element, fvGeometry, scvIdx); + relativePermeabilityWetting_ = + MaterialLaw::krw(matParams, + fluidState_.saturation(wPhaseIdx)); + + porosity_ = problem.spatialParams().porosity(element, fvGeometry, scvIdx); + + // energy related quantities not contained in the fluid state + asImp_().updateEnergy_(priVars, problem, element, fvGeometry, scvIdx, isOldSol); + } + + /*! + * \copydoc BoxModel::completeFluidState + */ + static void completeFluidState(const PrimaryVariables& priVars, + const Problem& problem, + const Element& element, + const FVElementGeometry& fvGeometry, + const int scvIdx, + FluidState& fluidState) + { + // temperature + Scalar t = Implementation::temperature_(priVars, problem, element, + fvGeometry, scvIdx); + fluidState.setTemperature(t); + + // pressures + Scalar pnRef = problem.referencePressure(element, fvGeometry, scvIdx); + const MaterialLawParams &matParams = + problem.spatialParams().materialLawParams(element, fvGeometry, scvIdx); + Scalar minPc = MaterialLaw::pC(matParams, 1.0); + fluidState.setPressure(wPhaseIdx, priVars[pwIdx]); + fluidState.setPressure(nPhaseIdx, std::max(pnRef, priVars[pwIdx] + minPc)); + + // saturations + Scalar Sw = MaterialLaw::Sw(matParams, fluidState.pressure(nPhaseIdx) - fluidState.pressure(wPhaseIdx)); + fluidState.setSaturation(wPhaseIdx, Sw); + fluidState.setSaturation(nPhaseIdx, 1 - Sw); + + // density and viscosity + typename FluidSystem::ParameterCache paramCache; + paramCache.updateAll(fluidState); + fluidState.setDensity(wPhaseIdx, FluidSystem::density(fluidState, paramCache, wPhaseIdx)); + fluidState.setDensity(nPhaseIdx, 1e-10); + + fluidState.setViscosity(wPhaseIdx, FluidSystem::viscosity(fluidState, paramCache, wPhaseIdx)); + fluidState.setViscosity(nPhaseIdx, 1e-10); + } + + /*! + * \brief Return the fluid configuration at the given primary + * variables + */ + const FluidState &fluidState() const + { return fluidState_; } + + /*! + * \brief Returns the average porosity [] within the control volume. + * + * The porosity is defined as the ratio of the pore space to the + * total volume, i.e. \f[ \Phi := \frac{V_{pore}}{V_{pore} + V_{rock}} \f] + */ + Scalar porosity() const + { return porosity_; } + + /*! + * \brief Returns the average absolute saturation [] of a given + * fluid phase within the finite volume. + * + * The saturation of a fluid phase is defined as the fraction of + * the pore volume filled by it, i.e. + * \f[ S_\alpha := \frac{V_\alpha}{V_{pore}} = \phi \frac{V_\alpha}{V} \f] + * + * \param phaseIdx The index of the fluid phase + */ + Scalar saturation(const int phaseIdx) const + { return fluidState_.saturation(phaseIdx); } + + /*! + * \brief Returns the average mass density \f$\mathrm{[kg/m^3]}\f$ of a given + * fluid phase within the control volume. + * + * \param phaseIdx The index of the fluid phase + */ + Scalar density(const int phaseIdx) const + { return fluidState_.density(phaseIdx); } + + /*! + * \brief Returns the effective pressure \f$\mathrm{[Pa]}\f$ of a given phase within + * the control volume. + * + * For the non-wetting phase (i.e. the gas phase), we assume + * infinite mobility, which implies that the non-wetting phase + * pressure is equal to the finite volume's reference pressure + * defined by the problem. + * + * \param phaseIdx The index of the fluid phase + */ + Scalar pressure(const int phaseIdx) const + { return fluidState_.pressure(phaseIdx); } + + /*! + * \brief Returns average temperature \f$\mathrm{[K]}\f$ inside the control volume. + * + * Note that we assume thermodynamic equilibrium, i.e. the + * temperature of the rock matrix and of all fluid phases are + * identical. + */ + Scalar temperature() const + { return fluidState_.temperature(); } + + /*! + * \brief Returns the effective mobility \f$\mathrm{[1/(Pa*s)]}\f$ of a given phase within + * the control volume. + * + * The mobility of a fluid phase is defined as the relative + * permeability of the phase (given by the chosen material law) + * divided by the dynamic viscosity of the fluid, i.e. + * \f[ \lambda_\alpha := \frac{k_{r\alpha}}{\mu_\alpha} \f] + * + * \param phaseIdx The index of the fluid phase + */ + Scalar mobility(const int phaseIdx) const + { return relativePermeability(phaseIdx)/fluidState_.viscosity(phaseIdx); } + + /*! + * \brief Returns relative permeability [-] of a given phase within + * the control volume. + * + * \param phaseIdx The index of the fluid phase + */ + Scalar relativePermeability(const int phaseIdx) const + { + if (phaseIdx == wPhaseIdx) + return relativePermeabilityWetting_; + return 1; + } + + /*! + * \brief Returns the effective capillary pressure \f$\mathrm{[Pa]}\f$ within the + * control volume. + * + * The capillary pressure is defined as the difference in + * pressures of the non-wetting and the wetting phase, i.e. + * \f[ p_c = p_n - p_w \f] + */ + Scalar capillaryPressure() const + { return fluidState_.pressure(nPhaseIdx) - fluidState_.pressure(wPhaseIdx); } + +protected: + static Scalar temperature_(const PrimaryVariables &primaryVariables, + const Problem& problem, + const Element &element, + const FVElementGeometry &fvGeometry, + const int scvIdx) + { + return problem.boxTemperature(element, fvGeometry, scvIdx); + } + + /*! + * \brief Called by update() to compute the energy related quantities + */ + void updateEnergy_(const PrimaryVariables &priVars, + const Problem &problem, + const Element &element, + const FVElementGeometry &fvGeometry, + const int scvIdx, + const bool isOldSol) + { } + + FluidState fluidState_; + Scalar relativePermeabilityWetting_; + Scalar porosity_; + +private: + Implementation &asImp_() + { return *static_cast<Implementation*>(this); } + + const Implementation &asImp_() const + { return *static_cast<const Implementation*>(this); } +}; + +} + +#endif diff --git a/dumux/io/artmeshreader.hh b/dumux/io/artgridcreator.hh similarity index 51% rename from dumux/io/artmeshreader.hh rename to dumux/io/artgridcreator.hh index be77711f96..a8a76a518b 100644 --- a/dumux/io/artmeshreader.hh +++ b/dumux/io/artgridcreator.hh @@ -21,8 +21,8 @@ * for modeling lower dimensional discrete fracture-matrix problems. */ -#ifndef DUMUX_IO_ARTMESH_READER_HH -#define DUMUX_IO_ARTMESH_READER_HH +#ifndef DUMUX_ARTGRIDCREATOR_HH +#define DUMUX_ARTGRIDCREATOR_HH #include <algorithm> #include <iostream> @@ -31,9 +31,11 @@ #include <iomanip> #include <dune/grid/common/mcmgmapper.hh> -#include <dune/grid/uggrid.hh> +#include <dune/grid/common/gridfactory.hh> #include <dumux/common/math.hh> +#include <dumux/common/propertysystem.hh> +#include <dumux/common/parameters.hh> #include <dumux/common/valgrind.hh> // plot the vertices, edges, elements @@ -42,66 +44,42 @@ namespace Dumux { -#ifdef HAVE_UG -typedef Dune::UGGrid<2> GridType; // Currently implemented for 2D fractured systems -#else -typedef Dune::YaspGrid<2> GridType; -#endif -typedef Dune::FieldVector<double, 3> Coordinates; -typedef std::vector<Coordinates> VerticesVector; -typedef Dune::FieldVector<int, 3> EdgePoints; -typedef std::vector<EdgePoints> EdgesVector; -typedef Dune::FieldVector<int, 4> Faces; -typedef std::vector<Faces> FacesVector; - -typedef double NumberType; // TODO: from parameter system? - -/* -The number of - boundary vertices, - boundary edges (connecting two boundary vertices), - boundary faces (connecting n boundary edges) and - boundary elements (should be 0) -are read from the boundarty description file and stored here. -*/ -int bVertices, bEdges, bFaces, bElements; - -// Store the vertex coordinates, edge infos, face infos. -Dune::FieldVector<double, 2> vertexPosition; - -template<int dim> -struct P1Layout +namespace Properties { - bool contains (Dune::GeometryType gt) - { - return (gt.dim() == 0); - } -}; - +NEW_PROP_TAG(Scalar); +NEW_PROP_TAG(Grid); +NEW_PROP_TAG(GridCreator); +} + /*! * \brief Reads in Artmesh files. */ -template <class GridType> -class ArtReader { +template <class TypeTag> +class ArtGridCreator +{ + typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; + typedef typename GET_PROP_TYPE(TypeTag, Grid) Grid; + typedef Grid* GridPointer; + typedef Dune::FieldVector<double, 3> Coordinates; + typedef std::vector<Coordinates> VerticesVector; + typedef Dune::FieldVector<int, 3> EdgePoints; + typedef std::vector<EdgePoints> EdgesVector; + typedef Dune::FieldVector<int, 4> Faces; + typedef std::vector<Faces> FacesVector; public: /*! - * \brief Reads the file formats obtained with the Artmesh generator - * - * \param artFileName Path to the Artmesh file. + * \brief Create the Grid */ - void read_art_file(const char *artFileName) + static void makeGrid() { + const std::string artFileName = GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, std::string, Grid, File); + std::cout << "Opening " << artFileName << std::endl; std::ifstream inFile(artFileName); - //intialize - Coordinates coordinate_; - EdgePoints edge_points_; - Faces faces_; std::string jump; - while (inFile >> jump) { //do nothing with the first lines in the code @@ -110,7 +88,7 @@ public: { inFile >> jump; double dummy = atof(jump.c_str()); - vertexNumber = dummy; + vertexNumber_ = dummy; break; } } @@ -120,7 +98,7 @@ public: { inFile >> jump; double dummy = atof(jump.c_str()); - edgeNumber = dummy; + edgeNumber_ = dummy; break; } } @@ -130,7 +108,7 @@ public: { inFile >> jump; double dummy = atof(jump.c_str()); - faceNumber = dummy; + faceNumber_ = dummy; break; } } @@ -140,7 +118,7 @@ public: { inFile >> jump; double dummy = atof(jump.c_str()); - elementNumber = dummy; + elementNumber_ = dummy; break; } } @@ -172,14 +150,15 @@ public: break; } double dummy = atof(jump.c_str()); - coordinate_[0] = dummy; - for (int k=1; k<3; k++) + Coordinates coordinate; + coordinate[0] = dummy; + for (int k = 1; k < 3; k++) { inFile >> jump; dummy = atof(jump.c_str()); - coordinate_[k] = dummy; + coordinate[k] = dummy; } - vertices_.push_back(coordinate_); + vertices_.push_back(coordinate); } /////Reading Edges while (inFile >> jump) @@ -199,14 +178,15 @@ public: break; } double dummy = atof(jump.c_str()); - edge_points_[0] = dummy; - for (int k=1; k<3; k++) + EdgePoints edgePoints; + edgePoints[0] = dummy; + for (int k = 1; k < 3; k++) { inFile >> jump; dummy = atof(jump.c_str()); - edge_points_[k] = dummy; + edgePoints[k] = dummy; } - edges_vector_.push_back(edge_points_); + edges_.push_back(edgePoints); } /////Reading Faces @@ -227,171 +207,90 @@ public: break; } double dummy = atof(jump.c_str()); - faces_[0] = dummy; - for (int k=1; k<4; k++) + Faces facePoints; + facePoints[0] = dummy; + for (int k = 1; k < 4; k++) { inFile >> jump; dummy = atof(jump.c_str()); - faces_[k] = dummy; + facePoints[k] = dummy; } - faces_vector_.push_back(faces_); + faces_.push_back(facePoints); } - } - void outputARTtoScreen() - { - ////////OUTPUT for verification - ////////////////////////////////////////////////////////////////// - std::cout << std::endl << "printing VERTICES" << std::endl; - for (int i = 0; i < vertices_.size(); i++) - { - for (int j=0; j < 3; j++) - std::cout << vertices_[i][j]<<"\t"; - std::cout << std::endl; - } - - std::cout << std::endl << "printing EDGES" << std::endl; - for (int i = 0; i < edges_vector_.size(); i++) - { - for (int j=0; j < 3; j++) - std::cout<<edges_vector_[i][j]<<"\t"; - std::cout<<std::endl; - } - ////Printing Faces - std::cout << std::endl << "printing FACES" << std::endl; - for (int i = 0; i < faces_vector_.size(); i++) - { - for (int j=0; j < 4; j++) - { - std::cout<<faces_vector_[i][j]<<" "; - } - std::cout<<std::endl; - } - - std::cout << std::endl << "Total number of vertices "<<vertexNumber<<std::endl; - std::cout << "Total number of edges: "<<edgeNumber<<std::endl; - std::cout << "Total number of faces: "<<faceNumber<<std::endl; - std::cout << "Total number of elements: "<<elementNumber<<std::endl; - - if (vertices_.size() != vertexNumber ) - { - std::cout << "The total given number of vertices: "<<vertexNumber - <<" is not the same with the read number of entries: " - <<vertices_.size()<<"" << std::endl; - } - if (edges_vector_.size() != edgeNumber ) - { - std::cout << "The total given number of edges: "<<edgeNumber - <<" is not the same with the read number of entries: " - <<edges_vector_.size()<<"" << std::endl; - } - if (faces_vector_.size() != faceNumber ) - { - std::cout << "The total given number of faces: "<<faceNumber - <<" is not the same with the read number of entries: " - <<faces_vector_.size()<<"" << std::endl; - } - } - - /*! - * \brief Create the UG grid from the read in data - */ - GridType* createGrid() - { - // set up the grid factory + + // set up the grid factory Dune::FieldVector<double,2> position; - Dune::GridFactory<GridType> factory; - bVertices = vertexNumber; - bEdges = edgeNumber; - bFaces = faceNumber; - VerticesVector verts(bVertices); - EdgesVector edges(bEdges); - FacesVector elems(bFaces); + Dune::GridFactory<Grid> factory; +#if PLOT // Plot the vertices - #if PLOT - std::cout << "*================*"<<std::endl; - std::cout << "* Vertices"<<std::endl; - std::cout << "*================*"<<std::endl; - #endif - for (int k=0; k<bVertices; k++) + std::cout << "*================*" << std::endl; + std::cout << "* Vertices" << std::endl; + std::cout << "*================*" << std::endl; +#endif + for (int k = 0; k < vertexNumber_; k++) { - verts[k][0] = vertices_[k][0]; - verts[k][1] = vertices_[k][1]; - verts[k][2] = vertices_[k][2]; - - //Printing the vertices vector - #if PLOT - std::cout << verts[k][0]<<"\t\t"; - std::cout << verts[k][1]<<"\t\t"; - std::cout << verts[k][2]<<std::endl; - #endif - for (int i=0; i<2; i++) +#if PLOT + // Printing the vertices vector + std::cout << vertices_[k][0] << "\t\t"; + std::cout << vertices_[k][1] << "\t\t"; + std::cout << vertices_[k][2] << std::endl; +#endif + for (int i = 0; i < 2; i++) { - position[i]=verts[k][i]; + position[i] = vertices_[k][i]; } factory.insertVertex(position); } - #if PLOT - std::cout << "*================*"<<std::endl; - std::cout << "* Edges"<<std::endl; - std::cout << "*================*"<<std::endl; - #endif - for (int k=0; k<bEdges; k++) + +#if PLOT + std::cout << "*================*" << std::endl; + std::cout << "* Edges" << std::endl; + std::cout << "*================*" << std::endl; + for (int k = 0; k < edgeNumber_; k++) { - edges[k][0]=edges_vector_[k][0]; - edges[k][1]=edges_vector_[k][1]; - edges[k][2]=edges_vector_[k][2]; - #if PLOT - //Printing the Edge vector - std::cout<<edges[k][0]<<"\t\t"; - std::cout<<edges[k][1]<<"\t"; - std::cout<<edges[k][2]<<std::endl; - #endif + // Printing the Edge vector + std::cout << edges_[k][0] << "\t\t"; + std::cout << edges_[k][1] << "\t"; + std::cout << edges_[k][2] << std::endl; } // Plot the Elements (Faces) - #if PLOT - std::cout << "*================*"<<std::endl; - std::cout << "* Faces"<<std::endl; - std::cout << "*================*"<<std::endl; - #endif - - for (int k=0; k<faceNumber; k++) + std::cout << "*================*" << std::endl; + std::cout << "* Faces" << std::endl; + std::cout << "*================*" << std::endl; + for (int k = 0; k < faceNumber_; k++) { - elems[k][0]=faces_vector_[k][1]; - elems[k][1]=faces_vector_[k][2]; - elems[k][2]=faces_vector_[k][3]; - #if PLOT - std::cout<<elems[k][0]<<"\t"; - std::cout<<elems[k][1]<<"\t"; - std::cout<<elems[k][2]<<std::endl; - #endif + std::cout << faces_[k][1] << "\t"; + std::cout << faces_[k][2] << "\t"; + std::cout << faces_[k][3] << std::endl; } - +#endif + //***********************************************************************// //Create the Elements in Dune::GridFactory // //***********************************************************************// - for (int i=0; i<bFaces; i++) + for (int i=0; i<faceNumber_; i++) { std::vector<unsigned int> nodeIdx(3); Dune::FieldVector<double,3> point(0); - #if PLOT - std::cout << "====================================="<<std::endl; - std::cout << "globalElemIdx "<<i<<std::endl; - std::cout << "====================================="<<std::endl; - #endif +#if PLOT + std::cout << "=====================================" << std::endl; + std::cout << "globalElemIdx " << i << std::endl; + std::cout << "=====================================" << std::endl; +#endif int edgeIdx = 0; //first node of the element - from first edge Node 1 - nodeIdx[0] = edges[elems[i][edgeIdx]][1]; + nodeIdx[0] = edges_[faces_[i][edgeIdx+1]][1]; //second node of the element- from first edge Node 2 - nodeIdx[1] = edges[elems[i][edgeIdx]][2]; + nodeIdx[1] = edges_[faces_[i][edgeIdx+1]][2]; //third node of the element - from the second edge - nodeIdx[2] = edges[elems[i][edgeIdx+1]][1]; + nodeIdx[2] = edges_[faces_[i][edgeIdx+2]][1]; // if the nodes of the edges are identical swap if (nodeIdx[1] == nodeIdx[2] || nodeIdx[0] == nodeIdx[2]) { - nodeIdx[2] = edges[elems[i][edgeIdx+1]][2]; + nodeIdx[2] = edges_[faces_[i][edgeIdx+2]][2]; } /* Check if the order of the nodes is trigonometric @@ -400,63 +299,181 @@ public: Dune::FieldVector<double, 2> v(0); Dune::FieldVector<double, 2> w(0); double cross1; - v[0] = verts[nodeIdx[0]][0] - verts[nodeIdx[1]][0]; - v[1] = verts[nodeIdx[0]][1] - verts[nodeIdx[1]][1]; - w[0] = verts[nodeIdx[0]][0] - verts[nodeIdx[2]][0]; - w[1] = verts[nodeIdx[0]][1] - verts[nodeIdx[2]][1]; + v[0] = vertices_[nodeIdx[0]][0] - vertices_[nodeIdx[1]][0]; + v[1] = vertices_[nodeIdx[0]][1] - vertices_[nodeIdx[1]][1]; + w[0] = vertices_[nodeIdx[0]][0] - vertices_[nodeIdx[2]][0]; + w[1] = vertices_[nodeIdx[0]][1] - vertices_[nodeIdx[2]][1]; cross1 = v[0]*w[1]-v[1]*w[0]; //If the cross product is negative switch the order of the vertices if (cross1 < 0) { - nodeIdx[0] = edges[elems[i][edgeIdx]][2]; //node 0 is node 1 - nodeIdx[1] = edges[elems[i][edgeIdx]][1]; //node 1 is node 0 + nodeIdx[0] = edges_[faces_[i][edgeIdx+1]][2]; //node 0 is node 1 + nodeIdx[1] = edges_[faces_[i][edgeIdx+1]][1]; //node 1 is node 0 } - v[0] = verts[nodeIdx[0]][0] - verts[nodeIdx[1]][0]; - v[1] = verts[nodeIdx[0]][1] - verts[nodeIdx[1]][1]; - w[0] = verts[nodeIdx[0]][0] - verts[nodeIdx[2]][0]; - w[1] = verts[nodeIdx[0]][1] - verts[nodeIdx[2]][1]; + v[0] = vertices_[nodeIdx[0]][0] - vertices_[nodeIdx[1]][0]; + v[1] = vertices_[nodeIdx[0]][1] - vertices_[nodeIdx[1]][1]; + w[0] = vertices_[nodeIdx[0]][0] - vertices_[nodeIdx[2]][0]; + w[1] = vertices_[nodeIdx[0]][1] - vertices_[nodeIdx[2]][1]; factory.insertElement(Dune::GeometryType(Dune::GeometryType::simplex,2), nodeIdx); - #if PLOT - std::cout << "edges of the element "<< elems[i] << std::endl; +#if PLOT + std::cout << "edges of the element "<< faces_[i] << std::endl; std::cout << "nodes of the element " << nodeIdx[0] << ", " << nodeIdx[1] << ", " << nodeIdx[2] << std::endl; - std::cout << "1st "<<nodeIdx[0]<<"\t"<<"2nd "<<nodeIdx[1] - << "\t"<<"3rd "<<nodeIdx[2] + std::cout << "1st " << nodeIdx[0] + << "\t" << "2nd " << nodeIdx[1] + << "\t" << "3rd " << nodeIdx[2] << std::endl; - #endif - if (nodeIdx[0] == nodeIdx[1]||nodeIdx[1] == nodeIdx[2] - ||nodeIdx[0] == nodeIdx[2] ) +#endif + if (nodeIdx[0] == nodeIdx[1] || nodeIdx[1] == nodeIdx[2] + || nodeIdx[0] == nodeIdx[2]) { std::cout << "Error. The node index is identical in the element" << std::endl; } - } - Valgrind::CheckDefined(verts); + grid_ = factory.createGrid(); + } + + static void outputARTtoScreen() + { + ////////OUTPUT for verification + ////////////////////////////////////////////////////////////////// + std::cout << std::endl << "printing VERTICES" << std::endl; + for (int i = 0; i < vertices_.size(); i++) + { + for (int j = 0; j < 3; j++) + std::cout << vertices_[i][j] << "\t"; + std::cout << std::endl; + } - return factory.createGrid(); + std::cout << std::endl << "printing EDGES" << std::endl; + for (int i = 0; i < edges_.size(); i++) + { + for (int j = 0; j < 3; j++) + std::cout << edges_[i][j] << "\t"; + std::cout << std::endl; + } + ////Printing Faces + std::cout << std::endl << "printing FACES" << std::endl; + for (int i = 0; i < faces_.size(); i++) + { + for (int j = 0; j < 4; j++) + std::cout << faces_[i][j] << " "; + std::cout << std::endl; + } + + std::cout << std::endl << "Total number of vertices " << vertexNumber_ << std::endl; + std::cout << "Total number of edges: " << edgeNumber_ << std::endl; + std::cout << "Total number of faces: " << faceNumber_ << std::endl; + std::cout << "Total number of elements: " << elementNumber_ << std::endl; + + if (vertices_.size() != vertexNumber_ ) + { + std::cout << "The total given number of vertices: " << vertexNumber_ + << " is not the same with the read number of entries: " + << vertices_.size() << "" << std::endl; + } + if (edges_.size() != edgeNumber_ ) + { + std::cout << "The total given number of edges: " << edgeNumber_ + << " is not the same with the read number of entries: " + << edges_.size() << "" << std::endl; + } + if (faces_.size() != faceNumber_ ) + { + std::cout << "The total given number of faces: " << faceNumber_ + << " is not the same with the read number of entries: " + << faces_.size() << "" << std::endl; + } } -public: - VerticesVector vertices_; - EdgesVector edges_vector_; - FacesVector faces_vector_ ; - int vertexNumber; - int edgeNumber; - int faceNumber; //in 2D - int elementNumber; //in 3D + /*! + * \brief Returns a reference to the grid. + */ + static Grid &grid() + { + return *grid_; + }; + + /*! + * \brief Distributes the grid on all processes of a parallel + * computation. + */ + static void loadBalance() + { + grid_->loadBalance(); + }; + + static int vertexNumber() + { + return vertexNumber_; + } + + static int edgeNumber() + { + return edgeNumber_; + } + + static int faceNumber() + { + return faceNumber_; + } + + static VerticesVector& vertices() + { + return vertices_; + } + + static EdgesVector& edges() + { + return edges_; + } + + static FacesVector& faces() + { + return faces_; + } + +private: + static VerticesVector vertices_; + static EdgesVector edges_; + static FacesVector faces_ ; + static int vertexNumber_; + static int edgeNumber_; + static int faceNumber_; //in 2D + static int elementNumber_; //in 3D + static GridPointer grid_; }; +template <class TypeTag> +typename Dumux::ArtGridCreator<TypeTag>::GridPointer ArtGridCreator<TypeTag>::grid_; +template <class TypeTag> +typename Dumux::ArtGridCreator<TypeTag>::VerticesVector ArtGridCreator<TypeTag>::vertices_; +template <class TypeTag> +typename Dumux::ArtGridCreator<TypeTag>::EdgesVector ArtGridCreator<TypeTag>::edges_; +template <class TypeTag> +typename Dumux::ArtGridCreator<TypeTag>::FacesVector ArtGridCreator<TypeTag>::faces_; +template <class TypeTag> +int ArtGridCreator<TypeTag>::vertexNumber_; +template <class TypeTag> +int ArtGridCreator<TypeTag>::edgeNumber_; +template <class TypeTag> +int ArtGridCreator<TypeTag>::faceNumber_; +template <class TypeTag> +int ArtGridCreator<TypeTag>::elementNumber_; + /*! * \brief Maps the fractures from the Artmesh to the UG grid ones */ -template<class GridType> +template<class TypeTag> class FractureMapper { + typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView; + typedef typename GET_PROP_TYPE(TypeTag, GridCreator) GridCreator; public: // mapper: one data element in every entity template<int dim> @@ -467,62 +484,33 @@ public: return gt.dim() == dim-1; } }; - template<int dim> - struct VertexLayout - { - bool contains (Dune::GeometryType gt) - { - return gt.dim() == 0; - } - }; - typedef typename GridType::LeafGridView GV; - typedef typename GridType::ctype DT; - enum {dim=GridType::dimension}; - typedef typename GV::IndexSet IS; - typedef typename GridType::template Codim<0>::Entity Entity; - typedef typename GV::template Codim<dim>::Iterator VertexLeafIterator; - typedef typename GV::template Codim<0>::Iterator ElementLeafIterator; - typedef typename GridType::template Codim<0>::EntityPointer EEntityPointer; - typedef typename GridType::Traits::GlobalIdSet IDS; - typedef typename IDS::IdType IdType; - typedef std::set<IdType> GIDSet; - typedef Dune::MultipleCodimMultipleGeomTypeMapper<GV,FaceLayout> FaceMapper; - typedef Dune::MultipleCodimMultipleGeomTypeMapper<GV,VertexLayout> VertexMapper; - typedef Dumux::ArtReader<GridType> ArtReader; + typedef typename GridView::ctype DT; + enum {dim = GridView::dimension}; + typedef typename GridView::template Codim<dim>::Iterator VertexIterator; + typedef typename GridView::template Codim<0>::Iterator ElementIterator; + typedef Dune::MultipleCodimMultipleGeomTypeMapper<GridView, FaceLayout> FaceMapper; + typedef Dune::MultipleCodimMultipleGeomTypeMapper<GridView, Dune::MCMGVertexLayout> VertexMapper; public: - /*! - * \brief Constructor - * - * \param grid The grid - * \param geomArt Artmesh reader - */ - FractureMapper (const GridType& grid, ArtReader geomArt) - : grid_(grid), - facemapper(grid.leafView()), - vertexmapper(grid.leafView()), - artgeom_(geomArt) - {} - /*! * \brief Constructor * * \param grid The grid */ - FractureMapper (const GridType& grid) - : grid_(grid), - facemapper(grid.leafView()), - vertexmapper(grid.leafView()) + FractureMapper (const GridView& gridView) + : gridView_(gridView), + faceMapper_(gridView), + vertexMapper_(gridView) {} /*! * */ - void fractureMapper() + void map() { //call the new_read art - int nVertices = artgeom_.vertexNumber; - int nEdges = artgeom_.edgeNumber; + int nVertices = GridCreator::vertexNumber(); + int nEdges = GridCreator::edgeNumber(); //The vertexes which are located on fractures isDuneFractureVertex_.resize(nVertices); std::fill(isDuneFractureVertex_.begin(), isDuneFractureVertex_.end(), false); @@ -532,10 +520,8 @@ public: fractureEdgesIdx_.resize(nEdges); std::fill(isDuneFractureEdge_.begin(), isDuneFractureEdge_.end(), false); - GV leafView = grid_.leafView(); - - ElementLeafIterator eendit = leafView. template end<0>(); - for (ElementLeafIterator it = leafView .template begin<0>(); it != eendit; ++it) + ElementIterator eendit = gridView_.template end<0>(); + for (ElementIterator it = gridView_.template begin<0>(); it != eendit; ++it) { Dune::GeometryType gt = it->geometry().type(); const typename Dune::GenericReferenceElementContainer<DT,dim>::value_type& @@ -544,7 +530,7 @@ public: // Loop over element faces for (int i = 0; i < refElem.size(1); i++) { - int indexFace = facemapper.map(*it, i, 1); + int indexFace = faceMapper_.map(*it, i, 1); /* * it maps the local element vertices "localV1Idx" -> indexVertex1 * then it gets the coordinates of the nodes in the ART file and @@ -552,47 +538,44 @@ public: */ int localV1Idx = refElem.subEntity(i, 1, 0, dim); int localV2Idx = refElem.subEntity(i, 1, 1, dim); - int indexVertex1 = vertexmapper.map(*it, localV1Idx, dim); - int indexVertex2 = vertexmapper.map(*it, localV2Idx, dim); + int indexVertex1 = vertexMapper_.map(*it, localV1Idx, dim); + int indexVertex2 = vertexMapper_.map(*it, localV2Idx, dim); Dune::FieldVector<DT, dim> nodeART_from; Dune::FieldVector<DT, dim> nodeART_to; Dune::FieldVector<DT, dim> nodeDune_from; Dune::FieldVector<DT, dim> nodeDune_to; - nodeDune_from = it-> geometry(). corner (localV1Idx); - nodeDune_to = it-> geometry(). corner (localV2Idx); + nodeDune_from = it->geometry().corner(localV1Idx); + nodeDune_to = it->geometry().corner(localV2Idx); for (int j=0; j < nEdges; j++) { - nodeART_from[0] = artgeom_.vertices_[artgeom_.edges_vector_[j][1]][0]; - nodeART_from[1] = artgeom_.vertices_[artgeom_.edges_vector_[j][1]][1]; - nodeART_to[0] = artgeom_.vertices_[artgeom_.edges_vector_[j][2]][0]; - nodeART_to[1] = artgeom_.vertices_[artgeom_.edges_vector_[j][2]][1]; + nodeART_from[0] = GridCreator::vertices()[GridCreator::edges()[j][1]][0]; + nodeART_from[1] = GridCreator::vertices()[GridCreator::edges()[j][1]][1]; + nodeART_to[0] = GridCreator::vertices()[GridCreator::edges()[j][2]][0]; + nodeART_to[1] = GridCreator::vertices()[GridCreator::edges()[j][2]][1]; if ((nodeART_from == nodeDune_from && nodeART_to == nodeDune_to) || (nodeART_from == nodeDune_to && nodeART_to == nodeDune_from)) { #if PLOT - std::cout << " ART edge index is "<< j; -// std::cout << " Node ART_from: " -// << nodeART_from<<std::endl; - std::cout << " Dune edge is " << indexFace - <<std::endl; + std::cout << " ART edge index is "<< j + << ", Dune edge is " << indexFace << std::endl; #endif /* assigns a value 1 for the edges * which are fractures */ - if (artgeom_.edges_vector_[j][0] < 0) + if (GridCreator::edges()[j][0] < 0) { isDuneFractureEdge_[indexFace] = true; - fractureEdgesIdx_[indexFace] = artgeom_.edges_vector_[j][0]; + fractureEdgesIdx_[indexFace] = GridCreator::edges()[j][0]; isDuneFractureVertex_[indexVertex1]=true; isDuneFractureVertex_[indexVertex2]=true; #if PLOT - std::cout << "isDuneFractureEdge_ "<< - isDuneFractureEdge_[indexFace]<<"\t"; - std::cout << "vertex1 "<<indexVertex1<<"\t"; - std::cout << "vertex2 "<<indexVertex2<<"" << std::endl; + std::cout << "isDuneFractureEdge_ " + << isDuneFractureEdge_[indexFace] << "\t" + << "vertex1 " << indexVertex1 << "\t" + << "vertex2 " << indexVertex2 << "" << std::endl; #endif } } @@ -602,8 +585,8 @@ public: #if PLOT int i=0; - for (VertexLeafIterator vIt=leafView. template begin<dim> (); - vIt != leafView. template end<dim> (); ++vIt) + for (VertexIterator vIt=gridView_.template begin<dim>(); + vIt != gridView_.template end<dim>(); ++vIt) { Dune::GeometryType gt = vIt->type(); std::cout << "visiting " << gt @@ -615,18 +598,31 @@ public: #endif } + + bool isDuneFractureVertex(unsigned int i) const + { + return isDuneFractureVertex_[i]; + } + + bool isDuneFractureEdge(unsigned int i) const + { + return isDuneFractureEdge_[i]; + } + + int fractureEdgesIdx(unsigned int i) const + { + return fractureEdgesIdx_[i]; + } private: - const GridType& grid_; - FaceMapper facemapper; - VertexMapper vertexmapper; - ArtReader artgeom_; -public: - std::vector<bool> isDuneFractureVertex_; - std::vector<bool> isDuneFractureEdge_; - std::vector<int> fractureEdgesIdx_; + const GridView gridView_; + FaceMapper faceMapper_; + VertexMapper vertexMapper_; + std::vector<bool> isDuneFractureVertex_; + std::vector<bool> isDuneFractureEdge_; + std::vector<int> fractureEdgesIdx_; }; } // end namespace -#endif // DUMUX_IO_ARTMESH_READER_HH +#endif // DUMUX_ARTGRIDCREATOR_HH diff --git a/test/Makefile.am b/test/Makefile.am index 745926a41a..b8f8ea0a80 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -1,4 +1,4 @@ -SUBDIRS = boxmodels common decoupled freeflow material +SUBDIRS = common decoupled freeflow implicit material EXTRA_DIST = CMakeLists.txt diff --git a/test/boxmodels/2pdfm/test_2pdfm.cc b/test/boxmodels/2pdfm/test_2pdfm.cc deleted file mode 100644 index f51fbce54c..0000000000 --- a/test/boxmodels/2pdfm/test_2pdfm.cc +++ /dev/null @@ -1,136 +0,0 @@ -/***************************************************************************** - * See the file COPYING for full copying permissions. * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 2 of the License, or * - * (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see <http://www.gnu.org/licenses/>. * - *****************************************************************************/ -/*! - * \file - * - * \brief Test for the two-phase flow with discrete fracture-matrix (discrete - * fracture model) and box model scheme. - */ -#include "config.h" - -#include "2pdfmtestproblem.hh" -#include <dumux/common/start.hh> - -/*! - * \brief Provides an interface for customizing error messages associated with - * reading in parameters. - * - * \param progName The name of the program, that was tried to be started. - */ -void usage(const char *progName) -{ - std::string errorMessageOut = "\nUsage: "; - errorMessageOut += progName; - errorMessageOut += " [--restart restartTime] grid tEnd dt\n"; - std::cout << errorMessageOut << std::endl; - exit(1); -} - -//////////////////////// -// the main function -//////////////////////// -int main(int argc, char** argv) -{ - try { - FILE *file; - typedef TTAG(TwoPDFMTestProblem) TypeTag; - typedef GET_PROP_TYPE(TypeTag, Scalar) Scalar; - typedef GET_PROP_TYPE(TypeTag, TimeManager) TimeManager; - typedef GET_PROP_TYPE(TypeTag, Grid) Grid; - typedef GET_PROP_TYPE(TypeTag, Problem) Problem; - typedef Dune::FieldVector<Scalar, Grid::dimensionworld> GlobalPosition; - - // initialize MPI, finalize is done automatically on exit - Dune::MPIHelper::instance(argc, argv); - - //////////////////////////////////////////////////////////// - // parse the command line arguments - //////////////////////////////////////////////////////////// - if (argc < 4) - { - usage(argv[0]); - } - - // deal with the restart stuff - int argPos = 1; - bool restart = false; - double restartTime = 0; - if (std::string("--restart") == argv[argPos]) - { - restart = true; - ++argPos; - - std::istringstream(argv[argPos++]) >> restartTime; - } - - if (argc - argPos != 3) - { - usage(argv[0]); - } - - // read the initial time step and the end time - double tEnd, dt; - const char *artFileName = argv[argPos++]; - std::istringstream(argv[argPos++]) >> tEnd; - std::istringstream(argv[argPos++]) >> dt; - file = fopen(artFileName,"r"); - if (file == NULL) - { - std::cout << "Could not open artmesh file '" - << artFileName << "'" << std::endl; - exit(1); - } - - //////////////////////////////////////////////////////////// - // create the grid from ART Reader - //////////////////////////////////////////////////////////// - - Dumux::ArtReader<Grid> dfmArtmeshGeometry; // instantiate ArtReader - Grid *gridT; - dfmArtmeshGeometry.read_art_file(artFileName); - gridT = dfmArtmeshGeometry.createGrid(); - gridT->loadBalance(); - Dumux::FractureMapper<Grid> ModelARTReader(*gridT, dfmArtmeshGeometry); // map fractures to grid - ModelARTReader.fractureMapper(); - - //////////////////////////////////////////////////////////// - // instantiate and run the concrete problem - //////////////////////////////////////////////////////////// - // instantiate and run the concrete problem - TimeManager timeManager; - Problem problem(timeManager, - gridT->leafView(), - ModelARTReader.isDuneFractureVertex_, - ModelARTReader.isDuneFractureEdge_, - ModelARTReader.fractureEdgesIdx_); - timeManager.init(problem, 0, dt, tEnd, restart); - if (restart) - { - problem.restart(restartTime); - } - timeManager.run(); - return 0; - } - catch (Dune::Exception &e) { - std::cerr << "Dune reported error: " << e << std::endl; - } - catch (...) { - std::cerr << "Unknown exception thrown!\n"; - throw; - } - return EXIT_SUCCESS; -} diff --git a/test/boxmodels/1p/1ptest-reference.vtu b/test/implicit/1p/1ptest-reference.vtu similarity index 100% rename from test/boxmodels/1p/1ptest-reference.vtu rename to test/implicit/1p/1ptest-reference.vtu diff --git a/test/boxmodels/1p/1ptestproblem.hh b/test/implicit/1p/1ptestproblem.hh similarity index 100% rename from test/boxmodels/1p/1ptestproblem.hh rename to test/implicit/1p/1ptestproblem.hh diff --git a/test/boxmodels/1p/1ptestspatialparams.hh b/test/implicit/1p/1ptestspatialparams.hh similarity index 100% rename from test/boxmodels/1p/1ptestspatialparams.hh rename to test/implicit/1p/1ptestspatialparams.hh diff --git a/test/boxmodels/1p/CMakeLists.txt b/test/implicit/1p/CMakeLists.txt similarity index 100% rename from test/boxmodels/1p/CMakeLists.txt rename to test/implicit/1p/CMakeLists.txt diff --git a/test/boxmodels/1p/Makefile.am b/test/implicit/1p/Makefile.am similarity index 100% rename from test/boxmodels/1p/Makefile.am rename to test/implicit/1p/Makefile.am diff --git a/test/boxmodels/1p/grids/test_1p_1d.dgf b/test/implicit/1p/grids/test_1p_1d.dgf similarity index 100% rename from test/boxmodels/1p/grids/test_1p_1d.dgf rename to test/implicit/1p/grids/test_1p_1d.dgf diff --git a/test/boxmodels/1p/grids/test_1p_2d.dgf b/test/implicit/1p/grids/test_1p_2d.dgf similarity index 100% rename from test/boxmodels/1p/grids/test_1p_2d.dgf rename to test/implicit/1p/grids/test_1p_2d.dgf diff --git a/test/boxmodels/1p/grids/test_1p_3d.dgf b/test/implicit/1p/grids/test_1p_3d.dgf similarity index 100% rename from test/boxmodels/1p/grids/test_1p_3d.dgf rename to test/implicit/1p/grids/test_1p_3d.dgf diff --git a/test/boxmodels/1p/test_1p.cc b/test/implicit/1p/test_1p.cc similarity index 100% rename from test/boxmodels/1p/test_1p.cc rename to test/implicit/1p/test_1p.cc diff --git a/test/boxmodels/1p/test_1p.input b/test/implicit/1p/test_1p.input similarity index 100% rename from test/boxmodels/1p/test_1p.input rename to test/implicit/1p/test_1p.input diff --git a/test/boxmodels/1p2c/1p2coutflowproblem.hh b/test/implicit/1p2c/1p2coutflowproblem.hh similarity index 100% rename from test/boxmodels/1p2c/1p2coutflowproblem.hh rename to test/implicit/1p2c/1p2coutflowproblem.hh diff --git a/test/boxmodels/1p2c/1p2coutflowspatialparams.hh b/test/implicit/1p2c/1p2coutflowspatialparams.hh similarity index 100% rename from test/boxmodels/1p2c/1p2coutflowspatialparams.hh rename to test/implicit/1p2c/1p2coutflowspatialparams.hh diff --git a/test/boxmodels/1p2c/CMakeLists.txt b/test/implicit/1p2c/CMakeLists.txt similarity index 100% rename from test/boxmodels/1p2c/CMakeLists.txt rename to test/implicit/1p2c/CMakeLists.txt diff --git a/test/boxmodels/1p2c/Makefile.am b/test/implicit/1p2c/Makefile.am similarity index 100% rename from test/boxmodels/1p2c/Makefile.am rename to test/implicit/1p2c/Makefile.am diff --git a/test/boxmodels/1p2c/grids/test_1p2c.dgf b/test/implicit/1p2c/grids/test_1p2c.dgf similarity index 100% rename from test/boxmodels/1p2c/grids/test_1p2c.dgf rename to test/implicit/1p2c/grids/test_1p2c.dgf diff --git a/test/boxmodels/1p2c/outflow-reference.vtu b/test/implicit/1p2c/outflow-reference.vtu similarity index 100% rename from test/boxmodels/1p2c/outflow-reference.vtu rename to test/implicit/1p2c/outflow-reference.vtu diff --git a/test/boxmodels/1p2c/test_1p2c.cc b/test/implicit/1p2c/test_1p2c.cc similarity index 100% rename from test/boxmodels/1p2c/test_1p2c.cc rename to test/implicit/1p2c/test_1p2c.cc diff --git a/test/boxmodels/1p2c/test_1p2c.input b/test/implicit/1p2c/test_1p2c.input similarity index 100% rename from test/boxmodels/1p2c/test_1p2c.input rename to test/implicit/1p2c/test_1p2c.input diff --git a/test/boxmodels/2p/CMakeLists.txt b/test/implicit/2p/CMakeLists.txt similarity index 100% rename from test/boxmodels/2p/CMakeLists.txt rename to test/implicit/2p/CMakeLists.txt diff --git a/test/boxmodels/2p/Makefile.am b/test/implicit/2p/Makefile.am similarity index 100% rename from test/boxmodels/2p/Makefile.am rename to test/implicit/2p/Makefile.am diff --git a/test/boxmodels/2p/grids/test_2p.dgf b/test/implicit/2p/grids/test_2p.dgf similarity index 100% rename from test/boxmodels/2p/grids/test_2p.dgf rename to test/implicit/2p/grids/test_2p.dgf diff --git a/test/boxmodels/2p/lens-reference.vtu b/test/implicit/2p/lens-reference.vtu similarity index 100% rename from test/boxmodels/2p/lens-reference.vtu rename to test/implicit/2p/lens-reference.vtu diff --git a/test/boxmodels/2p/lensproblem.hh b/test/implicit/2p/lensproblem.hh similarity index 100% rename from test/boxmodels/2p/lensproblem.hh rename to test/implicit/2p/lensproblem.hh diff --git a/test/boxmodels/2p/lensspatialparams.hh b/test/implicit/2p/lensspatialparams.hh similarity index 100% rename from test/boxmodels/2p/lensspatialparams.hh rename to test/implicit/2p/lensspatialparams.hh diff --git a/test/boxmodels/2p/test_2p.cc b/test/implicit/2p/test_2p.cc similarity index 100% rename from test/boxmodels/2p/test_2p.cc rename to test/implicit/2p/test_2p.cc diff --git a/test/boxmodels/2p/test_2p.input b/test/implicit/2p/test_2p.input similarity index 100% rename from test/boxmodels/2p/test_2p.input rename to test/implicit/2p/test_2p.input diff --git a/test/boxmodels/2p2c/CMakeLists.txt b/test/implicit/2p2c/CMakeLists.txt similarity index 100% rename from test/boxmodels/2p2c/CMakeLists.txt rename to test/implicit/2p2c/CMakeLists.txt diff --git a/test/boxmodels/2p2c/Makefile.am b/test/implicit/2p2c/Makefile.am similarity index 100% rename from test/boxmodels/2p2c/Makefile.am rename to test/implicit/2p2c/Makefile.am diff --git a/test/boxmodels/2p2c/grids/test_2p2c.dgf b/test/implicit/2p2c/grids/test_2p2c.dgf similarity index 100% rename from test/boxmodels/2p2c/grids/test_2p2c.dgf rename to test/implicit/2p2c/grids/test_2p2c.dgf diff --git a/test/boxmodels/2p2c/injection-reference.vtu b/test/implicit/2p2c/injection-reference.vtu similarity index 100% rename from test/boxmodels/2p2c/injection-reference.vtu rename to test/implicit/2p2c/injection-reference.vtu diff --git a/test/boxmodels/2p2c/injectionproblem.hh b/test/implicit/2p2c/injectionproblem.hh similarity index 100% rename from test/boxmodels/2p2c/injectionproblem.hh rename to test/implicit/2p2c/injectionproblem.hh diff --git a/test/boxmodels/2p2c/injectionspatialparams.hh b/test/implicit/2p2c/injectionspatialparams.hh similarity index 100% rename from test/boxmodels/2p2c/injectionspatialparams.hh rename to test/implicit/2p2c/injectionspatialparams.hh diff --git a/test/boxmodels/2p2c/test_2p2c.cc b/test/implicit/2p2c/test_2p2c.cc similarity index 100% rename from test/boxmodels/2p2c/test_2p2c.cc rename to test/implicit/2p2c/test_2p2c.cc diff --git a/test/boxmodels/2p2c/test_2p2c.input b/test/implicit/2p2c/test_2p2c.input similarity index 100% rename from test/boxmodels/2p2c/test_2p2c.input rename to test/implicit/2p2c/test_2p2c.input diff --git a/test/boxmodels/2p2cni/CMakeLists.txt b/test/implicit/2p2cni/CMakeLists.txt similarity index 100% rename from test/boxmodels/2p2cni/CMakeLists.txt rename to test/implicit/2p2cni/CMakeLists.txt diff --git a/test/boxmodels/2p2cni/Makefile.am b/test/implicit/2p2cni/Makefile.am similarity index 100% rename from test/boxmodels/2p2cni/Makefile.am rename to test/implicit/2p2cni/Makefile.am diff --git a/test/boxmodels/2p2cni/grids/test_2p2cni.dgf b/test/implicit/2p2cni/grids/test_2p2cni.dgf similarity index 100% rename from test/boxmodels/2p2cni/grids/test_2p2cni.dgf rename to test/implicit/2p2cni/grids/test_2p2cni.dgf diff --git a/test/boxmodels/2p2cni/test_2p2cni.cc b/test/implicit/2p2cni/test_2p2cni.cc similarity index 100% rename from test/boxmodels/2p2cni/test_2p2cni.cc rename to test/implicit/2p2cni/test_2p2cni.cc diff --git a/test/boxmodels/2p2cni/test_2p2cni.input b/test/implicit/2p2cni/test_2p2cni.input similarity index 100% rename from test/boxmodels/2p2cni/test_2p2cni.input rename to test/implicit/2p2cni/test_2p2cni.input diff --git a/test/boxmodels/2p2cni/waterair-reference.vtu b/test/implicit/2p2cni/waterair-reference.vtu similarity index 100% rename from test/boxmodels/2p2cni/waterair-reference.vtu rename to test/implicit/2p2cni/waterair-reference.vtu diff --git a/test/boxmodels/2p2cni/waterairproblem.hh b/test/implicit/2p2cni/waterairproblem.hh similarity index 100% rename from test/boxmodels/2p2cni/waterairproblem.hh rename to test/implicit/2p2cni/waterairproblem.hh diff --git a/test/boxmodels/2p2cni/waterairspatialparams.hh b/test/implicit/2p2cni/waterairspatialparams.hh similarity index 100% rename from test/boxmodels/2p2cni/waterairspatialparams.hh rename to test/implicit/2p2cni/waterairspatialparams.hh diff --git a/test/boxmodels/2pdfm/2pdfmspatialparams.hh b/test/implicit/2pdfm/2pdfmspatialparams.hh similarity index 74% rename from test/boxmodels/2pdfm/2pdfmspatialparams.hh rename to test/implicit/2pdfm/2pdfmspatialparams.hh index 425476ece7..bda6131572 100644 --- a/test/boxmodels/2pdfm/2pdfmspatialparams.hh +++ b/test/implicit/2pdfm/2pdfmspatialparams.hh @@ -24,7 +24,6 @@ #define DUMUX_TEST_2PDFM_SPATIAL_PARAMETERS_HH #include <dumux/boxmodels/2pdfm/2pdfmmodel.hh> -#include <dumux/io/artmeshreader.hh> #include <dumux/material/fluidmatrixinteractions/2p/efftoabslaw.hh> #include <dumux/material/fluidmatrixinteractions/2p/regularizedbrookscorey.hh> #include <dumux/material/spatialparams/boxspatialparams.hh> @@ -34,18 +33,18 @@ namespace Dumux //forward declaration template<class TypeTag> -class TwoPDFMSpatialParameters; +class TwoPDFMSpatialParams; namespace Properties { // The spatial parameters TypeTag -NEW_TYPE_TAG(TwoPDFMSpatialParameters); +NEW_TYPE_TAG(TwoPDFMSpatialParams); // Set the spatial parameters -SET_TYPE_PROP(TwoPDFMSpatialParameters, SpatialParams, Dumux::TwoPDFMSpatialParameters<TypeTag>); +SET_TYPE_PROP(TwoPDFMSpatialParams, SpatialParams, Dumux::TwoPDFMSpatialParams<TypeTag>); // Set the material Law -SET_PROP(TwoPDFMSpatialParameters, MaterialLaw) +SET_PROP(TwoPDFMSpatialParams, MaterialLaw) { private: // define the material law which is parameterized by effective @@ -64,7 +63,7 @@ public: * twophase box model */ template<class TypeTag> -class TwoPDFMSpatialParameters : public BoxSpatialParams<TypeTag> +class TwoPDFMSpatialParams : public BoxSpatialParams<TypeTag> { template<int dim> @@ -75,14 +74,6 @@ class TwoPDFMSpatialParameters : public BoxSpatialParams<TypeTag> return gt.dim() == dim - 1; } }; - template<int dim> - struct VertexLayout - { - bool contains (Dune::GeometryType gt) - { - return gt.dim() == 0; - } - }; typedef BoxSpatialParams<TypeTag> ParentType; typedef typename GET_PROP_TYPE(TypeTag, Grid) Grid; @@ -90,8 +81,8 @@ class TwoPDFMSpatialParameters : public BoxSpatialParams<TypeTag> typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; typedef typename Grid::ctype CoordScalar; - typedef Dune::MultipleCodimMultipleGeomTypeMapper<GridView,VertexLayout> VertexMapper; - typedef Dune::MultipleCodimMultipleGeomTypeMapper<GridView,FaceLayout> FaceMapper; + typedef Dune::MultipleCodimMultipleGeomTypeMapper<GridView, Dune::MCMGVertexLayout> VertexMapper; + typedef Dune::MultipleCodimMultipleGeomTypeMapper<GridView, FaceLayout> FaceMapper; enum { dim = GridView::dimension, @@ -108,21 +99,13 @@ public: typedef typename GET_PROP_TYPE(TypeTag, MaterialLaw) MaterialLaw; typedef typename MaterialLaw::Params MaterialLawParams; - TwoPDFMSpatialParameters(const GridView& gridView) - : ParentType(gridView) + TwoPDFMSpatialParams(const GridView& gridView) + : ParentType(gridView), gridView_(gridView), + faceMapper_(gridView), vertexMapper_(gridView), + fractureMapper_(gridView) { - gridView_ = 0; - facemapper_ = 0; - vertexmapper_ = 0; - - setupFractureMatrixSoilParameters(); - } + inactivateFractures_ = false; - /** - * Set the soil properties of fractures and domain. - */ - void setupFractureMatrixSoilParameters() - { Scalar mD = 1e-12 * 1e-3; //miliDarcy SwrF_ = 0.00; @@ -150,6 +133,8 @@ public: porosityMatrix_ = 0.25; porosityFracture_ = 0.10; fractureWidth_ = 1e-2; + + fractureMapper_.map(); } /*! @@ -234,7 +219,7 @@ public: const FVElementGeometry &fvGeometry, int scvIdx) const { - int globalIdx = vertexmapper().map(element, scvIdx, dim); + DUNE_UNUSED int globalIdx = vertexMapper_.map(element, scvIdx, dim); // be picky if called for non-fracture vertices assert(isVertexFracture(globalIdx)); @@ -254,8 +239,8 @@ public: { return false; } - int globalIdx = vertexmapper().map(element, localVertexIdx, dim); - return isDuneFractureVertex_[globalIdx]; + int globalIdx = vertexMapper_.map(element, localVertexIdx, dim); + return fractureMapper_.isDuneFractureVertex(globalIdx); } /*! @@ -269,7 +254,7 @@ public: { return false; } - return isDuneFractureVertex_[globalIdx]; + return fractureMapper_.isDuneFractureVertex(globalIdx); } /*! @@ -280,22 +265,10 @@ public: */ bool isEdgeFracture(const Element &element, int localFaceIdx) const { - int globalIdx = facemapper().map(element, localFaceIdx, 1); - return isDuneFractureEdge_[globalIdx]; + int globalIdx = faceMapper_.map(element, localFaceIdx, 1); + return fractureMapper_.isDuneFractureEdge(globalIdx); } - /*! - * \brief Returns the vertex mapper. - */ - const VertexMapper &vertexmapper() const - { return *vertexmapper_; } - - /*! - * \brief Returns the face mapper. - */ - const FaceMapper &facemapper() const - { return *facemapper_; } - /*! * \brief Returns the width of the fracture. * @@ -317,41 +290,6 @@ public: return fractureWidth_; } - /*! - * \brief Set the grid view. - * - * \param gv The new grid view to be set. - */ - void setGridView(const GridView &gv) - { - delete gridView_; - delete facemapper_; - delete vertexmapper_; - - gridView_ = new GridView(gv); - facemapper_ = new FaceMapper(gv); - vertexmapper_ = new VertexMapper(gv); - } - - /*! - * \brief Set which vertices and edges are fractures. - * - * \param isDuneFractureVertex Vector of bools which indicates fracture vertices - * \param isDuneFractureEdge Vector of bools which indicates fracture edges - * \param fractureEdgesIdx Vector of edge indices which are fractures - * \param inactivateFractures Deactivates fractures - */ - const void setFractureBoolVectors(const std::vector<bool>& isDuneFractureVertex, - const std::vector<bool>& isDuneFractureEdge, - const std::vector<int>& fractureEdgesIdx, - bool inactivateFractures) - { - isDuneFractureVertex_ = isDuneFractureVertex; - isDuneFractureEdge_ = isDuneFractureEdge; - inactivateFractures_ = inactivateFractures; - fractureEdgesIdx_ = fractureEdgesIdx; - } - Scalar SwrF_; Scalar SwrM_; Scalar SnrF_; @@ -362,8 +300,6 @@ public: Scalar pdM_; private: - MaterialLawParams materialParams_; - Scalar KMatrix_; Scalar KFracture_; Scalar porosityMatrix_; @@ -375,14 +311,11 @@ private: MaterialLawParams rockMatrixMaterialParams_; bool inactivateFractures_; - std::vector<bool> isFracture_; - std::vector<bool> isDuneFractureVertex_; - std::vector<bool> isDuneFractureEdge_; - std::vector<int> fractureEdgesIdx_; - - VertexMapper *vertexmapper_; - FaceMapper *facemapper_; - GridView *gridView_; + const GridView gridView_; + const FaceMapper faceMapper_; + const VertexMapper vertexMapper_; + + Dumux::FractureMapper<TypeTag> fractureMapper_; }; } // end namespace diff --git a/test/boxmodels/2pdfm/2pdfmtestproblem.hh b/test/implicit/2pdfm/2pdfmtestproblem.hh similarity index 83% rename from test/boxmodels/2pdfm/2pdfmtestproblem.hh rename to test/implicit/2pdfm/2pdfmtestproblem.hh index e36bf1159f..a887e6c65c 100644 --- a/test/boxmodels/2pdfm/2pdfmtestproblem.hh +++ b/test/implicit/2pdfm/2pdfmtestproblem.hh @@ -36,6 +36,7 @@ #include <dumux/material/components/simpleh2o.hh> #include <dumux/material/components/dnapl.hh> #include <dumux/material/fluidsystems/h2on2fluidsystem.hh> +#include <dumux/io/artgridcreator.hh> #include "2pdfmspatialparams.hh" @@ -47,7 +48,7 @@ class TwoPDFMTestProblem; namespace Properties { -NEW_TYPE_TAG(TwoPDFMTestProblem, INHERITS_FROM(BoxTwoPDFM, TwoPDFMSpatialParameters)); +NEW_TYPE_TAG(TwoPDFMTestProblem, INHERITS_FROM(BoxTwoPDFM, TwoPDFMSpatialParams)); // Set the grid type #if HAVE_UG @@ -56,6 +57,9 @@ SET_TYPE_PROP(TwoPDFMTestProblem, Grid, Dune::UGGrid<2>); SET_TYPE_PROP(TwoPDFMTestProblem, Grid, Dune::YaspGrid<2>); #endif +// set the GridCreator property +SET_TYPE_PROP(TwoPDFMTestProblem, GridCreator, Dumux::ArtGridCreator<TypeTag>); + // Set the problem property SET_TYPE_PROP(TwoPDFMTestProblem, Problem, Dumux::TwoPDFMTestProblem<TypeTag>); @@ -145,13 +149,10 @@ class TwoPDFMTestProblem : public PorousMediaBoxProblem<TypeTag> typedef typename GridView::template Codim<0>::Entity Element; typedef typename GridView::template Codim<dim>::Entity Vertex; - typedef typename GridView::Intersection Intersection; - typedef typename GridView::template Codim<0>::Iterator ElementIterator; typedef typename GET_PROP_TYPE(TypeTag, PrimaryVariables) PrimaryVariables; typedef typename GET_PROP_TYPE(TypeTag, BoundaryTypes) BoundaryTypes; typedef typename GET_PROP_TYPE(TypeTag, TimeManager) TimeManager; - typedef typename GridType::ctype DT; typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; typedef Dune::FieldVector<Scalar, dimWorld> GlobalPosition; @@ -167,25 +168,12 @@ public: * \param fractureEdgesIdx Vector of edge indices which are fractures */ TwoPDFMTestProblem(TimeManager &timeManager, - const GridView &gridView, - std::vector<bool>& isDuneFractureVertex, - std::vector<bool>& isDuneFractureEdge, - std::vector<int>& fractureEdgesIdx) + const GridView &gridView) : ParentType(timeManager, gridView), - isDuneFractureVertex_(isDuneFractureVertex), - isDuneFractureEdge_(isDuneFractureEdge), - fractureEdgesIdx_(fractureEdgesIdx), useInterfaceCondition_(true) { eps_ = 3e-6; temperature_ = 273.15 + 20; // -> 20°C - - inactivateFractures_ = false; - this->spatialParams().setGridView(gridView); - this->spatialParams().setFractureBoolVectors(isDuneFractureVertex_, - isDuneFractureEdge_, fractureEdgesIdx_, inactivateFractures_); - bboxMin_ = this->bboxMin(); - bboxMax_ = this->bboxMax(); } /*! @@ -385,51 +373,10 @@ private: return globalPos[1] > this->bboxMax()[1] - eps_; } - bool onInlet_(const GlobalPosition &globalPos) const - { - Scalar width = this->bboxMax()[0] - this->bboxMin()[0]; - Scalar lambda = (this->bboxMax()[0] - globalPos[0])/width; - return onUpperBoundary_(globalPos) && 0.5 < lambda && lambda < 2.0/3.0; - } - - bool upperRightCorner(const GlobalPosition &globalPos) const - { - return ((globalPos[0]>bboxMax_[0] - eps_) - &&(globalPos[1]>bboxMax_[1] - eps_)); - } - - /*! - * \brief Checks whether the fracture intersects the boundary. - * - * \param element The current element - * \param scvIdx Index of the sub-control volume to be checked - */ - bool isFractureVertexOnBoundary_(const Element &element, int scvIdx) const - { - const GlobalPosition &globalPos = element.geometry().corner(scvIdx); - int globalIdx = this->soil().vertexmapper().map(element, scvIdx, dim); - - int isVF = this->soil().isVertexFracture(globalIdx); - return ((isVF && onUpperBoundary_(globalPos)) - || (isVF && onLowerBoundary_(globalPos)) - || (isVF && onLeftBoundary_(globalPos)) - || (isVF && onRightBoundary_(globalPos))); - } - Scalar temperature_; Scalar eps_; - GlobalPosition bboxMin_; - GlobalPosition bboxMax_; - std::vector<bool> isDuneFractureVertex_; - std::vector<bool> isDuneFractureEdge_; - std::vector<bool> fractureVolFractionVector; - std::vector<int> fractureEdgesIdx_; - std::vector<Scalar> distNodesOnBoundary; - bool useInterfaceCondition_; - bool inactivateFractures_; - }; } //end namespace diff --git a/test/boxmodels/2pdfm/Makefile.am b/test/implicit/2pdfm/Makefile.am similarity index 100% rename from test/boxmodels/2pdfm/Makefile.am rename to test/implicit/2pdfm/Makefile.am diff --git a/test/boxmodels/2pdfm/grids/2pdfmartmesh.net b/test/implicit/2pdfm/grids/2pdfmartmesh.net similarity index 100% rename from test/boxmodels/2pdfm/grids/2pdfmartmesh.net rename to test/implicit/2pdfm/grids/2pdfmartmesh.net diff --git a/test/implicit/2pdfm/test_2pdfm.cc b/test/implicit/2pdfm/test_2pdfm.cc new file mode 100644 index 0000000000..39e61e87e9 --- /dev/null +++ b/test/implicit/2pdfm/test_2pdfm.cc @@ -0,0 +1,62 @@ +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \file + * + * \brief Test for the two-phase flow with discrete fracture-matrix (discrete + * fracture model) and box model scheme. + */ +#include "config.h" + +#include "2pdfmtestproblem.hh" +#include <dumux/common/start.hh> + +/*! + * \brief Provides an interface for customizing error messages associated with + * reading in parameters. + * + * \param progName The name of the program, that was tried to be started. + * \param errorMsg The error message that was issued by the start function. + * Comprises the thing that went wrong and a general help message. + */ +void usage(const char *progName, const std::string &errorMsg) +{ + if (errorMsg.size() > 0) { + std::string errorMessageOut = "\nUsage: "; + errorMessageOut += progName; + errorMessageOut += " [options]\n"; + errorMessageOut += errorMsg; + errorMessageOut += "\n\nThe list of mandatory options for this program is:\n" + "\t-TimeManager.TEnd End of the simulation [s] \n" + "\t-TimeManager.DtInitial Initial timestep size [s] \n" + "\t-Grid.File Name of the file containing the grid \n" + "\t definition in ART format\n" + "\n"; + + std::cout << errorMessageOut + << "\n"; + } +} + +//////////////////////// +// the main function +//////////////////////// +int main(int argc, char** argv) +{ + typedef TTAG(TwoPDFMTestProblem) ProblemTypeTag; + return Dumux::start<ProblemTypeTag>(argc, argv, usage); +} diff --git a/test/implicit/2pdfm/test_2pdfm.input b/test/implicit/2pdfm/test_2pdfm.input new file mode 100644 index 0000000000..538324350a --- /dev/null +++ b/test/implicit/2pdfm/test_2pdfm.input @@ -0,0 +1,28 @@ +############################################################### +# Parameter file for test_2pdfm. +# Everything behind a '#' is a comment. +# Type "./test_2pdfm --help" for more information. +############################################################### + +############################################################### +# Mandatory arguments +############################################################### + +[TimeManager] +DtInitial = 1 # [s] +TEnd = 20 # [s] + +[Grid] +File = ./grids/2pdfmartmesh.net + +############################################################### +# Simulation restart +# +# DuMux simulations can be restarted from *.drs files +# Set Restart to the value of a specific file, +# e.g.: 'Restart = 27184.1' for the restart file +# name_time=27184.1_rank=0.drs +# Please comment in the two lines below, if restart is desired. +############################################################### +# [TimeManager] +# Restart = ... diff --git a/test/boxmodels/2pni/CMakeLists.txt b/test/implicit/2pni/CMakeLists.txt similarity index 100% rename from test/boxmodels/2pni/CMakeLists.txt rename to test/implicit/2pni/CMakeLists.txt diff --git a/test/boxmodels/2pni/Makefile.am b/test/implicit/2pni/Makefile.am similarity index 100% rename from test/boxmodels/2pni/Makefile.am rename to test/implicit/2pni/Makefile.am diff --git a/test/boxmodels/2pni/grids/test_2pni.dgf b/test/implicit/2pni/grids/test_2pni.dgf similarity index 100% rename from test/boxmodels/2pni/grids/test_2pni.dgf rename to test/implicit/2pni/grids/test_2pni.dgf diff --git a/test/boxmodels/2pni/injection2pni-reference.vtu b/test/implicit/2pni/injection2pni-reference.vtu similarity index 100% rename from test/boxmodels/2pni/injection2pni-reference.vtu rename to test/implicit/2pni/injection2pni-reference.vtu diff --git a/test/boxmodels/2pni/injectionproblem2pni.hh b/test/implicit/2pni/injectionproblem2pni.hh similarity index 100% rename from test/boxmodels/2pni/injectionproblem2pni.hh rename to test/implicit/2pni/injectionproblem2pni.hh diff --git a/test/boxmodels/2pni/test_2pni.cc b/test/implicit/2pni/test_2pni.cc similarity index 100% rename from test/boxmodels/2pni/test_2pni.cc rename to test/implicit/2pni/test_2pni.cc diff --git a/test/boxmodels/2pni/test_2pni.input b/test/implicit/2pni/test_2pni.input similarity index 100% rename from test/boxmodels/2pni/test_2pni.input rename to test/implicit/2pni/test_2pni.input diff --git a/test/boxmodels/3p3c/CMakeLists.txt b/test/implicit/3p3c/CMakeLists.txt similarity index 100% rename from test/boxmodels/3p3c/CMakeLists.txt rename to test/implicit/3p3c/CMakeLists.txt diff --git a/test/boxmodels/3p3c/Makefile.am b/test/implicit/3p3c/Makefile.am similarity index 100% rename from test/boxmodels/3p3c/Makefile.am rename to test/implicit/3p3c/Makefile.am diff --git a/test/boxmodels/3p3c/grids/test_3p3c.dgf b/test/implicit/3p3c/grids/test_3p3c.dgf similarity index 100% rename from test/boxmodels/3p3c/grids/test_3p3c.dgf rename to test/implicit/3p3c/grids/test_3p3c.dgf diff --git a/test/boxmodels/3p3c/grids/test_3p3c_coarse.dgf b/test/implicit/3p3c/grids/test_3p3c_coarse.dgf similarity index 100% rename from test/boxmodels/3p3c/grids/test_3p3c_coarse.dgf rename to test/implicit/3p3c/grids/test_3p3c_coarse.dgf diff --git a/test/boxmodels/3p3c/infiltration-reference.vtu b/test/implicit/3p3c/infiltration-reference.vtu similarity index 100% rename from test/boxmodels/3p3c/infiltration-reference.vtu rename to test/implicit/3p3c/infiltration-reference.vtu diff --git a/test/boxmodels/3p3c/infiltrationproblem.hh b/test/implicit/3p3c/infiltrationproblem.hh similarity index 100% rename from test/boxmodels/3p3c/infiltrationproblem.hh rename to test/implicit/3p3c/infiltrationproblem.hh diff --git a/test/boxmodels/3p3c/infiltrationspatialparameters.hh b/test/implicit/3p3c/infiltrationspatialparameters.hh similarity index 100% rename from test/boxmodels/3p3c/infiltrationspatialparameters.hh rename to test/implicit/3p3c/infiltrationspatialparameters.hh diff --git a/test/boxmodels/3p3c/test_3p3c.cc b/test/implicit/3p3c/test_3p3c.cc similarity index 100% rename from test/boxmodels/3p3c/test_3p3c.cc rename to test/implicit/3p3c/test_3p3c.cc diff --git a/test/boxmodels/3p3c/test_3p3c.input b/test/implicit/3p3c/test_3p3c.input similarity index 100% rename from test/boxmodels/3p3c/test_3p3c.input rename to test/implicit/3p3c/test_3p3c.input diff --git a/test/boxmodels/3p3cni/CMakeLists.txt b/test/implicit/3p3cni/CMakeLists.txt similarity index 100% rename from test/boxmodels/3p3cni/CMakeLists.txt rename to test/implicit/3p3cni/CMakeLists.txt diff --git a/test/boxmodels/3p3cni/Makefile.am b/test/implicit/3p3cni/Makefile.am similarity index 100% rename from test/boxmodels/3p3cni/Makefile.am rename to test/implicit/3p3cni/Makefile.am diff --git a/test/boxmodels/3p3cni/column3p3cni-reference.vtu b/test/implicit/3p3cni/column3p3cni-reference.vtu similarity index 100% rename from test/boxmodels/3p3cni/column3p3cni-reference.vtu rename to test/implicit/3p3cni/column3p3cni-reference.vtu diff --git a/test/boxmodels/3p3cni/columnxylolproblem.hh b/test/implicit/3p3cni/columnxylolproblem.hh similarity index 100% rename from test/boxmodels/3p3cni/columnxylolproblem.hh rename to test/implicit/3p3cni/columnxylolproblem.hh diff --git a/test/boxmodels/3p3cni/columnxylolspatialparams.hh b/test/implicit/3p3cni/columnxylolspatialparams.hh similarity index 100% rename from test/boxmodels/3p3cni/columnxylolspatialparams.hh rename to test/implicit/3p3cni/columnxylolspatialparams.hh diff --git a/test/boxmodels/3p3cni/grids/column.dgf b/test/implicit/3p3cni/grids/column.dgf similarity index 100% rename from test/boxmodels/3p3cni/grids/column.dgf rename to test/implicit/3p3cni/grids/column.dgf diff --git a/test/boxmodels/3p3cni/grids/kuev_3p3cni.dgf b/test/implicit/3p3cni/grids/kuev_3p3cni.dgf similarity index 100% rename from test/boxmodels/3p3cni/grids/kuev_3p3cni.dgf rename to test/implicit/3p3cni/grids/kuev_3p3cni.dgf diff --git a/test/boxmodels/3p3cni/grids/kuev_3p3cni_coarse.dgf b/test/implicit/3p3cni/grids/kuev_3p3cni_coarse.dgf similarity index 100% rename from test/boxmodels/3p3cni/grids/kuev_3p3cni_coarse.dgf rename to test/implicit/3p3cni/grids/kuev_3p3cni_coarse.dgf diff --git a/test/boxmodels/3p3cni/kuevetteproblem.hh b/test/implicit/3p3cni/kuevetteproblem.hh similarity index 100% rename from test/boxmodels/3p3cni/kuevetteproblem.hh rename to test/implicit/3p3cni/kuevetteproblem.hh diff --git a/test/boxmodels/3p3cni/kuevettespatialparams.hh b/test/implicit/3p3cni/kuevettespatialparams.hh similarity index 100% rename from test/boxmodels/3p3cni/kuevettespatialparams.hh rename to test/implicit/3p3cni/kuevettespatialparams.hh diff --git a/test/boxmodels/3p3cni/test_3p3cni.cc b/test/implicit/3p3cni/test_3p3cni.cc similarity index 100% rename from test/boxmodels/3p3cni/test_3p3cni.cc rename to test/implicit/3p3cni/test_3p3cni.cc diff --git a/test/boxmodels/3p3cni/test_3p3cni.input b/test/implicit/3p3cni/test_3p3cni.input similarity index 100% rename from test/boxmodels/3p3cni/test_3p3cni.input rename to test/implicit/3p3cni/test_3p3cni.input diff --git a/test/boxmodels/CMakeLists.txt b/test/implicit/CMakeLists.txt similarity index 100% rename from test/boxmodels/CMakeLists.txt rename to test/implicit/CMakeLists.txt diff --git a/test/boxmodels/Makefile.am b/test/implicit/Makefile.am similarity index 100% rename from test/boxmodels/Makefile.am rename to test/implicit/Makefile.am diff --git a/test/boxmodels/co2/CMakeLists.txt b/test/implicit/co2/CMakeLists.txt similarity index 100% rename from test/boxmodels/co2/CMakeLists.txt rename to test/implicit/co2/CMakeLists.txt diff --git a/test/boxmodels/co2/Makefile.am b/test/implicit/co2/Makefile.am similarity index 100% rename from test/boxmodels/co2/Makefile.am rename to test/implicit/co2/Makefile.am diff --git a/test/boxmodels/co2/co2-reference.vtu b/test/implicit/co2/co2-reference.vtu similarity index 100% rename from test/boxmodels/co2/co2-reference.vtu rename to test/implicit/co2/co2-reference.vtu diff --git a/test/boxmodels/co2/co2values.inc b/test/implicit/co2/co2values.inc similarity index 100% rename from test/boxmodels/co2/co2values.inc rename to test/implicit/co2/co2values.inc diff --git a/test/boxmodels/co2/grids/heterogeneousSmall.dgf b/test/implicit/co2/grids/heterogeneousSmall.dgf similarity index 100% rename from test/boxmodels/co2/grids/heterogeneousSmall.dgf rename to test/implicit/co2/grids/heterogeneousSmall.dgf diff --git a/test/boxmodels/co2/heterogeneousco2tables.hh b/test/implicit/co2/heterogeneousco2tables.hh similarity index 100% rename from test/boxmodels/co2/heterogeneousco2tables.hh rename to test/implicit/co2/heterogeneousco2tables.hh diff --git a/test/boxmodels/co2/heterogeneousproblem.hh b/test/implicit/co2/heterogeneousproblem.hh similarity index 100% rename from test/boxmodels/co2/heterogeneousproblem.hh rename to test/implicit/co2/heterogeneousproblem.hh diff --git a/test/boxmodels/co2/heterogeneousspatialparameters.hh b/test/implicit/co2/heterogeneousspatialparameters.hh similarity index 100% rename from test/boxmodels/co2/heterogeneousspatialparameters.hh rename to test/implicit/co2/heterogeneousspatialparameters.hh diff --git a/test/boxmodels/co2/test_co2.cc b/test/implicit/co2/test_co2.cc similarity index 100% rename from test/boxmodels/co2/test_co2.cc rename to test/implicit/co2/test_co2.cc diff --git a/test/boxmodels/co2/test_co2.input b/test/implicit/co2/test_co2.input similarity index 100% rename from test/boxmodels/co2/test_co2.input rename to test/implicit/co2/test_co2.input diff --git a/test/boxmodels/co2ni/CMakeLists.txt b/test/implicit/co2ni/CMakeLists.txt similarity index 100% rename from test/boxmodels/co2ni/CMakeLists.txt rename to test/implicit/co2ni/CMakeLists.txt diff --git a/test/boxmodels/co2ni/Makefile.am b/test/implicit/co2ni/Makefile.am similarity index 100% rename from test/boxmodels/co2ni/Makefile.am rename to test/implicit/co2ni/Makefile.am diff --git a/test/boxmodels/co2ni/co2ni-reference.vtu b/test/implicit/co2ni/co2ni-reference.vtu similarity index 100% rename from test/boxmodels/co2ni/co2ni-reference.vtu rename to test/implicit/co2ni/co2ni-reference.vtu diff --git a/test/boxmodels/co2ni/co2values.inc b/test/implicit/co2ni/co2values.inc similarity index 100% rename from test/boxmodels/co2ni/co2values.inc rename to test/implicit/co2ni/co2values.inc diff --git a/test/boxmodels/co2ni/grids/heterogeneousSmall.dgf b/test/implicit/co2ni/grids/heterogeneousSmall.dgf similarity index 100% rename from test/boxmodels/co2ni/grids/heterogeneousSmall.dgf rename to test/implicit/co2ni/grids/heterogeneousSmall.dgf diff --git a/test/boxmodels/co2ni/heterogeneousco2tables.hh b/test/implicit/co2ni/heterogeneousco2tables.hh similarity index 100% rename from test/boxmodels/co2ni/heterogeneousco2tables.hh rename to test/implicit/co2ni/heterogeneousco2tables.hh diff --git a/test/boxmodels/co2ni/heterogeneousproblemni.hh b/test/implicit/co2ni/heterogeneousproblemni.hh similarity index 100% rename from test/boxmodels/co2ni/heterogeneousproblemni.hh rename to test/implicit/co2ni/heterogeneousproblemni.hh diff --git a/test/boxmodels/co2ni/heterogeneousspatialparametersni.hh b/test/implicit/co2ni/heterogeneousspatialparametersni.hh similarity index 100% rename from test/boxmodels/co2ni/heterogeneousspatialparametersni.hh rename to test/implicit/co2ni/heterogeneousspatialparametersni.hh diff --git a/test/boxmodels/co2ni/test_co2ni.cc b/test/implicit/co2ni/test_co2ni.cc similarity index 100% rename from test/boxmodels/co2ni/test_co2ni.cc rename to test/implicit/co2ni/test_co2ni.cc diff --git a/test/boxmodels/co2ni/test_co2ni.input b/test/implicit/co2ni/test_co2ni.input similarity index 100% rename from test/boxmodels/co2ni/test_co2ni.input rename to test/implicit/co2ni/test_co2ni.input diff --git a/test/boxmodels/mpnc/CMakeLists.txt b/test/implicit/mpnc/CMakeLists.txt similarity index 100% rename from test/boxmodels/mpnc/CMakeLists.txt rename to test/implicit/mpnc/CMakeLists.txt diff --git a/test/boxmodels/mpnc/Makefile.am b/test/implicit/mpnc/Makefile.am similarity index 100% rename from test/boxmodels/mpnc/Makefile.am rename to test/implicit/mpnc/Makefile.am diff --git a/test/boxmodels/mpnc/forchheimer1p-reference.vtp b/test/implicit/mpnc/forchheimer1p-reference.vtp similarity index 100% rename from test/boxmodels/mpnc/forchheimer1p-reference.vtp rename to test/implicit/mpnc/forchheimer1p-reference.vtp diff --git a/test/boxmodels/mpnc/forchheimer1pproblem.hh b/test/implicit/mpnc/forchheimer1pproblem.hh similarity index 100% rename from test/boxmodels/mpnc/forchheimer1pproblem.hh rename to test/implicit/mpnc/forchheimer1pproblem.hh diff --git a/test/boxmodels/mpnc/forchheimer2p-reference.vtu b/test/implicit/mpnc/forchheimer2p-reference.vtu similarity index 100% rename from test/boxmodels/mpnc/forchheimer2p-reference.vtu rename to test/implicit/mpnc/forchheimer2p-reference.vtu diff --git a/test/boxmodels/mpnc/forchheimer2pproblem.hh b/test/implicit/mpnc/forchheimer2pproblem.hh similarity index 100% rename from test/boxmodels/mpnc/forchheimer2pproblem.hh rename to test/implicit/mpnc/forchheimer2pproblem.hh diff --git a/test/boxmodels/mpnc/forchheimerspatialparams.hh b/test/implicit/mpnc/forchheimerspatialparams.hh similarity index 100% rename from test/boxmodels/mpnc/forchheimerspatialparams.hh rename to test/implicit/mpnc/forchheimerspatialparams.hh diff --git a/test/boxmodels/mpnc/grids/forchheimer1d.dgf b/test/implicit/mpnc/grids/forchheimer1d.dgf similarity index 100% rename from test/boxmodels/mpnc/grids/forchheimer1d.dgf rename to test/implicit/mpnc/grids/forchheimer1d.dgf diff --git a/test/boxmodels/mpnc/grids/obstacle_24x16.dgf b/test/implicit/mpnc/grids/obstacle_24x16.dgf similarity index 100% rename from test/boxmodels/mpnc/grids/obstacle_24x16.dgf rename to test/implicit/mpnc/grids/obstacle_24x16.dgf diff --git a/test/boxmodels/mpnc/grids/obstacle_48x32.dgf b/test/implicit/mpnc/grids/obstacle_48x32.dgf similarity index 100% rename from test/boxmodels/mpnc/grids/obstacle_48x32.dgf rename to test/implicit/mpnc/grids/obstacle_48x32.dgf diff --git a/test/boxmodels/mpnc/obstacle-reference.vtu b/test/implicit/mpnc/obstacle-reference.vtu similarity index 100% rename from test/boxmodels/mpnc/obstacle-reference.vtu rename to test/implicit/mpnc/obstacle-reference.vtu diff --git a/test/boxmodels/mpnc/obstacleproblem.hh b/test/implicit/mpnc/obstacleproblem.hh similarity index 100% rename from test/boxmodels/mpnc/obstacleproblem.hh rename to test/implicit/mpnc/obstacleproblem.hh diff --git a/test/boxmodels/mpnc/obstaclespatialparams.hh b/test/implicit/mpnc/obstaclespatialparams.hh similarity index 100% rename from test/boxmodels/mpnc/obstaclespatialparams.hh rename to test/implicit/mpnc/obstaclespatialparams.hh diff --git a/test/boxmodels/mpnc/test_forchheimer1p.cc b/test/implicit/mpnc/test_forchheimer1p.cc similarity index 100% rename from test/boxmodels/mpnc/test_forchheimer1p.cc rename to test/implicit/mpnc/test_forchheimer1p.cc diff --git a/test/boxmodels/mpnc/test_forchheimer1p.input b/test/implicit/mpnc/test_forchheimer1p.input similarity index 100% rename from test/boxmodels/mpnc/test_forchheimer1p.input rename to test/implicit/mpnc/test_forchheimer1p.input diff --git a/test/boxmodels/mpnc/test_forchheimer2p.cc b/test/implicit/mpnc/test_forchheimer2p.cc similarity index 100% rename from test/boxmodels/mpnc/test_forchheimer2p.cc rename to test/implicit/mpnc/test_forchheimer2p.cc diff --git a/test/boxmodels/mpnc/test_forchheimer2p.input b/test/implicit/mpnc/test_forchheimer2p.input similarity index 100% rename from test/boxmodels/mpnc/test_forchheimer2p.input rename to test/implicit/mpnc/test_forchheimer2p.input diff --git a/test/boxmodels/mpnc/test_mpnc.cc b/test/implicit/mpnc/test_mpnc.cc similarity index 100% rename from test/boxmodels/mpnc/test_mpnc.cc rename to test/implicit/mpnc/test_mpnc.cc diff --git a/test/boxmodels/mpnc/test_mpnc.input b/test/implicit/mpnc/test_mpnc.input similarity index 100% rename from test/boxmodels/mpnc/test_mpnc.input rename to test/implicit/mpnc/test_mpnc.input diff --git a/test/boxmodels/richards/CMakeLists.txt b/test/implicit/richards/CMakeLists.txt similarity index 100% rename from test/boxmodels/richards/CMakeLists.txt rename to test/implicit/richards/CMakeLists.txt diff --git a/test/boxmodels/richards/Makefile.am b/test/implicit/richards/Makefile.am similarity index 100% rename from test/boxmodels/richards/Makefile.am rename to test/implicit/richards/Makefile.am diff --git a/test/boxmodels/richards/grids/richardslens-24x16.dgf b/test/implicit/richards/grids/richardslens-24x16.dgf similarity index 100% rename from test/boxmodels/richards/grids/richardslens-24x16.dgf rename to test/implicit/richards/grids/richardslens-24x16.dgf diff --git a/test/boxmodels/richards/grids/richardslens-48x32.dgf b/test/implicit/richards/grids/richardslens-48x32.dgf similarity index 100% rename from test/boxmodels/richards/grids/richardslens-48x32.dgf rename to test/implicit/richards/grids/richardslens-48x32.dgf diff --git a/test/boxmodels/richards/grids/richardslens-96x64.dgf b/test/implicit/richards/grids/richardslens-96x64.dgf similarity index 100% rename from test/boxmodels/richards/grids/richardslens-96x64.dgf rename to test/implicit/richards/grids/richardslens-96x64.dgf diff --git a/test/boxmodels/richards/richardslens-reference-parallel.vtu b/test/implicit/richards/richardslens-reference-parallel.vtu similarity index 100% rename from test/boxmodels/richards/richardslens-reference-parallel.vtu rename to test/implicit/richards/richardslens-reference-parallel.vtu diff --git a/test/boxmodels/richards/richardslens-reference.vtu b/test/implicit/richards/richardslens-reference.vtu similarity index 100% rename from test/boxmodels/richards/richardslens-reference.vtu rename to test/implicit/richards/richardslens-reference.vtu diff --git a/test/boxmodels/richards/richardslensproblem.hh b/test/implicit/richards/richardslensproblem.hh similarity index 100% rename from test/boxmodels/richards/richardslensproblem.hh rename to test/implicit/richards/richardslensproblem.hh diff --git a/test/boxmodels/richards/richardslensspatialparams.hh b/test/implicit/richards/richardslensspatialparams.hh similarity index 100% rename from test/boxmodels/richards/richardslensspatialparams.hh rename to test/implicit/richards/richardslensspatialparams.hh diff --git a/test/boxmodels/richards/test_richards.cc b/test/implicit/richards/test_richards.cc similarity index 100% rename from test/boxmodels/richards/test_richards.cc rename to test/implicit/richards/test_richards.cc diff --git a/test/boxmodels/richards/test_richards.input b/test/implicit/richards/test_richards.input similarity index 100% rename from test/boxmodels/richards/test_richards.input rename to test/implicit/richards/test_richards.input -- GitLab